librqbit_dualstack_sockets/
bind_device.rs

1#[cfg(test)]
2pub(crate) mod tests;
3
4use crate::Error;
5use std::{ffi::CString, num::NonZeroU32, str::FromStr};
6
7#[derive(Debug, Clone)]
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    pub fn index(&self) -> NonZeroU32 {
32        self.index
33    }
34
35    pub fn name(&self) -> &str {
36        // We constructed from a string so this can't fail
37        unsafe { std::str::from_utf8_unchecked(self.name.to_bytes()) }
38    }
39
40    #[cfg(target_os = "macos")]
41    pub fn bind_sref(&self, sref: &socket2::Socket, is_v6: bool) -> crate::Result<()> {
42        if is_v6 {
43            sref.bind_device_by_index_v6(Some(self.index))
44                .map_err(Error::BindDeviceSetDeviceError)
45        } else {
46            sref.bind_device_by_index_v4(Some(self.index))
47                .map_err(Error::BindDeviceSetDeviceError)
48        }
49    }
50
51    #[cfg(not(any(target_os = "macos", windows)))]
52    pub fn bind_sref(&self, sref: &socket2::Socket, _is_v6: bool) -> crate::Result<()> {
53        let name = self.name.as_bytes_with_nul();
54        sref.bind_device(Some(name))
55            .map_err(Error::BindDeviceSetDeviceError)
56    }
57
58    #[cfg(windows)]
59    pub fn bind_sref(&self, _sref: &socket2::Socket, _is_v6: bool) -> crate::Result<()> {
60        Err(Error::BindDeviceNotSupported)
61    }
62}
63
64impl FromStr for BindDevice {
65    type Err = crate::Error;
66
67    fn from_str(s: &str) -> Result<Self, Self::Err> {
68        Self::new_from_name(s)
69    }
70}