#[cfg(test)]
mod tests;
use core::time;
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
str::FromStr,
};
use defguard_wireguard_rs::{
host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi,
};
fn get_network_address(ip: Ipv4Addr, prefix: u8) -> Ipv4Addr {
let mask: u32 = !0 << (32 - prefix);
let ip_u32: u32 = u32::from(ip);
Ipv4Addr::from(ip_u32 & mask)
}
fn get_first_ip(network_address: Ipv4Addr) -> Ipv4Addr {
let ip_u32: u32 = u32::from(network_address);
Ipv4Addr::from(ip_u32 + 1)
}
pub fn disconnect_from_wireguard() {
let ifname: String = if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") {
"wg0".into()
} else {
"utun3".into()
};
let wgapi = WGApi::new(ifname.clone(), false).unwrap();
let wire_data = wgapi.read_interface_data();
if wire_data.is_ok() {
let del = wgapi.remove_interface();
if del.is_ok() {
println!("Interface deleted");
}
}
}
pub async fn connect_to_wireguard(
server_endpoint: SocketAddr,
server_public_key: String,
client_private_key: String,
client_address: String,
client_port: Option<u32>,
client_addresses_masks: Option<Vec<String>>,
network_prefix: u8,
) -> Result<(), Box<dyn std::error::Error>> {
let ifname: String = if cfg!(target_os = "linux") || cfg!(target_os = "freebsd") {
"wg0".into()
} else {
"utun3".into()
};
let wgapi = WGApi::new(ifname.clone(), false)?;
let wire_data = wgapi.read_interface_data();
let ip: Ipv4Addr = client_address.parse().expect("Invalid IP address");
let network_address = get_network_address(ip, network_prefix);
if wire_data.is_ok() {
let first_ip = get_first_ip(network_address);
let ip_ping: IpAddr = IpAddr::V4(first_ip);
let pinged = rust_simple_ping::ping(Some(ip_ping), None).await;
match pinged {
Ok(_pinged) => {
let vpn_host = wire_data.unwrap();
let first_peer = vpn_host.clone().peers.into_keys().next().unwrap();
let peer_endpoint = vpn_host.peers.get(&first_peer).unwrap().endpoint.unwrap();
if peer_endpoint != server_endpoint {
println!("wire_data: {:?}", vpn_host);
println!("Endpoint changed, reconnecting");
disconnect_from_wireguard();
} else {
println!("VPN: OK");
return Ok(());
}
}
Err(e) => {
println!("Error: {:?}", e);
println!("VPN: OFF");
disconnect_from_wireguard();
}
}
}
wgapi.create_interface()?;
let peer_public_key: Key = server_public_key.parse()?;
let mut peer = Peer::new(peer_public_key);
peer.endpoint = Some(server_endpoint);
peer.persistent_keepalive_interval = Some(25);
if let Some(client_addresses_masks) = client_addresses_masks {
for addr in client_addresses_masks {
peer.allowed_ips.push(IpAddrMask::from_str(&addr)?);
}
} else {
peer.allowed_ips.push(
IpAddrMask::from_str(
(network_address.to_string().as_str().to_owned()
+ "/"
+ network_prefix.to_string().as_str())
.as_str(),
)
.unwrap(),
);
}
let mut default_client_port: u32 = 12345;
if let Some(client_port) = client_port {
default_client_port = client_port;
}
let interface_config = InterfaceConfiguration {
name: ifname.clone(),
prvkey: client_private_key,
address: client_address,
port: default_client_port,
peers: vec![peer],
};
#[cfg(not(windows))]
wgapi.configure_interface(&interface_config)?;
#[cfg(windows)]
wgapi.configure_interface(&interface_config, &[])?;
wgapi.configure_peer_routing(&interface_config.peers)?;
println!("VPN: ON");
Ok(())
}
pub async fn routine_connect_to_wireguard(
server_endpoint: SocketAddr,
server_public_key: String,
client_private_key: String,
client_address: String,
client_port: Option<u32>,
client_addresses_masks: Option<Vec<String>>,
network_prefix: u8,
) -> Result<(), Box<dyn std::error::Error>> {
loop {
let connect = connect_to_wireguard(
server_endpoint,
server_public_key.clone(),
client_private_key.clone(),
client_address.clone(),
client_port,
client_addresses_masks.clone(),
network_prefix,
)
.await;
if let Err(e) = connect {
eprintln!("Error: {:?}", e);
break;
}
std::thread::sleep(time::Duration::from_secs(30));
}
Ok(())
}