diff --git a/README.md b/README.md index d40d874..cee3662 100644 --- a/README.md +++ b/README.md @@ -27,12 +27,11 @@ This is what currently works: However there are some open issues: -* Encryption is not very mature. Do not use it for any relevant data. -* The protocol, especially the encryption part, can change. +* Encryption has not been thoroughly reviewed, use with care. +* The protocol can still change. * Running on IPv6 is not supported. * The software is not very well tested. * The closing message is not sent to peers. -* Usage of rust panics instead of pretty error messages. * The coverage score includes all unused methods from *libsodium* Please feel free to help and contribute code. diff --git a/src/cloud.rs b/src/cloud.rs index ec80718..2df7d94 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -13,7 +13,6 @@ use epoll; use super::types::{Table, Protocol, Range, Error, NetworkId}; use super::device::Device; use super::udpmessage::{encode, decode, Options, Message}; -use super::{ethernet, ip}; use super::crypto::Crypto; struct PeerList { @@ -108,7 +107,7 @@ impl GenericCloud

{ crypto: Crypto) -> Self { let socket = match UdpSocket::bind(&listen as &str) { Ok(socket) => socket, - _ => panic!("Failed to open socket") + _ => fail!("Failed to open socket {}", listen) }; let mut options = Options::default(); options.network_id = network_id; @@ -271,35 +270,33 @@ impl GenericCloud

{ } pub fn run(&mut self) { - let epoll_handle = epoll::create1(0).expect("Failed to create epoll handle"); + let epoll_handle = try_fail!(epoll::create1(0), "Failed to create epoll handle: {}"); let socket_fd = self.socket.as_raw_fd(); let device_fd = self.device.as_raw_fd(); let mut socket_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 0}; let mut device_event = epoll::EpollEvent{events: epoll::util::event_type::EPOLLIN, data: 1}; - epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, socket_fd, &mut socket_event).expect("Failed to add socket to epoll handle"); - epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, device_fd, &mut device_event).expect("Failed to add device to epoll handle"); + try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, socket_fd, &mut socket_event), "Failed to add socket to epoll handle: {}"); + try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, device_fd, &mut device_event), "Failed to add device to epoll handle: {}"); let mut events = [epoll::EpollEvent{events: 0, data: 0}; 2]; let mut buffer = [0; 64*1024]; loop { - let count = epoll::wait(epoll_handle, &mut events, 1000).expect("Epoll wait failed"); + let count = try_fail!(epoll::wait(epoll_handle, &mut events, 1000), "Epoll wait failed: {}"); // Process events for i in 0..count { match &events[i as usize].data { - &0 => match self.socket.recv_from(&mut buffer) { - Ok((size, src)) => { - match decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) { - Ok(_) => (), - Err(e) => error!("Error: {:?}", e) - } - }, - Err(_error) => panic!("Failed to read from network socket") - }, - &1 => match self.device.read(&mut buffer) { - Ok(size) => match self.handle_interface_data(&buffer[..size]) { + &0 => { + let (size, src) = try_fail!(self.socket.recv_from(&mut buffer), "Failed to read from network socket: {}"); + match decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) { Ok(_) => (), Err(e) => error!("Error: {:?}", e) - }, - Err(_error) => panic!("Failed to read from tap device") + } + }, + &1 => { + let size = try_fail!(self.device.read(&mut buffer), "Failed to read from tap device: {}"); + match self.handle_interface_data(&buffer[..size]) { + Ok(_) => (), + Err(e) => error!("Error: {:?}", e) + } }, _ => unreachable!() } @@ -315,7 +312,3 @@ impl GenericCloud

{ } } } - - -pub type TapCloud = GenericCloud; -pub type TunCloud = GenericCloud; diff --git a/src/ip.rs b/src/ip.rs index a10aeed..8891532 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -91,7 +91,7 @@ impl Table for RoutingTable { } fn housekeep(&mut self) { - //nothin to do + //nothing to do } fn remove_all(&mut self, _addr: SocketAddr) { diff --git a/src/main.rs b/src/main.rs index 06a027c..2ee49b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ extern crate rustc_serialize; extern crate epoll; #[cfg(feature = "crypto")] extern crate libsodium_sys; -mod util; +#[macro_use] mod util; mod types; mod crypto; mod udpmessage; @@ -24,8 +24,8 @@ use std::process::Command; use device::Device; use ethernet::SwitchTable; use ip::RoutingTable; -use types::{Error, Mode, Type, Range, Table}; -use cloud::{TapCloud, TunCloud}; +use types::{Error, Mode, Type, Range, Table, Protocol}; +use cloud::GenericCloud; use udpmessage::VERSION; use crypto::Crypto; @@ -80,32 +80,13 @@ fn run_script(script: String, ifname: &str) { } } -fn main() { - let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()); - if args.flag_version { - println!("VpnCloud v{} ({}, protocol version {})", env!("CARGO_PKG_VERSION"), - if cfg!(feature = "crypto") { "with crypto support" } else { "without crypto support" }, - VERSION - ); - return; - } - log::set_logger(|max_log_level| { - assert!(!args.flag_verbose || !args.flag_quiet); - if args.flag_verbose { - max_log_level.set(log::LogLevelFilter::Debug); - } else if args.flag_quiet { - max_log_level.set(log::LogLevelFilter::Error); - } else { - max_log_level.set(log::LogLevelFilter::Info); - } - Box::new(SimpleLogger) - }).unwrap(); - debug!("Args: {:?}", args); - let device = Device::new(&args.flag_device, args.flag_type).expect("Failed to open virtual interface"); +fn run (args: Args) { + let device = try_fail!(Device::new(&args.flag_device, args.flag_type), + "Failed to open virtual {} interface {}: {}", args.flag_type, &args.flag_device); info!("Opened device {}", device.ifname()); let mut ranges = Vec::with_capacity(args.flag_subnet.len()); for s in args.flag_subnet { - ranges.push(Range::from_str(&s).expect("Invalid subnet")); + ranges.push(try_fail!(Range::from_str(&s), "Invalid subnet format: {} ({})", s)); } let dst_timeout = Duration::seconds(args.flag_dst_timeout as i64); let peer_timeout = Duration::seconds(args.flag_peer_timeout as i64); @@ -127,32 +108,42 @@ fn main() { Some(key) => Crypto::from_shared_key(&key), None => Crypto::None }; - match args.flag_type { - Type::Tap => { - let mut cloud = TapCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto); - if let Some(script) = args.flag_ifup { - run_script(script, cloud.ifname()); - } - for addr in &args.flag_connect { - cloud.connect(&addr as &str, true).expect("Failed to send"); - } - cloud.run(); - if let Some(script) = args.flag_ifdown { - run_script(script, cloud.ifname()); - } - }, - Type::Tun => { - let mut cloud = TunCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto); - if let Some(script) = args.flag_ifup { - run_script(script, cloud.ifname()); - } - for addr in &args.flag_connect { - cloud.connect(&addr as &str, true).expect("Failed to send"); - } - if let Some(script) = args.flag_ifdown { - run_script(script, cloud.ifname()); - } - cloud.run() - } - }; + let mut cloud = GenericCloud::::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto); + if let Some(script) = args.flag_ifup { + run_script(script, cloud.ifname()); + } + for addr in &args.flag_connect { + try_fail!(cloud.connect(&addr as &str, true), "Failed to send message to {}: {}", &addr); + } + cloud.run(); + if let Some(script) = args.flag_ifdown { + run_script(script, cloud.ifname()); + } +} + +fn main() { + let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()); + if args.flag_version { + println!("VpnCloud v{} ({}, protocol version {})", env!("CARGO_PKG_VERSION"), + if cfg!(feature = "crypto") { "with crypto support" } else { "without crypto support" }, + VERSION + ); + return; + } + log::set_logger(|max_log_level| { + assert!(!args.flag_verbose || !args.flag_quiet); + if args.flag_verbose { + max_log_level.set(log::LogLevelFilter::Debug); + } else if args.flag_quiet { + max_log_level.set(log::LogLevelFilter::Error); + } else { + max_log_level.set(log::LogLevelFilter::Info); + } + Box::new(SimpleLogger) + }).unwrap(); + debug!("Args: {:?}", args); + match args.flag_type { + Type::Tap => run::(args), + Type::Tun => run::(args), + } } diff --git a/src/types.rs b/src/types.rs index 1a56862..92014cb 100644 --- a/src/types.rs +++ b/src/types.rs @@ -95,11 +95,30 @@ impl FromStr for Range { pub enum Type { Tun, Tap } +impl fmt::Display for Type { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self { + &Type::Tun => write!(formatter, "tun"), + &Type::Tap => write!(formatter, "tap"), + } + } +} + #[derive(RustcDecodable, Debug)] pub enum Mode { Normal, Hub, Switch, Router } +impl fmt::Display for Mode { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self { + &Mode::Normal => write!(formatter, "normal"), + &Mode::Hub => write!(formatter, "hub"), + &Mode::Switch => write!(formatter, "switch"), + &Mode::Router => write!(formatter, "router"), + } + } +} pub trait Table { fn learn(&mut self, Address, Option, SocketAddr); @@ -120,7 +139,18 @@ pub enum Error { TunTapDevError(&'static str), CryptoError(&'static str) } - +impl fmt::Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self { + &Error::ParseError(ref msg) => write!(formatter, "{}", msg), + &Error::SocketError(ref msg) => write!(formatter, "{}", msg), + &Error::TunTapDevError(ref msg) => write!(formatter, "{}", msg), + &Error::CryptoError(ref msg) => write!(formatter, "{}", msg), + &Error::WrongNetwork(Some(net)) => write!(formatter, "wrong network id: {}", net), + &Error::WrongNetwork(None) => write!(formatter, "wrong network id: none"), + } + } +} #[test] fn address_fmt() { diff --git a/src/util.rs b/src/util.rs index 8a481a4..817681f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -19,3 +19,31 @@ pub fn to_vec(data: &[u8]) -> Vec { } v } + +macro_rules! fail { + ($format:expr) => ( { + use std::process; + error!($format); + process::exit(-1); + } ); + ($format:expr, $( $arg:expr ),+) => ( { + use std::process; + error!($format, $( $arg ),+ ); + process::exit(-1); + } ); +} + +macro_rules! try_fail { + ($val:expr, $format:expr) => ( { + match $val { + Ok(val) => val, + Err(err) => fail!($format, err) + } + } ); + ($val:expr, $format:expr, $( $arg:expr ),+) => ( { + match $val { + Ok(val) => val, + Err(err) => fail!($format, $( $arg ),+, err) + } + } ); +}