diff --git a/Cargo.lock b/Cargo.lock index 9c93f09..c6948f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -524,6 +524,16 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -698,6 +708,23 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "default-net" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c5a6569a908354d49b10db3c516d69aca1eccd97562fd31c98b13f00b73ca66" +dependencies = [ + "dlopen2", + "libc", + "memalloc", + "netlink-packet-core", + "netlink-packet-route", + "netlink-sys", + "once_cell", + "system-configuration", + "windows 0.48.0", +] + [[package]] name = "der" version = "0.7.10" @@ -772,6 +799,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "dlopen2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" +dependencies = [ + "libc", + "once_cell", + "winapi", +] + [[package]] name = "dns-lookup" version = "2.1.1" @@ -1694,6 +1732,12 @@ dependencies = [ "digest", ] +[[package]] +name = "memalloc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1" + [[package]] name = "memchr" version = "2.8.0" @@ -1761,6 +1805,54 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "netlink-packet-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror 1.0.69", +] + +[[package]] +name = "netlink-sys" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae" +dependencies = [ + "bytes", + "libc", + "log", +] + [[package]] name = "nom" version = "7.1.3" @@ -2153,6 +2245,7 @@ dependencies = [ "crc32fast", "daemonize", "dashmap 6.1.0", + "default-net", "dns-lookup", "etherparse", "futures-util", @@ -2179,11 +2272,13 @@ dependencies = [ "serde", "serde_json", "simple-dns", + "socket2", "structopt", "tokio", "tokio-util", "tracing", "winapi", + "windows-sys 0.61.2", "wintun", ] @@ -2575,7 +2670,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" dependencies = [ - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "jni", "log", @@ -2676,7 +2771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags 2.11.0", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3193,6 +3288,27 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tar" version = "0.4.45" @@ -3922,6 +4038,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows" version = "0.52.0" @@ -4322,7 +4447,7 @@ dependencies = [ "libloading", "log", "thiserror 1.0.69", - "windows", + "windows 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e2b6763..842c306 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,8 @@ ipnet = "2.12.0" arc-swap = "1.9.0" rustls-native-certs = "0.8.3" simple-dns = "0.11.2" +default-net = "0.22.0" +socket2 = "0.6.3" # rolling-file = { path = "../rolling-file" } [target.'cfg(unix)'.dependencies] @@ -54,6 +56,7 @@ daemonize = "0.5.0" [target.'cfg(windows)'.dependencies] wintun = "0.4.0" winapi = "0.3.9" +windows-sys = "0.61.2" [features] tun = [] diff --git a/src/network/node.rs b/src/network/node.rs index cb7973a..ef3341d 100755 --- a/src/network/node.rs +++ b/src/network/node.rs @@ -1,10 +1,10 @@ use arc_swap::ArcSwap; use dashmap::DashMap; +use default_net::Interface; use prost::Message; use quinn::Endpoint; use rsa::RsaPrivateKey; use sdlan_sn_rs::config::{AF_INET, AF_INET6}; -use tokio::net::UdpSocket; use std::any::Any; use std::future::Future; use std::net::SocketAddr; @@ -12,17 +12,22 @@ use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicU8, Ordering}; use std::sync::{Arc, Mutex, RwLock}; use std::time::Duration; use tokio::io::AsyncReadExt; +use tokio::net::UdpSocket; use tokio::sync::mpsc::Sender; use tracing::{debug, error, warn}; use crate::network::{ArpTable, RouteTable2}; -use crate::quic::quic_init; -use crate::{CommandLine, ConnectionInfo, DNSMatcher, MyEncryptor, RuleCache, get_base_dir}; use crate::pb::{ - SdlArpRequest, SdlEmpty, SdlStunProbe, SdlStunProbeReply, encode_to_tcp_message, encode_to_udp_message + encode_to_tcp_message, encode_to_udp_message, SdlArpRequest, SdlEmpty, SdlStunProbe, + SdlStunProbeReply, +}; +use crate::quic::quic_init; +use crate::tcp::{get_quic_write_conn, NatType, PacketType, StunProbeAttr}; +use crate::utils::Socket; +use crate::{ + get_base_dir, get_default_interface, CommandLine, ConnectionInfo, DNSMatcher, MyEncryptor, + RuleCache, }; -use crate::tcp::{NatType, PacketType, StunProbeAttr, get_quic_write_conn}; -use crate::utils::{Socket}; use sdlan_sn_rs::peer::{IpSubnet, V6Info}; @@ -32,7 +37,7 @@ use super::device::{DeviceConfig, Mode}; use super::tun::{new_iface, Iface}; use tokio::fs::File; -use sdlan_sn_rs::utils::{Mac, gen_rsa_keys, load_private_key_file, save_to_file}; +use sdlan_sn_rs::utils::{gen_rsa_keys, load_private_key_file, save_to_file, Mac}; use sdlan_sn_rs::utils::{Result, SDLanError}; static EDGE: OnceCell = OnceCell::new(); @@ -51,7 +56,7 @@ pub async fn init_edge( udp_sock_for_global_dns: Arc, hostname: String, server_ip: String, - install_channel: String + install_channel: String, ) -> Result<()> { // gen public key let rsa_path = format!("{}/.client", get_base_dir()); @@ -69,13 +74,33 @@ pub async fn init_edge( // let edge_uuid = create_or_load_uuid("")?; //let node_conf = parse_config(edge_uuid, &args).await?; - let Ok(sock_v4) = Socket::build(node_conf._local_port, true, false, args.tos).await else { + let default_interface = match get_default_interface() { + Ok(interface) => { + println!("get default interface: {:?}", interface); + Some(interface) + } + Err(e) => { + println!("failed to get default net interface: {}", e.as_str()); + None + } + }; + + let Ok(sock_v4) = Socket::build( + node_conf._local_port, + true, + false, + args.tos, + &default_interface, + ) + .await + else { panic!("failed to build sock for sock v4"); }; let mut sock_multicast = None; if !node_conf._drop_multicast { - sock_multicast = Some(Socket::build(MULTICAST_PORT, true, true, 0).await?); + sock_multicast = + Some(Socket::build(MULTICAST_PORT, true, true, 0, &default_interface).await?); } // let sock_multicast = Socket::build(MULTICAST_PORT, true, true, 0).await?; // allow multicast @@ -88,6 +113,7 @@ pub async fn init_edge( pubkey, node_conf, sock_v4, + &default_interface, sock_multicast, // token, // network_code, @@ -103,7 +129,8 @@ pub async fn init_edge( install_channel, ); - edge.route_table.parse_and_add_route(&args.route_file, &args.route_str); + edge.route_table + .parse_and_add_route(&args.route_file, &args.route_str); let matcher = Arc::clone(&edge.dns_matcher); do_init_edge(edge)?; @@ -147,7 +174,7 @@ pub struct StartStopInfo { pub struct StringToken(RwLock); -impl StringToken { +impl StringToken { pub fn new(value: T) -> Self { Self(RwLock::new(value)) } @@ -195,7 +222,7 @@ pub struct Node { pub access_token: StringToken, pub session_token: StringToken>, - + pub hostname: RwLock, pub quic_endpoint: Endpoint, @@ -214,7 +241,6 @@ pub struct Node { // user token info // pub _token: Mutex, // pub network_code: Mutex, - pub device_config: DeviceConfig, pub device: Iface, @@ -222,7 +248,6 @@ pub struct Node { pub authorized: AtomicBool, // pub header_key: RwLock>>, // pub encrypt_key: RwLock>>, - pub rsa_pubkey: String, pub rsa_private: RsaPrivateKey, @@ -262,7 +287,6 @@ pub struct Node { //cookie_match: DashMap>, pub cookie_match: Queryer, - // packet_id_match: DashMap>, } @@ -278,14 +302,14 @@ impl Node { } pub async fn start_without_feedback( - &self, - access_token: String, + &self, + access_token: String, network_id: u32, network_domain: &String, ip_net: u32, ip_net_bit_len: u8, - identity_id: u32, - hostname: Option + identity_id: u32, + hostname: Option, ) -> Result<()> { if let Some(host) = hostname { let idfile = format!("{}/.host", get_base_dir()); @@ -355,25 +379,30 @@ impl Node { // *self.network_code.lock().unwrap() = network_code; let id = self.get_next_packet_id(); - let res = self.cookie_match.do_action_and_wait_for( - 0, - || async { - let _ = self - .start_stop_sender - .send(StartStopInfo { - is_start: true, - pkt_id: Some(id), - }) - .await; - debug!("start with feedback"); - }, - timeout - ).await?; + let res = self + .cookie_match + .do_action_and_wait_for( + 0, + || async { + let _ = self + .start_stop_sender + .send(StartStopInfo { + is_start: true, + pkt_id: Some(id), + }) + .await; + debug!("start with feedback"); + }, + timeout, + ) + .await?; if let Ok(res) = res.downcast() { Ok(*res) } else { - Err(SDLanError::ConvertError("failed to convert feedback to RSFeedback".to_owned())) + Err(SDLanError::ConvertError( + "failed to convert feedback to RSFeedback".to_owned(), + )) } } @@ -394,6 +423,7 @@ impl Node { pubkey: String, config: NodeConfig, sock: Socket, + iface: &Option, multicast_sock: Option, // tcpsock: TCPSocket, // token: &str, @@ -416,14 +446,12 @@ impl Node { }; Self { - #[cfg(any(feature = "tun", target_os = "windows"))] arp_table: ArpTable::new(), packet_id: AtomicU32::new(1), encryptor: ArcSwap::from(Arc::new(MyEncryptor::new())), // encryptor: RwLock::new(MyEncryptor::new()), - network_id: AtomicU32::new(0), hostname: RwLock::new(hostname), @@ -437,7 +465,7 @@ impl Node { udp_sock_for_global_dns: udp_sock_for_global_dns, dns_matcher: Arc::new(DNSMatcher::new()), - quic_endpoint: quic_init(), + quic_endpoint: quic_init(iface), identity_id: IdentityID::new(0), access_token: StringToken::new(String::new()), @@ -445,7 +473,6 @@ impl Node { // _token: Mutex::new(token.to_owned()), // network_code: Mutex::new(network_code.to_owned()), - start_stop_sender: start_stop, connection_chan: connecting_chan, @@ -513,7 +540,7 @@ impl Node { } */ - /* + /* pub fn get_encrypt_key(&self) -> Arc> { self.encrypt_key.read().unwrap().clone() } @@ -568,7 +595,7 @@ impl Node { } */ - pub async fn send_arp_request(&self, gw_ip: u32, real_ip: u32) -> Result<()>{ + pub async fn send_arp_request(&self, gw_ip: u32, real_ip: u32) -> Result<()> { let arp_request = SdlArpRequest { // pkt_id: id, target_ip: gw_ip, @@ -666,12 +693,24 @@ impl Node { step: 0, }; - let result = self.cookie_match.send_message_to_udp_and_wait_for(&self.udp_sock_v4, cookie, probe, PacketType::StunProbe as u8, to_server, Duration::from_secs(3)).await?; + let result = self + .cookie_match + .send_message_to_udp_and_wait_for( + &self.udp_sock_v4, + cookie, + probe, + PacketType::StunProbe as u8, + to_server, + Duration::from_secs(3), + ) + .await?; if let Ok(res) = result.downcast() { return Ok(*res); } - return Err(SDLanError::ConvertError("failed to convert to StunprobeReply".to_owned())) + return Err(SDLanError::ConvertError( + "failed to convert to StunprobeReply".to_owned(), + )); // println!("==> sending probe request: {:?}", probe); } } @@ -877,7 +916,15 @@ impl Queryer { } } - pub async fn send_message_to_udp_and_wait_for(&self, sock: &Socket, id: u32, message: T, packet_type: u8, to_server: &SocketAddr, timeout: Duration) -> Result { + pub async fn send_message_to_udp_and_wait_for( + &self, + sock: &Socket, + id: u32, + message: T, + packet_type: u8, + to_server: &SocketAddr, + timeout: Duration, + ) -> Result { let (tx, rx) = tokio::sync::oneshot::channel(); self.mailbox.insert(id, tx); @@ -903,11 +950,15 @@ impl Queryer { Err(SDLanError::IOError("timed out".to_string())) } } - } - pub async fn do_action_and_wait_for(&self, id: u32, action: T, timeout: Duration) -> Result - where + pub async fn do_action_and_wait_for( + &self, + id: u32, + action: T, + timeout: Duration, + ) -> Result + where F: Future, T: Fn() -> F, { @@ -933,7 +984,13 @@ impl Queryer { } } - pub async fn send_message_to_quic_and_wait_for(&self, id: u32, message: T, packet_type: u8, timeout: Duration) -> Result { + pub async fn send_message_to_quic_and_wait_for( + &self, + id: u32, + message: T, + packet_type: u8, + timeout: Duration, + ) -> Result { let (tx, rx) = tokio::sync::oneshot::channel(); self.mailbox.insert(id, tx); @@ -957,4 +1014,4 @@ impl Queryer { } } } -} \ No newline at end of file +} diff --git a/src/quic/mod.rs b/src/quic/mod.rs index fc9a190..04560c8 100644 --- a/src/quic/mod.rs +++ b/src/quic/mod.rs @@ -1,23 +1,34 @@ use std::fs::File; use std::io::BufReader; +use std::net::SocketAddr; +use std::net::UdpSocket; +use std::os::windows::io::FromRawSocket; +use std::os::windows::io::IntoRawSocket; use std::path::Path; use std::sync::Arc; -use quinn::Endpoint; +use default_net::Interface; use quinn::crypto::rustls::QuicClientConfig; -use rustls::crypto::CryptoProvider; +use quinn::Endpoint; +use quinn::TokioRuntime; use rustls::crypto::ring; +use rustls::crypto::CryptoProvider; use rustls::pki_types::CertificateDer; use rustls::pki_types::PrivateKeyDer; use rustls::pki_types::ServerName; use rustls_pemfile::{certs, private_key}; +use socket2::Domain; +use socket2::Protocol; -pub fn quic_init() -> Endpoint { +use crate::get_default_interface; +use crate::set_unicast_if_v4; + +pub fn quic_init(iface: &Option) -> Endpoint { let default_provider = ring::default_provider(); CryptoProvider::install_default(default_provider).unwrap(); - /* + /* let certificate_chain = load_certs_from_pem("./ca/ca.crt").unwrap(); let mut root_store = rustls::RootCertStore::empty(); for cert in certificate_chain { @@ -26,9 +37,12 @@ pub fn quic_init() -> Endpoint { */ let mut root_store = rustls::RootCertStore::empty(); - rustls_native_certs::load_native_certs().expect("could not load platform certs").into_iter().for_each(|cert| { - root_store.add(cert).unwrap(); - }); + rustls_native_certs::load_native_certs() + .expect("could not load platform certs") + .into_iter() + .for_each(|cert| { + root_store.add(cert).unwrap(); + }); let mut rustls_config = rustls::ClientConfig::builder() // .with_root_certificates(rustls::RootCertStore::empty()) @@ -36,18 +50,41 @@ pub fn quic_init() -> Endpoint { .with_root_certificates(root_store) .with_no_client_auth(); - /* + /* let mut rustls_config = rustls::ClientConfig::builder() .dangerous() .with_custom_certificate_verifier(Arc::new(SkipServerVerification{})) .with_no_client_auth(); */ + let socket2 = + socket2::Socket::new(Domain::IPV4, socket2::Type::DGRAM, Some(Protocol::UDP)).unwrap(); + socket2 + .bind(&"0.0.0.0:0".parse::().unwrap().into()) + .unwrap(); + + if let Some(iface) = iface { + set_unicast_if_v4(&socket2, iface); + } + + let std_socket = unsafe { UdpSocket::from_raw_socket(socket2.into_raw_socket()) }; + std_socket.set_nonblocking(true); + rustls_config.alpn_protocols = vec![b"punchnet/1.0".to_vec()]; - let quinn_client_config = quinn::ClientConfig::new(Arc::new(QuicClientConfig::try_from(rustls_config).unwrap())); + let quinn_client_config = + quinn::ClientConfig::new(Arc::new(QuicClientConfig::try_from(rustls_config).unwrap())); + + let quinn_endpoint_config = quinn::EndpointConfig::default(); + + let mut endpoint = Endpoint::new( + quinn_endpoint_config, + None, + std_socket, + Arc::new(TokioRuntime), + ) + .unwrap(); - let mut endpoint = Endpoint::client("0.0.0.0:0".parse().unwrap()).unwrap(); endpoint.set_default_client_config(quinn_client_config); endpoint @@ -56,32 +93,53 @@ pub fn quic_init() -> Endpoint { #[derive(Debug)] struct SkipServerVerification; impl rustls::client::danger::ServerCertVerifier for SkipServerVerification { - fn verify_server_cert(&self, _: &CertificateDer<'_>, _: &[CertificateDer<'_>], _: &ServerName<'_>, _: &[u8], _: rustls::pki_types::UnixTime) -> Result { + fn verify_server_cert( + &self, + _: &CertificateDer<'_>, + _: &[CertificateDer<'_>], + _: &ServerName<'_>, + _: &[u8], + _: rustls::pki_types::UnixTime, + ) -> Result { Ok(rustls::client::danger::ServerCertVerified::assertion()) } - fn verify_tls12_signature(&self, _: &[u8], _: &CertificateDer<'_>, _: &rustls::DigitallySignedStruct) -> Result { + fn verify_tls12_signature( + &self, + _: &[u8], + _: &CertificateDer<'_>, + _: &rustls::DigitallySignedStruct, + ) -> Result { Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) } - fn verify_tls13_signature(&self, _: &[u8], _: &CertificateDer<'_>, _: &rustls::DigitallySignedStruct) -> Result { + fn verify_tls13_signature( + &self, + _: &[u8], + _: &CertificateDer<'_>, + _: &rustls::DigitallySignedStruct, + ) -> Result { Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) } fn supported_verify_schemes(&self) -> Vec { - rustls::crypto::ring::default_provider().signature_verification_algorithms.supported_schemes() + rustls::crypto::ring::default_provider() + .signature_verification_algorithms + .supported_schemes() } } -pub fn load_certs_from_pem(path: impl AsRef) -> Result>, Box> { +pub fn load_certs_from_pem( + path: impl AsRef, +) -> Result>, Box> { let file = File::open(path.as_ref())?; let mut reader = BufReader::new(file); let certs = certs(&mut reader) .into_iter() .map(|it| it.unwrap()) .collect::>(); - + Ok(certs) } -pub fn load_private_key_from_pem(path: impl AsRef) -> Option>{ +pub fn load_private_key_from_pem(path: impl AsRef) -> Option> { let Ok(file) = File::open(path.as_ref()) else { return None; }; diff --git a/src/utils/socks.rs b/src/utils/socks.rs index 840f754..00a1625 100755 --- a/src/utils/socks.rs +++ b/src/utils/socks.rs @@ -1,8 +1,10 @@ +use default_net::Interface; use sdlan_sn_rs::{ config::{AF_INET, AF_INET6}, utils::{Result, SDLanError}, }; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use socket2::{Domain, Protocol, SockAddr}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4}; use tokio::net::ToSocketAddrs; use tracing::{debug, error}; @@ -61,13 +63,34 @@ impl Socket { Ok(Self { udp }) } - pub async fn build(port: u16, bind_any: bool, join_multicast: bool, tos: u32) -> Result { + pub async fn build( + port: u16, + bind_any: bool, + join_multicast: bool, + tos: u32, + default_interface: &Option, + ) -> Result { let addr = match bind_any { true => "0.0.0.0", false => "127.0.0.1", }; let addr = format!("{}:{}", addr, port); - let udp = UdpSocket::bind(&addr).await?; + + let socket2 = + socket2::Socket::new(Domain::IPV4, socket2::Type::DGRAM, Some(Protocol::UDP))?; + + socket2.bind(&addr.parse::()?.into()); + + if let Some(iface) = default_interface { + set_unicast_if_v4(&socket2, iface); + } + + let std_socket: std::net::UdpSocket = socket2.into(); + std_socket.set_nonblocking(true); + + let udp = UdpSocket::from_std(std_socket)?; + + // let udp = UdpSocket::bind(&addr).await?; if join_multicast { if let Err(e) = udp.join_multicast_v4(Ipv4Addr::new(224, 0, 0, 69), Ipv4Addr::new(0, 0, 0, 0)) @@ -86,6 +109,29 @@ impl Socket { } } +pub fn set_unicast_if_v4(sock: &socket2::Socket, interface: &Interface) -> Result<()> { + use std::os::windows::io::AsRawSocket; + use windows_sys::Win32::Networking::WinSock::{setsockopt, IPPROTO_IP, IP_UNICAST_IF, SOCKET}; + + let idx = interface.index.to_be(); + let ret = unsafe { + setsockopt( + sock.as_raw_socket() as SOCKET, + IPPROTO_IP as i32, + IP_UNICAST_IF as i32, + &idx as *const _ as *const _, + std::mem::size_of_val(&idx) as i32, + ) + }; + if ret == 0 { + Ok(()) + } else { + Err(SDLanError::IOError( + std::io::Error::last_os_error().to_string(), + )) + } +} + /* pub async fn send_to_sock_v4_and_v6( // sk: &Socket, diff --git a/src/utils/system_action.rs b/src/utils/system_action.rs index a154bb4..8c6a820 100644 --- a/src/utils/system_action.rs +++ b/src/utils/system_action.rs @@ -1,6 +1,8 @@ use std::{net::Ipv4Addr, sync::Arc}; use arc_swap::ArcSwap; +use default_net::Interface; +use sdlan_sn_rs::utils::{Result, SDLanError}; #[derive(Default, Clone)] pub struct TrieNode { @@ -63,6 +65,13 @@ impl IpTrie { } } +pub fn get_default_interface() -> Result { + match default_net::get_default_interface() { + Ok(interface) => Ok(interface), + Err(e) => Err(SDLanError::IOError(e)), + } +} + #[cfg(test)] mod test { use std::net::Ipv4Addr;