mirror of https://github.com/dswd/vpncloud.git
Pretty error messages instead of panics
This commit is contained in:
parent
83fb941599
commit
12b5b17c3b
|
@ -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.
|
||||||
|
|
39
src/cloud.rs
39
src/cloud.rs
|
@ -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(_) => (),
|
|
||||||
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]) {
|
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => error!("Error: {:?}", e)
|
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!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
|
@ -315,7 +312,3 @@ impl<P: Protocol> GenericCloud<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub type TapCloud = GenericCloud<ethernet::Frame>;
|
|
||||||
pub type TunCloud = GenericCloud<ip::Packet>;
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
99
src/main.rs
99
src/main.rs
|
@ -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 => {
|
if let Some(script) = args.flag_ifup {
|
||||||
let mut cloud = TapCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
|
run_script(script, cloud.ifname());
|
||||||
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);
|
||||||
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 {
|
||||||
cloud.run();
|
run_script(script, cloud.ifname());
|
||||||
if let Some(script) = args.flag_ifdown {
|
}
|
||||||
run_script(script, cloud.ifname());
|
}
|
||||||
}
|
|
||||||
},
|
fn main() {
|
||||||
Type::Tun => {
|
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
|
||||||
let mut cloud = TunCloud::new(device, args.flag_listen, network_id, table, peer_timeout, learning, broadcasting, ranges, crypto);
|
if args.flag_version {
|
||||||
if let Some(script) = args.flag_ifup {
|
println!("VpnCloud v{} ({}, protocol version {})", env!("CARGO_PKG_VERSION"),
|
||||||
run_script(script, cloud.ifname());
|
if cfg!(feature = "crypto") { "with crypto support" } else { "without crypto support" },
|
||||||
}
|
VERSION
|
||||||
for addr in &args.flag_connect {
|
);
|
||||||
cloud.connect(&addr as &str, true).expect("Failed to send");
|
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 {
|
||||||
cloud.run()
|
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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
32
src/types.rs
32
src/types.rs
|
@ -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() {
|
||||||
|
|
28
src/util.rs
28
src/util.rs
|
@ -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)
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue