1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/// Representation of a winton adapter with safe idiomatic bindings to the functionality provided by
/// the WintunAdapter* C functions.
///
/// The [`Adapter::create`] and [`Adapter::open`] functions serve as the entry point to using
/// wintun functionality
use crate::error;
use crate::session;
use crate::util;
use crate::util::UnsafeHandle;
use crate::wintun_raw;
use crate::Wintun;

use std::ptr;
use std::sync::Arc;

use itertools::Itertools;
use log::*;
use once_cell::sync::OnceCell;
use rand::Rng;

use widestring::U16CStr;
use widestring::U16CString;

use winapi::{
    shared::winerror,
    um::{ipexport, iphlpapi, synchapi},
};

/// Wrapper around a <https://git.zx2c4.com/wintun/about/#wintun_adapter_handle>
pub struct Adapter {
    adapter: UnsafeHandle<wintun_raw::WINTUN_ADAPTER_HANDLE>,
    wintun: Wintun,
    guid: u128,
}

fn encode_utf16(string: &str, max_characters: usize) -> Result<U16CString, error::WintunError> {
    let utf16 = U16CString::from_str(string)?;
    if utf16.len() >= max_characters {
        //max_characters is the maximum number of characters including the null terminator. And .len() measures the
        //number of characters (excluding the null terminator). Therefore we can hold a string with
        //max_characters - 1 because the null terminator sits in the last element. However a string
        //of length max_characters needs max_characters + 1 to store the null terminator the >=
        //check holds
        Err(format!(
            //TODO: Better error handling
            "Length too large. Size: {}, Max: {}",
            utf16.len(),
            max_characters
        )
        .into())
    } else {
        Ok(utf16)
    }
}

fn encode_pool_name(name: &str) -> Result<U16CString, error::WintunError> {
    encode_utf16(name, crate::MAX_POOL)
}

fn encode_adapter_name(name: &str) -> Result<U16CString, error::WintunError> {
    encode_utf16(name, crate::MAX_POOL)
}

fn get_adapter_luid(wintun: &Wintun, adapter: wintun_raw::WINTUN_ADAPTER_HANDLE) -> u64 {
    let mut luid: wintun_raw::NET_LUID = unsafe { std::mem::zeroed() };
    unsafe { wintun.WintunGetAdapterLUID(adapter, &mut luid as *mut wintun_raw::NET_LUID) };
    unsafe { std::mem::transmute(luid) }
}

impl Adapter {
    //TODO: Call get last error for error information on failure and improve error types

    /// Creates a new wintun adapter inside the pool `pool` with name `name`
    ///
    /// Optionally a GUID can be specified that will become the GUID of this adapter once created.
    /// Adapters obtained via this function will be able to return their adapter index via
    /// [`Adapter::get_adapter_index`]
    pub fn create(
        wintun: &Wintun,
        pool: &str,
        name: &str,
        guid: Option<u128>,
    ) -> Result<Arc<Adapter>, error::WintunError> {
        let pool_utf16 = encode_pool_name(pool)?;
        let name_utf16 = encode_adapter_name(name)?;

        let guid = match guid {
            Some(guid) => guid,
            None => {
                // Use random bytes so that we can identify this adapter in get_adapter_index
                let mut guid_bytes: [u8; 16] = [0u8; 16];
                rand::thread_rng().fill(&mut guid_bytes);
                u128::from_ne_bytes(guid_bytes)
            }
        };
        //SAFETY: guid is a unique integer so transmuting either all zeroes or the user's preferred
        //guid to the winapi guid type is safe and will allow the windows kernel to see our GUID
        let guid_struct: wintun_raw::GUID = unsafe { std::mem::transmute(guid) };
        //TODO: The guid of the adapter once created might differ from the one provided because of
        //the byte order of the segments of the GUID struct that are larger than a byte. Verify
        //that this works as expected

        let guid_ptr = &guid_struct as *const wintun_raw::GUID;

        crate::log::set_default_logger_if_unset(wintun);

        //SAFETY: the function is loaded from the wintun dll properly, we are providing valid
        //pointers, and all the strings are correct null terminated UTF-16. This safety rationale
        //applies for all Wintun* functions below
        let result = unsafe {
            wintun.WintunCreateAdapter(pool_utf16.as_ptr(), name_utf16.as_ptr(), guid_ptr)
        };

        if result.is_null() {
            Err("Failed to crate adapter".into())
        } else {
            Ok(Arc::new(Adapter {
                adapter: UnsafeHandle(result),
                wintun: wintun.clone(),
                guid,
            }))
        }
    }

    /// Attempts to open an existing wintun interface name `name`.
    ///
    /// Adapters opened via this call will have an unknown GUID meaning [`Adapter::get_adapter_index`]
    /// will always fail because knowing the adapter's GUID is required to determine its index.
    /// Currently a workaround is to delete and re-create a new adapter every time one is needed so
    /// that it gets created with a known GUID, allowing [`Adapter::get_adapter_index`] to works as
    /// expected. There is likely a way to get the GUID of our adapter using the Windows Registry
    /// or via the Win32 API, so PR's that solve this issue are always welcome!
    pub fn open(wintun: &Wintun, name: &str) -> Result<Arc<Adapter>, error::WintunError> {
        let name_utf16 = encode_adapter_name(name)?;

        crate::log::set_default_logger_if_unset(wintun);

        let result = unsafe { wintun.WintunOpenAdapter(name_utf16.as_ptr()) };

        if result.is_null() {
            Err("WintunOpenAdapter failed".into())
        } else {
            Ok(Arc::new(Adapter {
                adapter: UnsafeHandle(result),
                wintun: wintun.clone(),
                // TODO: get GUID somehow
                guid: 0,
            }))
        }
    }

    /// Delete an adapter, consuming it in the process
    pub fn delete(self) -> Result<(), ()> {
        //Dropping an adapter closes it
        drop(self);
        // Return a result here so that if later the API changes to be fallible, we can support it
        // without making a breaking change
        Ok(())
    }

    /// Initiates a new wintun session on the given adapter.
    ///
    /// Capacity is the size in bytes of the ring buffer used internally by the driver. Must be
    /// a power of two between [`crate::MIN_RING_CAPACITY`] and [`crate::MIN_RING_CAPACITY`].
    pub fn start_session(
        self: &Arc<Self>,
        capacity: u32,
    ) -> Result<session::Session, error::WintunError> {
        let range = crate::MIN_RING_CAPACITY..=crate::MAX_RING_CAPACITY;
        if !range.contains(&capacity) {
            return Err(Box::new(error::ApiError::CapacityOutOfRange(
                error::OutOfRangeData {
                    range,
                    value: capacity,
                },
            )));
        }
        if !capacity.is_power_of_two() {
            return Err(Box::new(error::ApiError::CapacityNotPowerOfTwo(capacity)));
        }

        let result = unsafe { self.wintun.WintunStartSession(self.adapter.0, capacity) };

        if result.is_null() {
            Err("WintunStartSession failed".into())
        } else {
            Ok(session::Session {
                session: UnsafeHandle(result),
                wintun: self.wintun.clone(),
                read_event: OnceCell::new(),
                shutdown_event: unsafe {
                    //SAFETY: We follow the contract required by CreateEventA. See MSDN
                    //(the pointers are allowed to be null, and 0 is okay for the others)
                    UnsafeHandle(synchapi::CreateEventA(
                        std::ptr::null_mut(),
                        0,
                        0,
                        std::ptr::null_mut(),
                    ))
                },
                adapter: Arc::clone(self),
            })
        }
    }

    /// Returns the Win32 LUID for this adapter
    pub fn get_luid(&self) -> u64 {
        get_adapter_luid(&self.wintun, self.adapter.0)
    }

    /// Returns the Win32 interface index of this adapter. Useful for specifying the interface
    /// when executing `netsh interface ip` commands
    pub fn get_adapter_index(&self) -> Result<u32, error::WintunError> {
        let mut buf_len: u32 = 0;
        //First figure out the size of the buffer needed to store the adapter info
        //SAFETY: We are upholding the contract of GetInterfaceInfo. buf_len is a valid pointer to
        //stack memory
        let result =
            unsafe { iphlpapi::GetInterfaceInfo(std::ptr::null_mut(), &mut buf_len as *mut u32) };
        if result != winerror::NO_ERROR && result != winerror::ERROR_INSUFFICIENT_BUFFER {
            let err_msg = util::get_error_message(result);
            error!("Failed to get interface info: {}", err_msg);
            //TODO: Better error types
            return Err(format!("GetInterfaceInfo failed: {}", err_msg).into());
        }

        //Allocate a buffer of the requested size
        //IP_INTERFACE_INFO must be aligned by at least 4 byte boundaries so use u32 as the
        //underlying data storage type
        let buf_elements = buf_len as usize / std::mem::size_of::<u32>() + 1;
        //Round up incase integer division truncated a byte that filled a partial element
        let mut buf: Vec<u32> = vec![0; buf_elements];

        let buf_bytes = buf.len() * std::mem::size_of::<u32>();
        assert!(buf_bytes >= buf_len as usize);

        //SAFETY:
        //
        //  1. We are upholding the contract of GetInterfaceInfo.
        //  2. `final_buf_len` is an aligned, valid pointer to stack memory
        //  3. buf is a valid, non-null pointer to at least `buf_len` bytes of heap memory,
        //     aligned to at least 4 byte boundaries
        //
        //Get the info
        let mut final_buf_len: u32 = buf_len;
        let result = unsafe {
            iphlpapi::GetInterfaceInfo(
                buf.as_mut_ptr() as *mut ipexport::IP_INTERFACE_INFO,
                &mut final_buf_len as *mut u32,
            )
        };
        if result != winerror::NO_ERROR {
            let err_msg = util::get_error_message(result);
            //TODO: maybe over allocate the buffer in case the needed size changes between the two
            //calls to GetInterfaceInfo if another adapter is added
            error!(
                "Failed to get interface info a second time: {}. Original len: {}, final len: {}",
                err_msg, buf_len, final_buf_len
            );
            return Err(format!("GetInterfaceInfo failed a second time: {}", err_msg).into());
        }
        let info = buf.as_mut_ptr() as *const ipexport::IP_INTERFACE_INFO;
        //SAFETY:
        // info is a valid, non-null, at least 4 byte aligned pointer obtained from
        // Vec::with_capacity that is readable for up to `buf_len` bytes which is guaranteed to be
        // larger than on IP_INTERFACE_INFO struct as the kernel would never ask for less memory then
        // what it will write. The largest type inside IP_INTERFACE_INFO is a u32 therefore
        // a painter to IP_INTERFACE_INFO requires an alignment of at leant 4 bytes, which
        // Vec<u32>::as_mut_ptr() provides
        let adapter_base = unsafe { &*info };
        let adapter_count = adapter_base.NumAdapters;
        let first_adapter = &adapter_base.Adapter as *const ipexport::IP_ADAPTER_INDEX_MAP;

        // SAFETY:
        //  1. first_adapter is a valid, non null pointer, aligned to at least 4 byte boundaries
        //     obtained from moving a multiple of 4 offset into the buf given by Vec::with_capacity.
        //  2. We gave GetInterfaceInfo a buffer of at least least `buf_len` bytes to work with and it
        //     succeeded in writing the adapter information within the bounds of that buffer, otherwise
        //     it would've failed. Because the operation succeeded, we know that reading n=NumAdapters
        //     IP_ADAPTER_INDEX_MAP structs stays within the bounds of buf's buffer
        let interfaces =
            unsafe { std::slice::from_raw_parts(first_adapter, adapter_count as usize) };

        for interface in interfaces {
            let name =
                unsafe { U16CStr::from_ptr_str(&interface.Name as *const u16).to_string_lossy() };
            //Nam is something like: \DEVICE\TCPIP_{29C47F55-C7BD-433A-8BF7-408DFD3B3390}
            //where the GUID is the {29C4...90}, separated by dashes
            let open = name.chars().position(|c| c == '{').ok_or(format!(
                "Failed to find {{ character inside adapter name: {}",
                name
            ))?;
            let close = name.chars().position(|c| c == '}').ok_or(format!(
                "Failed to find }} character inside adapter name: {}",
                name
            ))?;
            let digits: Vec<u8> = name[open..close]
                .chars()
                .filter(|c| c.is_digit(16))
                .chunks(2)
                .into_iter()
                .filter_map(|mut chunk| {
                    //Filter out chunks that have < 2 digits
                    if let Some(a) = chunk.next() {
                        if let Some(b) = chunk.next() {
                            return Some((a, b));
                        }
                    }
                    None
                })
                .map(|digits| {
                    let chars: [u8; 2] = [digits.0 as u8, digits.1 as u8];
                    let s = std::str::from_utf8(&chars).unwrap();
                    u8::from_str_radix(s, 16).unwrap()
                })
                .collect();

            //Our index is the adapter which has a guid in its name that matches ours
            //For now we just check for a guid with the same hex bytes in any order
            //TODO: byte swap GUID from name so that we can compare self.guid with the parsed GUID
            //directly
            let mut match_count = 0;
            for byte in self.guid.to_ne_bytes() {
                if digits.contains(&byte) {
                    match_count += 1;
                }
            }
            if match_count == digits.len() {
                return Ok(interface.Index);
            }
        }
        Err("Unable to find matching GUID".into())
    }
}

impl Drop for Adapter {
    fn drop(&mut self) {
        //Close adapter on drop
        //This is why we need an Arc of wintun
        unsafe { self.wintun.WintunCloseAdapter(self.adapter.0) };
        self.adapter = UnsafeHandle(ptr::null_mut());
    }
}