Skip to content

Commit 90ae706

Browse files
committed
UDP outbound socket unified to one IPv6 socket
Older implementation uses 2 separated sockets for sending IPv4 and IPv6 packets, so one client may be mapped to 2 different outbound addresses. IPv6 UDP socket is capable to send packets to IPv4 targets, just use IPv4-mapped-IPv6 addresses as target addresses. NOTE: OpenBSD's IPv6 socket are always IPv6-only. https://man.openbsd.org/OpenBSD-current/ip6#IPV6_V6ONLY
1 parent 0685e4a commit 90ae706

File tree

2 files changed

+96
-30
lines changed

2 files changed

+96
-30
lines changed

crates/shadowsocks-service/src/local/net/udp/association.rs

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{
44
cell::RefCell,
55
io::{self, ErrorKind},
66
marker::PhantomData,
7-
net::SocketAddr,
7+
net::{SocketAddr, SocketAddrV6},
88
sync::Arc,
99
time::Duration,
1010
};
@@ -19,7 +19,7 @@ use tokio::{sync::mpsc, task::JoinHandle, time};
1919

2020
use shadowsocks::{
2121
lookup_then,
22-
net::UdpSocket as ShadowUdpSocket,
22+
net::{AddrFamily, UdpSocket as ShadowUdpSocket},
2323
relay::{
2424
udprelay::{options::UdpSocketControlData, ProxySocket, MAXIMUM_UDP_PAYLOAD_SIZE},
2525
Address,
@@ -466,26 +466,59 @@ where
466466
}
467467
}
468468

469-
async fn send_received_bypassed_packet(&mut self, target_addr: SocketAddr, data: &[u8]) -> io::Result<()> {
470-
let socket = match target_addr {
471-
SocketAddr::V4(..) => match self.bypassed_ipv4_socket {
469+
async fn send_received_bypassed_packet(&mut self, mut target_addr: SocketAddr, data: &[u8]) -> io::Result<()> {
470+
const UDP_SOCKET_SUPPORT_DUAL_STACK: bool = cfg!(any(
471+
target_os = "linux",
472+
target_os = "android",
473+
target_os = "macos",
474+
target_os = "ios",
475+
target_os = "watchos",
476+
target_os = "tvos",
477+
target_os = "freebsd",
478+
target_os = "dragonfly",
479+
target_os = "netbsd",
480+
));
481+
482+
let socket = if UDP_SOCKET_SUPPORT_DUAL_STACK {
483+
match self.bypassed_ipv6_socket {
472484
Some(ref mut socket) => socket,
473485
None => {
474486
let socket =
475-
ShadowUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?;
476-
self.bypassed_ipv4_socket.insert(socket)
477-
}
478-
},
479-
SocketAddr::V6(..) => match self.bypassed_ipv6_socket {
480-
Some(ref mut socket) => socket,
481-
None => {
482-
let socket =
483-
ShadowUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?;
487+
ShadowUdpSocket::connect_any_with_opts(AddrFamily::Ipv6, self.context.connect_opts_ref())
488+
.await?;
484489
self.bypassed_ipv6_socket.insert(socket)
485490
}
486-
},
491+
}
492+
} else {
493+
match target_addr {
494+
SocketAddr::V4(..) => match self.bypassed_ipv4_socket {
495+
Some(ref mut socket) => socket,
496+
None => {
497+
let socket =
498+
ShadowUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref())
499+
.await?;
500+
self.bypassed_ipv4_socket.insert(socket)
501+
}
502+
},
503+
SocketAddr::V6(..) => match self.bypassed_ipv6_socket {
504+
Some(ref mut socket) => socket,
505+
None => {
506+
let socket =
507+
ShadowUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref())
508+
.await?;
509+
self.bypassed_ipv6_socket.insert(socket)
510+
}
511+
},
512+
}
487513
};
488514

515+
if UDP_SOCKET_SUPPORT_DUAL_STACK {
516+
if let SocketAddr::V4(saddr) = target_addr {
517+
let mapped_ip = saddr.ip().to_ipv6_mapped();
518+
target_addr = SocketAddr::V6(SocketAddrV6::new(mapped_ip, saddr.port(), 0, 0));
519+
}
520+
}
521+
489522
let n = socket.send_to(data, target_addr).await?;
490523
if n != data.len() {
491524
warn!(

crates/shadowsocks-service/src/server/udprelay.rs

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::{
44
cell::RefCell,
55
io::{self, ErrorKind},
6-
net::SocketAddr,
6+
net::{SocketAddr, SocketAddrV6},
77
sync::Arc,
88
time::Duration,
99
};
@@ -16,7 +16,7 @@ use rand::{rngs::SmallRng, Rng, SeedableRng};
1616
use shadowsocks::{
1717
crypto::{CipherCategory, CipherKind},
1818
lookup_then,
19-
net::{AcceptOpts, UdpSocket as OutboundUdpSocket},
19+
net::{AcceptOpts, AddrFamily, UdpSocket as OutboundUdpSocket},
2020
relay::{
2121
socks5::Address,
2222
udprelay::{options::UdpSocketControlData, ProxySocket, MAXIMUM_UDP_PAYLOAD_SIZE},
@@ -646,26 +646,59 @@ impl UdpAssociationContext {
646646
}
647647
}
648648

649-
async fn send_received_outbound_packet(&mut self, target_addr: SocketAddr, data: &[u8]) -> io::Result<()> {
650-
let socket = match target_addr {
651-
SocketAddr::V4(..) => match self.outbound_ipv4_socket {
649+
async fn send_received_outbound_packet(&mut self, mut target_addr: SocketAddr, data: &[u8]) -> io::Result<()> {
650+
const UDP_SOCKET_SUPPORT_DUAL_STACK: bool = cfg!(any(
651+
target_os = "linux",
652+
target_os = "android",
653+
target_os = "macos",
654+
target_os = "ios",
655+
target_os = "watchos",
656+
target_os = "tvos",
657+
target_os = "freebsd",
658+
target_os = "dragonfly",
659+
target_os = "netbsd",
660+
));
661+
662+
let socket = if UDP_SOCKET_SUPPORT_DUAL_STACK {
663+
match self.outbound_ipv6_socket {
652664
Some(ref mut socket) => socket,
653665
None => {
654666
let socket =
655-
OutboundUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?;
656-
self.outbound_ipv4_socket.insert(socket)
657-
}
658-
},
659-
SocketAddr::V6(..) => match self.outbound_ipv6_socket {
660-
Some(ref mut socket) => socket,
661-
None => {
662-
let socket =
663-
OutboundUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref()).await?;
667+
OutboundUdpSocket::connect_any_with_opts(AddrFamily::Ipv6, self.context.connect_opts_ref())
668+
.await?;
664669
self.outbound_ipv6_socket.insert(socket)
665670
}
666-
},
671+
}
672+
} else {
673+
match target_addr {
674+
SocketAddr::V4(..) => match self.outbound_ipv4_socket {
675+
Some(ref mut socket) => socket,
676+
None => {
677+
let socket =
678+
OutboundUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref())
679+
.await?;
680+
self.outbound_ipv4_socket.insert(socket)
681+
}
682+
},
683+
SocketAddr::V6(..) => match self.outbound_ipv6_socket {
684+
Some(ref mut socket) => socket,
685+
None => {
686+
let socket =
687+
OutboundUdpSocket::connect_any_with_opts(&target_addr, self.context.connect_opts_ref())
688+
.await?;
689+
self.outbound_ipv6_socket.insert(socket)
690+
}
691+
},
692+
}
667693
};
668694

695+
if UDP_SOCKET_SUPPORT_DUAL_STACK {
696+
if let SocketAddr::V4(saddr) = target_addr {
697+
let mapped_ip = saddr.ip().to_ipv6_mapped();
698+
target_addr = SocketAddr::V6(SocketAddrV6::new(mapped_ip, saddr.port(), 0, 0));
699+
}
700+
}
701+
669702
let n = socket.send_to(data, target_addr).await?;
670703
if n != data.len() {
671704
warn!(

0 commit comments

Comments
 (0)