Merge branch 'ipv6-detect' into 'main'
ipv6 detection See merge request veilid/veilid!226
This commit is contained in:
		@@ -104,7 +104,7 @@ struct NetworkInner {
 | 
			
		||||
    public_dial_info_check_punishment: Option<Box<dyn FnOnce() + Send + 'static>>,
 | 
			
		||||
    /// udp socket record for bound-first sockets, which are used to guarantee a port is available before
 | 
			
		||||
    /// creating a 'reuseport' socket there. we don't want to pick ports that other programs are using
 | 
			
		||||
    bound_first_udp: BTreeMap<u16, Option<(socket2::Socket, socket2::Socket)>>,
 | 
			
		||||
    bound_first_udp: BTreeMap<u16, (Option<socket2::Socket>, Option<socket2::Socket>)>,
 | 
			
		||||
    /// mapping of protocol handlers to accept messages from a set of bound socket addresses
 | 
			
		||||
    inbound_udp_protocol_handlers: BTreeMap<SocketAddr, RawUdpProtocolHandler>,
 | 
			
		||||
    /// outbound udp protocol handler for udpv4
 | 
			
		||||
@@ -113,7 +113,7 @@ struct NetworkInner {
 | 
			
		||||
    outbound_udpv6_protocol_handler: Option<RawUdpProtocolHandler>,
 | 
			
		||||
    /// tcp socket record for bound-first sockets, which are used to guarantee a port is available before
 | 
			
		||||
    /// creating a 'reuseport' socket there. we don't want to pick ports that other programs are using
 | 
			
		||||
    bound_first_tcp: BTreeMap<u16, Option<(socket2::Socket, socket2::Socket)>>,
 | 
			
		||||
    bound_first_tcp: BTreeMap<u16, (Option<socket2::Socket>, Option<socket2::Socket>)>,
 | 
			
		||||
    /// TLS handling socket controller
 | 
			
		||||
    tls_acceptor: Option<TlsAcceptor>,
 | 
			
		||||
    /// Multiplexer record for protocols on low level TCP sockets
 | 
			
		||||
 
 | 
			
		||||
@@ -85,12 +85,17 @@ impl Network {
 | 
			
		||||
        if inner.bound_first_udp.contains_key(&udp_port) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check for ipv6
 | 
			
		||||
        let has_v6 = is_ipv6_supported();
 | 
			
		||||
 | 
			
		||||
        // If the address is specified, only use the specified port and fail otherwise
 | 
			
		||||
        let mut bound_first_socket_v4 = None;
 | 
			
		||||
        let mut bound_first_socket_v6 = None;
 | 
			
		||||
        if let Ok(bfs4) =
 | 
			
		||||
            new_bound_first_udp_socket(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), udp_port))
 | 
			
		||||
        {
 | 
			
		||||
            if has_v6 {
 | 
			
		||||
                if let Ok(bfs6) = new_bound_first_udp_socket(SocketAddr::new(
 | 
			
		||||
                    IpAddr::V6(Ipv6Addr::UNSPECIFIED),
 | 
			
		||||
                    udp_port,
 | 
			
		||||
@@ -98,24 +103,26 @@ impl Network {
 | 
			
		||||
                    bound_first_socket_v4 = Some(bfs4);
 | 
			
		||||
                    bound_first_socket_v6 = Some(bfs6);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                bound_first_socket_v4 = Some(bfs4);
 | 
			
		||||
            }
 | 
			
		||||
        if let (Some(bfs4), Some(bfs6)) = (bound_first_socket_v4, bound_first_socket_v6) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if bound_first_socket_v4.is_none() && (has_v6 && bound_first_socket_v6.is_none()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cfg_if! {
 | 
			
		||||
            if #[cfg(windows)] {
 | 
			
		||||
                // On windows, drop the socket. This is a race condition, but there's
 | 
			
		||||
                // no way around it. This isn't for security anyway, it's to prevent multiple copies of the
 | 
			
		||||
                // app from binding on the same port.
 | 
			
		||||
                    drop(bfs4);
 | 
			
		||||
                    drop(bfs6);
 | 
			
		||||
                    inner.bound_first_udp.insert(udp_port, None);
 | 
			
		||||
                inner.bound_first_udp.insert(udp_port, (None, None));
 | 
			
		||||
            } else {
 | 
			
		||||
                    inner.bound_first_udp.insert(udp_port, Some((bfs4, bfs6)));
 | 
			
		||||
                inner.bound_first_udp.insert(udp_port, (bound_first_socket_v4, bound_first_socket_v6));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        true
 | 
			
		||||
        } else {
 | 
			
		||||
            false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bind_first_tcp_port(&self, tcp_port: u16) -> bool {
 | 
			
		||||
@@ -123,12 +130,17 @@ impl Network {
 | 
			
		||||
        if inner.bound_first_tcp.contains_key(&tcp_port) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check for ipv6
 | 
			
		||||
        let has_v6 = is_ipv6_supported();
 | 
			
		||||
 | 
			
		||||
        // If the address is specified, only use the specified port and fail otherwise
 | 
			
		||||
        let mut bound_first_socket_v4 = None;
 | 
			
		||||
        let mut bound_first_socket_v6 = None;
 | 
			
		||||
        if let Ok(bfs4) =
 | 
			
		||||
            new_bound_first_tcp_socket(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), tcp_port))
 | 
			
		||||
        {
 | 
			
		||||
            if has_v6 {
 | 
			
		||||
                if let Ok(bfs6) = new_bound_first_tcp_socket(SocketAddr::new(
 | 
			
		||||
                    IpAddr::V6(Ipv6Addr::UNSPECIFIED),
 | 
			
		||||
                    tcp_port,
 | 
			
		||||
@@ -136,24 +148,26 @@ impl Network {
 | 
			
		||||
                    bound_first_socket_v4 = Some(bfs4);
 | 
			
		||||
                    bound_first_socket_v6 = Some(bfs6);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                bound_first_socket_v4 = Some(bfs4);
 | 
			
		||||
            }
 | 
			
		||||
        if let (Some(bfs4), Some(bfs6)) = (bound_first_socket_v4, bound_first_socket_v6) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if bound_first_socket_v4.is_none() && (has_v6 && bound_first_socket_v6.is_none()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cfg_if! {
 | 
			
		||||
            if #[cfg(windows)] {
 | 
			
		||||
                // On windows, drop the socket. This is a race condition, but there's
 | 
			
		||||
                // no way around it. This isn't for security anyway, it's to prevent multiple copies of the
 | 
			
		||||
                // app from binding on the same port.
 | 
			
		||||
                    drop(bfs4);
 | 
			
		||||
                    drop(bfs6);
 | 
			
		||||
                    inner.bound_first_tcp.insert(tcp_port, None);
 | 
			
		||||
                inner.bound_first_tcp.insert(tcp_port, (None, None));
 | 
			
		||||
            } else {
 | 
			
		||||
                    inner.bound_first_tcp.insert(tcp_port, Some((bfs4, bfs6)));
 | 
			
		||||
                inner.bound_first_tcp.insert(tcp_port, (bound_first_socket_v4, bound_first_socket_v6));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        true
 | 
			
		||||
        } else {
 | 
			
		||||
            false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn free_bound_first_ports(&self) {
 | 
			
		||||
@@ -204,10 +218,7 @@ impl Network {
 | 
			
		||||
        if listen_address.is_empty() {
 | 
			
		||||
            // If listen address is empty, find us a port iteratively
 | 
			
		||||
            let port = self.find_available_udp_port(5150)?;
 | 
			
		||||
            let ip_addrs = vec![
 | 
			
		||||
                IpAddr::V4(Ipv4Addr::UNSPECIFIED),
 | 
			
		||||
                IpAddr::V6(Ipv6Addr::UNSPECIFIED),
 | 
			
		||||
            ];
 | 
			
		||||
            let ip_addrs = available_unspecified_addresses();
 | 
			
		||||
            Ok((port, ip_addrs))
 | 
			
		||||
        } else {
 | 
			
		||||
            // If no address is specified, but the port is, use ipv4 and ipv6 unspecified
 | 
			
		||||
@@ -227,10 +238,7 @@ impl Network {
 | 
			
		||||
        if listen_address.is_empty() {
 | 
			
		||||
            // If listen address is empty, find us a port iteratively
 | 
			
		||||
            let port = self.find_available_tcp_port(5150)?;
 | 
			
		||||
            let ip_addrs = vec![
 | 
			
		||||
                IpAddr::V4(Ipv4Addr::UNSPECIFIED),
 | 
			
		||||
                IpAddr::V6(Ipv6Addr::UNSPECIFIED),
 | 
			
		||||
            ];
 | 
			
		||||
            let ip_addrs = available_unspecified_addresses();
 | 
			
		||||
            Ok((port, ip_addrs))
 | 
			
		||||
        } else {
 | 
			
		||||
            // If no address is specified, but the port is, use ipv4 and ipv6 unspecified
 | 
			
		||||
 
 | 
			
		||||
@@ -345,9 +345,14 @@ impl Network {
 | 
			
		||||
                outbound.insert(ProtocolType::WSS);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // XXX: See issue #92
 | 
			
		||||
            let family_global = AddressTypeSet::from(AddressType::IPV4);
 | 
			
		||||
            let family_local = AddressTypeSet::from(AddressType::IPV4);
 | 
			
		||||
            let supported_address_types: AddressTypeSet = if is_ipv6_supported() {
 | 
			
		||||
                AddressType::IPV4 | AddressType::IPV6
 | 
			
		||||
            } else {
 | 
			
		||||
                AddressType::IPV4.into()
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            let family_global = supported_address_types;
 | 
			
		||||
            let family_local = supported_address_types;
 | 
			
		||||
 | 
			
		||||
            let public_internet_capabilities = {
 | 
			
		||||
                PUBLIC_INTERNET_CAPABILITIES
 | 
			
		||||
 
 | 
			
		||||
@@ -239,13 +239,41 @@ pub fn compatible_unspecified_socket_addr(socket_addr: &SocketAddr) -> SocketAdd
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cfg_if! {
 | 
			
		||||
    if #[cfg(not(target_arch = "wasm32"))] {
 | 
			
		||||
        use std::net::UdpSocket;
 | 
			
		||||
 | 
			
		||||
        static IPV6_IS_SUPPORTED: Mutex<Option<bool>> = Mutex::new(None);
 | 
			
		||||
 | 
			
		||||
        pub fn is_ipv6_supported() -> bool {
 | 
			
		||||
            let mut opt_supp = IPV6_IS_SUPPORTED.lock();
 | 
			
		||||
            if let Some(supp) = *opt_supp {
 | 
			
		||||
                return supp;
 | 
			
		||||
            }
 | 
			
		||||
            // Not exhaustive but for our use case it should be sufficient. If no local ports are available for binding, Veilid isn't going to work anyway :P
 | 
			
		||||
            let supp = UdpSocket::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)).is_ok();
 | 
			
		||||
            *opt_supp = Some(supp);
 | 
			
		||||
            supp
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn available_unspecified_addresses() -> Vec<IpAddr> {
 | 
			
		||||
    if is_ipv6_supported() {
 | 
			
		||||
        vec![
 | 
			
		||||
            IpAddr::V4(Ipv4Addr::UNSPECIFIED),
 | 
			
		||||
            IpAddr::V6(Ipv6Addr::UNSPECIFIED),
 | 
			
		||||
        ]
 | 
			
		||||
    } else {
 | 
			
		||||
        vec![IpAddr::V4(Ipv4Addr::UNSPECIFIED)]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<SocketAddr>, String> {
 | 
			
		||||
    // If no address is specified, but the port is, use ipv4 and ipv6 unspecified
 | 
			
		||||
    // If the address is specified, only use the specified port and fail otherwise
 | 
			
		||||
    let ip_addrs = [
 | 
			
		||||
        IpAddr::V4(Ipv4Addr::UNSPECIFIED),
 | 
			
		||||
        IpAddr::V6(Ipv6Addr::UNSPECIFIED),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    let ip_addrs = available_unspecified_addresses();
 | 
			
		||||
 | 
			
		||||
    Ok(if let Some(portstr) = listen_address.strip_prefix(':') {
 | 
			
		||||
        let port = portstr
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
#![cfg(target_arch = "wasm32")]
 | 
			
		||||
 | 
			
		||||
use super::*;
 | 
			
		||||
use core::sync::atomic::{AtomicI8, Ordering};
 | 
			
		||||
use js_sys::{global, Reflect};
 | 
			
		||||
@@ -58,3 +60,25 @@ pub struct JsValueError(String);
 | 
			
		||||
pub fn map_jsvalue_error(x: JsValue) -> JsValueError {
 | 
			
		||||
    JsValueError(x.as_string().unwrap_or_default())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static IPV6_IS_SUPPORTED: Mutex<Option<bool>> = Mutex::new(None);
 | 
			
		||||
 | 
			
		||||
pub fn is_ipv6_supported() -> bool {
 | 
			
		||||
    let mut opt_supp = IPV6_IS_SUPPORTED.lock();
 | 
			
		||||
    if let Some(supp) = *opt_supp {
 | 
			
		||||
        return supp;
 | 
			
		||||
    }
 | 
			
		||||
    // let supp = match UdpSocket::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)) {
 | 
			
		||||
    //     Ok(_) => true,
 | 
			
		||||
    //     Err(e) => !matches!(
 | 
			
		||||
    //         e.kind(),
 | 
			
		||||
    //         std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::Unsupported
 | 
			
		||||
    //     ),
 | 
			
		||||
    // };
 | 
			
		||||
 | 
			
		||||
    // XXX: See issue #92
 | 
			
		||||
    let supp = false;
 | 
			
		||||
 | 
			
		||||
    *opt_supp = Some(supp);
 | 
			
		||||
    supp
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user