Skip to content

Commit eddd439

Browse files
committed
fix(local-redir): EINVAL if platform doesn't support IPv4-mapped-IPv6
- ref #1543
1 parent ac67a08 commit eddd439

File tree

1 file changed

+110
-101
lines changed
  • crates/shadowsocks-service/src/local/redir/udprelay

1 file changed

+110
-101
lines changed

crates/shadowsocks-service/src/local/redir/udprelay/mod.rs

Lines changed: 110 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -101,124 +101,133 @@ impl UdpInboundWrite for UdpRedirInboundWriter {
101101
// then we should always use IPv6 sockets for sending IPv4 packets.
102102
static SUPPORT_IPV6_TRANSPARENT: AtomicBool = AtomicBool::new(true);
103103

104-
#[allow(unused_mut)]
105-
let mut addr = match *remote_addr {
106-
Address::SocketAddress(sa) => {
107-
if SUPPORT_IPV6_TRANSPARENT.load(Ordering::Relaxed) {
108-
match sa {
109-
// Converts IPv4 address to IPv4-mapped-IPv6
110-
// All sockets will be created in IPv6 (nearly all modern OS supports IPv6 sockets)
111-
SocketAddr::V4(ref v4) => SocketAddr::new(v4.ip().to_ipv6_mapped().into(), v4.port()),
112-
SocketAddr::V6(..) => sa,
113-
}
114-
} else {
115-
match sa {
116-
// Converts IPv4-mapped-IPv6 to IPv4
117-
SocketAddr::V4(..) => sa,
118-
SocketAddr::V6(ref v6) => match v6.ip().to_ipv4_mapped() {
119-
Some(v4) => SocketAddr::new(v4.into(), v6.port()),
120-
None => sa,
121-
},
104+
loop {
105+
let mut addr_mapped_ipv6 = false;
106+
107+
let addr = match *remote_addr {
108+
Address::SocketAddress(sa) => {
109+
if SUPPORT_IPV6_TRANSPARENT.load(Ordering::Relaxed) {
110+
match sa {
111+
// Converts IPv4 address to IPv4-mapped-IPv6
112+
// All sockets will be created in IPv6 (nearly all modern OS supports IPv6 sockets)
113+
SocketAddr::V4(ref v4) => {
114+
addr_mapped_ipv6 = true;
115+
SocketAddr::new(v4.ip().to_ipv6_mapped().into(), v4.port())
116+
}
117+
SocketAddr::V6(..) => sa,
118+
}
119+
} else {
120+
match sa {
121+
// Converts IPv4-mapped-IPv6 to IPv4
122+
SocketAddr::V4(..) => sa,
123+
SocketAddr::V6(ref v6) => match v6.ip().to_ipv4_mapped() {
124+
Some(v4) => SocketAddr::new(v4.into(), v6.port()),
125+
None => sa,
126+
},
127+
}
122128
}
123129
}
124-
}
125-
Address::DomainNameAddress(..) => {
126-
let err = io::Error::new(
127-
ErrorKind::InvalidInput,
128-
"redir destination must not be an domain name address",
129-
);
130-
return Err(err);
131-
}
132-
};
133-
134-
let inbound = {
135-
let mut cache = self.inbound_cache.cache.lock().await;
136-
if let Some(socket) = cache.get(&addr) {
137-
socket.clone()
138-
} else {
139-
// Create a socket binds to destination addr
140-
// This only works for systems that supports binding to non-local addresses
141-
//
142-
// This socket has to set SO_REUSEADDR and SO_REUSEPORT.
143-
// Outbound addresses could be connected from different source addresses.
144-
let inbound = match UdpRedirSocket::bind_nonlocal(self.redir_ty, addr, &self.socket_opts) {
145-
Ok(s) => s,
146-
#[cfg(unix)]
147-
Err(err) => match err.raw_os_error() {
148-
None => return Err(err),
149-
// https://github.com/shadowsocks/shadowsocks-rust/issues/988
150-
// IPV6_TRANSPARENT was supported since 2.6.37.
151-
Some(libc::ENOPROTOOPT) if addr.is_ipv6() => {
152-
SUPPORT_IPV6_TRANSPARENT.store(false, Ordering::Relaxed);
130+
Address::DomainNameAddress(..) => {
131+
let err = io::Error::new(
132+
ErrorKind::InvalidInput,
133+
"redir destination must not be an domain name address",
134+
);
135+
return Err(err);
136+
}
137+
};
153138

154-
addr = match *remote_addr {
155-
Address::SocketAddress(sa) => {
156-
match sa {
157-
// Converts IPv4-mapped-IPv6 to IPv4
158-
SocketAddr::V4(..) => sa,
159-
SocketAddr::V6(ref v6) => match v6.ip().to_ipv4_mapped() {
160-
Some(v4) => SocketAddr::new(v4.into(), v6.port()),
161-
None => return Err(err),
162-
},
163-
}
164-
}
165-
Address::DomainNameAddress(..) => unreachable!(),
166-
};
167-
168-
UdpRedirSocket::bind_nonlocal(self.redir_ty, addr, &self.socket_opts)?
169-
}
170-
Some(_) => return Err(err),
171-
},
172-
#[cfg(not(unix))]
173-
Err(err) => return Err(err),
174-
};
139+
let inbound = {
140+
let mut cache = self.inbound_cache.cache.lock().await;
141+
if let Some(socket) = cache.get(&addr) {
142+
socket.clone()
143+
} else {
144+
// Create a socket binds to destination addr
145+
// This only works for systems that supports binding to non-local addresses
146+
//
147+
// This socket has to set SO_REUSEADDR and SO_REUSEPORT.
148+
// Outbound addresses could be connected from different source addresses.
149+
let inbound = match UdpRedirSocket::bind_nonlocal(self.redir_ty, addr, &self.socket_opts) {
150+
Ok(s) => s,
151+
#[cfg(unix)]
152+
Err(err) => match err.raw_os_error() {
153+
None => return Err(err),
154+
// https://github.com/shadowsocks/shadowsocks-rust/issues/988
155+
// IPV6_TRANSPARENT was supported since 2.6.37.
156+
Some(libc::ENOPROTOOPT) if addr_mapped_ipv6 => {
157+
SUPPORT_IPV6_TRANSPARENT.store(false, Ordering::Relaxed);
158+
debug!("redir destination socket doesn't support IPv6, addr cannot be IPv4-mapped-IPv6: {}", addr);
159+
continue;
160+
}
161+
Some(_) => return Err(err),
162+
},
163+
#[cfg(not(unix))]
164+
Err(err) => return Err(err),
165+
};
175166

176-
// UDP socket could be shared between threads and is safe to be manipulated by multiple threads
177-
let inbound = Arc::new(inbound);
178-
cache.insert(addr, inbound.clone());
167+
// UDP socket could be shared between threads and is safe to be manipulated by multiple threads
168+
let inbound = Arc::new(inbound);
169+
cache.insert(addr, inbound.clone());
179170

180-
inbound
171+
inbound
172+
}
173+
};
174+
175+
match (addr, peer_addr) {
176+
(SocketAddr::V4(..), SocketAddr::V4(..)) | (SocketAddr::V6(..), SocketAddr::V6(..)) => {}
177+
(SocketAddr::V4(..), SocketAddr::V6(v6_peer_addr)) => {
178+
if let Some(v4_ip) = v6_peer_addr.ip().to_ipv4_mapped() {
179+
peer_addr = SocketAddr::new(v4_ip.into(), v6_peer_addr.port());
180+
} else {
181+
warn!(
182+
"udp redir send back {} bytes, remote: {}, peer: {}, protocol not match",
183+
data.len(),
184+
addr,
185+
peer_addr
186+
);
187+
}
188+
}
189+
(SocketAddr::V6(..), SocketAddr::V4(v4_peer_addr)) => {
190+
peer_addr = SocketAddr::new(v4_peer_addr.ip().to_ipv6_mapped().into(), v4_peer_addr.port());
191+
}
181192
}
182-
};
183193

184-
match (addr, peer_addr) {
185-
(SocketAddr::V4(..), SocketAddr::V4(..)) | (SocketAddr::V6(..), SocketAddr::V6(..)) => {}
186-
(SocketAddr::V4(..), SocketAddr::V6(v6_peer_addr)) => {
187-
if let Some(v4_ip) = v6_peer_addr.ip().to_ipv4_mapped() {
188-
peer_addr = SocketAddr::new(v4_ip.into(), v6_peer_addr.port());
189-
} else {
194+
let send_result = inbound.send_to(data, peer_addr).await.map(|n| {
195+
if n < data.len() {
190196
warn!(
191-
"udp redir send back {} bytes, remote: {}, peer: {}, protocol not match",
197+
"udp redir send back data (actual: {} bytes, sent: {} bytes), remote: {}, peer: {}",
198+
n,
192199
data.len(),
193-
addr,
200+
remote_addr,
194201
peer_addr
195202
);
196203
}
197-
}
198-
(SocketAddr::V6(..), SocketAddr::V4(v4_peer_addr)) => {
199-
peer_addr = SocketAddr::new(v4_peer_addr.ip().to_ipv6_mapped().into(), v4_peer_addr.port());
200-
}
201-
}
202204

203-
inbound.send_to(data, peer_addr).await.map(|n| {
204-
if n < data.len() {
205-
warn!(
206-
"udp redir send back data (actual: {} bytes, sent: {} bytes), remote: {}, peer: {}",
205+
trace!(
206+
"udp redir send back data {} bytes, remote: {}, peer: {}, socket_opts: {:?}",
207207
n,
208-
data.len(),
209208
remote_addr,
210-
peer_addr
209+
peer_addr,
210+
self.socket_opts
211211
);
212+
});
213+
214+
match send_result {
215+
Ok(()) => return Ok(()),
216+
Err(err) => {
217+
match err.kind() {
218+
// Invalid Argument
219+
ErrorKind::InvalidInput if addr_mapped_ipv6 => {
220+
SUPPORT_IPV6_TRANSPARENT.store(false, Ordering::Relaxed);
221+
debug!(
222+
"redir destination socket doesn't support IPv6, addr cannot be IPv4-mapped-IPv6: {}",
223+
addr
224+
);
225+
}
226+
_ => return Err(err),
227+
}
228+
}
212229
}
213-
214-
trace!(
215-
"udp redir send back data {} bytes, remote: {}, peer: {}, socket_opts: {:?}",
216-
n,
217-
remote_addr,
218-
peer_addr,
219-
self.socket_opts
220-
);
221-
})
230+
}
222231
}
223232
}
224233

0 commit comments

Comments
 (0)