diff --git a/src/lib.rs b/src/lib.rs index 5cd7b68..acb7cd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -727,8 +727,10 @@ impl CodexConnectorClient { max_frame_bytes: usize, response_timeout: Option, ) -> Result { - if !endpoint.ip().is_loopback() - || endpoint.port() == 0 + if endpoint.port() == 0 + || endpoint.ip().is_unspecified() + || endpoint.ip().is_multicast() + || endpoint.ip() == std::net::Ipv4Addr::BROADCAST || !(4096..=u32::MAX as usize).contains(&max_frame_bytes) || response_timeout.is_some_and(|timeout| timeout.is_zero()) { @@ -770,6 +772,45 @@ impl CodexConnectorClient { } } +#[cfg(test)] +mod client_endpoint_tests { + use super::*; + + #[test] + fn client_admits_explicit_unicast_and_refuses_non_destinations() { + for endpoint in ["127.0.0.1:4103", "172.19.0.1:4103", "[::1]:4103"] { + assert!( + CodexConnectorClient::new( + endpoint.parse().unwrap(), + "bounded-client-endpoint-key", + 64 * 1024, + Some(Duration::from_secs(1)), + ) + .is_ok(), + "{endpoint} should be an admissible explicit destination" + ); + } + for endpoint in [ + "0.0.0.0:4103", + "255.255.255.255:4103", + "224.0.0.1:4103", + "[::]:4103", + "[ff02::1]:4103", + "127.0.0.1:0", + ] { + assert!(matches!( + CodexConnectorClient::new( + endpoint.parse().unwrap(), + "bounded-client-endpoint-key", + 64 * 1024, + Some(Duration::from_secs(1)), + ), + Err(CodexConnectorClientError::InvalidConfig) + )); + } + } +} + #[derive(Debug, Error)] pub enum CodexConnectorClientError { #[error("invalid connector client configuration")]