diff --git a/src/packet.rs b/src/packet.rs index 74789d5..702796a 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -364,7 +364,10 @@ pub fn universe_to_ipv6_multicast_addr(universe: u16) -> Result { .into()) } -/// Checks if the given universe is a valid universe to send on (within allowed range). +/// Checks if the given universe is a valid universe to send on or listen to (within allowed range). +/// +/// This includes `E131_DISCOVERY_UNIVERSE`, which carries universe discovery packets. Use +/// [`is_data_universe_in_range`] for universes that carry DMX data or synchronisation packets. /// /// # Errors /// `IllegalUniverse`: Returned if the universe is outwith the allowed range of universes @@ -378,6 +381,20 @@ pub fn is_universe_in_range(universe: u16) -> Result<()> { Ok(()) } +/// Checks if the given universe can carry DMX data or synchronisation packets. +/// +/// As per ANSI E1.31-2016 Section 6.2.7 data universes are limited to the range 1 to 63999, so unlike +/// [`is_universe_in_range`] this excludes `E131_DISCOVERY_UNIVERSE` (64214), which is reserved for universe discovery. +/// +/// # Errors +/// `IllegalUniverse`: Returned if the universe is outwith [`E131_MIN_MULTICAST_UNIVERSE`, `E131_MAX_MULTICAST_UNIVERSE`]. +pub fn is_data_universe_in_range(universe: u16) -> Result<()> { + if !(E131_MIN_MULTICAST_UNIVERSE..=E131_MAX_MULTICAST_UNIVERSE).contains(&universe) { + return Err(SacnError::IllegalUniverse(universe)); + } + Ok(()) +} + /// Fills the given array of bytes with the given length n with bytes of value 0. #[inline] fn zeros(buf: &mut [u8], n: usize) { diff --git a/src/source.rs b/src/source.rs index f701751..07c159a 100644 --- a/src/source.rs +++ b/src/source.rs @@ -631,9 +631,9 @@ impl SacnSourceInternal { /// If a universe is already registered then this method has no effect. /// /// # Errors - /// `IllegalUniverse`: Returned if the universe is outwith the allowed range, see (`is_universe_in_range`)[`fn.is_universe_in_range.packet`]. + /// `IllegalUniverse`: Returned if the universe can't carry data, see (`is_data_universe_in_range`)[`fn.is_data_universe_in_range.packet`]. fn register_universe(&mut self, universe: u16) -> Result<()> { - is_universe_in_range(universe)?; + is_data_universe_in_range(universe)?; if self.universes.is_empty() { self.universes.push(universe); @@ -672,11 +672,11 @@ impl SacnSourceInternal { /// Checks if the given universe is a valid universe to send on (within allowed range) and that it is registered with this `SacnSourceInternal`. /// /// # Errors - /// `IllegalUniverse`: Returned if the universe is outwith the allowed range, see (`is_universe_in_range`)[`fn.is_universe_in_range.packet`]. + /// `IllegalUniverse`: Returned if the universe can't carry data, see (`is_data_universe_in_range`)[`fn.is_data_universe_in_range.packet`]. /// /// `UniverseNotRegistered`: Returned if the universe is not registered on the given `SacnSourceInternal`. fn universe_allowed(&self, u: &u16) -> Result<()> { - is_universe_in_range(*u)?; + is_data_universe_in_range(*u)?; if !self.universes.contains(u) { return Err(SacnError::UniverseNotRegistered(*u)); @@ -751,7 +751,7 @@ impl SacnSourceInternal { // Check that the synchronisation universe is a valid universe. It doesn't need to be registered: a synchronisation // universe carries no data, so it isn't advertised in universe discovery. if let Some(sync_addr) = synchronisation_addr { - is_universe_in_range(sync_addr) + is_data_universe_in_range(sync_addr) .map_err(|_e| SacnError::IllegalSyncUniverse(sync_addr))?; } @@ -894,7 +894,7 @@ impl SacnSourceInternal { /// /// `SacnParsePackError`: Returned if the sync packet fails to be packed. fn send_sync_packet(&self, universe: u16, dst_ip: Option) -> Result<()> { - is_universe_in_range(universe)?; + is_data_universe_in_range(universe)?; let ip = if let Some(dst) = dst_ip { dst.into() diff --git a/tests/src_tests.rs b/tests/src_tests.rs index ee9605f..18cbabb 100644 --- a/tests/src_tests.rs +++ b/tests/src_tests.rs @@ -488,24 +488,52 @@ fn test_register_above_max_universe() { } } -/// Attempt to register the discovery universe. Even though this is higher than the maximum allowed universe this should succeed as per ANSI E1.31-2018 Section 6.2.7. +/// Attempt to register the discovery universe. This should fail: as per ANSI E1.31-2016 Section 6.2.7 data universes are +/// limited to 1 to 63999, and universe 64214 is reserved for universe discovery packets. /// Extreme test. #[test] #[serial] fn test_register_discovery_universe() { let mut src = SacnSource::new_v4("Controller").unwrap(); match src.register_universes(&[E131_DISCOVERY_UNIVERSE]) { - Err(e) => { - assert!( - false, - "Unexpected error returned when attempting to register discovery universe, {:?}", - e - ); - } - _ => { - assert!(true, "Registration successful"); - } + Err(SacnError::IllegalUniverse(_)) => {} + other => panic!("Expected IllegalUniverse when registering the discovery universe, got {other:?}"), } + assert!(src.universes().unwrap().is_empty()); +} + +/// Data and sync packets can't be sent on the discovery universe, even without registering it. +#[test] +#[serial] +fn test_send_on_discovery_universe_rejected() { + let mut src = SacnSource::new_v4("Controller").unwrap(); + src.register_universe(1).unwrap(); + + match src.send(&[E131_DISCOVERY_UNIVERSE], &[0u8; 513], None, None, None) { + Err(SacnError::IllegalUniverse(_)) => {} + other => panic!("Expected IllegalUniverse for data on the discovery universe, got {other:?}"), + } + + match src.send(&[1], &[0u8; 513], None, None, Some(E131_DISCOVERY_UNIVERSE)) { + Err(SacnError::IllegalSyncUniverse(_)) => {} + other => panic!("Expected IllegalSyncUniverse for the discovery universe as sync address, got {other:?}"), + } + + match src.send_sync_packet(E131_DISCOVERY_UNIVERSE, None) { + Err(SacnError::IllegalUniverse(_)) => {} + other => panic!("Expected IllegalUniverse for a sync packet on the discovery universe, got {other:?}"), + } +} + +#[test] +fn test_is_data_universe_in_range() { + assert!(is_data_universe_in_range(E131_MIN_MULTICAST_UNIVERSE).is_ok()); + assert!(is_data_universe_in_range(E131_MAX_MULTICAST_UNIVERSE).is_ok()); + assert!(is_data_universe_in_range(0).is_err()); + assert!(is_data_universe_in_range(E131_MAX_MULTICAST_UNIVERSE + 1).is_err()); + assert!(is_data_universe_in_range(E131_DISCOVERY_UNIVERSE).is_err()); + // The discovery universe is still a valid universe to send discovery packets on and listen to. + assert!(is_universe_in_range(E131_DISCOVERY_UNIVERSE).is_ok()); } /// Attempt to register the maximum allowed universe, this should succeed as the allowed range is inclusive of this universe.