Skip to main content

gstthreadshare/
net.rs

1// GStreamer
2//
3// Copyright (C) 2015-2023 Sebastian Dröge <sebastian@centricular.com>
4//
5// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
6// If a copy of the MPL was not distributed with this file, You can obtain one at
7// <https://mozilla.org/MPL/2.0/>.
8//
9// SPDX-License-Identifier: MPL-2.0
10
11use 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    /// Join multicast address for a given interface.
32    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    /// Leave multicast address for a given interface.
41    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            // SAFETY: Requires a valid ip_mreq or ip_mreqn struct to be passed together
81            // with its size for checking which of the two it is. On errors a negative
82            // integer is returned.
83            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                // SAFETY: Requires a valid ip_mreq or ip_mreqn struct to be passed together
109                // with its size for checking which of the two it is. On errors a negative
110                // integer is returned.
111                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                // SAFETY: Requires a valid in_addr struct to be passed together with its size for
131                // checking which of the two it is. On errors a negative integer is returned.
132                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                // SAFETY: Requires a valid in_addr struct or interface index in network byte order
150                // to be passed together with its size for checking which of the two it is. On
151                // errors a negative integer is returned.
152                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            // SAFETY: Requires a valid in_addr struct to be passed together with its size for
193            // checking which of the two it is. On errors a negative integer is returned.
194            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            // SAFETY: Requires a valid ip_mreq struct to be passed together with its size for checking
246            // validity. On errors a negative integer is returned.
247            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            // SAFETY: Requires a valid ip_mreqn struct to be passed together
261            // with its size for checking which of the two it is. On errors a negative
262            // integer is returned.
263            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    /// Join multicast address for a given interface.
297    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        // let ip_addr = Ipv4Addr::new(0, 0, 0, iface.index.unwrap() as u8);
304        // socket.join_multicast_v4(addr, &ip_addr).unwrap();
305        // return Ok(());
306    }
307
308    /// Leave multicast address for a given interface.
309    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        // let ip_addr = Ipv4Addr::new(0, 0, 0, iface.index.unwrap() as u8);
316        // socket.leave_multicast_v4(addr, &ip_addr).unwrap();
317        // return Ok(());
318    }
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        // SAFETY: Requires a valid ip_mreq struct to be passed together with its size for checking
342        // validity. On errors a negative integer is returned.
343        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        // SAFETY: Requires a valid IN_ADDR struct to be passed together with its size for checking
363        // which of the two it is. On errors a negative integer is returned.
364        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}