1use std::borrow::Cow;
2
3use socket2::{Domain, Socket};
4
5pub struct BindDeviceOption<'a> {
7 name: Cow<'a, str>,
9 #[allow(dead_code)]
11 domain: Domain,
12}
13
14impl<'a> BindDeviceOption<'a> {
15 pub fn v4(name: &'a str) -> Self {
16 Self {
17 name: Cow::Borrowed(name),
18 domain: Domain::IPV4,
19 }
20 }
21
22 pub fn v6(name: &'a str) -> Self {
23 Self {
24 name: Cow::Borrowed(name),
25 domain: Domain::IPV6,
26 }
27 }
28}
29
30pub trait AddressBinding {
31 fn bind_to_device(&self, opt: BindDeviceOption) -> std::io::Result<()>;
37}
38
39impl AddressBinding for Socket {
40 #[cfg(not(target_os = "windows"))]
41 #[allow(unreachable_code)]
42 fn bind_to_device(&self, opt: BindDeviceOption) -> std::io::Result<()> {
43 #[cfg(any(
44 target_os = "ios",
45 target_os = "macos",
46 target_os = "tvos",
47 target_os = "watchos"
48 ))]
49 {
50 let ifindex = std::num::NonZeroU32::new(unsafe {
51 libc::if_nametoindex(opt.name.as_ptr() as *const _)
52 });
53 match opt.domain {
54 Domain::IPV4 => self.bind_device_by_index_v4(ifindex)?,
55 Domain::IPV6 => self.bind_device_by_index_v6(ifindex)?,
56 _ => {}
57 }
58 return Ok(());
59 }
60
61 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
62 {
63 self.bind_device(Some(opt.name.as_bytes()))?;
64 return Ok(());
65 }
66
67 #[cfg(feature = "log")]
68 tracing::warn!(
69 "bind device name [{}] on unsupported OS [{}]",
70 opt.name,
71 std::env::consts::OS,
72 );
73 Ok(())
74 }
75
76 #[cfg(target_os = "windows")]
77 fn bind_to_device(&self, opt: BindDeviceOption) -> std::io::Result<()> {
78 let ip = crate::utils::win_ifname_to_addr(&opt.name)?;
79
80 #[cfg(feature = "log")]
81 tracing::trace!(
82 "bind adapter by name [{}] and ip [{}] on Windows",
83 opt.name,
84 ip,
85 );
86
87 let addr = std::net::SocketAddr::new(ip, 0);
88 self.bind(&socket2::SockAddr::from(addr))?;
89 Ok(())
90 }
91}
92
93#[test]
94fn test_bind_iface() {
95 use crate::binds::{AddressBinding, BindDeviceOption};
96 let iface = "your/interface/name";
97 match socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None) {
98 Err(e) => println!("create socket error: {:?}", e),
99 Ok(socket) => {
100 if let Err(e) = socket.bind_to_device(BindDeviceOption::v4(iface)) {
101 println!("bind device error: {:?}", e);
102 }
103 }
104 }
105}