Skip to content

Commit

Permalink
fix(tuic): not connecting on windows (#605)
Browse files Browse the repository at this point in the history
  • Loading branch information
ibigbug authored Sep 23, 2024
1 parent 0f353a8 commit 168a225
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 23 deletions.
26 changes: 9 additions & 17 deletions clash_lib/src/proxy/tuic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,8 @@ use rustls::client::ClientConfig as TlsConfig;
use self::types::{CongestionControl, TuicConnection, UdpRelayMode, UdpSession};

use super::{
datagram::UdpPacket,
utils::{get_outbound_interface, Interface},
ConnectorType, HandlerCommonOptions, OutboundHandler, OutboundType,
datagram::UdpPacket, ConnectorType, HandlerCommonOptions, OutboundHandler,
OutboundType,
};

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -165,7 +164,7 @@ impl Handler {
async fn init_endpoint(
opts: HandlerOptions,
resolver: ThreadSafeDNSResolver,
#[cfg(any(target_os = "linux", target_os = "android"))] so_mark: Option<u32>,
sess: &Session,
) -> Result<TuicEndpoint> {
let mut crypto =
TlsConfig::builder_with_protocol_versions(&[&rustls::version::TLS13])
Expand Down Expand Up @@ -199,22 +198,20 @@ impl Handler {
quinn_config.transport_config(Arc::new(transport_config));

let socket = {
let iface = get_outbound_interface();

if resolver.ipv6() {
new_udp_socket(
Some((Ipv6Addr::UNSPECIFIED, 0).into()),
iface.map(|x| Interface::Name(x.name.clone())),
sess.iface.clone(),
#[cfg(any(target_os = "linux", target_os = "android"))]
so_mark,
sess.so_mark,
)
.await?
} else {
new_udp_socket(
Some((Ipv4Addr::UNSPECIFIED, 0).into()),
iface.map(|x| Interface::Name(x.name.clone())),
sess.iface.clone(),
#[cfg(any(target_os = "linux", target_os = "android"))]
so_mark,
sess.so_mark,
)
.await?
}
Expand Down Expand Up @@ -250,17 +247,12 @@ impl Handler {
async fn get_conn(
&self,
resolver: &ThreadSafeDNSResolver,
#[allow(unused)] sess: &Session, // linux only
sess: &Session,
) -> Result<Arc<TuicConnection>> {
let endpoint = self
.ep
.get_or_try_init(|| {
Self::init_endpoint(
self.opts.clone(),
resolver.clone(),
#[cfg(any(target_os = "linux", target_os = "android"))]
sess.so_mark,
)
Self::init_endpoint(self.opts.clone(), resolver.clone(), sess)
})
.await?;

Expand Down
43 changes: 37 additions & 6 deletions clash_lib/src/proxy/utils/platform/win.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use network_interface::NetworkInterfaceConfig;
use std::{io, net::SocketAddr, os::windows::io::AsRawSocket};
use tracing::{error, trace, warn};
use tracing::{debug, error, warn};
use windows::Win32::{
Foundation::GetLastError,
Networking::WinSock::{
Expand All @@ -19,17 +19,18 @@ pub(crate) fn must_bind_socket_on_interface(
Interface::IpAddr(ip) => socket.bind(&SocketAddr::new(*ip, 0).into()),
Interface::Name(name) => {
// TODO: we should avoid calling `show` multiple times
let idx = network_interface::NetworkInterface::show()
let iface = network_interface::NetworkInterface::show()
.map_err(|x| new_io_error(x.to_string().as_str()))?
.into_iter()
.find_map(|iface| {
if &iface.name == name {
Some(iface.index)
Some(iface)
} else {
None
}
})
.unwrap_or_default();
});

let idx = iface.as_ref().map(|iface| iface.index).unwrap_or_default();
if idx == 0 {
warn!("failed to get interface index for {}", name);
return Err(io::Error::new(
Expand All @@ -38,7 +39,37 @@ pub(crate) fn must_bind_socket_on_interface(
));
}

trace!("interface {} index is {}", name, idx);
let ip = match iface {
Some(iface) => iface.addr.iter().find_map(|addr| {
if family == socket2::Domain::IPV4 {
if addr.ip().is_ipv4() {
Some(addr.ip())
} else {
None
}
} else if addr.ip().is_ipv6() {
Some(addr.ip())
} else {
None
}
}),
None => None,
};

debug!(
"binding socket to interface: {}, index {}, ip {:?}",
name, idx, ip
);

if let Some(ip) = ip {
socket.bind(&SocketAddr::new(ip, 0).into())?;
} else {
warn!("failed to get address for interface {}", name);
return Err(io::Error::new(
io::ErrorKind::Other,
format!("failed to get address for interface {}", name),
));
}

let handle = SOCKET(socket.as_raw_socket().try_into().unwrap());

Expand Down

0 comments on commit 168a225

Please sign in to comment.