librqbit_dualstack_sockets/
bind_device.rs

1#[cfg(test)]
2mod tests;
3
4use crate::Error;
5use std::{ffi::CString, num::NonZeroU32, str::FromStr};
6
7#[derive(Debug)]
8pub struct BindDevice {
9    #[allow(unused)]
10    index: NonZeroU32,
11    #[allow(unused)]
12    name: CString,
13}
14
15impl BindDevice {
16    #[cfg(not(windows))]
17    pub fn new_from_name(name: &str) -> crate::Result<Self> {
18        let name = CString::new(name).map_err(|_| Error::BindDeviceInvalid)?;
19
20        let index = unsafe { libc::if_nametoindex(name.as_ptr()) };
21        let index = NonZeroU32::new(index)
22            .ok_or_else(|| Error::BindDeviceInvalidError(std::io::Error::last_os_error()))?;
23        Ok(Self { index, name })
24    }
25
26    #[cfg(windows)]
27    pub fn new_from_name(name: &str) -> crate::Result<Self> {
28        Err(Error::BindDeviceNotSupported)
29    }
30
31    #[cfg(target_os = "macos")]
32    pub fn bind_sref(&self, sref: &socket2::Socket, is_v6: bool) -> crate::Result<()> {
33        if is_v6 {
34            sref.bind_device_by_index_v6(Some(self.index))
35                .map_err(Error::BindDeviceSetDeviceError)
36        } else {
37            sref.bind_device_by_index_v4(Some(self.index))
38                .map_err(Error::BindDeviceSetDeviceError)
39        }
40    }
41
42    #[cfg(not(any(target_os = "macos", windows)))]
43    pub fn bind_sref(&self, sref: &socket2::Socket, _is_v6: bool) -> crate::Result<()> {
44        let name = self.name.as_bytes();
45        sref.bind_device(Some(name))
46            .map_err(Error::BindDeviceSetDeviceError)
47    }
48
49    #[cfg(windows)]
50    pub fn bind_sref(&self, _sref: &socket2::Socket, _is_v6: bool) -> crate::Result<()> {
51        Err(Error::BindDeviceNotSupported)
52    }
53}
54
55impl FromStr for BindDevice {
56    type Err = crate::Error;
57
58    fn from_str(s: &str) -> Result<Self, Self::Err> {
59        Self::new_from_name(s)
60    }
61}