From 29eabb8a2ffe7d8c9fe2696c7398e261a64d320f Mon Sep 17 00:00:00 2001 From: Han Xu Date: Mon, 21 Jul 2025 21:30:30 -0700 Subject: [PATCH 1/2] feat: add new wrapper methods for PktInfoUdpSocket add PktInfoUdpSocket::try_clone_std add two methods to drop multicast group membership --- src/unix.rs | 17 +++++++++++++++++ src/win.rs | 17 +++++++++++++++++ tests/ipv4_test.rs | 10 ++++++++++ tests/ipv6_test.rs | 6 ++++++ 4 files changed, 50 insertions(+) diff --git a/src/unix.rs b/src/unix.rs index f7a9db9..09e7fb2 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -88,6 +88,11 @@ impl PktInfoUdpSocket { self.socket.join_multicast_v4(addr, interface) } + /// Drop membership in a multicast group for IPv4. + pub fn leave_multicast_v4(&self, addr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + self.socket.leave_multicast_v4(addr, interface) + } + pub fn set_multicast_if_v4(&self, interface: &Ipv4Addr) -> io::Result<()> { self.socket.set_multicast_if_v4(interface) } @@ -104,6 +109,11 @@ impl PktInfoUdpSocket { self.socket.join_multicast_v6(addr, interface) } + /// Drop membership in a multicast group for IPv6. + pub fn leave_multicast_v6(&self, addr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.socket.leave_multicast_v6(addr, interface) + } + pub fn set_multicast_if_v6(&self, interface: u32) -> io::Result<()> { self.socket.set_multicast_if_v6(interface) } @@ -223,4 +233,11 @@ impl PktInfoUdpSocket { Some(info) => Ok((bytes_recv as _, info)), } } + + /// Creates a new independently owned std UdpSocket from this PktInfoUdpSocket. + /// + /// This is useful to mix and match functionality from this crate with stdlib or other crates. + pub fn try_clone_std(&self) -> io::Result { + Ok(self.socket.try_clone()?.into()) + } } diff --git a/src/win.rs b/src/win.rs index dfee078..948413d 100644 --- a/src/win.rs +++ b/src/win.rs @@ -136,6 +136,11 @@ impl PktInfoUdpSocket { self.socket.join_multicast_v4(addr, interface) } + /// Drop membership in a multicast group for IPv4. + pub fn leave_multicast_v4(&self, addr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + self.socket.leave_multicast_v4(addr, interface) + } + pub fn set_multicast_if_v4(&self, interface: &Ipv4Addr) -> io::Result<()> { self.socket.set_multicast_if_v4(interface) } @@ -152,6 +157,11 @@ impl PktInfoUdpSocket { self.socket.join_multicast_v6(addr, interface) } + /// Drop membership in a multicast group for IPv6. + pub fn leave_multicast_v6(&self, addr: &Ipv6Addr, interface: u32) -> io::Result<()> { + self.socket.leave_multicast_v6(addr, interface) + } + pub fn set_multicast_if_v6(&self, interface: u32) -> io::Result<()> { self.socket.set_multicast_if_v6(interface) } @@ -268,4 +278,11 @@ impl PktInfoUdpSocket { Some(info) => Ok((read_bytes as usize, info)), } } + + /// Creates a new independently owned std UdpSocket from this PktInfoUdpSocket. + /// + /// This is useful to mix and match functionality from this crate with stdlib or other crates. + pub fn try_clone_std(&self) -> io::Result { + Ok(self.socket.try_clone()?.into()) + } } diff --git a/tests/ipv4_test.rs b/tests/ipv4_test.rs index 789fe56..685e8e5 100644 --- a/tests/ipv4_test.rs +++ b/tests/ipv4_test.rs @@ -47,6 +47,16 @@ fn ipv4_test() -> io::Result<()> { socket.set_multicast_ttl_v4(255)?; socket.bind(&SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port).into())?; + let std_udp_socket = socket.try_clone_std()?; + assert!( + std_udp_socket.local_addr()? == SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port) + ); + + { + let output_socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?; + let data = "Hello"; + output_socket.send_to(data.as_bytes(), &local_addr)?; + } { let output_socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?; let data = "Hello"; diff --git a/tests/ipv6_test.rs b/tests/ipv6_test.rs index ffd771b..4e93bfb 100644 --- a/tests/ipv6_test.rs +++ b/tests/ipv6_test.rs @@ -45,6 +45,12 @@ fn ipv6_test() -> io::Result<()> { socket.set_multicast_hops_v6(255)?; socket.bind(&local_addr)?; + let std_udp_socket = socket.try_clone_std()?; + assert_eq!( + std_udp_socket.local_addr()?, + local_addr.as_socket().unwrap() + ); + { let output_socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?; let data = "Hello"; From 7c41eb28f8907eae3b5b9621b0fa26d4e1557de6 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Sun, 27 Jul 2025 10:03:00 -0700 Subject: [PATCH 2/2] remove duplicated code in test --- tests/ipv4_test.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/ipv4_test.rs b/tests/ipv4_test.rs index 685e8e5..c301586 100644 --- a/tests/ipv4_test.rs +++ b/tests/ipv4_test.rs @@ -52,11 +52,6 @@ fn ipv4_test() -> io::Result<()> { std_udp_socket.local_addr()? == SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port) ); - { - let output_socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?; - let data = "Hello"; - output_socket.send_to(data.as_bytes(), &local_addr)?; - } { let output_socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?; let data = "Hello";