Pretty error messages instead of panics

This commit is contained in:
Dennis Schwerdel 2015-11-25 12:29:12 +01:00
parent 83fb941599
commit 12b5b17c3b
6 changed files with 123 additions and 82 deletions

View File

@ -27,12 +27,11 @@ This is what currently works:
However there are some open issues: However there are some open issues:
* Encryption is not very mature. Do not use it for any relevant data. * Encryption has not been thoroughly reviewed, use with care.
* The protocol, especially the encryption part, can change. * The protocol can still change.
* Running on IPv6 is not supported. * Running on IPv6 is not supported.
* The software is not very well tested. * The software is not very well tested.
* The closing message is not sent to peers. * 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* * The coverage score includes all unused methods from *libsodium*
Please feel free to help and contribute code. Please feel free to help and contribute code.

View File

@ -13,7 +13,6 @@ use epoll;
use super::types::{Table, Protocol, Range, Error, NetworkId}; use super::types::{Table, Protocol, Range, Error, NetworkId};
use super::device::Device; use super::device::Device;
use super::udpmessage::{encode, decode, Options, Message}; use super::udpmessage::{encode, decode, Options, Message};
use super::{ethernet, ip};
use super::crypto::Crypto; use super::crypto::Crypto;
struct PeerList { struct PeerList {
@ -108,7 +107,7 @@ impl<P: Protocol> GenericCloud<P> {
crypto: Crypto) -> Self { crypto: Crypto) -> Self {
let socket = match UdpSocket::bind(&listen as &str) { let socket = match UdpSocket::bind(&listen as &str) {
Ok(socket) => socket, Ok(socket) => socket,
_ => panic!("Failed to open socket") _ => fail!("Failed to open socket {}", listen)
}; };
let mut options = Options::default(); let mut options = Options::default();
options.network_id = network_id; options.network_id = network_id;
@ -271,35 +270,33 @@ impl<P: Protocol> GenericCloud<P> {
} }
pub fn run(&mut self) { 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 socket_fd = self.socket.as_raw_fd();
let device_fd = self.device.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 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}; 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"); try_fail!(epoll::ctl(epoll_handle, epoll::util::ctl_op::ADD, socket_fd, &mut socket_event), "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, device_fd, &mut device_event), "Failed to add device to epoll handle: {}");
let mut events = [epoll::EpollEvent{events: 0, data: 0}; 2]; let mut events = [epoll::EpollEvent{events: 0, data: 0}; 2];
let mut buffer = [0; 64*1024]; let mut buffer = [0; 64*1024];
loop { 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 // Process events
for i in 0..count { for i in 0..count {
match &events[i as usize].data { match &events[i as usize].data {
&0 => match self.socket.recv_from(&mut buffer) { &0 => {
Ok((size, src)) => { 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)) { match decode(&mut buffer[..size], &mut self.crypto).and_then(|(options, msg)| self.handle_net_message(src, options, msg)) {
Ok(_) => (), Ok(_) => (),
Err(e) => error!("Error: {:?}", e) Err(e) => error!("Error: {:?}", e)
} }
}, },
Err(_error) => panic!("Failed to read from network socket") &1 => {
}, let size = try_fail!(self.device.read(&mut buffer), "Failed to read from tap device: {}");
&1 => match self.device.read(&mut buffer) { match self.handle_interface_data(&buffer[..size]) {
Ok(size) => match self.handle_interface_data(&buffer[..size]) {
Ok(_) => (), Ok(_) => (),
Err(e) => error!("Error: {:?}", e) Err(e) => error!("Error: {:?}", e)
}, }
Err(_error) => panic!("Failed to read from tap device")
}, },
_ => unreachable!() _ => unreachable!()
} }
@ -315,7 +312,3 @@ impl<P: Protocol> GenericCloud<P> {
} }
} }
} }
pub type TapCloud = GenericCloud<ethernet::Frame>;
pub type TunCloud = GenericCloud<ip::Packet>;

View File

@ -91,7 +91,7 @@ impl Table for RoutingTable {
} }
fn housekeep(&mut self) { fn housekeep(&mut self) {
//nothin to do //nothing to do
} }
fn remove_all(&mut self, _addr: SocketAddr) { fn remove_all(&mut self, _addr: SocketAddr) {

View File

@ -5,7 +5,7 @@ extern crate rustc_serialize;
extern crate epoll; extern crate epoll;
#[cfg(feature = "crypto")] extern crate libsodium_sys; #[cfg(feature = "crypto")] extern crate libsodium_sys;
mod util; #[macro_use] mod util;
mod types; mod types;
mod crypto; mod crypto;
mod udpmessage; mod udpmessage;
@ -24,8 +24,8 @@ use std::process::Command;
use device::Device; use device::Device;
use ethernet::SwitchTable; use ethernet::SwitchTable;
use ip::RoutingTable; use ip::RoutingTable;
use types::{Error, Mode, Type, Range, Table}; use types::{Error, Mode, Type, Range, Table, Protocol};
use cloud::{TapCloud, TunCloud}; use cloud::GenericCloud;
use udpmessage::VERSION; use udpmessage::VERSION;
use crypto::Crypto; use crypto::Crypto;
@ -80,32 +80,13 @@ fn run_script(script: String, ifname: &str) {
} }
} }
fn main() { fn run<T: Protocol> (args: Args) {
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()); let device = try_fail!(Device::new(&args.flag_device, args.flag_type),
if args.flag_version { "Failed to open virtual {} interface {}: {}", args.flag_type, &args.flag_device);
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");
info!("Opened device {}", device.ifname()); info!("Opened device {}", device.ifname());
let mut ranges = Vec::with_capacity(args.flag_subnet.len()); let mut ranges = Vec::with_capacity(args.flag_subnet.len());
for s in args.flag_subnet { 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 dst_timeout = Duration::seconds(args.flag_dst_timeout as i64);
let peer_timeout = Duration::seconds(args.flag_peer_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), Some(key) => Crypto::from_shared_key(&key),
None => Crypto::None None => Crypto::None
}; };
match args.flag_type { let mut cloud = GenericCloud::<T>::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
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 { if let Some(script) = args.flag_ifup {
run_script(script, cloud.ifname()); run_script(script, cloud.ifname());
} }
for addr in &args.flag_connect { for addr in &args.flag_connect {
cloud.connect(&addr as &str, true).expect("Failed to send"); try_fail!(cloud.connect(&addr as &str, true), "Failed to send message to {}: {}", &addr);
} }
cloud.run(); cloud.run();
if let Some(script) = args.flag_ifdown { if let Some(script) = args.flag_ifdown {
run_script(script, cloud.ifname()); 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"); 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;
} }
if let Some(script) = args.flag_ifdown { log::set_logger(|max_log_level| {
run_script(script, cloud.ifname()); 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);
} }
cloud.run() Box::new(SimpleLogger)
}).unwrap();
debug!("Args: {:?}", args);
match args.flag_type {
Type::Tap => run::<ethernet::Frame>(args),
Type::Tun => run::<ip::Packet>(args),
} }
};
} }

View File

@ -95,11 +95,30 @@ impl FromStr for Range {
pub enum Type { pub enum Type {
Tun, Tap 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)] #[derive(RustcDecodable, Debug)]
pub enum Mode { pub enum Mode {
Normal, Hub, Switch, Router 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 { pub trait Table {
fn learn(&mut self, Address, Option<u8>, SocketAddr); fn learn(&mut self, Address, Option<u8>, SocketAddr);
@ -120,7 +139,18 @@ pub enum Error {
TunTapDevError(&'static str), TunTapDevError(&'static str),
CryptoError(&'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] #[test]
fn address_fmt() { fn address_fmt() {

View File

@ -19,3 +19,31 @@ pub fn to_vec(data: &[u8]) -> Vec<u8> {
} }
v 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)
}
} );
}