tap_windows/lib.rs
1//! # tap-windows
2//! Library to interface with the tap-windows driver
3//! created by OpenVPN to manage tap interfaces.
4//! Look at the documentation for `Device` for a
5//! pretty simple example on how to use this library.
6#![cfg(windows)]
7
8/// Encode a string as a utf16 buffer
9fn encode_utf16(string: &str) -> Vec<u16> {
10 use std::iter::once;
11 string.encode_utf16().chain(once(0)).collect()
12}
13
14/// Decode a string from a utf16 buffer
15fn decode_utf16(string: &[u16]) -> String {
16 let end = string.iter().position(|b| *b == 0).unwrap_or(string.len());
17 String::from_utf16_lossy(&string[..end])
18}
19
20mod ffi;
21mod iface;
22mod netsh;
23
24use std::{io, net, time};
25use winapi::shared::ifdef::NET_LUID;
26use winapi::um::winioctl::*;
27use winapi::um::winnt::HANDLE;
28
29/// A tap-windows device handle, it offers facilities to:
30/// - create, open and delete interfaces
31/// - write and read the current configuration
32/// - write and read packets from the device
33/// Example
34/// ```no_run
35/// use tap_windows::Device;
36/// use std::io::Read;
37///
38/// const MY_INTERFACE: &str = "My Interface";
39///
40/// // Try to open the device
41/// let mut dev = Device::open(MY_INTERFACE)
42/// .or_else(|_| -> std::io::Result<_> {
43/// // The device does not exists...
44/// // try creating a new one
45///
46/// let dev = Device::create()?;
47/// dev.set_name(MY_INTERFACE)?;
48///
49/// Ok(dev)
50/// })
51/// // Everything failed, just panic
52/// .expect("Failed to open device");
53///
54/// // Set the device ip
55/// dev.set_ip([192, 168, 60, 1], [255, 255, 255, 0])
56/// .expect("Failed to set device ip");
57///
58/// // Setup read buffer
59/// let mtu = dev.get_mtu().unwrap_or(1500);
60/// let mut buf = vec![0; mtu as usize];
61///
62/// // Read a single packet from the device
63/// let amt = dev.read(&mut buf)
64/// .expect("Failed to read packet");
65///
66/// // Print it
67/// println!("{:#?}", &buf[..amt]);
68/// ```
69pub struct Device {
70 luid: NET_LUID,
71 handle: HANDLE,
72}
73
74impl Device {
75 /// Creates a new tap-windows device
76 /// Example
77 /// ```no_run
78 /// use tap_windows::Device;
79 ///
80 /// let dev = Device::create()
81 /// .expect("Failed to create device");
82 ///
83 /// println!("{:?}", dev.get_name());
84 /// ```
85 pub fn create() -> io::Result<Self> {
86 let luid = iface::create_interface()?;
87
88 // Even after retrieving the luid, we might need to wait
89 let start = time::Instant::now();
90 let handle = loop {
91 // If we surpassed 2 seconds just return
92 let now = time::Instant::now();
93 if now - start > time::Duration::from_secs(2) {
94 return Err(io::Error::new(
95 io::ErrorKind::TimedOut,
96 "Interface timed out",
97 ));
98 }
99
100 match iface::open_interface(&luid) {
101 Err(_) => {
102 std::thread::yield_now();
103 continue;
104 }
105 Ok(handle) => break handle,
106 };
107 };
108
109 Ok(Self { luid, handle })
110 }
111
112 /// Opens an existing tap-windows device by name
113 /// Example
114 /// ```no_run
115 /// use tap_windows::Device;
116 ///
117 /// let dev = Device::open("My Own Device")
118 /// .expect("Failed to open device");
119 ///
120 /// println!("{:?}", dev.get_name());
121 /// ```
122 pub fn open(name: &str) -> io::Result<Self> {
123 let name = encode_utf16(name);
124
125 let luid = ffi::alias_to_luid(&name)?;
126 iface::check_interface(&luid)?;
127
128 let handle = iface::open_interface(&luid)?;
129
130 Ok(Self { luid, handle })
131 }
132
133 /// Deletes the interface before closing it.
134 /// By default interfaces are never deleted on Drop,
135 /// with this you can choose if you want deletion or not
136 /// Example
137 /// ```no_run
138 /// use tap_windows::Device;
139 ///
140 /// let dev = Device::create()
141 /// .expect("Failed to create device");
142 ///
143 /// println!("{:?}", dev.get_name());
144 ///
145 /// // Perform a quick cleanup before exiting
146 /// dev.delete().expect("Failed to delete device");
147 /// ```
148 pub fn delete(self) -> io::Result<()> {
149 iface::delete_interface(&self.luid)?;
150
151 Ok(())
152 }
153
154 /// Sets the status of the interface to connected.
155 /// Equivalent to `.set_status(true)`
156 pub fn up(&self) -> io::Result<()> {
157 self.set_status(true)
158 }
159
160 /// Sets the status of the interface to disconnected.
161 /// Equivalent to `.set_status(false)`
162 pub fn down(&self) -> io::Result<()> {
163 self.set_status(false)
164 }
165
166 /// Retieve the mac of the interface
167 pub fn get_mac(&self) -> io::Result<[u8; 6]> {
168 let mut mac = [0; 6];
169
170 ffi::device_io_control(
171 self.handle,
172 CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS),
173 &(),
174 &mut mac,
175 )
176 .map(|_| mac)
177 }
178
179 /// Retrieve the version of the driver
180 pub fn get_version(&self) -> io::Result<[u32; 3]> {
181 let mut version = [0; 3];
182
183 ffi::device_io_control(
184 self.handle,
185 CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS),
186 &(),
187 &mut version,
188 )
189 .map(|_| version)
190 }
191
192 /// Retieve the mtu of the interface
193 pub fn get_mtu(&self) -> io::Result<u32> {
194 let mut mtu = 0;
195
196 ffi::device_io_control(
197 self.handle,
198 CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS),
199 &(),
200 &mut mtu,
201 )
202 .map(|_| mtu)
203 }
204
205 /// Retrieve the name of the interface
206 pub fn get_name(&self) -> io::Result<String> {
207 ffi::luid_to_alias(&self.luid).map(|name| decode_utf16(&name))
208 }
209
210 /// Set the name of the interface
211 pub fn set_name(&self, newname: &str) -> io::Result<()> {
212 let name = self.get_name()?;
213 netsh::set_interface_name(&name, newname)
214 }
215
216 /// Set the ip of the interface
217 /// ```no_run
218 /// use tap_windows::Device;
219 ///
220 /// let dev = Device::create()
221 /// .expect("Failed to create device");
222 ///
223 /// dev.set_ip([192, 168, 60, 1], [255, 255, 255, 0])
224 /// .expect("Failed to set interface ip");
225 ///
226 /// println!("{:?}", dev.get_name());
227 /// ```
228 pub fn set_ip<A, B>(&self, address: A, mask: B) -> io::Result<()>
229 where
230 A: Into<net::Ipv4Addr>,
231 B: Into<net::Ipv4Addr>,
232 {
233 let name = self.get_name()?;
234 let address = address.into().to_string();
235 let mask = mask.into().to_string();
236
237 netsh::set_interface_ip(&name, &address, &mask)
238 }
239
240 /// Set the status of the interface, true for connected,
241 /// false for disconnected.
242 pub fn set_status(&self, status: bool) -> io::Result<()> {
243 let status: u32 = if status { 1 } else { 0 };
244
245 ffi::device_io_control(
246 self.handle,
247 CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS),
248 &status,
249 &mut (),
250 )
251 }
252}
253
254impl io::Read for Device {
255 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
256 ffi::read_file(self.handle, buf).map(|res| res as _)
257 }
258}
259
260impl io::Write for Device {
261 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
262 ffi::write_file(self.handle, buf).map(|res| res as _)
263 }
264
265 fn flush(&mut self) -> io::Result<()> {
266 Ok(())
267 }
268}
269
270impl Drop for Device {
271 fn drop(&mut self) {
272 let _ = ffi::close_handle(self.handle);
273 }
274}