wintun_bindings/
adapter.rs

1/// Representation of a winton adapter with safe idiomatic bindings to the functionality provided by
2/// the WintunAdapter* C functions.
3///
4/// The [`Adapter::create`] and [`Adapter::open`] functions serve as the entry point to using
5/// wintun functionality
6use crate::{
7    Wintun,
8    error::{Error, OutOfRangeData},
9    handle::{SafeEvent, UnsafeHandle},
10    session::Session,
11    util::{self},
12    wintun_raw,
13};
14use std::{
15    ffi::OsStr,
16    net::{IpAddr, Ipv4Addr},
17    os::windows::prelude::OsStrExt,
18    ptr,
19    sync::Arc,
20    sync::OnceLock,
21};
22use windows_sys::{
23    Win32::NetworkManagement::{IpHelper::ConvertLengthToIpv4Mask, Ndis::NET_LUID_LH},
24    core::GUID,
25};
26
27/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_adapter_handle>
28pub struct Adapter {
29    adapter: UnsafeHandle<wintun_raw::WINTUN_ADAPTER_HANDLE>,
30    pub(crate) wintun: Wintun,
31    guid: u128,
32    index: u32,
33    luid: NET_LUID_LH,
34}
35
36impl Adapter {
37    /// Returns the `Friendly Name` of this adapter,
38    /// which is the human readable name shown in Windows
39    pub fn get_name(&self) -> Result<String, Error> {
40        Ok(crate::ffi::luid_to_alias(&self.luid)?)
41    }
42
43    /// Sets the `Friendly Name` of this adapter,
44    /// which is the human readable name shown in Windows
45    ///
46    /// Note: This is different from `Adapter Name`, which is a GUID.
47    pub fn set_name(&self, name: &str) -> Result<(), Error> {
48        // use command `netsh interface set interface name="oldname" newname="mynewname"`
49
50        let args = &[
51            "interface",
52            "set",
53            "interface",
54            &format!("name=\"{}\"", self.get_name()?),
55            &format!("newname=\"{}\"", name),
56        ];
57        util::run_command("netsh", args)?;
58
59        Ok(())
60    }
61
62    pub fn get_guid(&self) -> u128 {
63        self.guid
64    }
65
66    /// Creates a new wintun adapter inside the name `name` with tunnel type `tunnel_type`
67    ///
68    /// Optionally a GUID can be specified that will become the GUID of this adapter once created.
69    pub fn create(wintun: &Wintun, name: &str, tunnel_type: &str, guid: Option<u128>) -> Result<Arc<Adapter>, Error> {
70        let name_utf16: Vec<_> = name.encode_utf16().chain(std::iter::once(0)).collect();
71        let tunnel_type_utf16: Vec<u16> = tunnel_type.encode_utf16().chain(std::iter::once(0)).collect();
72
73        let mut guid = match guid {
74            Some(guid) => guid,
75            None => {
76                let mut guid: GUID = unsafe { std::mem::zeroed() };
77                unsafe { windows_sys::Win32::System::Rpc::UuidCreate(&mut guid as *mut GUID) };
78                util::win_guid_to_u128(&guid)
79            }
80        };
81
82        crate::log::set_default_logger_if_unset(wintun);
83
84        let guid_s: GUID = GUID::from_u128(guid);
85        let result = unsafe { wintun.WintunCreateAdapter(name_utf16.as_ptr(), tunnel_type_utf16.as_ptr(), &guid_s) };
86
87        if result.is_null() {
88            return crate::log::extract_wintun_log_error("WintunCreateAdapter failed")?;
89        }
90        let mut call = || -> Result<Arc<Adapter>, Error> {
91            let luid = crate::ffi::alias_to_luid(name)?;
92            let index = crate::ffi::luid_to_index(&luid)?;
93            let real_guid = util::win_guid_to_u128(&crate::ffi::luid_to_guid(&luid)?);
94            if guid != real_guid {
95                let real_guid_s = util::guid_to_win_style_string(&GUID::from_u128(real_guid))?;
96                let guid_s = util::guid_to_win_style_string(&GUID::from_u128(guid))?;
97                let (major, minor, build) = util::get_windows_version()?;
98                log::warn!(
99                    "Windows {major}.{minor}.{build} internal bug cause the GUID mismatch: Expected {guid_s}, got {real_guid_s}"
100                );
101                guid = real_guid;
102            }
103            Ok(Arc::new(Adapter {
104                adapter: UnsafeHandle(result),
105                wintun: wintun.clone(),
106                guid,
107                index,
108                luid,
109            }))
110        };
111        match call() {
112            Ok(adapter) => Ok(adapter),
113            Err(e) => {
114                unsafe { wintun.WintunCloseAdapter(result) };
115                Err(e)
116            }
117        }
118    }
119
120    /// Attempts to open an existing wintun interface name `name`.
121    pub fn open(wintun: &Wintun, name: &str) -> Result<Arc<Adapter>, Error> {
122        let name_utf16: Vec<u16> = OsStr::new(name).encode_wide().chain(std::iter::once(0)).collect();
123
124        crate::log::set_default_logger_if_unset(wintun);
125
126        let result = unsafe { wintun.WintunOpenAdapter(name_utf16.as_ptr()) };
127
128        if result.is_null() {
129            return crate::log::extract_wintun_log_error("WintunOpenAdapter failed")?;
130        }
131        let call = || -> Result<Arc<Adapter>, Error> {
132            let luid = crate::ffi::alias_to_luid(name)?;
133            let index = crate::ffi::luid_to_index(&luid)?;
134            let guid = crate::ffi::luid_to_guid(&luid)?;
135            let guid = util::win_guid_to_u128(&guid);
136            Ok(Arc::new(Adapter {
137                adapter: UnsafeHandle(result),
138                wintun: wintun.clone(),
139                guid,
140                index,
141                luid,
142            }))
143        };
144        match call() {
145            Ok(adapter) => Ok(adapter),
146            Err(e) => {
147                unsafe { wintun.WintunCloseAdapter(result) };
148                Err(e)
149            }
150        }
151    }
152
153    /// Delete an adapter, consuming it in the process
154    pub fn delete(self) -> Result<(), Error> {
155        //Dropping an adapter closes it
156        drop(self);
157        // Return a result here so that if later the API changes to be fallible, we can support it
158        // without making a breaking change
159        Ok(())
160    }
161
162    fn validate_capacity(capacity: u32) -> Result<(), Error> {
163        let range = crate::MIN_RING_CAPACITY..=crate::MAX_RING_CAPACITY;
164        if !range.contains(&capacity) {
165            return Err(Error::CapacityOutOfRange(OutOfRangeData { range, value: capacity }));
166        }
167        if !capacity.is_power_of_two() {
168            return Err(Error::CapacityNotPowerOfTwo(capacity));
169        }
170        Ok(())
171    }
172
173    /// Initiates a new wintun session on the given adapter.
174    ///
175    /// Capacity is the size in bytes of the ring buffer used internally by the driver. Must be
176    /// a power of two between [`crate::MIN_RING_CAPACITY`] and [`crate::MAX_RING_CAPACITY`] inclusive.
177    pub fn start_session(self: &Arc<Self>, capacity: u32) -> Result<Arc<Session>, Error> {
178        Self::validate_capacity(capacity)?;
179
180        let result = unsafe { self.wintun.WintunStartSession(self.adapter.0, capacity) };
181
182        if result.is_null() {
183            return crate::log::extract_wintun_log_error("WintunStartSession failed")?;
184        }
185        // Manual reset, because we use this event once and it must fire on all threads
186        let shutdown_event = SafeEvent::new(true, false)?;
187        Ok(Arc::new(Session {
188            inner: UnsafeHandle(result),
189            read_event: OnceLock::new(),
190            shutdown_event: Arc::new(shutdown_event),
191            adapter: self.clone(),
192        }))
193    }
194
195    /// Returns the Win32 LUID for this adapter
196    pub fn get_luid(&self) -> NET_LUID_LH {
197        self.luid
198    }
199
200    /// Set `MTU` of this adapter
201    pub fn set_mtu(&self, mtu: usize) -> Result<(), Error> {
202        util::set_adapter_mtu(&self.luid, mtu, false)?;
203        // FIXME: Here we set the IPv6 MTU as well for consistency, but for some users it may not be expected.
204        util::set_adapter_mtu(&self.luid, mtu, true)?;
205        Ok(())
206    }
207
208    /// Returns `MTU` of this adapter
209    pub fn get_mtu(&self) -> Result<usize, Error> {
210        // FIXME: Here we get the IPv4 MTU only, but for some users it may not be expected.
211        Ok(util::get_adapter_mtu(&self.luid, false)? as _)
212    }
213
214    /// Returns the Win32 interface index of this adapter. Useful for specifying the interface
215    /// when executing `netsh interface ip` commands
216    pub fn get_adapter_index(&self) -> Result<u32, Error> {
217        Ok(self.index)
218    }
219
220    /// Sets the IP address for this adapter, using command `netsh`.
221    pub fn set_address(&self, address: Ipv4Addr) -> Result<(), Error> {
222        let binding = self.get_addresses()?;
223        let old_address = binding.iter().find(|addr| matches!(addr, IpAddr::V4(_)));
224        let mask = match old_address {
225            Some(IpAddr::V4(addr)) => self.get_netmask_of_address(&(*addr).into())?,
226            _ => "255.255.255.0".parse()?,
227        };
228        let gateway = self
229            .get_gateways()?
230            .iter()
231            .find(|addr| matches!(addr, IpAddr::V4(_)))
232            .cloned();
233        self.set_network_addresses_tuple(address.into(), mask, gateway)?;
234        Ok(())
235    }
236
237    /// Sets the gateway for this adapter, using command `netsh`.
238    pub fn set_gateway(&self, gateway: Option<Ipv4Addr>) -> Result<(), Error> {
239        let binding = self.get_addresses()?;
240        let address = binding.iter().find(|addr| matches!(addr, IpAddr::V4(_)));
241        let address = match address {
242            Some(IpAddr::V4(addr)) => addr,
243            _ => return Err("Unable to find IPv4 address".into()),
244        };
245        let mask = self.get_netmask_of_address(&(*address).into())?;
246        let gateway = gateway.map(|addr| addr.into());
247        self.set_network_addresses_tuple((*address).into(), mask, gateway)?;
248        Ok(())
249    }
250
251    /// Sets the subnet mask for this adapter, using command `netsh`.
252    pub fn set_netmask(&self, mask: Ipv4Addr) -> Result<(), Error> {
253        let binding = self.get_addresses()?;
254        let address = binding.iter().find(|addr| matches!(addr, IpAddr::V4(_)));
255        let address = match address {
256            Some(IpAddr::V4(addr)) => addr,
257            _ => return Err("Unable to find IPv4 address".into()),
258        };
259        let gateway = self
260            .get_gateways()?
261            .iter()
262            .find(|addr| matches!(addr, IpAddr::V4(_)))
263            .cloned();
264        self.set_network_addresses_tuple((*address).into(), mask.into(), gateway)?;
265        Ok(())
266    }
267
268    /// Sets the DNS servers for this adapter
269    pub fn set_dns_servers(&self, dns_servers: &[IpAddr]) -> Result<(), Error> {
270        let interface = GUID::from_u128(self.get_guid());
271        if let Err(e) = util::set_interface_dns_servers(interface, dns_servers) {
272            log::debug!("Failed to set DNS servers in first attempt: \"{}\", try another...", e);
273            util::set_interface_dns_servers_via_cmd(&self.get_name()?, dns_servers)?;
274        }
275        Ok(())
276    }
277
278    /// Sets the network addresses of this adapter, including network address, subnet mask, and gateway
279    pub fn set_network_addresses_tuple(
280        &self,
281        address: IpAddr,
282        mask: IpAddr,
283        gateway: Option<IpAddr>,
284    ) -> Result<(), Error> {
285        let name = self.get_name()?;
286        // command line: `netsh interface ipv4 set address name="YOUR_INTERFACE_NAME" source=static address=IP_ADDRESS mask=SUBNET_MASK gateway=GATEWAY`
287        // or shorter command: `netsh interface ipv4 set address name="YOUR_INTERFACE_NAME" static IP_ADDRESS SUBNET_MASK GATEWAY`
288        // for example: `netsh interface ipv4 set address name="Wi-Fi" static 192.168.3.8 255.255.255.0 192.168.3.1`
289        let mut args: Vec<String> = vec![
290            "interface".into(),
291            if address.is_ipv4() {
292                "ipv4".into()
293            } else {
294                "ipv6".into()
295            },
296            "set".into(),
297            "address".into(),
298            format!("name=\"{}\"", name),
299            "source=static".into(),
300            format!("address={}", address),
301            format!("mask={}", mask),
302        ];
303        if let Some(gateway) = gateway {
304            args.push(format!("gateway={}", gateway));
305        }
306        util::run_command("netsh", &args.iter().map(|s| s.as_str()).collect::<Vec<&str>>())?;
307        Ok(())
308    }
309
310    /// Returns the IP addresses of this adapter, including IPv4 and IPv6 addresses
311    pub fn get_addresses(&self) -> Result<Vec<IpAddr>, Error> {
312        let name = util::guid_to_win_style_string(&GUID::from_u128(self.guid))?;
313
314        let mut adapter_addresses = vec![];
315
316        util::get_adapters_addresses(|adapter| {
317            let name_iter = match unsafe { util::win_pstr_to_string(adapter.AdapterName) } {
318                Ok(name) => name,
319                Err(err) => {
320                    log::error!("Failed to parse adapter name: {}", err);
321                    return false;
322                }
323            };
324            if name_iter == name {
325                let mut current_address = adapter.FirstUnicastAddress;
326                while !current_address.is_null() {
327                    let address = unsafe { (*current_address).Address };
328                    match util::retrieve_ipaddr_from_socket_address(&address) {
329                        Ok(addr) => adapter_addresses.push(addr),
330                        Err(err) => {
331                            log::error!("Failed to parse address: {}", err);
332                        }
333                    }
334                    unsafe { current_address = (*current_address).Next };
335                }
336            }
337            true
338        })?;
339
340        Ok(adapter_addresses)
341    }
342
343    /// Returns the gateway addresses of this adapter, including IPv4 and IPv6 addresses
344    pub fn get_gateways(&self) -> Result<Vec<IpAddr>, Error> {
345        let name = util::guid_to_win_style_string(&GUID::from_u128(self.guid))?;
346        let mut gateways = vec![];
347        util::get_adapters_addresses(|adapter| {
348            let name_iter = match unsafe { util::win_pstr_to_string(adapter.AdapterName) } {
349                Ok(name) => name,
350                Err(err) => {
351                    log::error!("Failed to parse adapter name: {}", err);
352                    return false;
353                }
354            };
355            if name_iter == name {
356                let mut current_gateway = adapter.FirstGatewayAddress;
357                while !current_gateway.is_null() {
358                    let gateway = unsafe { (*current_gateway).Address };
359                    match util::retrieve_ipaddr_from_socket_address(&gateway) {
360                        Ok(addr) => gateways.push(addr),
361                        Err(err) => {
362                            log::error!("Failed to parse gateway: {}", err);
363                        }
364                    }
365                    unsafe { current_gateway = (*current_gateway).Next };
366                }
367            }
368            true
369        })?;
370        Ok(gateways)
371    }
372
373    /// Returns the subnet mask of the given address
374    pub fn get_netmask_of_address(&self, target_address: &IpAddr) -> Result<IpAddr, Error> {
375        let name = util::guid_to_win_style_string(&GUID::from_u128(self.guid))?;
376        let mut subnet_mask = None;
377        util::get_adapters_addresses(|adapter| {
378            let name_iter = match unsafe { util::win_pstr_to_string(adapter.AdapterName) } {
379                Ok(name) => name,
380                Err(err) => {
381                    log::warn!("Failed to parse adapter name: {}", err);
382                    return false;
383                }
384            };
385            if name_iter == name {
386                let mut current_address = adapter.FirstUnicastAddress;
387                while !current_address.is_null() {
388                    let address = unsafe { (*current_address).Address };
389                    let address = match util::retrieve_ipaddr_from_socket_address(&address) {
390                        Ok(addr) => addr,
391                        Err(err) => {
392                            log::warn!("Failed to parse address: {}", err);
393                            return false;
394                        }
395                    };
396                    if address == *target_address {
397                        let masklength = unsafe { (*current_address).OnLinkPrefixLength };
398                        match address {
399                            IpAddr::V4(_) => {
400                                let mut mask = 0_u32;
401                                match unsafe { ConvertLengthToIpv4Mask(masklength as u32, &mut mask as *mut u32) } {
402                                    0 => {}
403                                    err => {
404                                        log::warn!("Failed to convert length to mask: {}", err);
405                                        return false;
406                                    }
407                                }
408                                subnet_mask = Some(IpAddr::V4(Ipv4Addr::from(mask.to_le_bytes())));
409                            }
410                            IpAddr::V6(_) => match util::ipv6_netmask_for_prefix(masklength) {
411                                Ok(v) => subnet_mask = Some(IpAddr::V6(v)),
412                                Err(err) => {
413                                    log::warn!("Failed to convert length to mask: {}", err);
414                                    return false;
415                                }
416                            },
417                        }
418                        break;
419                    }
420                    unsafe { current_address = (*current_address).Next };
421                }
422            }
423            true
424        })?;
425
426        Ok(subnet_mask.ok_or("Unable to find matching address")?)
427    }
428}
429
430impl Drop for Adapter {
431    fn drop(&mut self) {
432        let _name = self.get_name();
433        //Close adapter on drop
434        //This is why we need an Arc of wintun
435        unsafe { self.wintun.WintunCloseAdapter(self.adapter.0) };
436        self.adapter = UnsafeHandle(ptr::null_mut());
437        #[cfg(feature = "winreg")]
438        if let Ok(name) = _name {
439            // Delete registry related to network card
440            _ = delete_adapter_info_from_reg(&name);
441        }
442    }
443}
444
445/// This function is used to avoid the adapter name and guid being recorded in the registry
446#[cfg(feature = "winreg")]
447pub(crate) fn delete_adapter_info_from_reg(dev_name: &str) -> std::io::Result<()> {
448    use winreg::{RegKey, enums::HKEY_LOCAL_MACHINE, enums::KEY_ALL_ACCESS};
449    let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
450    let profiles_key = hklm.open_subkey_with_flags(
451        "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Profiles",
452        KEY_ALL_ACCESS,
453    )?;
454
455    for sub_key_name in profiles_key.enum_keys().filter_map(Result::ok) {
456        let sub_key = profiles_key.open_subkey(&sub_key_name)?;
457        match sub_key.get_value::<String, _>("ProfileName") {
458            Ok(profile_name) => {
459                if dev_name == profile_name {
460                    match profiles_key.delete_subkey_all(&sub_key_name) {
461                        Ok(_) => log::info!("Successfully deleted Profiles sub_key: {}", sub_key_name),
462                        Err(e) => log::warn!("Failed to delete Profiles sub_key {}: {}", sub_key_name, e),
463                    }
464                }
465            }
466            Err(e) => log::warn!("Failed to read ProfileName for sub_key {}: {}", sub_key_name, e),
467        }
468    }
469    let unmanaged_key = hklm.open_subkey_with_flags(
470        "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Signatures\\Unmanaged",
471        KEY_ALL_ACCESS,
472    )?;
473    for sub_key_name in unmanaged_key.enum_keys().filter_map(Result::ok) {
474        let sub_key = unmanaged_key.open_subkey(&sub_key_name)?;
475        match sub_key.get_value::<String, _>("Description") {
476            Ok(description) => {
477                if dev_name == description {
478                    match unmanaged_key.delete_subkey_all(&sub_key_name) {
479                        Ok(_) => log::info!("Successfully deleted Unmanaged sub_key: {}", sub_key_name),
480                        Err(e) => log::warn!("Failed to delete Unmanaged sub_key {}: {}", sub_key_name, e),
481                    }
482                }
483            }
484            Err(e) => log::warn!("Failed to read Description for sub_key {}: {}", sub_key_name, e),
485        }
486    }
487    Ok(())
488}