aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJake McGinty <me@jake.su>2017-12-22 13:59:24 -0600
committerJake McGinty <me@jake.su>2017-12-22 14:00:06 -0600
commite6aadd208877f7b187046d0656f72dd39501a0a1 (patch)
treed27e80246ad503615909eda752281f26db5ff457 /src
downloadwireguard-rs-e6aadd208877f7b187046d0656f72dd39501a0a1.tar.xz
wireguard-rs-e6aadd208877f7b187046d0656f72dd39501a0a1.zip
early, early rough draft
Diffstat (limited to 'src')
-rw-r--r--src/error.rs27
-rw-r--r--src/interface/config.rs300
-rw-r--r--src/interface/mod.rs317
-rw-r--r--src/main.rs100
-rw-r--r--src/protocol/mod.rs3
-rw-r--r--src/protocol/peer.rs189
-rw-r--r--src/types.rs107
7 files changed, 1043 insertions, 0 deletions
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..4492444
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,27 @@
+use daemonize;
+use std::io;
+
+ error_chain! {
+ foreign_links {
+ Io(io::Error) #[doc = "Error during IO"];
+ Daemonize(daemonize::DaemonizeError) #[doc = "Error during IO"];
+ }
+
+ errors {
+// Launch(phase: LaunchStage) {
+// description("An error occurred during startup")
+// display("Startup aborted: {:?} did not complete successfully", phase)
+// }
+//
+// ConfigLoad(path: String) {
+// description("Config file not found")
+// display("Unable to read file `{}`", path)
+// }
+ }
+ }
+
+// impl From<LaunchStage> for ErrorKind {
+// fn from(v: LaunchStage) -> Self {
+// ErrorKind::Launch(v)
+// }
+// }
diff --git a/src/interface/config.rs b/src/interface/config.rs
new file mode 100644
index 0000000..6a76a23
--- /dev/null
+++ b/src/interface/config.rs
@@ -0,0 +1,300 @@
+//! The configuration logic for userspace WireGuard.
+
+// Dev notes:
+// * Configuration service should use channels to report updates it receives over its interface.
+
+use bytes::BytesMut;
+use error::Result;
+use std;
+use std::fs::{create_dir, remove_file};
+use std::iter::Iterator;
+use std::path::{Path, PathBuf};
+use std::io;
+use std::str;
+use std::net::{SocketAddr, IpAddr};
+use types::{PeerInfo, InterfaceInfo};
+use hex::{FromHex};
+
+use futures::{Future, Stream};
+use futures::unsync::mpsc;
+use tokio_io::{AsyncRead, AsyncWrite};
+use tokio_io::codec::{Encoder, Decoder};
+
+#[derive(Debug)]
+pub enum Command {
+ Set(usize, Vec<UpdateEvent>),
+ Get(usize)
+}
+
+#[derive(Debug)]
+#[allow(dead_code)]
+pub enum UpdateEvent {
+ PrivateKey([u8; 32]),
+ ListenPort(u16),
+ UpdatePeer(PeerInfo),
+ RemovePeer([u8; 32]),
+ RemoveAllPeers,
+}
+
+impl UpdateEvent {
+ fn from(items: Vec<(String, String)>) -> Vec<UpdateEvent> {
+ let mut events = vec![];
+ let mut public_key: Option<[u8; 32]> = None;
+ let mut preshared_key: Option<[u8; 32]> = None;
+ let mut allowed_ips: Vec<(IpAddr, u32)> = vec![];
+ let mut keep_alive_interval: Option<u16> = None;
+ let mut endpoint: Option<SocketAddr> = None;
+
+ for (key, value) in items {
+ match key.as_ref() {
+ "private_key" => {
+ let key = <[u8; 32]>::from_hex(&value).unwrap();
+ events.push(UpdateEvent::PrivateKey(key));
+ },
+ "listen_port" => { events.push(UpdateEvent::ListenPort(value.parse().unwrap())); },
+ "public_key" => {
+ if let Some(ref pubkey) = public_key {
+ events.push(UpdateEvent::UpdatePeer(PeerInfo {
+ pub_key: pubkey.clone(),
+ psk: preshared_key.clone(),
+ endpoint: endpoint.clone(),
+ allowed_ips: allowed_ips.clone(),
+ keep_alive_interval: keep_alive_interval.clone(),
+ }));
+ }
+ let key = <[u8; 32]>::from_hex(&value).unwrap();
+ public_key = Some(key);
+ },
+ "preshared_key" => { preshared_key = Some(<[u8; 32]>::from_hex(&value).unwrap()); },
+ "allowed_ip" => {
+ let (ip, cidr) = value.split_at(value.find('/').unwrap());
+ allowed_ips.push((ip.parse().unwrap(), (&cidr[1..]).parse().unwrap()))
+ },
+ "persistent_keepalive_interval" => {
+ keep_alive_interval = Some(value.parse().unwrap());
+ },
+ "endpoint" => { endpoint = Some(value.parse().unwrap()); },
+ _ => {}
+ }
+ }
+
+ if let Some(ref pubkey) = public_key {
+ events.push(UpdateEvent::UpdatePeer(PeerInfo {
+ pub_key: pubkey.clone(),
+ psk: preshared_key.clone(),
+ endpoint: endpoint.clone(),
+ allowed_ips: allowed_ips.clone(),
+ keep_alive_interval: keep_alive_interval.clone(),
+ }));
+ }
+ debug!("events {:?}", events);
+ events
+ }
+}
+
+pub struct ConfigurationCodec;
+
+impl Decoder for ConfigurationCodec {
+ type Item = Command;
+ type Error = io::Error;
+
+ fn decode(&mut self, buf: &mut BytesMut) -> std::result::Result<Option<Self::Item>, Self::Error> {
+ // Determine we have a full command ready for parsing.
+ let mut items = Vec::new();
+ let utf8 = String::from_utf8(buf.to_vec()).unwrap();
+ let mut data_iter = utf8.split("\n\n");
+ let blob = data_iter.next().unwrap();
+ if data_iter.next().is_none() {
+ return Ok(None)
+ }
+
+ // Parse the key-value pairs into something more usable
+ for line in blob.split('\n') {
+ let mut entry = line.split('=');
+ items.push((entry.next().unwrap().to_owned(), entry.next().unwrap().to_owned()));
+ }
+ buf.split_to(blob.len()+1);
+
+ let (ref cmd, ref version) = items.remove(0);
+ let command = if cmd == "get" {
+ Command::Get(version.parse().unwrap())
+ } else {
+ Command::Set(version.parse().unwrap(), UpdateEvent::from(items))
+ };
+
+ Ok(Some(command))
+ }
+}
+
+impl Encoder for ConfigurationCodec {
+ type Item = String;
+ type Error = io::Error;
+
+ fn encode(&mut self, msg: Self::Item, buf: &mut BytesMut) -> std::result::Result<(), Self::Error> {
+ buf.extend(msg.as_bytes());
+ buf.extend(b"\n\n");
+ Ok(())
+ }
+}
+
+
+//pub struct ConfigurationService {
+// interface_name: String,
+// peers: Rc<RefCell<HashMap<[u8; 32], Rc<RefCell<Peer>>>>>,
+// interface_info: Rc<RefCell<InterfaceInfo>>,
+// tx: mpsc::Sender<UpdateEvent>,
+//}
+
+//impl Service for ConfigurationService {
+// type Request = Command;
+// type Response = String;
+// type Error = io::Error;
+// type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;
+//
+// fn call(&self, req: Self::Request) -> Self::Future {
+// debug!("{:?}", req);
+// match req {
+// Command::Get(version) => {
+// // see: https://www.wireguard.com/xplatform/
+// // this is just bullshit fillin
+// let buf = "private_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a
+//listen_port=12912
+//public_key=b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33
+//preshared_key=188515093e952f5f22e865cef3012e72f8b5f0b598ac0309d5dacce3b70fcf52
+//allowed_ip=192.168.4.4/32
+//endpoint=[abcd:23::33%2]:51820
+//public_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376
+//tx_bytes=38333
+//rx_bytes=2224
+//allowed_ip=192.168.4.6/32
+//persistent_keepalive_interval=111
+//endpoint=182.122.22.19:3233
+//public_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58
+//endpoint=5.152.198.39:51820
+//allowed_ip=192.168.4.10/32
+//allowed_ip=192.168.4.11/32
+//tx_bytes=1212111
+//rx_bytes=1929999999
+//errno=0
+//\n";
+// Box::new(future::ok(buf.into()))
+// },
+// Command::Set(version, items) => {
+// let mut public_key = None;
+// let mut preshared_key = None;
+// let mut allowed_ips: Vec<(IpAddr, u32)> = vec![];
+// let mut persistent_keepalive_interval: Option<u16> = None;
+// let mut endpoint: Option<SocketAddr> = None;
+//
+// for (key, value) in items {
+// match key.as_ref() {
+//// "private_key" => { config.key = Some(value); },
+//// "fwmark" => { config.fwmark = Some(value.parse().unwrap()); },
+//// "listen_port" => { config.listen_port = Some(value.parse().unwrap()); },
+// "public_key" => {
+// if let Some(ref pubkey) = public_key {
+//// config.peers.push(Peer {
+//// peer_pubkey: [0u8; 32],
+//// psk: preshared_key,
+//// endpoint: endpoint,
+//// allowed_ips: allowed_ips.clone(),
+//// keep_alive_interval: persistent_keepalive_interval,
+//// });
+// }
+// public_key = Some(value);
+// },
+// "preshared_key" => { preshared_key = Some([0u8; 32]); },
+// "allowed_ip" => {
+// let (ip, cidr) = value.split_at(value.find('/').unwrap());
+// debug!("parsed allowed ip as ({}, {})", ip, &cidr[1..]);
+// allowed_ips.push((ip.parse().unwrap(), (&cidr[1..]).parse().unwrap()))
+// },
+// "persistent_keepalive_interval" => {
+// debug!("persistent_keepalive_interval");
+// persistent_keepalive_interval = Some(value.parse().unwrap());
+// },
+// "endpoint" => { endpoint = Some(value.parse().unwrap()); },
+// _ => {}
+// }
+// }
+// Box::new(future::ok("errno=0\nerrno=0\n\n".into()))
+// },
+// _ => {
+// Box::new(future::ok("errno=1\nerrno=1\n\n".into()))
+// }
+// }
+// }
+//}
+
+pub struct ConfigurationServiceManager {
+ interface_name: String,
+}
+
+impl ConfigurationServiceManager {
+ pub fn new(interface_name: &str) -> Self {
+ ConfigurationServiceManager {
+ interface_name: interface_name.into(),
+ }
+ }
+
+ /// Creates a new `WireGuard` instance
+ pub fn get_path(name: &str) -> Result<PathBuf> {
+ // let _tun = Tun::create(Some("hey"));
+ // Create the socket directory if not existing
+ let mut socket_path = Self::get_run_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)?;
+ }
+
+ Ok(socket_path)
+ }
+
+ #[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(())
+ }
+
+ /// Returns the path where the socket and pid file will be stored
+ pub fn get_run_path() -> PathBuf {
+ if Path::new("/run").exists() {
+ PathBuf::from("/run")
+ } else {
+ PathBuf::from("/var").join("run")
+ }
+ }
+}
+
+impl Drop for ConfigurationServiceManager {
+ fn drop(&mut self) {
+ let mut socket_path = Self::get_run_path().join("wireguard");
+ socket_path.push(&self.interface_name);
+ socket_path.set_extension("sock");
+ if socket_path.exists() {
+ debug!("Removing socket on drop: {}", socket_path.display());
+ let _ = remove_file(&socket_path);
+ }
+ }
+}
diff --git a/src/interface/mod.rs b/src/interface/mod.rs
new file mode 100644
index 0000000..8cbbfb3
--- /dev/null
+++ b/src/interface/mod.rs
@@ -0,0 +1,317 @@
+mod config;
+
+use self::config::{ConfigurationServiceManager, UpdateEvent, Command, ConfigurationCodec};
+use base64;
+use hex;
+use byteorder::{ByteOrder, BigEndian, LittleEndian};
+use snow::NoiseBuilder;
+use protocol::Peer;
+use std::io;
+use std::rc::Rc;
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::net::SocketAddr;
+use std::time::Duration;
+use types::{InterfaceInfo};
+
+use pnet::packet::ipv4::Ipv4Packet;
+
+use futures::{Future, Stream, Sink, future, unsync, sync, stream};
+use tokio_core::reactor::{Core, Handle};
+use tokio_core::net::{UdpSocket, UdpCodec};
+use tokio_utun::{UtunStream, UtunCodec};
+use tokio_io::{AsyncRead};
+use tokio_io::codec::{Framed, Encoder, Decoder};
+use tokio_uds::{UnixListener};
+use tokio_timer::{Interval, Timer};
+
+fn debug_packet(header: &str, packet: &[u8]) {
+ let packet = Ipv4Packet::new(packet);
+ debug!("{} {:?}", header, packet);
+}
+
+pub struct Interface {
+ name: String,
+ info: Rc<RefCell<InterfaceInfo>>,
+ peers: Rc<RefCell<HashMap<[u8; 32], Rc<RefCell<Peer>>>>>,
+ ids: Rc<RefCell<HashMap<u32, Rc<RefCell<Peer>>>>>,
+}
+
+struct VecUdpCodec;
+impl UdpCodec for VecUdpCodec {
+ type In = (SocketAddr, Vec<u8>);
+ type Out = (SocketAddr, Vec<u8>);
+
+ fn decode(&mut self, src: &SocketAddr, buf: &[u8]) -> io::Result<Self::In> {
+ Ok((*src, buf.to_vec()))
+ }
+
+ fn encode(&mut self, msg: Self::Out, buf: &mut Vec<u8>) -> SocketAddr {
+ let (addr, mut data) = msg;
+ buf.append(&mut data);
+ addr
+ }
+}
+
+struct VecUtunCodec;
+#[allow(dead_code)]
+enum UtunPacket {
+ Inet4(Vec<u8>),
+ Inet6(Vec<u8>),
+}
+impl UtunCodec for VecUtunCodec {
+ type In = Vec<u8>;
+ type Out = Vec<u8>;
+
+ fn decode(&mut self, buf: &[u8]) -> io::Result<Self::In> {
+ debug!("utun packet type {}", buf[3]);
+ Ok(buf[4..].to_vec())
+ }
+
+ fn encode(&mut self, mut msg: Self::Out, buf: &mut Vec<u8>) {
+ buf.extend_from_slice(&[0u8, 0, 0, 2]);
+ buf.append(&mut msg);
+ }
+}
+
+impl Interface {
+ pub fn new(name: &str) -> Self {
+ let info = Rc::new(RefCell::new(InterfaceInfo::default()));
+ let peers = Rc::new(RefCell::new(HashMap::new()));
+ let ids = Rc::new(RefCell::new(HashMap::new()));
+ let _config_service = ConfigurationServiceManager::new(name);
+ Interface {
+ name: name.to_owned(),
+ info,
+ peers,
+ ids,
+ }
+ }
+
+ pub fn start(&mut self) {
+ let mut core = Core::new().unwrap();
+
+ let (utun_tx, utun_rx) = unsync::mpsc::channel::<Vec<u8>>(1024);
+ let udp_socket = UdpSocket::bind(&([0,0,0,0], 0).into(), &core.handle()).unwrap();
+ let (tx, rx) = unsync::mpsc::channel::<(SocketAddr, Vec<u8>)>(1024);
+ let (udp_writer, udp_reader) = udp_socket.framed(VecUdpCodec{}).split();
+ let udp_read_fut = udp_reader.for_each({
+ let ids_ref = self.ids.clone();
+ let handle = core.handle();
+ let tx = tx.clone();
+ let interface_info = self.info.clone();
+ move |(_socket_addr, packet)| {
+ debug!("got a UDP packet of length {}, packet type {}", packet.len(), packet[0]);
+ match packet[0] {
+ 1 => {
+ info!("got handshake initialization.");
+ },
+ 2 => {
+ let their_index = LittleEndian::read_u32(&packet[4..]);
+ let our_index = LittleEndian::read_u32(&packet[8..]);
+ let mut ids = ids_ref.borrow_mut();
+ let peer_ref = ids.get(&our_index).unwrap().clone();
+ let mut peer = peer_ref.borrow_mut();
+ peer.sessions.next.as_mut().unwrap().their_index = their_index;
+ let payload_len = peer.next_noise().expect("pending noise session")
+ .read_message(&packet[12..60], &mut []).unwrap();
+ assert!(payload_len == 0);
+ peer.ratchet_session().unwrap();
+ info!("got handshake response, ratcheted session.");
+ let tx = tx.clone();
+
+ let interface_info = interface_info.borrow();
+ let noise = NoiseBuilder::new("Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s".parse().unwrap())
+ .local_private_key(&interface_info.private_key.expect("no private key!"))
+ .remote_public_key(&peer.info.pub_key)
+ .prologue("WireGuard v1 zx2c4 Jason@zx2c4.com".as_bytes())
+ .psk(2, &peer.info.psk.expect("no psk!"))
+ .build_initiator().unwrap();
+ peer.set_next_session(noise.into());
+
+ let _ = ids.insert(peer.our_next_index().unwrap(), peer_ref.clone());
+
+ let init_packet = peer.get_handshake_packet();
+ let endpoint = peer.info.endpoint.unwrap().clone();
+
+ let timer = Timer::default();
+ let sleep = timer.sleep(Duration::from_secs(120));
+ let boop = sleep.and_then({
+ let handle = handle.clone();
+ let peer_ref = peer_ref.clone();
+ let interface_info = interface_info.clone();
+ move |_| {
+ info!("sending rekey!");
+ handle.spawn(tx.clone().send((endpoint, init_packet))
+ .map(|_| ())
+ .map_err(|_| ()));
+ Ok(())
+ }
+ }).map_err(|_|());
+ handle.spawn(boop);
+ },
+ 4 => {
+ let our_index_received = LittleEndian::read_u32(&packet[4..]);
+ let nonce = LittleEndian::read_u64(&packet[8..]);
+
+ let mut raw_packet = [0u8; 1500];
+ let ids = ids_ref.borrow();
+ let lookup = ids.get(&our_index_received);
+ if let Some(ref peer) = lookup {
+ let mut peer = peer.borrow_mut();
+ // info!("retrieved peer with pubkey {}", base64::encode(&peer.pubkey));
+ // info!("ok going to try to decrypt");
+
+ peer.rx_bytes += packet.len();
+ let noise = peer.current_noise().expect("current noise session");
+ noise.set_receiving_nonce(nonce).unwrap();
+ let payload_len = noise.read_message(&packet[16..], &mut raw_packet).unwrap();
+ debug_packet("received TRANSPORT: ", &raw_packet[..payload_len]);
+ handle.spawn(utun_tx.clone().send(raw_packet[..payload_len].to_owned())
+ .map(|_| ())
+ .map_err(|_| ()));
+ }
+ },
+ _ => unimplemented!()
+ }
+ Ok(())
+ }
+ }).map_err(|_| ());
+
+ let udp_write_fut = udp_writer.sink_map_err(|_| ()).send_all(
+ rx.map(|(addr, packet)| {
+ debug!("sending encrypted UDP packet");
+ (addr, packet)
+ }).map_err(|_| ())).map_err(|_| ());
+
+ let utun_stream = UtunStream::connect(&self.name, &core.handle()).unwrap().framed(VecUtunCodec{});
+ let (utun_writer, utun_reader) = utun_stream.split();
+ let utun_fut = utun_reader.for_each({
+ let ids = self.ids.clone();
+ let utun_handle = core.handle();
+ let udp_tx = tx.clone();
+ move |packet| {
+ debug_packet("received UTUN packet: ", &packet);
+ let mut ping_packet = [0u8; 1500];
+ let ids = ids.borrow();
+ let (_key, peer) = ids.iter().next().unwrap(); // TODO destination IP peer lookup
+ let mut peer = peer.borrow_mut();
+ ping_packet[0] = 4;
+ let their_index = peer.their_current_index().expect("no current index for them");
+ let endpoint = peer.info.endpoint.unwrap();
+ peer.tx_bytes += packet.len();
+ let noise = peer.current_noise().expect("current noise session");
+ LittleEndian::write_u32(&mut ping_packet[4..], their_index);
+ LittleEndian::write_u64(&mut ping_packet[8..], noise.sending_nonce().unwrap());
+ let len = noise.write_message(&packet, &mut ping_packet[16..]).expect("failed to encrypt outgoing UDP packet");
+ utun_handle.spawn(udp_tx.clone().send((endpoint, ping_packet[..(16+len)].to_owned()))
+ .map(|_| ())
+ .map_err(|_| ()));
+ Ok(())
+ }
+ }).map_err(|_| ());
+
+ let utun_write_fut = utun_writer.sink_map_err(|_| ()).send_all(
+ utun_rx.map(|packet| {
+ debug_packet("sending UTUN: ", &packet);
+ packet
+ }).map_err(|_| ())).map_err(|_| ());
+
+ let handle = core.handle();
+ let listener = UnixListener::bind(ConfigurationServiceManager::get_path(&self.name).unwrap(), &handle).unwrap();
+ let (config_tx, config_rx) = sync::mpsc::channel::<UpdateEvent>(1024);
+ let h = handle.clone();
+ let config_server = listener.incoming().for_each({
+ let config_tx = config_tx.clone();
+ let info = self.info.clone();
+ let peers = self.peers.clone();
+ move |(stream, _)| {
+ let (sink, stream) = stream.framed(ConfigurationCodec {}).split();
+ debug!("UnixServer connection.");
+
+ let handle = h.clone();
+ let responses = stream.and_then({
+ let config_tx = config_tx.clone();
+ let info = info.clone();
+ let peers = peers.clone();
+ move |command| {
+ match command {
+ Command::Set(_version, items) => {
+ config_tx.clone().send_all(stream::iter_ok(items)).wait().unwrap();
+ future::ok("errno=0\nerrno=0\n\n".to_string())
+ },
+ Command::Get(_version) => {
+ let info = info.borrow();
+ let peers = peers.borrow();
+ let mut s = String::new();
+ if let Some(private_key) = info.private_key {
+ s.push_str(&format!("private_key={}\n", hex::encode(private_key)));
+ }
+
+ for (_, peer) in peers.iter() {
+ s.push_str(&peer.borrow().to_config_string());
+ }
+ future::ok(format!("{}errno=0\n\n", s))
+ }
+ }
+ }
+ });
+
+ let fut = sink.send_all(responses).map(|_| ()).map_err(|_| ());
+
+ handle.spawn(fut);
+
+ Ok(())
+ }
+ }).map_err(|_| ());
+
+ let config_fut = config_rx.for_each({
+ let tx = tx.clone();
+ let handle = handle.clone();
+ move |event| {
+ let interface_info = self.info.clone();
+ match event {
+ UpdateEvent::PrivateKey(private_key) => {
+ let mut interface_info = interface_info.borrow_mut();
+ interface_info.private_key = Some(private_key);
+ debug!("set new private key");
+ },
+ UpdateEvent::ListenPort(port) => {
+ let mut interface_info = interface_info.borrow_mut();
+ interface_info.listen_port = Some(port);
+ debug!("set new listen port");
+ },
+ UpdateEvent::UpdatePeer(info) => {
+ info!("added new peer: {}", info);
+ let interface_info = interface_info.borrow();
+ let mut noise = NoiseBuilder::new("Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s".parse().unwrap())
+ .local_private_key(&interface_info.private_key.expect("no private key!"))
+ .remote_public_key(&info.pub_key)
+ .prologue("WireGuard v1 zx2c4 Jason@zx2c4.com".as_bytes())
+ .psk(2, &info.psk.expect("no psk!"))
+ .build_initiator().unwrap();
+
+ let mut peer = Peer::new(info.clone());
+ peer.set_next_session(noise.into());
+
+ let init_packet = peer.get_handshake_packet();
+ let our_index = peer.our_next_index().unwrap();
+ let peer = Rc::new(RefCell::new(peer));
+
+ let _ = self.ids.borrow_mut().insert(our_index, peer.clone());
+ let _ = self.peers.borrow_mut().insert(info.pub_key, peer);
+
+ handle.spawn(tx.clone().send((info.endpoint.unwrap(), init_packet))
+ .map(|_| ())
+ .map_err(|_| ()));
+ },
+ _ => unimplemented!()
+ }
+
+ future::ok(())
+ }
+ }).map_err(|_| ());
+
+ core.run(utun_fut.join(utun_write_fut.join(udp_read_fut.join(udp_write_fut.join(config_fut.join(config_server)))))).unwrap();
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..0f3f9e4
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,100 @@
+#![allow(unused_imports)]
+
+#[macro_use] extern crate log;
+extern crate env_logger;
+
+#[macro_use] extern crate structopt_derive;
+#[macro_use] extern crate error_chain;
+
+
+extern crate daemonize;
+extern crate rand;
+extern crate nix;
+extern crate structopt;
+
+extern crate mio;
+
+extern crate bytes;
+extern crate futures;
+extern crate tokio_core;
+extern crate tokio_io;
+extern crate tokio_proto;
+extern crate tokio_service;
+extern crate tokio_uds;
+extern crate tokio_utun;
+extern crate tokio_timer;
+
+extern crate snow;
+extern crate base64;
+extern crate hex;
+extern crate time;
+extern crate byteorder;
+extern crate crypto;
+extern crate pnet;
+
+mod error;
+mod interface;
+mod protocol;
+mod types;
+
+use daemonize::Daemonize;
+use error::{ErrorKind, Error, Result};
+use std::path::PathBuf;
+use interface::Interface;
+use structopt::StructOpt;
+
+#[derive(StructOpt, Debug)]
+#[structopt(name = "wgrs", about = "WireGuard - a network tunnel")]
+struct Opt {
+ /// A flag, true if used in the command line.
+ #[structopt(short = "d", long = "debug", help = "Activate debug mode")]
+ debug: bool,
+
+ /// An argument of type float, with a default value.
+ #[structopt(short = "f", long = "foreground", help = "Run in the foreground")]
+ foreground: bool,
+
+ /// Needed parameter, the first on the command line.
+ #[structopt(help = "WireGuard interface name", default_value = "utun4")]
+ interface: String,
+
+ /// An optional parameter, will be `None` if not present on the
+ /// command line.
+ #[structopt(help = "Output file, stdout if not present")]
+ output: Option<String>,
+}
+
+fn main() {
+ env_logger::init().unwrap();
+ let opt = Opt::from_args();
+
+ if !opt.foreground {
+ daemonize().expect("failed to daemonize");
+ }
+
+ Interface::new(&opt.interface).start();
+// WireGuard::start(interface_name).expect("failed to start WireGuard interface");
+}
+
+fn daemonize() -> Result<()> {
+ if !nix::unistd::getuid().is_root() {
+ bail!("You are not the root user which can spawn the daemon.");
+ }
+
+ debug!("Starting daemon.");
+
+ let pid_path = PathBuf::new(); // TODO temporary
+
+// let pid_path = WireGuard::get_run_path();
+
+ let daemonize = Daemonize::new()
+ .pid_file(pid_path.join("wireguard.pid"))
+ .chown_pid_file(true)
+ .working_directory(pid_path)
+ .user("nobody")
+ .group("daemon")
+ .umask(0o077);
+
+ daemonize.start()?;
+ Ok(())
+}
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
new file mode 100644
index 0000000..bae5340
--- /dev/null
+++ b/src/protocol/mod.rs
@@ -0,0 +1,3 @@
+mod peer;
+
+pub use self::peer::Peer;
diff --git a/src/protocol/peer.rs b/src/protocol/peer.rs
new file mode 100644
index 0000000..e26e1bf
--- /dev/null
+++ b/src/protocol/peer.rs
@@ -0,0 +1,189 @@
+use byteorder::{ByteOrder, BigEndian, LittleEndian};
+use crypto::blake2s::Blake2s;
+use snow::{self, NoiseBuilder};
+use pnet::packet::Packet;
+use pnet::packet::ip::IpNextHeaderProtocols;
+use pnet::packet::ipv4::{self, MutableIpv4Packet};
+use pnet::packet::icmp::{self, MutableIcmpPacket, IcmpTypes, echo_reply, echo_request};
+use std::{self, io};
+use std::fmt::{self, Debug, Display, Formatter};
+use std::net::{Ipv4Addr, IpAddr, SocketAddr, ToSocketAddrs};
+use std::str::FromStr;
+use std::thread::JoinHandle;
+use base64;
+use hex;
+use time;
+use rand::{self, Rng};
+use types::PeerInfo;
+
+use futures::{self, Future};
+use tokio_core::reactor::Handle;
+use tokio_core::net::{UdpSocket, UdpCodec};
+
+pub struct Peer {
+ pub info: PeerInfo,
+ pub sessions: Sessions,
+ pub tx_bytes: usize,
+ pub rx_bytes: usize,
+}
+
+pub struct Session {
+ pub noise: snow::Session,
+ pub our_index: u32,
+ pub their_index: u32,
+}
+
+impl Session {
+ #[allow(dead_code)]
+ pub fn with_their_index(session: snow::Session, their_index: u32) -> Session {
+ Session {
+ noise: session,
+ our_index: rand::thread_rng().gen::<u32>(),
+ their_index,
+ }
+ }
+
+ pub fn into_transport_mode(self) -> Session {
+ Session {
+ noise: self.noise.into_transport_mode().unwrap(),
+ our_index: self.our_index,
+ their_index: self.their_index,
+ }
+ }
+}
+
+impl From<snow::Session> for Session {
+ fn from(session: snow::Session) -> Self {
+ Session {
+ noise: session,
+ our_index: 0,
+ their_index: 0,
+ }
+ }
+}
+
+pub struct Sessions {
+ pub past: Option<Session>,
+ pub current: Option<Session>,
+ pub next: Option<Session>,
+}
+
+impl Display for Peer {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "Peer({})", self.info)
+ }
+}
+
+impl Debug for Peer {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "Peer( endpoint: {:?}, pubkey: [redacted], psk: [redacted] )", self.info.endpoint)
+ }
+}
+
+fn memcpy(out: &mut [u8], data: &[u8]) {
+ out[..data.len()].copy_from_slice(data);
+}
+
+impl Peer {
+ pub fn new(info: PeerInfo) -> Peer {
+ Peer {
+ info,
+ sessions: Sessions {
+ past: None,
+ current: None,
+ next: None
+ },
+ tx_bytes: 0,
+ rx_bytes: 0,
+ }
+ }
+
+ pub fn set_next_session(&mut self, session: Session) {
+ let _ = std::mem::replace(&mut self.sessions.next, Some(session));
+ }
+
+ pub fn ratchet_session(&mut self) -> Result<(), ()> {
+ let next = std::mem::replace(&mut self.sessions.next, None).ok_or(())?;
+ let next = next.into_transport_mode();
+
+ let current = std::mem::replace(&mut self.sessions.current, Some(next));
+ let _ = std::mem::replace(&mut self.sessions.past, current);
+ Ok(())
+ }
+
+ pub fn current_noise(&mut self) -> Option<&mut snow::Session> {
+ if let Some(ref mut session) = self.sessions.current {
+ Some(&mut session.noise)
+ } else {
+ None
+ }
+ }
+
+ pub fn next_noise(&mut self) -> Option<&mut snow::Session> {
+ if let Some(ref mut session) = self.sessions.next {
+ Some(&mut session.noise)
+ } else {
+ None
+ }
+ }
+
+ pub fn our_next_index(&self) -> Option<u32> {
+ if let Some(ref session) = self.sessions.next {
+ Some(session.our_index)
+ } else {
+ None
+ }
+ }
+
+ pub fn our_current_index(&self) -> Option<u32> {
+ if let Some(ref session) = self.sessions.current {
+ Some(session.our_index)
+ } else {
+ None
+ }
+ }
+
+ pub fn their_current_index(&self) -> Option<u32> {
+ if let Some(ref session) = self.sessions.current {
+ Some(session.their_index)
+ } else {
+ None
+ }
+ }
+
+ pub fn get_handshake_packet(&mut self) -> Vec<u8> {
+ let now = time::get_time();
+ let mut tai64n = [0; 12];
+ BigEndian::write_i64(&mut tai64n[0..], 4611686018427387914 + now.sec);
+ BigEndian::write_i32(&mut tai64n[8..], now.nsec);
+ let mut initiation_packet = vec![0; 148];
+ initiation_packet[0] = 1; /* Type: Initiation */
+ initiation_packet[1] = 0; /* Reserved */
+ initiation_packet[2] = 0; /* Reserved */
+ initiation_packet[3] = 0; /* Reserved */
+ LittleEndian::write_u32(&mut initiation_packet[4..], self.our_next_index().unwrap());
+ self.sessions.next.as_mut().unwrap().noise.write_message(&tai64n, &mut initiation_packet[8..]).unwrap();
+ let mut mac_key_input = [0; 40];
+ let mut mac_key = [0; 32];
+ memcpy(&mut mac_key_input, b"mac1----");
+ memcpy(&mut mac_key_input[8..], &self.info.pub_key);
+ Blake2s::blake2s(&mut mac_key, &mac_key_input, &[0; 0]);
+ let mut mac = [0; 16];
+ Blake2s::blake2s(&mut mac, &initiation_packet[0..116], &mac_key);
+ memcpy(&mut initiation_packet[116..], &mac);
+
+ initiation_packet
+ }
+
+ pub fn to_config_string(&self) -> String {
+ let mut s = format!("public_key={}\n", hex::encode(&self.info.pub_key));
+ if let Some(ref psk) = self.info.psk {
+ s.push_str(&format!("preshared_key={}\n", hex::encode(psk)));
+ }
+ if let Some(ref endpoint) = self.info.endpoint {
+ s.push_str(&format!("endpoint={}:{}\n", endpoint.ip().to_string(),endpoint.port()));
+ }
+ s.push_str(&format!("tx_bytes={}\nrx_bytes={}\n", self.tx_bytes, self.rx_bytes));
+ s
+ }
+}
diff --git a/src/types.rs b/src/types.rs
new file mode 100644
index 0000000..29b02bb
--- /dev/null
+++ b/src/types.rs
@@ -0,0 +1,107 @@
+use base64;
+use std::io::{self, Cursor, Read, Write};
+use byteorder::{BigEndian, LittleEndian, WriteBytesExt, ReadBytesExt};
+use std::fmt::{self, Display, Formatter};
+use std::net::{Ipv4Addr, IpAddr, SocketAddr};
+
+#[derive(Clone, Debug)]
+pub struct PeerInfo {
+ pub pub_key: [u8; 32],
+ pub psk: Option<[u8; 32]>,
+ pub endpoint: Option<SocketAddr>,
+ pub allowed_ips: Vec<(IpAddr, u32)>,
+ pub keep_alive_interval: Option<u16>,
+}
+
+impl Display for PeerInfo {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ let encoded = base64::encode(&self.pub_key);
+ write!(f, "{}...{}", &encoded[..4], &encoded[encoded.len()-4..])
+ }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct InterfaceInfo {
+ pub private_key: Option<[u8; 32]>,
+ pub pub_key: Option<[u8; 32]>,
+ pub listen_port: Option<u16>,
+}
+
+pub enum Message {
+ HandshakeInitiation(HandshakeInitiationMessage),
+// HandshakeResponse(HandshakeResponseMessage),
+// Transport(TransportMessage),
+// CookieReply(CookieReplyMessage),
+ Other(Vec<u8>)
+}
+
+// TODO use TryFrom
+impl<'a> From<&'a [u8]> for Message {
+ fn from(bytes: &'a [u8]) -> Self {
+ use self::Message::*;
+ let mut cursor = Cursor::new(bytes);
+ match cursor.read_u8().unwrap() {
+ 1 => HandshakeInitiation(HandshakeInitiationMessage::from(&bytes[4..])),
+ _ => Other((&bytes[4..]).to_owned())
+ }
+ }
+}
+
+impl From<Message> for Vec<u8> {
+ fn from(message: Message) -> Self {
+ use self::Message::*;
+ match message {
+ HandshakeInitiation(message) => {
+ let mut bytes = vec![1u8, 0, 0, 0];
+ bytes.append(&mut message.into());
+ bytes
+ },
+ _ => unimplemented!()
+ }
+ }
+}
+
+pub struct HandshakeInitiationMessage {
+ pub sender_i: u32,
+ pub payload: [u8; 76],
+ pub mac1: [u8; 16],
+ pub mac2: [u8; 16]
+}
+
+impl HandshakeInitiationMessage {
+ pub fn new() -> Self {
+ HandshakeInitiationMessage {
+ sender_i: 0,
+ payload: [0u8; 76],
+ mac1: [0u8; 16],
+ mac2: [0u8; 16],
+ }
+ }
+}
+
+impl<'a> From<&'a [u8]> for HandshakeInitiationMessage {
+
+ fn from(bytes: &'a [u8]) -> Self {
+ let mut message = HandshakeInitiationMessage::new();
+ let mut cursor = Cursor::new(bytes);
+
+ message.sender_i = cursor.read_u32::<LittleEndian>().unwrap();
+ cursor.read_exact(&mut message.payload[..]).unwrap();
+ cursor.read_exact(&mut message.mac1[..]).unwrap();
+ cursor.read_exact(&mut message.mac2[..]).unwrap();
+ message
+ }
+}
+
+impl From<HandshakeInitiationMessage> for Vec<u8> {
+ fn from(message: HandshakeInitiationMessage) -> Self {
+ let mut cursor = vec![];
+ cursor.write_all(&[1u8, 0, 0, 0]).unwrap();
+ cursor.write_u32::<LittleEndian>(message.sender_i).unwrap();
+ cursor.write_all(&message.payload).unwrap();
+ cursor.write_all(&message.mac1).unwrap();
+ cursor.write_all(&message.mac2).unwrap();
+
+ cursor
+ }
+} \ No newline at end of file