redir local converts IPv4-mapped IPv6 addresses to IPv4

- Dual-stack IPv6 will always return destination addresses in IPv4-mapped
  IPv6 addresses
This commit is contained in:
zonyitoo
2021-03-12 01:06:35 +08:00
parent c2a6c20715
commit b632d35689
3 changed files with 53 additions and 7 deletions

View File

@@ -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<Ipv4Addr> {
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,
}
}

View File

@@ -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
}

View File

@@ -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: {}",