// Copyright 2017 Sascha Grunert, Sopium
// This file is part of WireGuard.rs.
// WireGuard.rs is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// WireGuard.rs is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with WireGuard.rs. If not, see .
//! # WireGuard.rs
//! ## Fast, modern and secure VPN tunnel
//!
//! Target of this project is to have a user space Rust implementation of `WireGuard`.
#![deny(missing_docs)]
#![feature(integer_atomics)]
#![feature(retain_hash_collection)]
extern crate daemonize;
#[macro_use]
extern crate log;
extern crate libc;
#[macro_use]
extern crate nix;
#[macro_use]
extern crate error_chain;
pub mod tun;
mod crypto;
pub mod protocol;
pub mod error;
mod uapi;
use error::*;
use uapi::{WgDevice, WgIpMask, WgPeer};
use std::fs::{create_dir, remove_file};
use std::mem::size_of;
use std::path::{Path, PathBuf};
use libc::{FIONREAD, ioctl};
use nix::poll::{EventFlags, poll, PollFd, POLLIN, POLLERR, POLLHUP, POLLNVAL};
use nix::sys::socket::{accept, AddressFamily, bind, listen, SockAddr, SockType, SockFlag, socket, UnixAddr};
use nix::unistd::{close, read};
/// The main `WireGuard` structure
pub struct WireGuard {
/// The file descriptor of the socket
fd: i32,
}
impl WireGuard {
/// Creates a new `WireGuard` instance
pub fn new(name: &str) -> Result {
// Create the unix socket
let fd = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), 0)?;
if fd < 0 {
bail!("Could not create local socket.");
}
debug!("Created local socket.");
// Create the socket directory if not existing
let mut socket_path = if Path::new("/run").exists() {
PathBuf::from("/run")
} else {
PathBuf::from("/var").join("run")
};
socket_path = socket_path.join("wireguard");
if !socket_path.exists() {
debug!("Creating socket path: {}", socket_path.display());
create_dir(&socket_path)?;
}
debug!("Setting chmod 0700 of socket path: {}",
socket_path.display());
Self::chmod(&socket_path, 0o700)?;
// Finish the socket path
socket_path.push(name);
socket_path.set_extension("sock");
if socket_path.exists() {
debug!("Removing existing socket: {}", socket_path.display());
remove_file(&socket_path)?;
}
// Create the `sockaddr_un`
let unix_addr = UnixAddr::new(&socket_path)?;
let addr = SockAddr::Unix(unix_addr);
// Bind the socket
debug!("Binding socket.");
bind(fd, &addr)?;
// Listen on the socket
debug!("Listening on socket.");
listen(fd, 100)?;
// Return the `WireGuard` instance
Ok(WireGuard { fd: fd })
}
/// Run the `WireGuard` instance
pub fn run(&self) -> Result<()> {
// A temporarily buffer to write in
let mut buffer = vec![];
debug!("Waiting for connections.");
loop {
// Accept new connections
trace!("Accepting new connection.");
let client = accept(self.fd)?;
if client < 0 {
error!("Can not 'accept' new connections.");
break;
}
// Poll for new events
trace!("Polling for events.");
let mut pollfd = [PollFd::new(client, POLLIN, EventFlags::empty())];
poll(&mut pollfd, -1)?;
// Check for the correct revents
if let Some(re) = pollfd[0].revents() {
if re.contains(POLLERR) || re.contains(POLLHUP) || re.contains(POLLNVAL) || !re.contains(POLLIN) {
close(client)?;
bail!("Polling failed.");
}
}
// Get the size of the message
trace!("Getting message size.");
let message_len = 0;
let ret = unsafe { ioctl(client, FIONREAD, &message_len) };
if ret < 0 || message_len == 0 {
close(client)?;
bail!("Call to 'ioctl' failed.");
}
// Resize the vector
buffer.resize(message_len, 0);
// Finally we receive the data
trace!("Reading message.");
let data_len = read(client, buffer.as_mut_slice())?;
if data_len == 0 {
close(client)?;
bail!("Could not receive data");
}
trace!("Message size: {}", data_len);
// If `data_len` is 1 and it is a NULL byte, it's a "get" request, so we send our
// device back.
let device;
if data_len == 1 && buffer[0] == 0 {
trace!("Got 'get' request, sending back to device");
// TODO:
// device = get_current_wireguard_device(&data_len);
// write(client, device, data_len as usize)?;
} else {
let wgdev_size = size_of::();
let wgpeer_size = size_of::();
let wgipmask_size = size_of::();
// Otherwise, we "set" the received wgdevice and send back the return status.
// Check the message size
if data_len < wgdev_size {
close(client)?;
bail!("Message size too small (< {})", wgdev_size)
}
// Get the `WireGuard` device
device = buffer.as_mut_ptr() as *mut WgDevice;
// Check that we're not out of bounds.
unsafe {
let mut peer = device.offset(wgdev_size as isize) as *mut WgPeer;
let num_peers = *(*device).peers.num_peers.as_ref();
trace!("Number of peers: {}", num_peers);
for i in 0..num_peers {
trace!("Processing peer {}", i);
// Calculate the current peer
let cur_peer_offset = wgpeer_size + wgipmask_size * (*peer).num_ipmasks as usize;
peer = peer.offset(cur_peer_offset as isize);
if peer.offset(wgpeer_size as isize) as *mut u8 > device.offset(data_len as isize) as *mut u8 {
close(client)?;
bail!("Message out of bounds, device data offset lower than overall peer offset.");
}
if peer.offset(cur_peer_offset as isize) as *mut u8 >
device.offset(data_len as isize) as *mut u8 {
close(client)?;
bail!("Message out of bounds, device data offset lower than current peer offset");
}
}
}
// TODO:
// let ret = set_current_wireguard_device(device);
// write(client, &ret, size_of_val(ret))?;
}
}
Ok(())
}
#[cfg(unix)]
/// Sets the permissions to a given `Path`
fn chmod(path: &Path, perms: u32) -> Result<()> {
use std::os::unix::prelude::PermissionsExt;
use std::fs::{set_permissions, Permissions};
set_permissions(path, Permissions::from_mode(perms))?;
Ok(())
}
#[cfg(windows)]
/// Sets the permissions to a given `Path`
fn chmod(_path: &Path, _perms: u32) -> Result<()> {
Ok(())
}
}