Initial commit
This commit is contained in:
commit
c1e7415871
56 changed files with 3225 additions and 0 deletions
24
tun/Cargo.toml
Normal file
24
tun/Cargo.toml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "tun"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
nix = { version = "0.25", features = ["ioctl"] }
|
||||
socket2 = "0.4"
|
||||
tokio = { version = "1.21", features = [] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
libloading = "0.7"
|
||||
widestring = "1.0"
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
anyhow = "1.0"
|
||||
bindgen = "0.61"
|
||||
hex-literal = "0.3"
|
||||
platforms = "3.0"
|
||||
reqwest = { version = "0.11", features = ["native-tls"] }
|
||||
sha2 = "0.10"
|
||||
tokio = { version = "1.21", features = ["rt"] }
|
||||
zip = { version = "0.6", features = ["deflate"] }
|
||||
79
tun/build.rs
Normal file
79
tun/build.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#[cfg(windows)]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
let buf = reqwest::get("https://www.wintun.net/builds/wintun-0.14.1.zip")
|
||||
.await?
|
||||
.bytes()
|
||||
.await?;
|
||||
assert_content_hash(
|
||||
&buf,
|
||||
hex_literal::hex!("07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51"),
|
||||
);
|
||||
let mut archive = zip::ZipArchive::new(Cursor::new(buf))?;
|
||||
|
||||
let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?);
|
||||
|
||||
let mut header = String::new();
|
||||
archive
|
||||
.by_name("wintun/include/wintun.h")?
|
||||
.read_to_string(&mut header)?;
|
||||
header.push_str(
|
||||
"WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter;
|
||||
WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
|
||||
WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
|
||||
WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
|
||||
WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver;
|
||||
WINTUN_SET_LOGGER_FUNC WintunSetLogger;
|
||||
WINTUN_START_SESSION_FUNC WintunStartSession;
|
||||
WINTUN_END_SESSION_FUNC WintunEndSession;
|
||||
WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
|
||||
WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
|
||||
WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
|
||||
WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket;
|
||||
WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
|
||||
WINTUN_SEND_PACKET_FUNC WintunSendPacket;",
|
||||
);
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header_contents("wintun.h", &header)
|
||||
.allowlist_function("Wintun.*")
|
||||
.allowlist_type("WINTUN_.*")
|
||||
.dynamic_library_name("wintun")
|
||||
.dynamic_link_require_all(true)
|
||||
.generate()
|
||||
.unwrap();
|
||||
bindings.write_to_file(out_dir.join("wintun.rs"))?;
|
||||
|
||||
let mut library = Vec::new();
|
||||
let platform = platforms::Platform::find(&env::var("TARGET")?).unwrap();
|
||||
let arch = match platform.target_arch {
|
||||
platforms::target::Arch::Arm => "arm",
|
||||
platforms::Arch::AArch64 => "arm64",
|
||||
platforms::Arch::X86 => "x86",
|
||||
platforms::Arch::X86_64 => "amd64",
|
||||
arch => panic!("{} is not a supported architecture", arch),
|
||||
};
|
||||
archive
|
||||
.by_name(&format!("wintun/bin/{}/wintun.dll", arch))?
|
||||
.read_to_end(&mut library)?;
|
||||
std::fs::write(out_dir.join("wintun.dll"), library)?;
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn assert_content_hash(content: &[u8], hash: [u8; 32]) {
|
||||
use sha2::digest::Update;
|
||||
use sha2::Digest;
|
||||
|
||||
let computed = sha2::Sha256::new().chain(content).finalize();
|
||||
assert_eq!(computed.as_slice(), &hash[..]);
|
||||
}
|
||||
53
tun/src/apple/kern_control.rs
Normal file
53
tun/src/apple/kern_control.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use libc::{sockaddr_ctl, AF_SYSTEM, AF_SYS_CONTROL};
|
||||
use std::io::Result;
|
||||
use std::mem::size_of;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
/// Trait to connect to kernel extensions on Apple platforms
|
||||
///
|
||||
/// Pulled from XNU source: https://github.com/apple/darwin-xnu/blob/main/bsd/sys/kern_control.h
|
||||
pub trait SysControlSocket {
|
||||
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr>;
|
||||
}
|
||||
|
||||
impl SysControlSocket for socket2::Socket {
|
||||
fn resolve(&self, name: &str, index: u32) -> Result<socket2::SockAddr> {
|
||||
let mut info = sys::ctl_info {
|
||||
ctl_id: 0,
|
||||
ctl_name: [0; 96],
|
||||
};
|
||||
info.ctl_name[..name.len()].copy_from_slice(name.as_bytes());
|
||||
|
||||
unsafe { sys::resolve_ctl_info(self.as_raw_fd(), &mut info as *mut sys::ctl_info)? };
|
||||
|
||||
let (_, addr) = unsafe {
|
||||
socket2::SockAddr::init(|addr_storage, len| {
|
||||
*len = size_of::<sockaddr_ctl>() as u32;
|
||||
|
||||
let mut addr: &mut sockaddr_ctl = &mut *addr_storage.cast();
|
||||
addr.sc_len = *len as u8;
|
||||
addr.sc_family = AF_SYSTEM as u8;
|
||||
addr.ss_sysaddr = AF_SYS_CONTROL as u16;
|
||||
addr.sc_id = info.ctl_id;
|
||||
addr.sc_unit = index;
|
||||
Ok(())
|
||||
})
|
||||
}?;
|
||||
|
||||
Ok(addr)
|
||||
}
|
||||
}
|
||||
|
||||
mod sys {
|
||||
use nix::ioctl_readwrite;
|
||||
|
||||
const MAX_KCTL_NAME: usize = 96;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ctl_info {
|
||||
pub ctl_id: u32,
|
||||
pub ctl_name: [u8; MAX_KCTL_NAME],
|
||||
}
|
||||
|
||||
ioctl_readwrite!(resolve_ctl_info, b'N', 3, ctl_info);
|
||||
}
|
||||
90
tun/src/apple/mod.rs
Normal file
90
tun/src/apple/mod.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
use socket2::SockAddr;
|
||||
use std::io::Result;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
|
||||
mod kern_control;
|
||||
mod queue;
|
||||
|
||||
pub use queue::TunQueue;
|
||||
|
||||
use crate::syscall;
|
||||
use crate::unix::copy_if_name;
|
||||
use kern_control::SysControlSocket;
|
||||
|
||||
pub struct TunInterface {
|
||||
socket: socket2::Socket,
|
||||
}
|
||||
|
||||
impl TunInterface {
|
||||
pub fn new() -> Result<TunInterface> {
|
||||
TunInterface::connect(None)
|
||||
}
|
||||
|
||||
fn connect(addr: Option<SockAddr>) -> Result<TunInterface> {
|
||||
use socket2::{Domain, Protocol, Socket, Type};
|
||||
|
||||
let socket = Socket::new(
|
||||
Domain::from(libc::AF_SYSTEM),
|
||||
Type::DGRAM,
|
||||
Some(Protocol::from(libc::SYSPROTO_CONTROL)),
|
||||
)?;
|
||||
let addr = match addr {
|
||||
Some(addr) => addr,
|
||||
None => socket.resolve(sys::UTUN_CONTROL_NAME, 0)?,
|
||||
};
|
||||
socket.connect(&addr)?;
|
||||
|
||||
Ok(TunInterface { socket })
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Result<String> {
|
||||
let mut buf = [0u8; libc::IFNAMSIZ];
|
||||
let mut len = buf.len() as libc::socklen_t;
|
||||
syscall!(getsockopt(
|
||||
self.as_raw_fd(),
|
||||
libc::SYSPROTO_CONTROL,
|
||||
sys::UTUN_OPT_IFNAME,
|
||||
buf.as_mut_ptr() as *mut libc::c_void,
|
||||
&mut len,
|
||||
))?;
|
||||
let name = copy_if_name(buf);
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
pub fn queue(&self) -> Result<TunQueue> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for TunInterface {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.socket.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for TunInterface {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.socket.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
mod sys {
|
||||
pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control";
|
||||
|
||||
pub const UTUN_OPT_IFNAME: libc::c_int = 2;
|
||||
|
||||
/// Copied from https://github.com/rust-lang/socket2/blob/61314a231f73964b3db969ef72c0e9479df320f3/src/sys/unix.rs#L168-L178
|
||||
/// getsockopt is not exposed by socket2
|
||||
#[macro_export]
|
||||
macro_rules! syscall {
|
||||
($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
let res = unsafe { libc::$fn($($arg, )*) };
|
||||
if res == -1 {
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(res)
|
||||
}
|
||||
}};
|
||||
}
|
||||
}
|
||||
17
tun/src/apple/queue.rs
Normal file
17
tun/src/apple/queue.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
|
||||
pub struct TunQueue {
|
||||
socket: socket2::Socket,
|
||||
}
|
||||
|
||||
impl AsRawFd for TunQueue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.socket.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for TunQueue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.socket.into_raw_fd()
|
||||
}
|
||||
}
|
||||
16
tun/src/lib.rs
Normal file
16
tun/src/lib.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#[cfg(target_vendor = "apple")]
|
||||
#[path = "apple/mod.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[path = "linux.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "windows/mod.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
||||
pub(crate) mod unix;
|
||||
|
||||
pub use imp::{TunInterface, TunQueue};
|
||||
56
tun/src/linux.rs
Normal file
56
tun/src/linux.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use std::fs::OpenOptions;
|
||||
use std::io::Result;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
||||
|
||||
use crate::unix::copy_if_name;
|
||||
|
||||
pub struct TunInterface {
|
||||
inner: socket2::Socket,
|
||||
}
|
||||
|
||||
impl TunInterface {
|
||||
pub fn new() -> Result<TunInterface> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open("/dev/net/tun")?;
|
||||
|
||||
let mut iff = libc::ifreq {
|
||||
ifr_name: [0; libc::IFNAMSIZ],
|
||||
ifr_ifru: libc::__c_anonymous_ifr_ifru {
|
||||
ifru_flags: (libc::IFF_TUN | libc::IFF_TUN_EXCL | libc::IFF_NO_PI) as i16,
|
||||
},
|
||||
};
|
||||
unsafe { sys::tun_set_iff(file.as_raw_fd(), &mut iff)? };
|
||||
|
||||
let inner = unsafe { socket2::Socket::from_raw_fd(file.into_raw_fd()) };
|
||||
Ok(TunInterface { inner })
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Result<String> {
|
||||
let mut iff = libc::ifreq {
|
||||
ifr_name: [0; libc::IFNAMSIZ],
|
||||
ifr_ifru: libc::__c_anonymous_ifr_ifru { ifru_flags: 0 },
|
||||
};
|
||||
unsafe { sys::tun_get_iff(self.inner.as_raw_fd(), &mut iff)? };
|
||||
|
||||
let name = copy_if_name(iff.ifr_name);
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
mod sys {
|
||||
use nix::{ioctl_read_bad, ioctl_write_ptr_bad, request_code_read, request_code_write};
|
||||
use std::mem::size_of;
|
||||
|
||||
ioctl_write_ptr_bad!(
|
||||
tun_set_iff,
|
||||
request_code_write!(b'T', 202, size_of::<libc::c_int>()),
|
||||
libc::ifreq
|
||||
);
|
||||
ioctl_read_bad!(
|
||||
tun_get_iff,
|
||||
request_code_read!(b'T', 210, size_of::<libc::c_int>()),
|
||||
libc::ifreq
|
||||
);
|
||||
}
|
||||
1
tun/src/queue.rs
Normal file
1
tun/src/queue.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
11
tun/src/unix.rs
Normal file
11
tun/src/unix.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use std::ffi::CStr;
|
||||
|
||||
pub fn copy_if_name(buf: [u8; libc::IFNAMSIZ]) -> String {
|
||||
// TODO: Switch to `CStr::from_bytes_until_nul` when stabilized
|
||||
unsafe {
|
||||
CStr::from_ptr(buf.as_ptr() as *const _)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
44
tun/src/windows/mod.rs
Normal file
44
tun/src/windows/mod.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use std::io::Result;
|
||||
use std::ptr;
|
||||
use widestring::{u16cstr, U16CString};
|
||||
|
||||
pub struct TunInterface {
|
||||
wintun: sys::wintun,
|
||||
handle: sys::WINTUN_ADAPTER_HANDLE,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl TunInterface {
|
||||
pub fn new() -> Result<TunInterface> {
|
||||
let name = U16CString::from(u16cstr!("ConradNet"));
|
||||
let wintun = sys::wintun::default();
|
||||
let handle =
|
||||
unsafe { wintun.WintunCreateAdapter(name.as_ptr(), name.as_ptr(), ptr::null()) };
|
||||
Ok(TunInterface {
|
||||
wintun,
|
||||
handle,
|
||||
name: String::from("ConradNet"),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TunInterface {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.wintun.WintunCloseAdapter(self.handle) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sys {
|
||||
#![allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
include!(concat!(env!("OUT_DIR"), "/wintun.rs"));
|
||||
|
||||
impl Default for wintun {
|
||||
fn default() -> Self {
|
||||
unsafe { wintun::new(format!("{}/wintun.dll", env!("OUT_DIR"))).unwrap() }
|
||||
}
|
||||
}
|
||||
}
|
||||
19
tun/src/windows/queue.rs
Normal file
19
tun/src/windows/queue.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use crate::TunInterface;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
|
||||
pub struct TunQueue {
|
||||
inner: crate::windows::sys::
|
||||
inner: socket2::Socket,
|
||||
}
|
||||
|
||||
impl AsRawFd for TunQueue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.socket.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for TunQueue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.socket.into_raw_fd()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue