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:
* 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.

View File

@ -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<P: Protocol> GenericCloud<P> {
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<P: Protocol> GenericCloud<P> {
}
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)) => {
&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 network socket")
},
&1 => match self.device.read(&mut buffer) {
Ok(size) => match self.handle_interface_data(&buffer[..size]) {
&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)
},
Err(_error) => panic!("Failed to read from tap device")
}
},
_ => 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) {
//nothin to do
//nothing to do
}
fn remove_all(&mut self, _addr: SocketAddr) {

View File

@ -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<T: Protocol> (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);
let mut cloud = GenericCloud::<T>::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");
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());
}
},
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()
}
};
}
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::<ethernet::Frame>(args),
Type::Tun => run::<ip::Packet>(args),
}
}

View File

@ -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<u8>, 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() {

View File

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