diff --git a/veilid-core/proto/veilid.capnp b/veilid-core/proto/veilid.capnp index 1077695f..4092ad09 100644 --- a/veilid-core/proto/veilid.capnp +++ b/veilid-core/proto/veilid.capnp @@ -60,16 +60,12 @@ struct Address { union { ipv4 @0 :AddressIPV4; ipv6 @1 :AddressIPV6; - hostname @2 :Text; } } struct SocketAddress { - union { - ipv4 @0 :AddressIPV4; - ipv6 @1 :AddressIPV6; - } - port @2 :UInt16; + address @0 :Address; + port @1 :UInt16; } enum ProtocolKind { @@ -80,25 +76,21 @@ enum ProtocolKind { } struct DialInfoUDP { - address @0 :Address; - port @1 :UInt16; + socketAddress @0 :SocketAddress; } struct DialInfoTCP { - address @0 :Address; - port @1 :UInt16; + socketAddress @0 :SocketAddress; } struct DialInfoWS { - host @0 :Text; - port @1 :UInt16; - path @2 :Text; + socketAddress @0 :SocketAddress; + request @1 :Text; } struct DialInfoWSS { - host @0 :Text; - port @1 :UInt16; - path @2 :Text; + socketAddress @0 :SocketAddress; + request @1 :Text; } struct DialInfo { diff --git a/veilid-core/src/attachment_manager.rs b/veilid-core/src/attachment_manager.rs index 015719d5..7b544d85 100644 --- a/veilid-core/src/attachment_manager.rs +++ b/veilid-core/src/attachment_manager.rs @@ -5,6 +5,7 @@ use crate::network_manager::*; use crate::xx::*; use crate::*; use core::convert::TryFrom; +use core::fmt; state_machine! { derive(Debug, PartialEq, Eq, Clone, Copy) @@ -65,9 +66,9 @@ state_machine! { }, } -impl ToString for AttachmentState { - fn to_string(&self) -> String { - match self { +impl fmt::Display for AttachmentState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let out = match self { AttachmentState::Attaching => "attaching".to_owned(), AttachmentState::AttachedWeak => "attached_weak".to_owned(), AttachmentState::AttachedGood => "attached_good".to_owned(), @@ -76,7 +77,8 @@ impl ToString for AttachmentState { AttachmentState::OverAttached => "over_attached".to_owned(), AttachmentState::Detaching => "detaching".to_owned(), AttachmentState::Detached => "detached".to_owned(), - } + }; + write!(f, "{}", out) } } diff --git a/veilid-core/src/intf/native/network/mod.rs b/veilid-core/src/intf/native/network/mod.rs index 3bc9d8b3..ca145e81 100644 --- a/veilid-core/src/intf/native/network/mod.rs +++ b/veilid-core/src/intf/native/network/mod.rs @@ -51,6 +51,15 @@ struct NetworkInner { listener_states: BTreeMap>>, udp_protocol_handlers: BTreeMap, tls_acceptor: Option, + udp_port: u16, + tcp_port: u16, + ws_port: u16, + wss_port: u16, + outbound_udpv4_protocol_handler: Option, + outbound_udpv6_protocol_handler: Option, + outbound_tcp_protocol_handler: Option, + outbound_ws_protocol_handler: Option, + outbound_wss_protocol_handler: Option, interfaces: NetworkInterfaces, } @@ -84,6 +93,15 @@ impl Network { listener_states: BTreeMap::new(), udp_protocol_handlers: BTreeMap::new(), tls_acceptor: None, + udp_port: 0u16, + tcp_port: 0u16, + ws_port: 0u16, + wss_port: 0u16, + outbound_udpv4_protocol_handler: None, + outbound_udpv6_protocol_handler: None, + outbound_tcp_protocol_handler: None, + outbound_ws_protocol_handler: None, + outbound_wss_protocol_handler: None, interfaces: NetworkInterfaces::new(), } } @@ -267,34 +285,9 @@ impl Network { c.network.tls.connection_initial_timeout, ) }; + // Create a reusable socket with no linger time, and no delay - let domain = Domain::for_address(addr); - let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)) - .map_err(|e| format!("Couldn't create TCP socket: {}", e))?; - if let Err(e) = socket.set_linger(None) { - warn!("Couldn't set TCP linger: {}", e); - } - if let Err(e) = socket.set_nodelay(true) { - warn!("Couldn't set TCP nodelay: {}", e); - } - if let Err(e) = socket.set_reuse_address(true) { - warn!("Couldn't set reuse address: {}", e); - } - cfg_if! { - if #[cfg(unix)] { - if let Err(e) = socket.set_reuse_port(true) { - warn!("Couldn't set reuse port: {}", e); - } - } - } - - // Bind a listener and stash it with the sockaddr in a table - trace!("spawn_socket_listener: binding to {}", addr); - let socket2_addr = socket2::SockAddr::from(addr); - socket - .bind(&socket2_addr) - .map_err(|e| format!("failed to bind TCP socket: {}", e))?; - + let socket = new_shared_tcp_socket(addr)?; // Listen on the socket socket .listen(128) @@ -461,30 +454,59 @@ impl Network { } //////////////////////////////////////////////////////////// - async fn spawn_udp_socket(&self, addr: SocketAddr) -> Result<(), String> { - trace!("spawn_udp_socket on {:?}", &addr); + async fn create_udp_outbound_sockets(&self) -> Result<(), String> { + let mut inner = self.inner.lock(); + let mut port = inner.udp_port; + // v4 + let socket_addr_v4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port); + if let Ok(socket) = new_shared_udp_socket(socket_addr_v4) { + log_net!("created udpv4 outbound socket on {:?}", &socket_addr_v4); + + // Pull the port if we randomly bound, so v6 can be on the same port + port = socket + .local_addr() + .map_err(map_to_string)? + .as_socket_ipv4() + .ok_or("expected ipv4 address type".to_owned())? + .port(); + + // Make an async UdpSocket from the socket2 socket + let std_udp_socket: std::net::UdpSocket = socket.into(); + let udp_socket = UdpSocket::from(std_udp_socket); + let socket_arc = Arc::new(udp_socket); + + // Create protocol handler + let udpv4_handler = + RawUdpProtocolHandler::new(inner.network_manager.clone(), socket_arc.clone()); + + inner.outbound_udpv4_protocol_handler = Some(udpv4_handler); + } + //v6 + let socket_addr_v6 = + SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), port); + if let Ok(socket) = new_shared_udp_socket(socket_addr_v6) { + log_net!("created udpv6 outbound socket on {:?}", &socket_addr_v6); + + // Make an async UdpSocket from the socket2 socket + let std_udp_socket: std::net::UdpSocket = socket.into(); + let udp_socket = UdpSocket::from(std_udp_socket); + let socket_arc = Arc::new(udp_socket); + + // Create protocol handler + let udpv6_handler = + RawUdpProtocolHandler::new(inner.network_manager.clone(), socket_arc.clone()); + + inner.outbound_udpv6_protocol_handler = Some(udpv6_handler); + } + + Ok(()) + } + + async fn spawn_udp_inbound_socket(&self, addr: SocketAddr) -> Result<(), String> { + log_net!("spawn_udp_inbound_socket on {:?}", &addr); // Create a reusable socket - let domain = Domain::for_address(addr); - let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP)) - .map_err(|e| format!("Couldn't create UDP socket: {}", e))?; - if let Err(e) = socket.set_reuse_address(true) { - warn!("Couldn't set reuse address: {}", e); - } - cfg_if! { - if #[cfg(unix)] { - if let Err(e) = socket.set_reuse_port(true) { - warn!("Couldn't set reuse port: {}", e); - } - } - } - - // Bind a listener and stash it with the sockaddr in a table - trace!("spawn_udp_socket: binding to {}", addr); - let socket2_addr = socket2::SockAddr::from(addr); - socket - .bind(&socket2_addr) - .map_err(|e| format!("failed to bind UDP socket: {}", e))?; + let socket = new_shared_udp_socket(addr)?; // Make an async UdpSocket from the socket2 socket let std_udp_socket: std::net::UdpSocket = socket.into(); @@ -582,7 +604,7 @@ impl Network { if !self.inner.lock().udp_protocol_handlers.contains_key(&addr) { let ldi_addrs = Self::translate_unspecified_address(&*self.inner.lock(), &addr); - self.clone().spawn_udp_socket(addr).await?; + self.clone().spawn_udp_inbound_socket(addr).await?; // Return local dial infos we listen on for ldi_addr in ldi_addrs { @@ -627,29 +649,28 @@ impl Network { return Some(ph.clone()); } } - // otherwise find the first udp protocol handler that matches the ip protocol version of the peer addr + + // otherwise find the outbound udp protocol handler that matches the ip protocol version of the peer addr let inner = self.inner.lock(); - for (local_addr, ph) in &inner.udp_protocol_handlers { - if Self::match_socket_addr(&*inner, local_addr, peer_socket_addr) { - return Some(ph.clone()); - } + match peer_socket_addr { + SocketAddr::V4(_) => inner.outbound_udpv4_protocol_handler.clone(), + SocketAddr::V6(_) => inner.outbound_udpv6_protocol_handler.clone(), } - None } - fn find_best_tcp_local_address(&self, peer_socket_addr: &SocketAddr) -> Option { - // Find a matching listening local tcp socket address if we can - let routing_table = self.routing_table(); - let dids = routing_table.local_dial_info_for_protocol(ProtocolType::TCP); + fn get_preferred_local_address( + &self, + local_port: u16, + peer_socket_addr: &SocketAddr, + ) -> SocketAddr { let inner = self.inner.lock(); - for did in dids { - if let Ok(local_addr) = did.dial_info.to_socket_addr() { - if Self::match_socket_addr(&*inner, &local_addr, peer_socket_addr) { - return Some(local_addr); - } - } + match peer_socket_addr { + SocketAddr::V4(_) => SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), local_port), + SocketAddr::V6(_) => SocketAddr::new( + IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), + local_port, + ), } - None } async fn send_data_to_existing_connection( @@ -744,17 +765,30 @@ impl Network { } DialInfo::TCP(_) => { let peer_socket_addr = dial_info.to_socket_addr().map_err(logthru_net!())?; - let some_local_addr = self.find_best_tcp_local_address(&peer_socket_addr); - RawTcpProtocolHandler::connect(network_manager, some_local_addr, peer_socket_addr) + let local_addr = + self.get_preferred_local_address(self.inner.lock().tcp_port, &peer_socket_addr); + RawTcpProtocolHandler::connect(network_manager, local_addr, peer_socket_addr) .await .map_err(logthru_net!())? } - DialInfo::WS(_) => WebsocketProtocolHandler::connect(network_manager, dial_info) - .await - .map_err(logthru_net!(error))?, - DialInfo::WSS(_) => WebsocketProtocolHandler::connect(network_manager, dial_info) + DialInfo::WS(_) => { + let remote_ip_addr = dial_info.resolve()?; + + let local_addr = + self.get_preferred_local_address(self.inner.lock().ws_port, &peer_socket_addr); + + WebsocketProtocolHandler::connect(network_manager, local_addr, dial_info) + .await + .map_err(logthru_net!(error))? + } + DialInfo::WSS(_) => { + let local_addr = + self.get_preferred_local_address(self.inner.lock().ws_port, &peer_socket_addr); + + WebsocketProtocolHandler::connect(network_manager, dial_info) .await .map_err(logthru_net!(error))?, + } }; conn.send(data).await.map_err(logthru_net!(error)) @@ -801,11 +835,12 @@ impl Network { c.network.protocol.udp.public_address.clone(), ) }; - trace!("UDP: starting listener at {:?}", listen_address); + info!("UDP: starting listener at {:?}", listen_address); let dial_infos = self.start_udp_handler(listen_address.clone()).await?; - trace!("UDP: listener started"); - for x in &dial_infos { + // Pick out UDP port for outbound connections (they will all be the same) + self.inner.lock().udp_port = x.port(); + // Register local dial info routing_table.register_local_dial_info(x.clone(), DialInfoOrigin::Static); } @@ -867,6 +902,9 @@ impl Network { let mut dial_infos: Vec = Vec::new(); for (a, p) in addresses { + // Pick out WS port for outbound connections (they will all be the same) + self.inner.lock().ws_port = p; + let di = DialInfo::ws(a.address_string(), p, path.clone()); dial_infos.push(di.clone()); routing_table.register_local_dial_info(di, DialInfoOrigin::Static); @@ -906,7 +944,7 @@ impl Network { ) }; trace!("WSS: starting listener at {}", listen_address); - let _ = self + let addresses = self .start_tcp_listener( listen_address.clone(), true, @@ -921,11 +959,14 @@ impl Network { // This is not the case with unencrypted websockets, which can be specified solely by an IP address // // let mut dial_infos: Vec = Vec::new(); - // for (a, p) in addresses { - // let di = DialInfo::wss(a.address_string(), p, path.clone()); - // dial_infos.push(di.clone()); - // routing_table.register_local_dial_info(di, DialInfoOrigin::Static); - // } + for (_, p) in addresses { + // Pick out WS port for outbound connections (they will all be the same) + self.inner.lock().wss_port = p; + + // let di = DialInfo::wss(a.address_string(), p, path.clone()); + // dial_infos.push(di.clone()); + // routing_table.register_local_dial_info(di, DialInfoOrigin::Static); + } // Add static public dialinfo if it's configured if let Some(url) = url.as_ref() { @@ -974,6 +1015,9 @@ impl Network { let mut dial_infos: Vec = Vec::new(); for (a, p) in addresses { + // Pick out TCP port for outbound connections (they will all be the same) + self.inner.lock().tcp_port = p; + let di = DialInfo::tcp(a.to_canonical(), p); dial_infos.push(di.clone()); routing_table.register_local_dial_info(di, DialInfoOrigin::Static); @@ -1019,24 +1063,29 @@ impl Network { pub async fn startup(&self) -> Result<(), String> { info!("starting network"); + let network_manager = self.inner.lock().network_manager.clone(); // initialize interfaces self.inner.lock().interfaces.refresh()?; - // get listen config - let (listen_udp, listen_tcp, listen_ws, listen_wss) = { + // get network config + let (enabled_udp, connect_tcp, listen_tcp, connect_ws, listen_ws, connect_wss, listen_wss) = { let c = self.config.get(); ( c.network.protocol.udp.enabled && c.capabilities.protocol_udp, + c.network.protocol.tcp.connect && c.capabilities.protocol_connect_tcp, c.network.protocol.tcp.listen && c.capabilities.protocol_accept_tcp, + c.network.protocol.ws.connect && c.capabilities.protocol_connect_ws, c.network.protocol.ws.listen && c.capabilities.protocol_accept_ws, + c.network.protocol.wss.connect && c.capabilities.protocol_connect_wss, c.network.protocol.wss.listen && c.capabilities.protocol_accept_wss, ) }; // start listeners - if listen_udp { + if enabled_udp { self.start_udp_listeners().await?; + self.create_udp_outbound_sockets().await?; } if listen_ws { self.start_ws_listeners().await?; diff --git a/veilid-core/src/intf/native/network/protocol/mod.rs b/veilid-core/src/intf/native/network/protocol/mod.rs index ab25d07d..9a9b2a60 100644 --- a/veilid-core/src/intf/native/network/protocol/mod.rs +++ b/veilid-core/src/intf/native/network/protocol/mod.rs @@ -6,6 +6,7 @@ pub mod ws; use super::listener_state::*; use crate::veilid_api::ProtocolType; use crate::xx::*; +use socket2::{Domain, Protocol, Socket, Type}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct DummyNetworkConnection {} @@ -61,3 +62,57 @@ impl NetworkConnection { } } } + +pub fn new_shared_udp_socket(local_address: SocketAddr) -> Result { + let domain = Domain::for_address(local_address); + let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP)) + .map_err(|e| format!("Couldn't create UDP socket: {}", e))?; + + if let Err(e) = socket.set_reuse_address(true) { + log_net!(error "Couldn't set reuse address: {}", e); + } + cfg_if! { + if #[cfg(unix)] { + if let Err(e) = socket.set_reuse_port(true) { + log_net!(error "Couldn't set reuse port: {}", e); + } + } + } + + let socket2_addr = socket2::SockAddr::from(local_address); + socket + .bind(&socket2_addr) + .map_err(|e| format!("failed to bind UDP socket: {}", e))?; + + Ok(socket) +} + +pub fn new_shared_tcp_socket(local_address: SocketAddr) -> Result { + let domain = Domain::for_address(local_address); + let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)) + .map_err(map_to_string) + .map_err(logthru_net!())?; + if let Err(e) = socket.set_linger(None) { + log_net!(error "Couldn't set TCP linger: {}", e); + } + if let Err(e) = socket.set_nodelay(true) { + log_net!(error "Couldn't set TCP nodelay: {}", e); + } + if let Err(e) = socket.set_reuse_address(true) { + log_net!(error "Couldn't set reuse address: {}", e); + } + cfg_if! { + if #[cfg(unix)] { + if let Err(e) = socket.set_reuse_port(true) { + log_net!(error "Couldn't set reuse port: {}", e); + } + } + } + + let socket2_addr = socket2::SockAddr::from(local_address); + if let Err(e) = socket.bind(&socket2_addr) { + log_net!(error "failed to bind TCP socket: {}", e); + } + + Ok(socket) +} diff --git a/veilid-core/src/intf/native/network/protocol/tcp.rs b/veilid-core/src/intf/native/network/protocol/tcp.rs index 95ba2af8..8cf7c4ee 100644 --- a/veilid-core/src/intf/native/network/protocol/tcp.rs +++ b/veilid-core/src/intf/native/network/protocol/tcp.rs @@ -6,7 +6,6 @@ use crate::*; use async_std::net::*; use async_std::prelude::*; use async_std::sync::Mutex as AsyncMutex; -use socket2::{Domain, Protocol, Socket, Type}; use std::fmt; struct RawTcpNetworkConnectionInner { @@ -170,40 +169,11 @@ impl RawTcpProtocolHandler { pub async fn connect( network_manager: NetworkManager, - preferred_local_address: Option, + local_address: SocketAddr, remote_socket_addr: SocketAddr, ) -> Result { - // Make a low level socket that can connect to the remote socket address - // and attempt to reuse the local address that our listening socket uses - // for hole-punch compatibility - let domain = Domain::for_address(remote_socket_addr); - let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)) - .map_err(map_to_string) - .map_err(logthru_net!())?; - if let Err(e) = socket.set_linger(None) { - log_net!("Couldn't set TCP linger: {}", e); - } - if let Err(e) = socket.set_nodelay(true) { - log_net!("Couldn't set TCP nodelay: {}", e); - } - if let Err(e) = socket.set_reuse_address(true) { - log_net!("Couldn't set reuse address: {}", e); - } - cfg_if! { - if #[cfg(unix)] { - if let Err(e) = socket.set_reuse_port(true) { - log_net!("Couldn't set reuse port: {}", e); - } - } - } - - // Try to bind it to the preferred local address - if let Some(some_local_addr) = preferred_local_address { - let socket2_addr = socket2::SockAddr::from(some_local_addr); - if let Err(e) = socket.bind(&socket2_addr) { - log_net!(error "failed to bind TCP socket: {}", e); - } - } + // Make a shared socket + let socket = new_shared_tcp_socket(local_address)?; // Connect to the remote address let remote_socket2_addr = socket2::SockAddr::from(remote_socket_addr); diff --git a/veilid-core/src/intf/native/network/protocol/ws.rs b/veilid-core/src/intf/native/network/protocol/ws.rs index 2dcd3e8c..be263574 100644 --- a/veilid-core/src/intf/native/network/protocol/ws.rs +++ b/veilid-core/src/intf/native/network/protocol/ws.rs @@ -244,19 +244,21 @@ impl WebsocketProtocolHandler { pub async fn connect( network_manager: NetworkManager, + local_address: SocketAddr, dial_info: &DialInfo, ) -> Result { + // Split dial info up let (tls, request, domain, port, protocol_type) = match &dial_info { DialInfo::WS(di) => ( false, - di.path.clone(), + format!("ws://{}:{}/{}", di.host, di.port, di.path), di.host.clone(), di.port, ProtocolType::WS, ), DialInfo::WSS(di) => ( true, - di.path.clone(), + format!("wss://{}:{}/{}", di.host, di.port, di.path), di.host.clone(), di.port, ProtocolType::WSS, @@ -264,24 +266,29 @@ impl WebsocketProtocolHandler { _ => panic!("invalid dialinfo for WS/WSS protocol"), }; - let tcp_stream = TcpStream::connect(format!("{}:{}", &domain, &port)) - .await + // Resolve remote address + let remote_ip_addr = dial_info.resolve()?; + let remote_socket_addr = SocketAddr::new(remote_ip_addr, port); + + // Make a shared socket + let socket = new_shared_tcp_socket(local_address)?; + + // Connect to the remote address + let remote_socket2_addr = socket2::SockAddr::from(remote_socket_addr); + socket + .connect(&remote_socket2_addr) .map_err(map_to_string) - .map_err(logthru_net!(error))?; - let local_addr = tcp_stream + .map_err(logthru_net!(error "addr={}", remote_socket_addr))?; + let std_stream: std::net::TcpStream = socket.into(); + let tcp_stream = TcpStream::from(std_stream); + + // See what local address we ended up with + let actual_local_addr = tcp_stream .local_addr() .map_err(map_to_string) - .map_err(logthru_net!(error))?; - let peer_socket_addr = tcp_stream - .peer_addr() - .map_err(map_to_string) - .map_err(logthru_net!(error))?; - let peer_addr = PeerAddress::new( - Address::from_socket_addr(peer_socket_addr), - peer_socket_addr.port(), - protocol_type, - ); + .map_err(logthru_net!())?; + // Negotiate TLS if this is WSS if tls { let connector = TlsConnector::default(); let tls_stream = connector @@ -294,9 +301,18 @@ impl WebsocketProtocolHandler { .map_err(map_to_string) .map_err(logthru_net!(error))?; let conn = NetworkConnection::Wss(WebsocketNetworkConnection::new(tls, ws_stream)); + + // Make the connection descriptor peer address + let peer_addr = PeerAddress::new( + Address::from_socket_addr(remote_socket_addr), + port, + ProtocolType::WSS, + ); + + // Register the WSS connection network_manager .on_new_connection( - ConnectionDescriptor::new(peer_addr, local_addr), + ConnectionDescriptor::new(peer_addr, actual_local_addr), conn.clone(), ) .await?; @@ -307,9 +323,18 @@ impl WebsocketProtocolHandler { .map_err(map_to_string) .map_err(logthru_net!(error))?; let conn = NetworkConnection::Ws(WebsocketNetworkConnection::new(tls, ws_stream)); + + // Make the connection descriptor peer address + let peer_addr = PeerAddress::new( + Address::from_socket_addr(remote_socket_addr), + port, + ProtocolType::WS, + ); + + // Register the WS connection network_manager .on_new_connection( - ConnectionDescriptor::new(peer_addr, local_addr), + ConnectionDescriptor::new(peer_addr, actual_local_addr), conn.clone(), ) .await?; diff --git a/veilid-core/src/routing_table/dial_info_entry.rs b/veilid-core/src/routing_table/dial_info_entry.rs index 9e169974..a97bdb97 100644 --- a/veilid-core/src/routing_table/dial_info_entry.rs +++ b/veilid-core/src/routing_table/dial_info_entry.rs @@ -25,7 +25,7 @@ impl DialInfoEntry { self.resolved_address } - pub fn resolve(&mut self) -> Result { + pub fn resolve(&mut self) -> Result { let addr = match self.dial_info.resolve() { Ok(a) => a, Err(_) => return Err("failed to resolve address".to_owned()), diff --git a/veilid-core/src/veilid_api/mod.rs b/veilid-core/src/veilid_api/mod.rs index b3ad639c..e04ef010 100644 --- a/veilid-core/src/veilid_api/mod.rs +++ b/veilid-core/src/veilid_api/mod.rs @@ -20,15 +20,105 @@ pub use core::str::FromStr; ///////////////////////////////////////////////////////////////////////////////////////////////////// +#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)] +pub enum VeilidAPIError { + Timeout, + Shutdown, + NodeNotFound(NodeId), + NoDialInfo(NodeId), + Internal(String), + Unimplemented(String), + ParseError { + message: String, + value: String, + }, + InvalidArgument { + context: String, + argument: String, + value: String, + }, + MissingArgument { + context: String, + argument: String, + }, +} + +impl fmt::Display for VeilidAPIError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + VeilidAPIError::Timeout => write!(f, "VeilidAPIError::Timeout"), + VeilidAPIError::Shutdown => write!(f, "VeilidAPIError::Shutdown"), + VeilidAPIError::NodeNotFound(ni) => write!(f, "VeilidAPIError::NodeNotFound({})", ni), + VeilidAPIError::NoDialInfo(ni) => write!(f, "VeilidAPIError::NoDialInfo({})", ni), + VeilidAPIError::Internal(e) => write!(f, "VeilidAPIError::Internal({})", e), + VeilidAPIError::Unimplemented(e) => write!(f, "VeilidAPIError::Unimplemented({})", e), + VeilidAPIError::ParseError { message, value } => { + write!(f, "VeilidAPIError::ParseError({}: {})", message, value) + } + VeilidAPIError::InvalidArgument { + context, + argument, + value, + } => { + write!( + f, + "VeilidAPIError::InvalidArgument({}: {} = {})", + context, argument, value + ) + } + VeilidAPIError::MissingArgument { context, argument } => { + write!( + f, + "VeilidAPIError::MissingArgument({}: {})", + context, argument + ) + } + } + } +} + +fn convert_rpc_error(x: RPCError) -> VeilidAPIError { + match x { + RPCError::Timeout => VeilidAPIError::Timeout, + RPCError::Unimplemented(s) => VeilidAPIError::Unimplemented(s), + RPCError::Internal(s) => VeilidAPIError::Internal(s), + RPCError::Protocol(s) => VeilidAPIError::Internal(s), + RPCError::InvalidFormat => VeilidAPIError::Internal("Invalid packet format".to_owned()), + } +} + +macro_rules! map_rpc_error { + () => { + |x| convert_rpc_error(x) + }; +} + +macro_rules! parse_error { + ($msg:expr, $val:expr) => { + VeilidAPIError::ParseError { + message: $msg.to_string(), + value: $val.to_string(), + } + }; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// + #[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord)] pub struct NodeId { pub key: DHTKey, } impl NodeId { pub fn new(key: DHTKey) -> Self { + assert!(key.valid); Self { key } } } +impl fmt::Display for NodeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", self.key.encode()) + } +} #[derive(Clone, Debug, Default, PartialOrd, PartialEq, Eq, Ord)] pub struct ValueKey { @@ -91,33 +181,26 @@ pub enum ProtocolType { } #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] -pub enum ProtocolAddressType { +pub enum ProtocolNetworkType { UDPv4, UDPv6, TCPv4, TCPv6, - WS, - WSS, } -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] pub enum Address { IPV4(Ipv4Addr), IPV6(Ipv6Addr), - Hostname(String), +} + +impl Default for Address { + fn default() -> Self { + Address::IPV4(Ipv4Addr::new(0, 0, 0, 0)) + } } impl Address { - pub fn to_canonical(&self) -> Address { - match self { - Address::IPV4(v4) => Address::IPV4(*v4), - Address::IPV6(v6) => match v6.to_ipv4() { - Some(v4) => Address::IPV4(v4), - None => Address::IPV6(*v6), - }, - Address::Hostname(h) => Address::Hostname(h.clone()), - } - } pub fn from_socket_addr(sa: SocketAddr) -> Address { match sa { SocketAddr::V4(v4) => Address::IPV4(*v4.ip()), @@ -128,77 +211,138 @@ impl Address { match self { Address::IPV4(v4) => v4.to_string(), Address::IPV6(v6) => v6.to_string(), - Address::Hostname(h) => h.clone(), } } pub fn address_string_with_port(&self, port: u16) -> String { match self { Address::IPV4(v4) => format!("{}:{}", v4.to_string(), port), Address::IPV6(v6) => format!("[{}]:{}", v6.to_string(), port), - Address::Hostname(h) => format!("{}:{}", h.clone(), port), } } - pub fn resolve(&self) -> Result { + pub fn is_public(&self) -> bool { match self { - Self::IPV4(a) => Ok(IpAddr::V4(*a)), - Self::IPV6(a) => Ok(IpAddr::V6(*a)), - Self::Hostname(h) => h - .parse() - .map_err(|e| format!("Failed to parse hostname: {}", e)), + Address::IPV4(v4) => ipv4addr_is_global(&v4), + Address::IPV6(v6) => ipv6addr_is_global(&v6), } } - pub fn address(&self) -> Result { + pub fn is_private(&self) -> bool { match self { - Self::IPV4(a) => Ok(IpAddr::V4(*a)), - Self::IPV6(a) => Ok(IpAddr::V6(*a)), - Self::Hostname(h) => Err(format!("Address not available for hostname: {}", h)), + Address::IPV4(v4) => ipv4addr_is_private(&v4), + Address::IPV6(v6) => ipv6addr_is_unicast_site_local(&v6), } } - pub fn to_socket_addr(&self, port: u16) -> Result { - let addr = self.address()?; - Ok(SocketAddr::new(addr, port)) + pub fn to_ip_addr(&self) -> IpAddr { + match self { + Self::IPV4(a) => IpAddr::V4(*a), + Self::IPV6(a) => IpAddr::V6(*a), + } + } + pub fn to_socket_addr(&self, port: u16) -> SocketAddr { + SocketAddr::new(self.to_ip_addr(), port) + } + pub fn to_canonical(&self) -> Address { + match self { + Address::IPV4(v4) => Address::IPV4(*v4), + Address::IPV6(v6) => match v6.to_ipv4() { + Some(v4) => Address::IPV4(v4), + None => Address::IPV6(*v6), + }, + } } } -impl core::str::FromStr for Address { - type Err = String; - fn from_str(host: &str) -> Result { +impl FromStr for Address { + type Err = VeilidAPIError; + fn from_str(host: &str) -> Result { if let Ok(addr) = Ipv4Addr::from_str(host) { Ok(Address::IPV4(addr)) } else if let Ok(addr) = Ipv6Addr::from_str(host) { Ok(Address::IPV6(addr)) - } else if !host.is_empty() { - Ok(Address::Hostname(host.to_string())) } else { - Err("unable to convert address to string".to_owned()) + Err(parse_error!("Address::from_str failed", host)) } } } -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Copy, Default, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] +pub struct SocketAddress { + address: Address, + port: u16, +} + +impl SocketAddress { + pub fn new(address: Address, port: u16) -> Self { + Self { address, port } + } + pub fn from_socket_addr(sa: SocketAddr) -> SocketAddress { + Self { + address: Address::from_socket_addr(sa), + port: sa.port(), + } + } + pub fn address(&self) -> Address { + self.address + } + pub fn port(&self) -> u16 { + self.port + } + pub fn to_canonical(&self) -> SocketAddress { + SocketAddress { + address: self.address.to_canonical(), + port: self.port, + } + } + pub fn to_ip_addr(&self) -> IpAddr { + self.address.to_ip_addr() + } + pub fn to_socket_addr(&self) -> SocketAddr { + self.address.to_socket_addr(self.port) + } +} + +impl fmt::Display for SocketAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}:{}", self.to_ip_addr(), self.port) + } +} + +impl FromStr for SocketAddress { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + let split = s.rsplit_once(':').ok_or_else(|| { + parse_error!("SocketAddress::from_str missing colon port separator", s) + })?; + let address = Address::from_str(split.0)?; + let port = u16::from_str(split.1).map_err(|e| { + parse_error!( + format!("SocketAddress::from_str failed parting port: {}", e), + s + ) + })?; + Ok(SocketAddress { address, port }) + } +} + +#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq)] pub struct DialInfoUDP { - pub address: Address, - pub port: u16, + pub socket_address: SocketAddress, } -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq)] pub struct DialInfoTCP { - pub address: Address, - pub port: u16, + pub socket_address: SocketAddress, } -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq)] pub struct DialInfoWS { - pub host: String, - pub port: u16, - pub path: String, + pub socket_address: SocketAddress, + pub request: String, } -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] +#[derive(Clone, Default, Debug, PartialEq, PartialOrd, Ord, Eq)] pub struct DialInfoWSS { - pub host: String, - pub port: u16, - pub path: String, + pub socket_address: SocketAddress, + pub request: String, } #[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] @@ -208,39 +352,124 @@ pub enum DialInfo { WS(DialInfoWS), WSS(DialInfoWSS), } +impl Default for DialInfo { + fn default() -> Self { + DialInfo::UDP(DialInfoUDP::default()) + } +} + +impl fmt::Display for DialInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + DialInfo::UDP(di) => write!(f, "udp|{}", di.socket_address), + DialInfo::TCP(di) => write!(f, "tcp|{}", di.socket_address), + DialInfo::WS(di) => write!(f, "ws|{}|{}", di.socket_address, di.request), + DialInfo::WSS(di) => write!(f, "wss|{}|{}", di.socket_address, di.request), + } + } +} + +impl FromStr for DialInfo { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + let (proto, rest) = s.split_once('|').ok_or_else(|| { + parse_error!("SocketAddress::from_str missing protocol '|' separator", s) + })?; + match proto { + "udp" => { + let socket_address = SocketAddress::from_str(rest)?; + Ok(DialInfo::udp(socket_address)) + } + "tcp" => { + let socket_address = SocketAddress::from_str(rest)?; + Ok(DialInfo::tcp(socket_address)) + } + "ws" => { + let (sa, rest) = s.split_once('|').ok_or_else(|| { + parse_error!( + "SocketAddress::from_str missing socket address '|' separator", + s + ) + })?; + let socket_address = SocketAddress::from_str(sa)?; + DialInfo::try_ws(socket_address, rest.to_string()) + } + "wss" => { + let (sa, rest) = s.split_once('|').ok_or_else(|| { + parse_error!( + "SocketAddress::from_str missing socket address '|' separator", + s + ) + })?; + let socket_address = SocketAddress::from_str(sa)?; + DialInfo::try_wss(socket_address, rest.to_string()) + } + } + } +} impl DialInfo { - pub fn udp_from_socketaddr(socketaddr: SocketAddr) -> Self { + pub fn udp_from_socketaddr(socket_addr: SocketAddr) -> Self { Self::UDP(DialInfoUDP { - address: Address::from_socket_addr(socketaddr).to_canonical(), - port: socketaddr.port(), + socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(), }) } - pub fn tcp_from_socketaddr(socketaddr: SocketAddr) -> Self { + pub fn tcp_from_socketaddr(socket_addr: SocketAddr) -> Self { Self::TCP(DialInfoTCP { - address: Address::from_socket_addr(socketaddr).to_canonical(), - port: socketaddr.port(), + socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(), }) } - pub fn udp(address: Address, port: u16) -> Self { - let address = address.to_canonical(); - if let Address::Hostname(_) = address { - panic!("invalid address type for protocol") + pub fn udp(socket_address: SocketAddress) -> Self { + Self::UDP(DialInfoUDP { + socket_address: socket_address.to_canonical(), + }) + } + pub fn tcp(socket_address: SocketAddress) -> Self { + Self::TCP(DialInfoTCP { + socket_address: socket_address.to_canonical(), + }) + } + pub fn try_ws(socket_address: SocketAddress, url: String) -> Result { + let split_url = SplitUrl::from_str(&url) + .map_err(|e| parse_error!(format!("unable to split WS url: {}", e), url))?; + if split_url.scheme != "ws" || !url.starts_with("ws://") { + return Err(parse_error!("incorrect scheme for WS dialinfo", url)); } - Self::UDP(DialInfoUDP { address, port }) - } - pub fn tcp(address: Address, port: u16) -> Self { - let address = address.to_canonical(); - if let Address::Hostname(_) = address { - panic!("invalid address type for protocol") + let url_port = split_url.port.unwrap_or(80u16); + if url_port != socket_address.port() { + return Err(parse_error!( + "socket address port doesn't match url port", + url + )); } - Self::TCP(DialInfoTCP { address, port }) + Ok(Self::WS(DialInfoWS { + socket_address: socket_address.to_canonical(), + request: url[5..].to_string(), + })) } - pub fn ws(host: String, port: u16, path: String) -> Self { - Self::WS(DialInfoWS { host, port, path }) - } - pub fn wss(host: String, port: u16, path: String) -> Self { - Self::WSS(DialInfoWSS { host, port, path }) + pub fn try_wss(socket_address: SocketAddress, url: String) -> Result { + let split_url = SplitUrl::from_str(&url) + .map_err(|e| parse_error!(format!("unable to split WSS url: {}", e), url))?; + if split_url.scheme != "wss" || !url.starts_with("wss://") { + return Err(parse_error!("incorrect scheme for WSS dialinfo", url)); + } + let url_port = split_url.port.unwrap_or(443u16); + if url_port != socket_address.port() { + return Err(parse_error!( + "socket address port doesn't match url port", + url + )); + } + if !Address::from_str(&split_url.host).is_err() { + return Err(parse_error!( + "WSS url can not use address format, only hostname format", + url + )); + } + Ok(Self::WSS(DialInfoWSS { + socket_address: socket_address.to_canonical(), + request: url[6..].to_string(), + })) } pub fn protocol_type(&self) -> ProtocolType { match self { @@ -250,234 +479,79 @@ impl DialInfo { Self::WSS(_) => ProtocolType::WSS, } } - - pub fn protocol_address_type(&self) -> ProtocolAddressType { + pub fn protocol_network_type(&self) -> ProtocolNetworkType { match self { - Self::UDP(di) => match di.address { - Address::IPV4(_) => ProtocolAddressType::UDPv4, - Address::IPV6(_) => ProtocolAddressType::UDPv6, - Address::Hostname(_) => panic!("invalid address type for protocol"), + Self::UDP(di) => match di.socket_address.address() { + Address::IPV4(_) => ProtocolNetworkType::UDPv4, + Address::IPV6(_) => ProtocolNetworkType::UDPv6, }, - Self::TCP(di) => match di.address { - Address::IPV4(_) => ProtocolAddressType::TCPv4, - Address::IPV6(_) => ProtocolAddressType::TCPv6, - Address::Hostname(_) => panic!("invalid address type for protocol"), + Self::TCP(di) => match di.socket_address.address() { + Address::IPV4(_) => ProtocolNetworkType::TCPv4, + Address::IPV6(_) => ProtocolNetworkType::TCPv6, }, - Self::WS(_) => ProtocolAddressType::WS, - Self::WSS(_) => ProtocolAddressType::WSS, - } - } - - pub fn try_udp_v4(&self) -> Option { - match self { - Self::UDP(v) => match v.address.to_socket_addr(v.port).ok() { - Some(SocketAddr::V4(v4)) => Some(v4), - _ => None, + Self::WS(di) => match di.socket_address.address() { + Address::IPV4(_) => ProtocolNetworkType::TCPv4, + Address::IPV6(_) => ProtocolNetworkType::TCPv6, }, - _ => None, - } - } - - pub fn try_udp_v6(&self) -> Option { - match self { - Self::UDP(v) => match v.address.to_socket_addr(v.port).ok() { - Some(SocketAddr::V6(v6)) => Some(v6), - _ => None, + Self::WSS(di) => match di.socket_address.address() { + Address::IPV4(_) => ProtocolNetworkType::TCPv4, + Address::IPV6(_) => ProtocolNetworkType::TCPv6, }, - _ => None, } } - - pub fn try_tcp_v4(&self) -> Option { + pub fn socket_address(&self) -> SocketAddress { match self { - Self::TCP(v) => match v.address.to_socket_addr(v.port).ok() { - Some(SocketAddr::V4(v4)) => Some(v4), - _ => None, - }, - _ => None, + Self::UDP(di) => di.socket_address, + Self::TCP(di) => di.socket_address, + Self::WS(di) => di.socket_address, + Self::WSS(di) => di.socket_address, } } - - pub fn try_tcp_v6(&self) -> Option { + pub fn to_ip_addr(&self) -> IpAddr { match self { - Self::TCP(v) => match v.address.to_socket_addr(v.port).ok() { - Some(SocketAddr::V6(v6)) => Some(v6), - _ => None, - }, - _ => None, - } - } - - pub fn try_ws(&self) -> Option { - match self { - Self::WS(v) => Some(v.host.clone()), - _ => None, - } - } - - pub fn try_wss(&self) -> Option { - match self { - Self::WSS(v) => Some(v.host.clone()), - _ => None, - } - } - - pub fn address_string(&self) -> String { - match self { - Self::UDP(di) => di.address.address_string(), - Self::TCP(di) => di.address.address_string(), - Self::WS(di) => di.host.clone(), - Self::WSS(di) => di.host.clone(), - } - } - pub fn address_string_with_port(&self) -> String { - match self { - Self::UDP(di) => di.address.address_string_with_port(di.port), - Self::TCP(di) => di.address.address_string_with_port(di.port), - Self::WS(di) => format!("{}:{}", di.host.clone(), di.port), - Self::WSS(di) => format!("{}:{}", di.host.clone(), di.port), - } - } - pub fn all_but_path(&self) -> String { - match self { - Self::UDP(di) => format!("udp://{}", di.address.address_string_with_port(di.port)), - Self::TCP(di) => format!("tcp://{}", di.address.address_string_with_port(di.port)), - Self::WS(di) => format!("ws://{}:{}", di.host.clone(), di.port), - Self::WSS(di) => format!("wss://{}:{}", di.host.clone(), di.port), - } - } - - pub fn to_url_string(&self, user: Option) -> String { - let user_string = match user { - Some(u) => format!("{}@", u), - None => "".to_owned(), - }; - match self { - Self::UDP(di) => format!( - "udp://{}{}", - user_string, - di.address.address_string_with_port(di.port) - ), - Self::TCP(di) => format!( - "tcp://{}{}", - user_string, - di.address.address_string_with_port(di.port) - ), - Self::WS(di) => format!( - "ws://{}{}:{}{}", - user_string, - di.host.clone(), - di.port, - prepend_slash(di.path.clone()) - ), - Self::WSS(di) => format!( - "wss://{}{}:{}{}", - user_string, - di.host.clone(), - di.port, - prepend_slash(di.path.clone()) - ), - } - } - - pub fn resolve(&self) -> Result { - match self { - Self::UDP(di) => { - let addr = di.address.resolve()?; - Ok(addr) - } - Self::TCP(di) => { - let addr = di.address.resolve()?; - Ok(addr) - } - Self::WS(di) => { - let addr: IpAddr = di - .host - .parse() - .map_err(|e| format!("Failed to parse WS host '{}': {}", di.host, e))?; - Ok(addr) - } - Self::WSS(di) => { - let addr: IpAddr = di - .host - .parse() - .map_err(|e| format!("Failed to parse WSS host '{}': {}", di.host, e))?; - Ok(addr) - } - } - } - pub fn address(&self) -> Result { - match self { - Self::UDP(di) => di.address.address(), - Self::TCP(di) => di.address.address(), - Self::WS(_) => Err("Address not available for WS protocol".to_owned()), - Self::WSS(_) => Err("Address not available for WSS protocol".to_owned()), + Self::UDP(di) => di.socket_address.to_ip_addr(), + Self::TCP(di) => di.socket_address.to_ip_addr(), + Self::WS(di) => di.socket_address.to_ip_addr(), + Self::WSS(di) => di.socket_address.to_ip_addr(), } } pub fn port(&self) -> u16 { match self { - Self::UDP(di) => di.port, - Self::TCP(di) => di.port, - Self::WS(di) => di.port, - Self::WSS(di) => di.port, + Self::UDP(di) => di.socket_address.port, + Self::TCP(di) => di.socket_address.port, + Self::WS(di) => di.socket_address.port, + Self::WSS(di) => di.socket_address.port, } } - pub fn path(&self) -> Result { + pub fn to_socket_addr(&self) -> SocketAddr { match self { - Self::UDP(_) => Err("path not available for udp protocol".to_owned()), - Self::TCP(_) => Err("path not available for tcp protocol".to_owned()), - Self::WS(di) => Ok(di.path.clone()), - Self::WSS(di) => Ok(di.path.clone()), + Self::UDP(di) => di.socket_address.to_socket_addr(), + Self::TCP(di) => di.socket_address.to_socket_addr(), + Self::WS(di) => di.socket_address.to_socket_addr(), + Self::WSS(di) => di.socket_address.to_socket_addr(), } } - pub fn to_socket_addr(&self) -> Result { + pub fn request(&self) -> Option { match self { - Self::UDP(di) => Ok(SocketAddr::new(di.address.address()?, di.port)), - Self::TCP(di) => Ok(SocketAddr::new(di.address.address()?, di.port)), - Self::WS(_) => Err("Can not directly convert WS hostname to socket addr".to_owned()), - Self::WSS(_) => Err("Can not directly convert WSS hostname to socket addr".to_owned()), + Self::UDP(_) => None, + Self::TCP(_) => None, + Self::WS(di) => Some(format!("ws://{}", di.request)), + Self::WSS(di) => Some(format!("wss://{}", di.request)), } } - - pub fn is_public(&self) -> Result { - let addr = self - .resolve() - .map_err(|_| "failed to resolve address".to_owned())?; - Ok(ipaddr_is_global(&addr)) + pub fn is_public(&self) -> bool { + self.socket_address().address().is_public() } - pub fn is_private(&self) -> Result { - let addr = self - .resolve() - .map_err(|_| "failed to resolve address".to_owned())?; - Ok(match addr { - IpAddr::V4(a) => ipv4addr_is_private(&a), - IpAddr::V6(a) => ipv6addr_is_unicast_site_local(&a), - }) + pub fn is_private(&self) -> bool { + self.socket_address().address().is_private() } - pub fn is_valid(&self) -> Result { - Ok(self.is_public()? || self.is_private()?) - } - pub fn is_loopback(&self) -> Result { - let addr = self - .resolve() - .map_err(|_| "failed to resolve address".to_owned())?; - Ok(ipaddr_is_loopback(&addr)) - } -} -impl ToString for DialInfo { - fn to_string(&self) -> String { - self.to_url_string(None) - } -} - -impl Default for DialInfo { - fn default() -> Self { - Self::UDP(DialInfoUDP { - address: Address::IPV4(Ipv4Addr::new(0, 0, 0, 0)), - port: 0u16, - }) + pub fn is_valid(&self) -> bool { + let socket_address = self.socket_address(); + let address = socket_address.address(); + let port = socket_address.port(); + (address.is_public() || address.is_private()) && port > 0 } } @@ -498,37 +572,40 @@ pub struct PeerInfo { #[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)] pub struct PeerAddress { - pub address: Address, - pub port: u16, + pub socket_address: SocketAddress, pub protocol_type: ProtocolType, } impl PeerAddress { - pub fn new(address: Address, port: u16, protocol_type: ProtocolType) -> Self { + pub fn new(socket_address: SocketAddress, protocol_type: ProtocolType) -> Self { Self { - address, - port, + socket_address, protocol_type, } } - pub fn to_socket_addr(&self) -> Result { - self.address.to_socket_addr(self.port) + pub fn to_socket_addr(&self) -> SocketAddr { + self.socket_address.to_socket_addr() } - pub fn protocol_address_type(&self) -> ProtocolAddressType { + + pub fn protocol_network_type(&self) -> ProtocolNetworkType { match self.protocol_type { - ProtocolType::UDP => match self.address { - Address::IPV4(_) => ProtocolAddressType::UDPv4, - Address::IPV6(_) => ProtocolAddressType::UDPv6, - Address::Hostname(_) => panic!("invalid address type for protocol"), + ProtocolType::UDP => match self.socket_address.address() { + Address::IPV4(_) => ProtocolNetworkType::UDPv4, + Address::IPV6(_) => ProtocolNetworkType::UDPv6, }, - ProtocolType::TCP => match self.address { - Address::IPV4(_) => ProtocolAddressType::TCPv4, - Address::IPV6(_) => ProtocolAddressType::TCPv6, - Address::Hostname(_) => panic!("invalid address type for protocol"), + ProtocolType::TCP => match self.socket_address.address() { + Address::IPV4(_) => ProtocolNetworkType::TCPv4, + Address::IPV6(_) => ProtocolNetworkType::TCPv6, + }, + ProtocolType::WS => match self.socket_address.address() { + Address::IPV4(_) => ProtocolNetworkType::TCPv4, + Address::IPV6(_) => ProtocolNetworkType::TCPv6, + }, + ProtocolType::WSS => match self.socket_address.address() { + Address::IPV4(_) => ProtocolNetworkType::TCPv4, + Address::IPV6(_) => ProtocolNetworkType::TCPv6, }, - ProtocolType::WS => ProtocolAddressType::WS, - ProtocolType::WSS => ProtocolAddressType::WSS, } } } @@ -555,101 +632,48 @@ impl ConnectionDescriptor { pub fn protocol_type(&self) -> ProtocolType { self.remote.protocol_type } - pub fn protocol_address_type(&self) -> ProtocolAddressType { - self.remote.protocol_address_type() + pub fn protocol_network_type(&self) -> ProtocolNetworkType { + self.remote.protocol_network_type() } } ////////////////////////////////////////////////////////////////////////// -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)] pub struct NodeDialInfoSingle { pub node_id: NodeId, pub dial_info: DialInfo, } -impl core::str::FromStr for NodeDialInfoSingle { - type Err = String; - fn from_str(url: &str) -> Result { - let mut cur_url = url; - let proto; - if url.starts_with("udp://") { - cur_url = &cur_url[6..]; - proto = ProtocolType::UDP; - } else if url.starts_with("tcp://") { - cur_url = &cur_url[6..]; - proto = ProtocolType::TCP; - } else if url.starts_with("ws://") { - cur_url = &cur_url[5..]; - proto = ProtocolType::WS; - } else if url.starts_with("wss://") { - cur_url = &cur_url[6..]; - proto = ProtocolType::WSS; - } else { - return Err(format!("unknown protocol: {}", url)); - } - - // parse out node id if we have one - let node_id = match cur_url.find('@') { - Some(x) => { - let n = NodeId::new(DHTKey::try_decode(&cur_url[0..x])?); - cur_url = &cur_url[x + 1..]; - n - } - None => { - return Err("NodeDialInfoSingle is missing the node id".to_owned()); - } - }; - - // parse out address - let address = match cur_url.rfind(':') { - Some(x) => { - let mut h = &cur_url[0..x]; - cur_url = &cur_url[x + 1..]; - - match proto { - ProtocolType::WS | ProtocolType::WSS => Address::Hostname(h.to_string()), - _ => { - // peel off square brackets on ipv6 address - if x >= 2 && &h[0..1] == "[" && &h[(h.len() - 1)..] == "]" { - h = &h[1..(h.len() - 1)]; - } - Address::from_str(h)? - } - } - } - None => { - return Err("NodeDialInfoSingle is missing the port".to_owned()); - } - }; - - // parse out port - let pathstart = cur_url.find('/').unwrap_or(cur_url.len()); - let port = - u16::from_str(&cur_url[0..pathstart]).map_err(|e| format!("port is invalid: {}", e))?; - cur_url = &cur_url[pathstart..]; - - // build NodeDialInfoSingle - Ok(NodeDialInfoSingle { - node_id, - dial_info: match proto { - ProtocolType::UDP => DialInfo::udp(address, port), - ProtocolType::TCP => DialInfo::tcp(address, port), - ProtocolType::WS => { - DialInfo::ws(address.address_string(), port, cur_url.to_string()) - } - ProtocolType::WSS => { - DialInfo::wss(address.address_string(), port, cur_url.to_string()) - } - }, - }) +impl fmt::Display for NodeDialInfoSingle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}@{}", self.node_id, self.dial_info) } } -impl ToString for NodeDialInfoSingle { - fn to_string(&self) -> String { - self.dial_info - .to_url_string(Some(self.node_id.key.encode())) +impl FromStr for NodeDialInfoSingle { + type Err = VeilidAPIError; + fn from_str(s: &str) -> Result { + // split out node id from the dial info + let (node_id_str, rest) = s.split_once('@').ok_or_else(|| { + parse_error!( + "NodeDialInfoSingle::from_str missing @ node id separator", + s + ) + })?; + + // parse out node id + let node_id = NodeId::new(DHTKey::try_decode(node_id_str).map_err(|e| { + parse_error!( + format!("NodeDialInfoSingle::from_str couldn't parse node id: {}", e), + s + ) + })?); + // parse out dial info + let dial_info = DialInfo::from_str(rest)?; + + // return completed NodeDialInfoSingle + Ok(NodeDialInfoSingle { node_id, dial_info }) } } @@ -705,41 +729,6 @@ cfg_if! { } } -#[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)] -pub enum VeilidAPIError { - Timeout, - Shutdown, - NodeNotFound(NodeId), - NoDialInfo(NodeId), - Internal(String), - Unimplemented(String), - InvalidArgument { - context: String, - argument: String, - value: String, - }, - MissingArgument { - context: String, - argument: String, - }, -} - -fn convert_rpc_error(x: RPCError) -> VeilidAPIError { - match x { - RPCError::Timeout => VeilidAPIError::Timeout, - RPCError::Unimplemented(s) => VeilidAPIError::Unimplemented(s), - RPCError::Internal(s) => VeilidAPIError::Internal(s), - RPCError::Protocol(s) => VeilidAPIError::Internal(s), - RPCError::InvalidFormat => VeilidAPIError::Internal("Invalid packet format".to_owned()), - } -} - -macro_rules! map_rpc_error { - () => { - |x| convert_rpc_error(x) - }; -} - #[derive(Clone, Debug, PartialOrd, PartialEq, Eq, Ord)] pub enum TunnelMode { Raw,