extern crate chrono;
extern crate libc;
extern crate wgctrl_sys;
use key::Key;
use std::ffi::{CStr, CString};
use std::io;
use std::mem;
use std::net::{IpAddr, SocketAddr};
use std::ptr;
use std::str;
use self::chrono::TimeZone;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct AllowedIp {
pub address: IpAddr,
pub cidr: u8,
}
impl<'a> From<&'a wgctrl_sys::wg_allowedip> for AllowedIp {
fn from(raw: &wgctrl_sys::wg_allowedip) -> AllowedIp {
let addr = match i32::from(raw.family) {
libc::AF_INET => IpAddr::V4(unsafe { raw.__bindgen_anon_1.ip4 }.s_addr.to_be().into()),
libc::AF_INET6 => IpAddr::V6(unsafe { raw.__bindgen_anon_1.ip6 }.s6_addr.into()),
_ => unreachable!(format!("Unsupported socket family {}!", raw.family)),
};
AllowedIp {
address: addr,
cidr: raw.cidr,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PeerConfig {
pub public_key: Key,
pub preshared_key: Option<Key>,
pub endpoint: Option<SocketAddr>,
pub persistent_keepalive_interval: Option<u16>,
pub allowed_ips: Vec<AllowedIp>,
__cant_construct_me: (),
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PeerStats {
pub last_handshake_time: Option<chrono::DateTime<chrono::Utc>>,
pub rx_bytes: u64,
pub tx_bytes: u64,
__cant_construct_me: (),
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PeerInfo {
pub config: PeerConfig,
pub stats: PeerStats,
}
fn parse_allowed_ips(peer: &wgctrl_sys::wg_peer) -> Vec<AllowedIp> {
let mut result = Vec::new();
let mut current_ip: *mut wgctrl_sys::wg_allowedip = peer.first_allowedip;
if current_ip.is_null() {
return result;
}
loop {
let ip = unsafe { &*current_ip };
result.push(AllowedIp::from(ip));
if current_ip == peer.last_allowedip {
break;
}
current_ip = ip.next_allowedip;
}
result
}
unsafe fn parse_endpoint(endpoint: &wgctrl_sys::wg_peer__bindgen_ty_1) -> Option<SocketAddr> {
match i32::from(endpoint.addr.sa_family) {
libc::AF_INET => Some(SocketAddr::new(
IpAddr::V4(endpoint.addr4.sin_addr.s_addr.into()),
u16::from_be(endpoint.addr4.sin_port),
)),
libc::AF_INET6 => Some(SocketAddr::new(
IpAddr::V6(endpoint.addr6.sin6_addr.s6_addr.into()),
u16::from_be(endpoint.addr6.sin6_port),
)),
0 => None,
_ => unreachable!(format!(
"Unsupported socket family: {}!",
endpoint.addr.sa_family
)),
}
}
impl<'a> From<&'a wgctrl_sys::wg_peer> for PeerInfo {
fn from(raw: &wgctrl_sys::wg_peer) -> PeerInfo {
use self::wgctrl_sys::wg_peer_flags as wgpf;
PeerInfo {
config: PeerConfig {
public_key: Key::from_raw(raw.public_key),
preshared_key: if (raw.flags & wgpf::WGPEER_HAS_PRESHARED_KEY).0 > 0 {
Some(Key::from_raw(raw.preshared_key))
} else {
None
},
endpoint: unsafe { parse_endpoint(&raw.endpoint) },
persistent_keepalive_interval: match raw.persistent_keepalive_interval {
0 => None,
x => Some(x),
},
allowed_ips: parse_allowed_ips(raw),
__cant_construct_me: (),
},
stats: PeerStats {
last_handshake_time: match (
raw.last_handshake_time.tv_sec,
raw.last_handshake_time.tv_nsec,
) {
(0, 0) => None,
(s, ns) => Some(chrono::Utc.timestamp(s, ns as u32)),
},
rx_bytes: raw.rx_bytes,
tx_bytes: raw.tx_bytes,
__cant_construct_me: (),
},
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DeviceInfo {
pub name: String,
pub public_key: Option<Key>,
pub private_key: Option<Key>,
pub fwmark: Option<u32>,
pub listen_port: Option<u16>,
pub peers: Vec<PeerInfo>,
__cant_construct_me: (),
}
fn parse_peers(dev: &wgctrl_sys::wg_device) -> Vec<PeerInfo> {
let mut result = Vec::new();
let mut current_peer = dev.first_peer;
if current_peer.is_null() {
return result;
}
loop {
let peer = unsafe { &*current_peer };
result.push(PeerInfo::from(peer));
if current_peer == dev.last_peer {
break;
}
current_peer = peer.next_peer;
}
result
}
fn parse_device_name(name: [i8; 16]) -> String {
let name: [u8; 16] = unsafe { mem::transmute(name) };
let idx: usize = name
.iter()
.position(|x| *x == 0)
.expect("Interface name too long?");
unsafe { str::from_utf8_unchecked(&name[..idx]) }.to_owned()
}
impl<'a> From<&'a wgctrl_sys::wg_device> for DeviceInfo {
fn from(raw: &wgctrl_sys::wg_device) -> DeviceInfo {
use self::wgctrl_sys::wg_device_flags as wgdf;
DeviceInfo {
name: parse_device_name(raw.name),
public_key: if (raw.flags & wgdf::WGDEVICE_HAS_PUBLIC_KEY).0 > 0 {
Some(Key::from_raw(raw.public_key))
} else {
None
},
private_key: if (raw.flags & wgdf::WGDEVICE_HAS_PRIVATE_KEY).0 > 0 {
Some(Key::from_raw(raw.private_key))
} else {
None
},
fwmark: match raw.fwmark {
0 => None,
x => Some(x),
},
listen_port: match raw.listen_port {
0 => None,
x => Some(x),
},
peers: parse_peers(&raw),
__cant_construct_me: (),
}
}
}
impl DeviceInfo {
pub fn enumerate() -> Result<Vec<String>, io::Error> {
let base = unsafe { wgctrl_sys::wg_list_device_names() };
if base.is_null() {
return Err(io::Error::last_os_error());
}
let mut current = base;
let mut result = Vec::new();
loop {
let next_dev = unsafe { CStr::from_ptr(current).to_bytes() };
let len = next_dev.len();
if len == 0 {
break;
}
current = unsafe { current.add(len + 1) };
result.push(unsafe { str::from_utf8_unchecked(next_dev) }.to_owned());
}
unsafe { libc::free(base as *mut libc::c_void) };
Ok(result)
}
pub fn get_by_name(name: &str) -> Result<Self, io::Error> {
let mut device: *mut wgctrl_sys::wg_device = ptr::null_mut();
let cs = CString::new(name)?;
let result = unsafe {
wgctrl_sys::wg_get_device(
(&mut device) as *mut _ as *mut *mut wgctrl_sys::wg_device,
cs.as_ptr(),
)
};
let result = if result == 0 {
Ok(DeviceInfo::from(unsafe { &*device }))
} else {
Err(io::Error::last_os_error())
};
unsafe { wgctrl_sys::wg_free_device(device) };
result
}
}