diff --git a/crates/shadowsocks-service/src/local/redir/mod.rs b/crates/shadowsocks-service/src/local/redir/mod.rs index 3374c4df..451f3796 100644 --- a/crates/shadowsocks-service/src/local/redir/mod.rs +++ b/crates/shadowsocks-service/src/local/redir/mod.rs @@ -1,5 +1,7 @@ //! Shadowsocks Local Transparent Proxy +use std::net::{Ipv4Addr, Ipv6Addr}; + pub use self::server::Redir; mod redir_ext; @@ -7,3 +9,13 @@ mod server; mod sys; mod tcprelay; mod udprelay; + +/// Helper function for converting IPv4 mapped IPv6 address +/// +/// This is the same as `Ipv6Addr::to_ipv4_mapped`, but it is still unstable in the current libstd +fn to_ipv4_mapped(ipv6: &Ipv6Addr) -> Option { + match ipv6.octets() { + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => Some(Ipv4Addr::new(a, b, c, d)), + _ => None, + } +} diff --git a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs index 0b5d6dcc..5072cf67 100644 --- a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs +++ b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs @@ -1,6 +1,11 @@ //! Shadowsocks TCP transparent proxy -use std::{io, net::SocketAddr, sync::Arc, time::Duration}; +use std::{ + io, + net::{IpAddr, SocketAddr}, + sync::Arc, + time::Duration, +}; use log::{debug, error, info, trace}; use shadowsocks::{lookup_then, net::TcpListener as ShadowTcpListener, relay::socks5::Address}; @@ -15,7 +20,10 @@ use crate::{ context::ServiceContext, loadbalancing::PingBalancer, net::{AutoProxyClientStream, AutoProxyIo}, - redir::redir_ext::{TcpListenerRedirExt, TcpStreamRedirExt}, + redir::{ + redir_ext::{TcpListenerRedirExt, TcpStreamRedirExt}, + to_ipv4_mapped, + }, utils::establish_tcp_tunnel, }, }; @@ -74,7 +82,7 @@ async fn handle_redir_client( balancer: PingBalancer, s: TcpStream, peer_addr: SocketAddr, - daddr: SocketAddr, + mut daddr: SocketAddr, nodelay: bool, ) -> io::Result<()> { // let svr_cfg = server.server_config(); @@ -90,6 +98,13 @@ async fn handle_redir_client( } // Get forward address from socket + // + // Try to convert IPv4 mapped IPv6 address for dual-stack mode. + if let SocketAddr::V6(ref a) = daddr { + if let Some(v4) = to_ipv4_mapped(a.ip()) { + daddr = SocketAddr::new(IpAddr::from(v4), a.port()); + } + } let target_addr = Address::from(daddr); establish_client_tcp_redir(context, balancer, s, peer_addr, &target_addr, nodelay).await } diff --git a/crates/shadowsocks-service/src/local/redir/udprelay/mod.rs b/crates/shadowsocks-service/src/local/redir/udprelay/mod.rs index 1e39bbdd..6c2097df 100644 --- a/crates/shadowsocks-service/src/local/redir/udprelay/mod.rs +++ b/crates/shadowsocks-service/src/local/redir/udprelay/mod.rs @@ -2,7 +2,7 @@ use std::{ io::{self, ErrorKind}, - net::SocketAddr, + net::{IpAddr, SocketAddr}, sync::Arc, time::Duration, }; @@ -20,7 +20,10 @@ use crate::{ context::ServiceContext, loadbalancing::PingBalancer, net::{UdpAssociationManager, UdpInboundWrite}, - redir::redir_ext::{RedirSocketOpts, UdpSocketRedirExt}, + redir::{ + redir_ext::{RedirSocketOpts, UdpSocketRedirExt}, + to_ipv4_mapped, + }, }, }; @@ -38,7 +41,16 @@ struct UdpRedirInboundWriter { impl UdpInboundWrite for UdpRedirInboundWriter { async fn send_to(&self, peer_addr: SocketAddr, remote_addr: &Address, data: &[u8]) -> io::Result<()> { let addr = match *remote_addr { - Address::SocketAddress(sa) => sa, + Address::SocketAddress(sa) => { + // Try to convert IPv4 mapped IPv6 address if server is running on dual-stack mode + match sa { + SocketAddr::V4(..) => sa, + SocketAddr::V6(ref v6) => match to_ipv4_mapped(v6.ip()) { + Some(v4) => SocketAddr::new(IpAddr::from(v4), v6.port()), + None => sa, + }, + } + } Address::DomainNameAddress(..) => { let err = io::Error::new( ErrorKind::InvalidInput, @@ -132,7 +144,7 @@ impl UdpRedir { let mut pkt_buf = [0u8; MAXIMUM_UDP_PAYLOAD_SIZE]; loop { - let (recv_len, src, dst) = match listener.recv_dest_from(&mut pkt_buf).await { + let (recv_len, src, mut dst) = match listener.recv_dest_from(&mut pkt_buf).await { Ok(o) => o, Err(err) => { error!("recv_dest_from failed with err: {}", err); @@ -162,6 +174,13 @@ impl UdpRedir { continue; } + // Try to convert IPv4 mapped IPv6 address for dual-stack mode. + if let SocketAddr::V6(ref a) = dst { + if let Some(v4) = to_ipv4_mapped(a.ip()) { + dst = SocketAddr::new(IpAddr::from(v4), a.port()); + } + } + if let Err(err) = manager.send_to(src, Address::from(dst), pkt).await { error!( "udp packet relay {} -> {} with {} bytes failed, error: {}",