librqbit_dualstack_sockets/
bind_device.rs1#[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 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}