1use getifaddrs::Interface;
12
13#[cfg(unix)]
14pub mod imp {
15 use super::*;
16
17 use std::{
18 io, mem,
19 net::{Ipv4Addr, UdpSocket},
20 os::unix::io::AsRawFd,
21 };
22
23 use libc::{
24 IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_IF, IPPROTO_IP, in_addr, ip_mreqn,
25 setsockopt,
26 };
27
28 #[cfg(any(target_os = "macos", target_os = "ios"))]
29 use libc::ip_mreq;
30
31 pub fn join_multicast_v4(
33 socket: &UdpSocket,
34 addr: &Ipv4Addr,
35 iface: &Interface,
36 ) -> Result<(), io::Error> {
37 multicast_group_operation_v4(socket, addr, iface, true)
38 }
39
40 pub fn leave_multicast_v4(
42 socket: &UdpSocket,
43 addr: &Ipv4Addr,
44 iface: &Interface,
45 ) -> Result<(), io::Error> {
46 multicast_group_operation_v4(socket, addr, iface, false)
47 }
48
49 fn multicast_group_operation_v4(
50 socket: &UdpSocket,
51 addr: &Ipv4Addr,
52 iface: &Interface,
53 join: bool,
54 ) -> Result<(), io::Error> {
55 let index = iface.index.unwrap_or(0);
56
57 #[cfg(not(any(
58 target_os = "solaris",
59 target_os = "illumos",
60 target_os = "macos",
61 target_os = "ios"
62 )))]
63 {
64 let group_op: i32 = if join {
65 IP_ADD_MEMBERSHIP
66 } else {
67 IP_DROP_MEMBERSHIP
68 };
69
70 let mreqn = ip_mreqn {
71 imr_multiaddr: in_addr {
72 s_addr: u32::from_ne_bytes(addr.octets()),
73 },
74 imr_address: in_addr {
75 s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
76 },
77 imr_ifindex: index as _,
78 };
79
80 unsafe {
84 if setsockopt(
85 socket.as_raw_fd(),
86 IPPROTO_IP,
87 group_op,
88 &mreqn as *const _ as *const _,
89 mem::size_of_val(&mreqn) as _,
90 ) < 0
91 {
92 return Err(io::Error::last_os_error());
93 }
94 }
95
96 #[cfg(not(any(target_os = "openbsd", target_os = "dragonfly", target_os = "netbsd")))]
97 {
98 let mreqn = ip_mreqn {
99 imr_multiaddr: in_addr {
100 s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
101 },
102 imr_address: in_addr {
103 s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
104 },
105 imr_ifindex: index as _,
106 };
107
108 unsafe {
112 if setsockopt(
113 socket.as_raw_fd(),
114 IPPROTO_IP,
115 IP_MULTICAST_IF,
116 &mreqn as *const _ as *const _,
117 mem::size_of_val(&mreqn) as _,
118 ) < 0
119 {
120 return Err(io::Error::last_os_error());
121 }
122 }
123 }
124 #[cfg(any(target_os = "openbsd", target_os = "dragonfly"))]
125 {
126 let addr = in_addr {
127 s_addr: u32::from_ne_bytes(ip_addr.octets()),
128 };
129
130 unsafe {
133 if setsockopt(
134 socket.as_raw_fd(),
135 IPPROTO_IP,
136 IP_MULTICAST_IF,
137 &addr as *const _ as *const _,
138 mem::size_of_val(&addr) as _,
139 ) < 0
140 {
141 return Err(io::Error::last_os_error());
142 }
143 }
144 }
145 #[cfg(target_os = "netbsd")]
146 {
147 let idx = (index as u32).to_be();
148
149 unsafe {
153 if setsockopt(
154 socket.as_raw_fd(),
155 IPPROTO_IP,
156 IP_MULTICAST_IF,
157 &idx as *const _ as *const _,
158 mem::size_of_val(&idx) as _,
159 ) < 0
160 {
161 return Err(io::Error::last_os_error());
162 }
163 }
164 }
165
166 Ok(())
167 }
168
169 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
170 {
171 let ip_addr = match iface.address {
172 IpAddr::V4(ipv4_addr) => ipv4_addr,
173 IpAddr::V6(_) => return Err(io::Error::other("Interface address is IPv6")),
174 };
175
176 if join {
177 socket.join_multicast_v4(addr, &ip_addr).with_context(|| {
178 format!(
179 "Failed joining multicast group for interface {} at address {}",
180 iface.name, ip_addr
181 )
182 })?;
183 } else {
184 socket.leave_multicast_v4(addr, &ip_addr).with_context(|| {
185 format!(
186 "Failed leave multicast group for interface {} at address {}",
187 iface.name, ip_addr
188 )
189 })?;
190 }
191
192 unsafe {
195 if setsockopt(
196 socket.as_raw_fd(),
197 IPPROTO_IP,
198 IP_MULTICAST_IF,
199 &addr as *const _ as *const _,
200 mem::size_of_val(&addr) as _,
201 ) < 0
202 {
203 return Err(io::Error::last_os_error());
204 }
205 }
206
207 Ok(())
208 }
209
210 #[cfg(any(target_os = "macos", target_os = "ios"))]
211 {
212 use getifaddrs::Address;
213
214 let ip_addr = match &iface.address {
215 Address::V4(ifaddr) => ifaddr.address,
216 Address::V6(_) => return Err(io::Error::other("Interface address is IPv6")),
217 Address::Mac(_) => return Err(io::Error::other("Interface address is Mac")),
218 };
219
220 let mreq = ip_mreq {
221 imr_multiaddr: in_addr {
222 s_addr: u32::from_ne_bytes(addr.octets()),
223 },
224 imr_interface: in_addr {
225 s_addr: u32::from_ne_bytes(ip_addr.octets()),
226 },
227 };
228
229 let mreqn = ip_mreqn {
230 imr_multiaddr: in_addr {
231 s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
232 },
233 imr_address: in_addr {
234 s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
235 },
236 imr_ifindex: index as _,
237 };
238
239 let group_op: i32 = if join {
240 IP_ADD_MEMBERSHIP
241 } else {
242 IP_DROP_MEMBERSHIP
243 };
244
245 unsafe {
248 if setsockopt(
249 socket.as_raw_fd(),
250 IPPROTO_IP,
251 group_op,
252 &mreq as *const _ as *const _,
253 mem::size_of_val(&mreq) as _,
254 ) < 0
255 {
256 return Err(io::Error::last_os_error());
257 }
258 }
259
260 unsafe {
264 if setsockopt(
265 socket.as_raw_fd(),
266 IPPROTO_IP,
267 IP_MULTICAST_IF,
268 &mreqn as *const _ as *const _,
269 mem::size_of_val(&mreqn) as _,
270 ) < 0
271 {
272 return Err(io::Error::last_os_error());
273 }
274 }
275
276 Ok(())
277 }
278 }
279}
280
281#[cfg(windows)]
282pub mod imp {
283 use super::*;
284
285 use std::{
286 io, mem,
287 net::{Ipv4Addr, UdpSocket},
288 os::windows::io::AsRawSocket,
289 };
290
291 use windows_sys::Win32::Networking::WinSock::{
292 IN_ADDR, IN_ADDR_0, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MREQ, IP_MULTICAST_IF,
293 IPPROTO_IP, WSAGetLastError, setsockopt,
294 };
295
296 pub fn join_multicast_v4(
298 socket: &UdpSocket,
299 addr: &Ipv4Addr,
300 iface: &Interface,
301 ) -> Result<(), io::Error> {
302 multicast_group_operation_v4(socket, addr, iface, IP_ADD_MEMBERSHIP)
303 }
307
308 pub fn leave_multicast_v4(
310 socket: &UdpSocket,
311 addr: &Ipv4Addr,
312 iface: &Interface,
313 ) -> Result<(), io::Error> {
314 multicast_group_operation_v4(socket, addr, iface, IP_DROP_MEMBERSHIP)
315 }
319
320 fn multicast_group_operation_v4(
321 socket: &UdpSocket,
322 addr: &Ipv4Addr,
323 iface: &Interface,
324 group_op: i32,
325 ) -> Result<(), io::Error> {
326 let index = iface.index.unwrap_or(0);
327
328 let mreq = IP_MREQ {
329 imr_multiaddr: IN_ADDR {
330 S_un: IN_ADDR_0 {
331 S_addr: u32::from_ne_bytes(addr.octets()),
332 },
333 },
334 imr_interface: IN_ADDR {
335 S_un: IN_ADDR_0 {
336 S_addr: u32::from_ne_bytes(Ipv4Addr::new(0, 0, 0, index as u8).octets()),
337 },
338 },
339 };
340
341 unsafe {
344 if setsockopt(
345 socket.as_raw_socket() as usize,
346 IPPROTO_IP,
347 group_op,
348 &mreq as *const _ as *const _,
349 mem::size_of_val(&mreq) as _,
350 ) < 0
351 {
352 return Err(io::Error::from_raw_os_error(WSAGetLastError()));
353 }
354 }
355
356 let ip_addr = IN_ADDR {
357 S_un: IN_ADDR_0 {
358 S_addr: u32::from_ne_bytes(Ipv4Addr::new(0, 0, 0, index as u8).octets()),
359 },
360 };
361
362 unsafe {
365 if setsockopt(
366 socket.as_raw_socket() as usize,
367 IPPROTO_IP,
368 IP_MULTICAST_IF,
369 &ip_addr as *const _ as *const _,
370 mem::size_of_val(&ip_addr) as _,
371 ) < 0
372 {
373 return Err(io::Error::last_os_error());
374 }
375 }
376
377 Ok(())
378 }
379}