mirror of https://github.com/dswd/vpncloud.git
Compare commits
No commits in common. "04d934491cdbc427b1dda2384ab5e88fa7e9e95d" and "4c55897e8c11afa4fd410a05e983458b8e9e168a" have entirely different histories.
04d934491c
...
4c55897e8c
|
@ -7,5 +7,3 @@ deb/vpncloud/vpncloud.1*
|
||||||
Stats.ods
|
Stats.ods
|
||||||
dist
|
dist
|
||||||
builder/cache
|
builder/cache
|
||||||
.idea
|
|
||||||
release.sh
|
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
|
|
||||||
This project follows [semantic versioning](http://semver.org).
|
This project follows [semantic versioning](http://semver.org).
|
||||||
|
|
||||||
### Unreleased
|
|
||||||
|
|
||||||
- [changed] Rust version 1.41.0
|
|
||||||
- [changed] Updated dependencies
|
|
||||||
|
|
||||||
### v1.0.0 (2019-03-21)
|
### v1.0.0 (2019-03-21)
|
||||||
|
|
||||||
- [added] Added ability to publish small beacons for rendezvous
|
- [added] Added ability to publish small beacons for rendezvous
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
|
@ -18,16 +18,16 @@ serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
log = { version = "0.4", features = ["std"] }
|
log = { version = "0.4", features = ["std"] }
|
||||||
signal = "0.7"
|
signal = "0.6"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
rand = "0.7"
|
rand = "0.6"
|
||||||
fnv = "1"
|
fnv = "1"
|
||||||
net2 = "0.2"
|
net2 = "0.2"
|
||||||
yaml-rust = "0.4"
|
yaml-rust = "0.4"
|
||||||
igd = "0.9"
|
igd = "^0.8.2"
|
||||||
siphasher = "0.3"
|
siphasher = "0.3"
|
||||||
daemonize = "0.4"
|
daemonize = "0.3"
|
||||||
ring = "0.16"
|
ring = "0.14"
|
||||||
base-62 = "0.1"
|
base-62 = "0.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
17
rustfmt.toml
17
rustfmt.toml
|
@ -1,17 +0,0 @@
|
||||||
use_small_heuristics = "Max"
|
|
||||||
comment_width = 120
|
|
||||||
fn_args_layout = "Compressed"
|
|
||||||
where_single_line = true
|
|
||||||
merge_imports = true
|
|
||||||
max_width = 120
|
|
||||||
force_multiline_blocks = true
|
|
||||||
normalize_comments = true
|
|
||||||
trailing_comma = "Never"
|
|
||||||
trailing_semicolon = false
|
|
||||||
use_field_init_shorthand = true
|
|
||||||
use_try_shorthand = true
|
|
||||||
wrap_comments = true
|
|
||||||
overflow_delimited_expr = true
|
|
||||||
blank_lines_upper_bound = 2
|
|
||||||
normalize_doc_attributes = true
|
|
||||||
inline_attribute_width = 50
|
|
|
@ -5,24 +5,20 @@
|
||||||
use base_62;
|
use base_62;
|
||||||
use ring::digest;
|
use ring::digest;
|
||||||
|
|
||||||
use std::{
|
use std::num::Wrapping;
|
||||||
fs::{self, File, Permissions},
|
use std::path::Path;
|
||||||
io::{self, Read, Write},
|
use std::io::{self, Write, Read};
|
||||||
marker::PhantomData,
|
use std::fs::{self, Permissions, File};
|
||||||
mem,
|
use std::os::unix::fs::PermissionsExt;
|
||||||
num::Wrapping,
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
os::unix::fs::PermissionsExt,
|
use std::sync::{Arc, Mutex};
|
||||||
path::Path,
|
use std::marker::PhantomData;
|
||||||
process::{Command, Stdio},
|
use std::mem;
|
||||||
sync::{
|
use std::thread;
|
||||||
atomic::{AtomicBool, Ordering},
|
use std::process::{Command, Stdio};
|
||||||
Arc, Mutex
|
|
||||||
},
|
|
||||||
thread
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::util::{Encoder, TimeSource};
|
use super::util::{Encoder, TimeSource};
|
||||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr};
|
||||||
|
|
||||||
|
|
||||||
const TYPE_BEGIN: u8 = 0;
|
const TYPE_BEGIN: u8 = 0;
|
||||||
|
@ -56,7 +52,10 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
|
||||||
Self {
|
Self {
|
||||||
magic: magic.to_owned(),
|
magic: magic.to_owned(),
|
||||||
shared_key: shared_key.to_owned(),
|
shared_key: shared_key.to_owned(),
|
||||||
future_peers: Arc::new(FutureResult { has_result: AtomicBool::new(false), result: Mutex::new(Vec::new()) }),
|
future_peers: Arc::new(FutureResult {
|
||||||
|
has_result: AtomicBool::new(false),
|
||||||
|
result: Mutex::new(Vec::new())
|
||||||
|
}),
|
||||||
_dummy_ts: PhantomData
|
_dummy_ts: PhantomData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,6 +95,7 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
|
||||||
fn end(&self) -> String {
|
fn end(&self) -> String {
|
||||||
base_62::encode(&self.get_keystream(TYPE_END, 0, 0))[0..5].to_string()
|
base_62::encode(&self.get_keystream(TYPE_END, 0, 0))[0..5].to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_data(&self, data: &mut Vec<u8>) {
|
fn encrypt_data(&self, data: &mut Vec<u8>) {
|
||||||
// Note: the 1 byte seed is only meant to protect from random changes,
|
// Note: the 1 byte seed is only meant to protect from random changes,
|
||||||
// not malicious ones. For full protection, at least 8 bytes (~12
|
// not malicious ones. For full protection, at least 8 bytes (~12
|
||||||
|
@ -196,12 +196,8 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
|
||||||
ip[i] = Encoder::read_u16(&dat[i*2..i*2+2]);
|
ip[i] = Encoder::read_u16(&dat[i*2..i*2+2]);
|
||||||
}
|
}
|
||||||
let port = Encoder::read_u16(&dat[16..]);
|
let port = Encoder::read_u16(&dat[16..]);
|
||||||
let addr = SocketAddr::V6(SocketAddrV6::new(
|
let addr = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(ip[0], ip[1], ip[2],
|
||||||
Ipv6Addr::new(ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]),
|
ip[3], ip[4], ip[5], ip[6], ip[7]), port, 0, 0));
|
||||||
port,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
));
|
|
||||||
peers.push(addr);
|
peers.push(addr);
|
||||||
}
|
}
|
||||||
peers
|
peers
|
||||||
|
@ -226,15 +222,9 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
|
||||||
let end = self.end();
|
let end = self.end();
|
||||||
let beacon = format!("{}{}{}", begin, data, end);
|
let beacon = format!("{}{}{}", begin, data, end);
|
||||||
debug!("Calling beacon command: {}", cmd);
|
debug!("Calling beacon command: {}", cmd);
|
||||||
let process = Command::new("sh")
|
let process = Command::new("sh").args(&["-c", cmd])
|
||||||
.args(&["-c", cmd])
|
.env("begin", begin).env("data", data).env("end", end).env("beacon", beacon)
|
||||||
.env("begin", begin)
|
.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
|
||||||
.env("data", data)
|
|
||||||
.env("end", end)
|
|
||||||
.env("beacon", beacon)
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.stderr(Stdio::piped())
|
|
||||||
.spawn()?;
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let output = process.wait_with_output().expect("Failed to wait on child");
|
let output = process.wait_with_output().expect("Failed to wait on child");
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
|
@ -266,9 +256,7 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
|
||||||
peers
|
peers
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_from_file<P: AsRef<Path>>(
|
pub fn read_from_file<P: AsRef<Path>>(&self, path: P, ttl_hours: Option<u16>) -> Result<Vec<SocketAddr>, io::Error> {
|
||||||
&self, path: P, ttl_hours: Option<u16>
|
|
||||||
) -> Result<Vec<SocketAddr>, io::Error> {
|
|
||||||
let mut f = File::open(&path)?;
|
let mut f = File::open(&path)?;
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
f.read_to_string(&mut contents)?;
|
f.read_to_string(&mut contents)?;
|
||||||
|
@ -279,13 +267,9 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
|
||||||
let begin = self.begin();
|
let begin = self.begin();
|
||||||
let end = self.end();
|
let end = self.end();
|
||||||
debug!("Calling beacon command: {}", cmd);
|
debug!("Calling beacon command: {}", cmd);
|
||||||
let process = Command::new("sh")
|
let process = Command::new("sh").args(&["-c", cmd])
|
||||||
.args(&["-c", cmd])
|
.env("begin", begin).env("end", end)
|
||||||
.env("begin", begin)
|
.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
|
||||||
.env("end", end)
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.stderr(Stdio::piped())
|
|
||||||
.spawn()?;
|
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let output = process.wait_with_output().expect("Failed to wait on child");
|
let output = process.wait_with_output().expect("Failed to wait on child");
|
||||||
|
@ -315,10 +299,10 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)] use crate::util::MockTimeSource;
|
|
||||||
#[cfg(test)] use std::str::FromStr;
|
#[cfg(test)] use std::str::FromStr;
|
||||||
#[cfg(test)] use std::time::Duration;
|
#[cfg(test)] use std::time::Duration;
|
||||||
#[cfg(test)] use tempfile;
|
#[cfg(test)] use tempfile;
|
||||||
|
#[cfg(test)] use crate::util::MockTimeSource;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encode() {
|
fn encode() {
|
||||||
|
@ -345,10 +329,7 @@ fn decode() {
|
||||||
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
|
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
|
||||||
assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None)));
|
assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None)));
|
||||||
peers.push(SocketAddr::from_str("[::1]:5678").unwrap());
|
peers.push(SocketAddr::from_str("[::1]:5678").unwrap());
|
||||||
assert_eq!(
|
assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("juWwKjF5qZG7PE5imnpi5XARaXnP3UsMsGBLxM4FNFDzvjlKt1SO55LN", None)));
|
||||||
format!("{:?}", peers),
|
|
||||||
format!("{:?}", ser.decode("juWwKjF5qZG7PE5imnpi5XARaXnP3UsMsGBLxM4FNFDzvjlKt1SO55LN", None))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -358,14 +339,8 @@ fn decode_split() {
|
||||||
let mut peers = Vec::new();
|
let mut peers = Vec::new();
|
||||||
peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap());
|
peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap());
|
||||||
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
|
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
|
||||||
assert_eq!(
|
assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("juWwK-hj.VT:Yj bw\tJj\ntY(AZ)lM[fE]j7üIDäO55LN", None)));
|
||||||
format!("{:?}", peers),
|
assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("j -, \nuW--wKhjVTYjbwJjtYAZlMfEj7IDO(5}5ÖÄÜ\nLN", None)));
|
||||||
format!("{:?}", ser.decode("juWwK-hj.VT:Yj bw\tJj\ntY(AZ)lM[fE]j7üIDäO55LN", None))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", peers),
|
|
||||||
format!("{:?}", ser.decode("j -, \nuW--wKhjVTYjbwJjtYAZlMfEj7IDO(5}5ÖÄÜ\nLN", None))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -375,10 +350,7 @@ fn decode_offset() {
|
||||||
let mut peers = Vec::new();
|
let mut peers = Vec::new();
|
||||||
peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap());
|
peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap());
|
||||||
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
|
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
|
||||||
assert_eq!(
|
assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("Hello World: juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN! End of the World", None)));
|
||||||
format!("{:?}", peers),
|
|
||||||
format!("{:?}", ser.decode("Hello World: juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN! End of the World", None))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -388,10 +360,7 @@ fn decode_multiple() {
|
||||||
let mut peers = Vec::new();
|
let mut peers = Vec::new();
|
||||||
peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap());
|
peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap());
|
||||||
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
|
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
|
||||||
assert_eq!(
|
assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("juWwKkBEVBp9SsDiN3BO55LN juWwKtGGPQz1gXIBd68O55LN", None)));
|
||||||
format!("{:?}", peers),
|
|
||||||
format!("{:?}", ser.decode("juWwKkBEVBp9SsDiN3BO55LN juWwKtGGPQz1gXIBd68O55LN", None))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -4,25 +4,22 @@
|
||||||
|
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
use std::{
|
use std::str::FromStr;
|
||||||
net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs, UdpSocket},
|
use std::net::{UdpSocket, ToSocketAddrs, Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||||
str::FromStr
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::MAGIC;
|
||||||
cloud::GenericCloud,
|
use super::config::Config;
|
||||||
config::Config,
|
use super::cloud::GenericCloud;
|
||||||
crypto::{Crypto, CryptoMethod},
|
use super::device::Type;
|
||||||
device::{TunTapDevice, Type},
|
use super::udpmessage::{Message, encode, decode};
|
||||||
ethernet::{self, SwitchTable},
|
use super::crypto::{Crypto, CryptoMethod};
|
||||||
ip::Packet,
|
use super::ethernet::{self, SwitchTable};
|
||||||
net::MockSocket,
|
use super::types::{Address, Table, Protocol};
|
||||||
poll::WaitImpl,
|
use super::ip::Packet;
|
||||||
types::{Address, Protocol, Table},
|
use super::util::{TimeSource, SystemTimeSource, MockTimeSource};
|
||||||
udpmessage::{decode, encode, Message},
|
use super::poll::WaitImpl;
|
||||||
util::{MockTimeSource, SystemTimeSource, TimeSource},
|
use super::device::TunTapDevice;
|
||||||
MAGIC
|
use super::net::MockSocket;
|
||||||
};
|
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn crypto_chacha20(b: &mut Bencher) {
|
fn crypto_chacha20(b: &mut Bencher) {
|
||||||
|
@ -102,7 +99,9 @@ fn switch_lookup(b: &mut Bencher) {
|
||||||
fn ethernet_parse(b: &mut Bencher) {
|
fn ethernet_parse(b: &mut Bencher) {
|
||||||
let mut data = [0; 1500];
|
let mut data = [0; 1500];
|
||||||
data[5] = 45;
|
data[5] = 45;
|
||||||
b.iter(|| ethernet::Frame::parse(&data).unwrap());
|
b.iter(|| {
|
||||||
|
ethernet::Frame::parse(&data).unwrap()
|
||||||
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +109,9 @@ fn ethernet_parse(b: &mut Bencher) {
|
||||||
fn ipv4_parse(b: &mut Bencher) {
|
fn ipv4_parse(b: &mut Bencher) {
|
||||||
let mut data = [0; 1500];
|
let mut data = [0; 1500];
|
||||||
data[0] = 4*16;
|
data[0] = 4*16;
|
||||||
b.iter(|| Packet::parse(&data).unwrap());
|
b.iter(|| {
|
||||||
|
Packet::parse(&data).unwrap()
|
||||||
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,13 +119,17 @@ fn ipv4_parse(b: &mut Bencher) {
|
||||||
fn ipv6_parse(b: &mut Bencher) {
|
fn ipv6_parse(b: &mut Bencher) {
|
||||||
let mut data = [0; 1500];
|
let mut data = [0; 1500];
|
||||||
data[0] = 6*16;
|
data[0] = 6*16;
|
||||||
b.iter(|| Packet::parse(&data).unwrap());
|
b.iter(|| {
|
||||||
|
Packet::parse(&data).unwrap()
|
||||||
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn now(b: &mut Bencher) {
|
fn now(b: &mut Bencher) {
|
||||||
b.iter(|| SystemTimeSource::now());
|
b.iter(|| {
|
||||||
|
SystemTimeSource::now()
|
||||||
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +139,9 @@ fn epoll_wait(b: &mut Bencher) {
|
||||||
let socketv6 = UdpSocket::bind("[::]:0").unwrap();
|
let socketv6 = UdpSocket::bind("[::]:0").unwrap();
|
||||||
let device = TunTapDevice::dummy("dummy", "/dev/zero", Type::Dummy).unwrap();
|
let device = TunTapDevice::dummy("dummy", "/dev/zero", Type::Dummy).unwrap();
|
||||||
let mut waiter = WaitImpl::testing(&socketv4, &socketv6, &device, 1000).unwrap();
|
let mut waiter = WaitImpl::testing(&socketv4, &socketv6, &device, 1000).unwrap();
|
||||||
b.iter(|| assert!(waiter.next().is_some()));
|
b.iter(|| {
|
||||||
|
assert!(waiter.next().is_some())
|
||||||
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,11 +152,7 @@ fn create_test_node() -> TestNode {
|
||||||
&Config::default(),
|
&Config::default(),
|
||||||
TunTapDevice::dummy("dummy", "/dev/null", Type::Tap).unwrap(),
|
TunTapDevice::dummy("dummy", "/dev/null", Type::Tap).unwrap(),
|
||||||
SwitchTable::new(1800, 10),
|
SwitchTable::new(1800, 10),
|
||||||
true,
|
true, true, vec![], Crypto::None, None
|
||||||
true,
|
|
||||||
vec![],
|
|
||||||
Crypto::None,
|
|
||||||
None
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +161,9 @@ fn handle_interface_data(b: &mut Bencher) {
|
||||||
let mut node = create_test_node();
|
let mut node = create_test_node();
|
||||||
let mut data = [0; 1500];
|
let mut data = [0; 1500];
|
||||||
data[105] = 45;
|
data[105] = 45;
|
||||||
b.iter(|| node.handle_interface_data(&mut data, 100, 1400).unwrap());
|
b.iter(|| {
|
||||||
|
node.handle_interface_data(&mut data, 100, 1400).unwrap()
|
||||||
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +173,9 @@ fn handle_net_message(b: &mut Bencher) {
|
||||||
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1));
|
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1));
|
||||||
let mut data = [0; 1500];
|
let mut data = [0; 1500];
|
||||||
data[105] = 45;
|
data[105] = 45;
|
||||||
b.iter(|| node.handle_net_message(addr.clone(), Message::Data(&mut data, 0, 1400)).unwrap());
|
b.iter(|| {
|
||||||
|
node.handle_net_message(addr.clone(), Message::Data(&mut data, 0, 1400)).unwrap()
|
||||||
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,6 +184,8 @@ fn udp_send(b: &mut Bencher) {
|
||||||
let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
|
let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||||
let data = [0; 1400];
|
let data = [0; 1400];
|
||||||
let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1);
|
let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1);
|
||||||
b.iter(|| sock.send_to(&data, &addr).unwrap());
|
b.iter(|| {
|
||||||
|
sock.send_to(&data, &addr).unwrap()
|
||||||
|
});
|
||||||
b.bytes = 1400;
|
b.bytes = 1400;
|
||||||
}
|
}
|
||||||
|
|
108
src/cloud.rs
108
src/cloud.rs
|
@ -2,34 +2,30 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use std::{
|
use std::net::{SocketAddr, ToSocketAddrs};
|
||||||
cmp::min,
|
use std::collections::HashMap;
|
||||||
collections::HashMap,
|
use std::io::{self, Write};
|
||||||
fmt,
|
use std::fmt;
|
||||||
fs::{self, File, Permissions},
|
use std::marker::PhantomData;
|
||||||
hash::BuildHasherDefault,
|
use std::hash::BuildHasherDefault;
|
||||||
io::{self, Write},
|
use std::cmp::min;
|
||||||
marker::PhantomData,
|
use std::fs::{self, File, Permissions};
|
||||||
net::{SocketAddr, ToSocketAddrs},
|
use std::os::unix::fs::PermissionsExt;
|
||||||
os::unix::fs::PermissionsExt
|
|
||||||
};
|
|
||||||
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use rand::{prelude::*, random, thread_rng};
|
use rand::{prelude::*, random, thread_rng};
|
||||||
|
|
||||||
use super::{
|
use super::config::Config;
|
||||||
beacon::BeaconSerializer,
|
use super::types::{Table, Protocol, Range, Error, HeaderMagic, NodeId};
|
||||||
config::Config,
|
use super::device::Device;
|
||||||
crypto::Crypto,
|
use super::udpmessage::{encode, decode, Message};
|
||||||
device::Device,
|
use super::crypto::Crypto;
|
||||||
net::Socket,
|
use super::port_forwarding::PortForwarding;
|
||||||
poll::{WaitImpl, WaitResult},
|
use super::util::{TimeSource, Time, Duration, resolve, CtrlC};
|
||||||
port_forwarding::PortForwarding,
|
use super::poll::{WaitImpl, WaitResult};
|
||||||
traffic::TrafficStats,
|
use super::traffic::TrafficStats;
|
||||||
types::{Error, HeaderMagic, NodeId, Protocol, Range, Table},
|
use super::beacon::BeaconSerializer;
|
||||||
udpmessage::{decode, encode, Message},
|
use super::net::Socket;
|
||||||
util::{resolve, CtrlC, Duration, Time, TimeSource}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type Hash = BuildHasherDefault<FnvHasher>;
|
pub type Hash = BuildHasherDefault<FnvHasher>;
|
||||||
|
|
||||||
|
@ -41,7 +37,7 @@ pub const STATS_INTERVAL: Time = 60;
|
||||||
struct PeerData {
|
struct PeerData {
|
||||||
timeout: Time,
|
timeout: Time,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
alt_addrs: Vec<SocketAddr>
|
alt_addrs: Vec<SocketAddr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PeerList<TS: TimeSource> {
|
pub struct PeerList<TS: TimeSource> {
|
||||||
|
@ -93,7 +89,7 @@ impl<TS: TimeSource> PeerList<TS> {
|
||||||
pub fn is_connected<Addr: ToSocketAddrs+fmt::Debug>(&self, addr: Addr) -> Result<bool, Error> {
|
pub fn is_connected<Addr: ToSocketAddrs+fmt::Debug>(&self, addr: Addr) -> Result<bool, Error> {
|
||||||
for addr in resolve(&addr)? {
|
for addr in resolve(&addr)? {
|
||||||
if self.contains_addr(&addr) {
|
if self.contains_addr(&addr) {
|
||||||
return Ok(true)
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(false)
|
Ok(false)
|
||||||
|
@ -235,11 +231,10 @@ pub struct GenericCloud<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSou
|
||||||
|
|
||||||
impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D, P, T, S, TS> {
|
impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D, P, T, S, TS> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(config: &Config, device: D, table: T,
|
||||||
config: &Config, device: D, table: T, learning: bool, broadcast: bool, addresses: Vec<Range>, crypto: Crypto,
|
learning: bool, broadcast: bool, addresses: Vec<Range>,
|
||||||
port_forwarding: Option<PortForwarding>
|
crypto: Crypto, port_forwarding: Option<PortForwarding>
|
||||||
) -> Self
|
) -> Self {
|
||||||
{
|
|
||||||
let socket4 = match S::listen_v4("0.0.0.0", config.port) {
|
let socket4 = match S::listen_v4("0.0.0.0", config.port) {
|
||||||
Ok(socket) => socket,
|
Ok(socket) => socket,
|
||||||
Err(err) => fail!("Failed to open ipv4 address 0.0.0.0:{}: {}", config.port, err)
|
Err(err) => fail!("Failed to open ipv4 address 0.0.0.0:{}: {}", config.port, err)
|
||||||
|
@ -304,9 +299,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
};
|
};
|
||||||
match socket.send(msg_data, *addr) {
|
match socket.send(msg_data, *addr) {
|
||||||
Ok(written) if written == msg_data.len() => Ok(()),
|
Ok(written) if written == msg_data.len() => Ok(()),
|
||||||
Ok(_) => {
|
Ok(_) => Err(Error::Socket("Sent out truncated packet", io::Error::new(io::ErrorKind::Other, "truncated"))),
|
||||||
Err(Error::Socket("Sent out truncated packet", io::Error::new(io::ErrorKind::Other, "truncated")))
|
|
||||||
}
|
|
||||||
Err(e) => Err(Error::Socket("IOError when sending", e))
|
Err(e) => Err(Error::Socket("IOError when sending", e))
|
||||||
}?
|
}?
|
||||||
}
|
}
|
||||||
|
@ -370,7 +363,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
address: add,
|
address: add,
|
||||||
tries: 0,
|
tries: 0,
|
||||||
timeout: 1,
|
timeout: 1,
|
||||||
resolved,
|
resolved: resolved,
|
||||||
next_resolve: now,
|
next_resolve: now,
|
||||||
next: now
|
next: now
|
||||||
})
|
})
|
||||||
|
@ -384,7 +377,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
fn is_own_address<Addr: ToSocketAddrs+fmt::Debug>(&self, addr: Addr) -> Result<bool, Error> {
|
fn is_own_address<Addr: ToSocketAddrs+fmt::Debug>(&self, addr: Addr) -> Result<bool, Error> {
|
||||||
for addr in resolve(&addr)? {
|
for addr in resolve(&addr)? {
|
||||||
if self.own_addresses.contains(&addr) {
|
if self.own_addresses.contains(&addr) {
|
||||||
return Ok(true)
|
return Ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(false)
|
Ok(false)
|
||||||
|
@ -532,13 +525,9 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
if let Some(ref path) = self.config.beacon_store {
|
if let Some(ref path) = self.config.beacon_store {
|
||||||
let peers: Vec<_> = self.own_addresses.choose_multiple(&mut thread_rng(),3).cloned().collect();
|
let peers: Vec<_> = self.own_addresses.choose_multiple(&mut thread_rng(),3).cloned().collect();
|
||||||
if path.starts_with('|') {
|
if path.starts_with('|') {
|
||||||
self.beacon_serializer
|
self.beacon_serializer.write_to_cmd(&peers, &path[1..]).map_err(|e| Error::Beacon("Failed to call beacon command", e))?;
|
||||||
.write_to_cmd(&peers, &path[1..])
|
|
||||||
.map_err(|e| Error::Beacon("Failed to call beacon command", e))?;
|
|
||||||
} else {
|
} else {
|
||||||
self.beacon_serializer
|
self.beacon_serializer.write_to_file(&peers, &path).map_err(|e| Error::Beacon("Failed to write beacon to file", e))?;
|
||||||
.write_to_file(&peers, &path)
|
|
||||||
.map_err(|e| Error::Beacon("Failed to write beacon to file", e))?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -549,15 +538,10 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
let peers;
|
let peers;
|
||||||
if let Some(ref path) = self.config.beacon_load {
|
if let Some(ref path) = self.config.beacon_load {
|
||||||
if path.starts_with('|') {
|
if path.starts_with('|') {
|
||||||
self.beacon_serializer
|
self.beacon_serializer.read_from_cmd(&path[1..], Some(50)).map_err(|e| Error::Beacon("Failed to call beacon command", e))?;
|
||||||
.read_from_cmd(&path[1..], Some(50))
|
|
||||||
.map_err(|e| Error::Beacon("Failed to call beacon command", e))?;
|
|
||||||
return Ok(())
|
return Ok(())
|
||||||
} else {
|
} else {
|
||||||
peers = self
|
peers = self.beacon_serializer.read_from_file(&path, Some(50)).map_err(|e| Error::Beacon("Failed to read beacon from file", e))?;
|
||||||
.beacon_serializer
|
|
||||||
.read_from_file(&path, Some(50))
|
|
||||||
.map_err(|e| Error::Beacon("Failed to read beacon from file", e))?;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
|
@ -571,9 +555,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
|
|
||||||
/// Calculates, resets and writes out the statistics to a file
|
/// Calculates, resets and writes out the statistics to a file
|
||||||
fn write_out_stats(&mut self) -> Result<(), io::Error> {
|
fn write_out_stats(&mut self) -> Result<(), io::Error> {
|
||||||
if self.config.stats_file.is_none() {
|
if self.config.stats_file.is_none() { return Ok(()) }
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
debug!("Writing out stats");
|
debug!("Writing out stats");
|
||||||
let mut f = File::create(self.config.stats_file.as_ref().unwrap())?;
|
let mut f = File::create(self.config.stats_file.as_ref().unwrap())?;
|
||||||
self.peers.write_out(&mut f)?;
|
self.peers.write_out(&mut f)?;
|
||||||
|
@ -607,8 +589,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
debug!("Read data from interface: src: {}, dst: {}, {} bytes", src, dst, end-start);
|
debug!("Read data from interface: src: {}, dst: {}, {} bytes", src, dst, end-start);
|
||||||
self.traffic.count_out_payload(dst, src, end-start);
|
self.traffic.count_out_payload(dst, src, end-start);
|
||||||
match self.table.lookup(&dst) {
|
match self.table.lookup(&dst) {
|
||||||
Some(addr) => {
|
Some(addr) => { // Peer found for destination
|
||||||
// Peer found for destination
|
|
||||||
debug!("Found destination for {} => {}", dst, addr);
|
debug!("Found destination for {} => {}", dst, addr);
|
||||||
self.send_msg(addr, &mut Message::Data(payload, start, end))?;
|
self.send_msg(addr, &mut Message::Data(payload, start, end))?;
|
||||||
if !self.peers.contains_addr(&addr) {
|
if !self.peers.contains_addr(&addr) {
|
||||||
|
@ -618,7 +599,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
self.table.remove(&dst);
|
self.table.remove(&dst);
|
||||||
self.connect_sock(addr)?;
|
self.connect_sock(addr)?;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => {
|
None => {
|
||||||
if self.broadcast {
|
if self.broadcast {
|
||||||
debug!("No destination for {} found, broadcasting", dst);
|
debug!("No destination for {} found, broadcasting", dst);
|
||||||
|
@ -674,14 +655,14 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
self.traffic.count_in_payload(src, dst, end-start);
|
self.traffic.count_in_payload(src, dst, end-start);
|
||||||
if let Err(e) = self.device.write(&mut payload[..end], start) {
|
if let Err(e) = self.device.write(&mut payload[..end], start) {
|
||||||
error!("Failed to send via device: {}", e);
|
error!("Failed to send via device: {}", e);
|
||||||
return Err(e)
|
return Err(e);
|
||||||
}
|
}
|
||||||
if self.learning {
|
if self.learning {
|
||||||
// Learn single address
|
// Learn single address
|
||||||
self.table.learn(src, None, peer);
|
self.table.learn(src, None, peer);
|
||||||
}
|
}
|
||||||
// Not adding peer in this case to increase performance
|
// Not adding peer in this case to increase performance
|
||||||
}
|
},
|
||||||
Message::Peers(peers) => {
|
Message::Peers(peers) => {
|
||||||
// Connect to sender if not connected
|
// Connect to sender if not connected
|
||||||
if !self.peers.contains_addr(&peer) {
|
if !self.peers.contains_addr(&peer) {
|
||||||
|
@ -696,7 +677,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
}
|
}
|
||||||
// Refresh peer
|
// Refresh peer
|
||||||
self.peers.refresh(&peer);
|
self.peers.refresh(&peer);
|
||||||
}
|
},
|
||||||
Message::Init(stage, node_id, ranges) => {
|
Message::Init(stage, node_id, ranges) => {
|
||||||
// Avoid connecting to self
|
// Avoid connecting to self
|
||||||
if node_id == self.node_id {
|
if node_id == self.node_id {
|
||||||
|
@ -721,7 +702,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
// Send peers in any case
|
// Send peers in any case
|
||||||
let peers = self.peers.as_vec();
|
let peers = self.peers.as_vec();
|
||||||
self.send_msg(peer, &mut Message::Peers(peers))?;
|
self.send_msg(peer, &mut Message::Peers(peers))?;
|
||||||
}
|
},
|
||||||
Message::Close => {
|
Message::Close => {
|
||||||
self.peers.remove(&peer);
|
self.peers.remove(&peer);
|
||||||
self.table.remove_all(&peer);
|
self.table.remove_all(&peer);
|
||||||
|
@ -778,8 +759,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
/// Also, this method will call `housekeep` every second.
|
/// Also, this method will call `housekeep` every second.
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
let ctrlc = CtrlC::new();
|
let ctrlc = CtrlC::new();
|
||||||
let waiter =
|
let waiter = try_fail!(WaitImpl::new(&self.socket4, &self.socket6, &self.device, 1000), "Failed to setup poll: {}");
|
||||||
try_fail!(WaitImpl::new(&self.socket4, &self.socket6, &self.device, 1000), "Failed to setup poll: {}");
|
|
||||||
let mut buffer = [0; 64*1024];
|
let mut buffer = [0; 64*1024];
|
||||||
let mut poll_error = false;
|
let mut poll_error = false;
|
||||||
for evt in waiter {
|
for evt in waiter {
|
||||||
|
@ -790,8 +770,8 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
}
|
}
|
||||||
error!("Poll wait failed: {}, retrying...", err);
|
error!("Poll wait failed: {}, retrying...", err);
|
||||||
poll_error = true;
|
poll_error = true;
|
||||||
}
|
},
|
||||||
WaitResult::Timeout => {}
|
WaitResult::Timeout => {},
|
||||||
WaitResult::SocketV4 => self.handle_socket_v4_event(&mut buffer),
|
WaitResult::SocketV4 => self.handle_socket_v4_event(&mut buffer),
|
||||||
WaitResult::SocketV6 => self.handle_socket_v6_event(&mut buffer),
|
WaitResult::SocketV6 => self.handle_socket_v6_event(&mut buffer),
|
||||||
WaitResult::Device => self.handle_device_event(&mut buffer)
|
WaitResult::Device => self.handle_device_event(&mut buffer)
|
||||||
|
@ -813,9 +793,9 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)] use super::device::MockDevice;
|
|
||||||
#[cfg(test)] use super::net::MockSocket;
|
#[cfg(test)] use super::net::MockSocket;
|
||||||
#[cfg(test)] use super::util::MockTimeSource;
|
#[cfg(test)] use super::util::MockTimeSource;
|
||||||
|
#[cfg(test)] use super::device::MockDevice;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl<P: Protocol, T: Table> GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource> {
|
impl<P: Protocol, T: Table> GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource> {
|
||||||
|
|
|
@ -2,17 +2,15 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use super::{Args, MAGIC};
|
use super::{MAGIC, Args};
|
||||||
|
|
||||||
use super::{
|
use super::device::Type;
|
||||||
crypto::CryptoMethod,
|
use super::types::{Mode, HeaderMagic};
|
||||||
device::Type,
|
use super::crypto::CryptoMethod;
|
||||||
types::{HeaderMagic, Mode},
|
use super::util::{Encoder, Duration};
|
||||||
util::{Duration, Encoder}
|
|
||||||
};
|
|
||||||
|
|
||||||
use siphasher::sip::SipHasher24;
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use siphasher::sip::SipHasher24;
|
||||||
|
|
||||||
|
|
||||||
const HASH_PREFIX: &str = "hash:";
|
const HASH_PREFIX: &str = "hash:";
|
||||||
|
@ -49,23 +47,13 @@ pub struct Config {
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Config {
|
Config {
|
||||||
device_type: Type::Tap,
|
device_type: Type::Tap, device_name: "vpncloud%d".to_string(), device_path: None,
|
||||||
device_name: "vpncloud%d".to_string(),
|
ifup: None, ifdown: None,
|
||||||
device_path: None,
|
crypto: CryptoMethod::ChaCha20, shared_key: None,
|
||||||
ifup: None,
|
|
||||||
ifdown: None,
|
|
||||||
crypto: CryptoMethod::ChaCha20,
|
|
||||||
shared_key: None,
|
|
||||||
magic: None,
|
magic: None,
|
||||||
port: 3210,
|
port: 3210, peers: vec![], peer_timeout: 1800, keepalive: None,
|
||||||
peers: vec![],
|
beacon_store: None, beacon_load: None, beacon_interval: 3600,
|
||||||
peer_timeout: 1800,
|
mode: Mode::Normal, dst_timeout: 300,
|
||||||
keepalive: None,
|
|
||||||
beacon_store: None,
|
|
||||||
beacon_load: None,
|
|
||||||
beacon_interval: 3600,
|
|
||||||
mode: Mode::Normal,
|
|
||||||
dst_timeout: 300,
|
|
||||||
subnets: vec![],
|
subnets: vec![],
|
||||||
port_forwarding: true,
|
port_forwarding: true,
|
||||||
daemonize: false,
|
daemonize: false,
|
||||||
|
@ -277,7 +265,7 @@ pub struct ConfigFile {
|
||||||
pub pid_file: Option<String>,
|
pub pid_file: Option<String>,
|
||||||
pub stats_file: Option<String>,
|
pub stats_file: Option<String>,
|
||||||
pub user: Option<String>,
|
pub user: Option<String>,
|
||||||
pub group: Option<String>
|
pub group: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -429,11 +417,7 @@ fn config_merge() {
|
||||||
crypto: CryptoMethod::ChaCha20,
|
crypto: CryptoMethod::ChaCha20,
|
||||||
shared_key: Some("anothersecret".to_string()),
|
shared_key: Some("anothersecret".to_string()),
|
||||||
port: 3211,
|
port: 3211,
|
||||||
peers: vec![
|
peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string(), "another:3210".to_string()],
|
||||||
"remote.machine.foo:3210".to_string(),
|
|
||||||
"remote.machine.bar:3210".to_string(),
|
|
||||||
"another:3210".to_string()
|
|
||||||
],
|
|
||||||
peer_timeout: 1801,
|
peer_timeout: 1801,
|
||||||
keepalive: Some(850),
|
keepalive: Some(850),
|
||||||
dst_timeout: 301,
|
dst_timeout: 301,
|
||||||
|
|
|
@ -4,10 +4,14 @@
|
||||||
|
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
use ring::{aead::*, pbkdf2, rand::*};
|
use ring::aead::*;
|
||||||
|
use ring::pbkdf2;
|
||||||
|
use ring::rand::*;
|
||||||
|
use ring::digest::*;
|
||||||
|
|
||||||
use super::types::Error;
|
use super::types::Error;
|
||||||
|
|
||||||
|
|
||||||
const SALT: &[u8; 32] = b"vpncloudVPNCLOUDvpncl0udVpnCloud";
|
const SALT: &[u8; 32] = b"vpncloudVPNCLOUDvpncl0udVpnCloud";
|
||||||
const HEX_PREFIX: &str = "hex:";
|
const HEX_PREFIX: &str = "hex:";
|
||||||
const HASH_PREFIX: &str = "hash:";
|
const HASH_PREFIX: &str = "hash:";
|
||||||
|
@ -21,7 +25,8 @@ pub enum CryptoMethod {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CryptoData {
|
pub struct CryptoData {
|
||||||
crypto_key: LessSafeKey,
|
sealing_key: SealingKey,
|
||||||
|
opening_key: OpeningKey,
|
||||||
nonce: Vec<u8>,
|
nonce: Vec<u8>,
|
||||||
key: Vec<u8>
|
key: Vec<u8>
|
||||||
}
|
}
|
||||||
|
@ -46,6 +51,7 @@ fn inc_nonce(nonce: &mut [u8]) {
|
||||||
warn!("Nonce overflowed");
|
warn!("Nonce overflowed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Crypto {
|
impl Crypto {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn method(&self) -> u8 {
|
pub fn method(&self) -> u8 {
|
||||||
|
@ -60,7 +66,7 @@ impl Crypto {
|
||||||
pub fn nonce_bytes(&self) -> usize {
|
pub fn nonce_bytes(&self) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
Crypto::None => 0,
|
Crypto::None => 0,
|
||||||
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => data.crypto_key.algorithm().nonce_len()
|
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => data.sealing_key.algorithm().nonce_len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +83,7 @@ impl Crypto {
|
||||||
pub fn additional_bytes(&self) -> usize {
|
pub fn additional_bytes(&self) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
Crypto::None => 0,
|
Crypto::None => 0,
|
||||||
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => data.crypto_key.algorithm().tag_len()
|
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => data.sealing_key.algorithm().tag_len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,22 +102,18 @@ impl Crypto {
|
||||||
fail!("Raw secret key must be exactly {} bytes long", algo.key_len());
|
fail!("Raw secret key must be exactly {} bytes long", algo.key_len());
|
||||||
}
|
}
|
||||||
for i in 0..algo.key_len() {
|
for i in 0..algo.key_len() {
|
||||||
key[i] = try_fail!(
|
key[i] = try_fail!(u8::from_str_radix(&password[2*i..=2*i+1], 16), "Failed to parse raw secret key: {}");
|
||||||
u8::from_str_radix(&password[2 * i..=2 * i + 1], 16),
|
|
||||||
"Failed to parse raw secret key: {}"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let password = if password.starts_with(HASH_PREFIX) { &password[HASH_PREFIX.len()..] } else { password };
|
let password = if password.starts_with(HASH_PREFIX) {
|
||||||
pbkdf2::derive(
|
&password[HASH_PREFIX.len()..]
|
||||||
pbkdf2::PBKDF2_HMAC_SHA256,
|
} else {
|
||||||
NonZeroU32::new(4096).unwrap(),
|
password
|
||||||
SALT,
|
};
|
||||||
password.as_bytes(),
|
pbkdf2::derive(&SHA256, NonZeroU32::new(4096).unwrap(), SALT, password.as_bytes(), &mut key);
|
||||||
&mut key
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
let crypto_key = LessSafeKey::new(UnboundKey::new(algo, &key[..algo.key_len()]).expect("Failed to create key"));
|
let sealing_key = SealingKey::new(algo, &key[..algo.key_len()]).expect("Failed to create key");
|
||||||
|
let opening_key = OpeningKey::new(algo, &key[..algo.key_len()]).expect("Failed to create key");
|
||||||
let mut nonce: Vec<u8> = Vec::with_capacity(algo.nonce_len());
|
let mut nonce: Vec<u8> = Vec::with_capacity(algo.nonce_len());
|
||||||
for _ in 0..algo.nonce_len() {
|
for _ in 0..algo.nonce_len() {
|
||||||
nonce.push(0);
|
nonce.push(0);
|
||||||
|
@ -120,7 +122,7 @@ impl Crypto {
|
||||||
if SystemRandom::new().fill(&mut nonce[1..]).is_err() {
|
if SystemRandom::new().fill(&mut nonce[1..]).is_err() {
|
||||||
fail!("Randomizing nonce failed");
|
fail!("Randomizing nonce failed");
|
||||||
}
|
}
|
||||||
let data = CryptoData { crypto_key, nonce, key };
|
let data = CryptoData { sealing_key, opening_key, nonce, key };
|
||||||
match method {
|
match method {
|
||||||
CryptoMethod::ChaCha20 => Crypto::ChaCha20Poly1305(data),
|
CryptoMethod::ChaCha20 => Crypto::ChaCha20Poly1305(data),
|
||||||
CryptoMethod::AES256 => Crypto::AES256GCM(data)
|
CryptoMethod::AES256 => Crypto::AES256GCM(data)
|
||||||
|
@ -132,7 +134,7 @@ impl Crypto {
|
||||||
Crypto::None => Ok(buf.len()),
|
Crypto::None => Ok(buf.len()),
|
||||||
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => {
|
Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => {
|
||||||
let nonce = Nonce::try_assume_unique_for_key(nonce).unwrap();
|
let nonce = Nonce::try_assume_unique_for_key(nonce).unwrap();
|
||||||
match data.crypto_key.open_in_place(nonce, Aad::from(header), buf) {
|
match open_in_place(&data.opening_key, nonce, Aad::from(header), 0, buf) {
|
||||||
Ok(plaintext) => Ok(plaintext.len()),
|
Ok(plaintext) => Ok(plaintext.len()),
|
||||||
Err(_) => Err(Error::Crypto("Failed to decrypt"))
|
Err(_) => Err(Error::Crypto("Failed to decrypt"))
|
||||||
}
|
}
|
||||||
|
@ -147,19 +149,18 @@ impl Crypto {
|
||||||
Crypto::ChaCha20Poly1305(ref mut data) | Crypto::AES256GCM(ref mut data) => {
|
Crypto::ChaCha20Poly1305(ref mut data) | Crypto::AES256GCM(ref mut data) => {
|
||||||
inc_nonce(&mut data.nonce);
|
inc_nonce(&mut data.nonce);
|
||||||
assert!(buf.len() - mlen >= tag_len);
|
assert!(buf.len() - mlen >= tag_len);
|
||||||
|
let buf = &mut buf[.. mlen + tag_len];
|
||||||
let nonce = Nonce::try_assume_unique_for_key(&data.nonce).unwrap();
|
let nonce = Nonce::try_assume_unique_for_key(&data.nonce).unwrap();
|
||||||
let tag = data
|
let new_len = seal_in_place(&data.sealing_key, nonce, Aad::from(header), buf, tag_len).expect("Failed to encrypt");
|
||||||
.crypto_key
|
|
||||||
.seal_in_place_separate_tag(nonce, Aad::from(header), &mut buf[..mlen])
|
|
||||||
.expect("Failed to encrypt");
|
|
||||||
buf[mlen..mlen + tag_len].copy_from_slice(tag.as_ref());
|
|
||||||
nonce_bytes.clone_from_slice(&data.nonce);
|
nonce_bytes.clone_from_slice(&data.nonce);
|
||||||
mlen + tag_len
|
new_len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encrypt_decrypt_chacha20poly1305() {
|
fn encrypt_decrypt_chacha20poly1305() {
|
||||||
let mut sender = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
|
let mut sender = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
|
||||||
|
|
|
@ -2,16 +2,15 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use std::{
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
collections::VecDeque,
|
use std::io::{self, Error as IoError, ErrorKind, Read, Write};
|
||||||
fmt, fs,
|
use std::fs;
|
||||||
io::{self, Error as IoError, ErrorKind, Read, Write},
|
use std::fmt;
|
||||||
os::unix::io::{AsRawFd, RawFd}
|
use std::collections::VecDeque;
|
||||||
};
|
|
||||||
|
|
||||||
use super::types::Error;
|
use super::types::Error;
|
||||||
|
|
||||||
extern "C" {
|
extern {
|
||||||
fn setup_tap_device(fd: i32, ifname: *mut u8) -> i32;
|
fn setup_tap_device(fd: i32, ifname: *mut u8) -> i32;
|
||||||
fn setup_tun_device(fd: i32, ifname: *mut u8) -> i32;
|
fn setup_tun_device(fd: i32, ifname: *mut u8) -> i32;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +78,7 @@ pub trait Device: AsRawFd {
|
||||||
pub struct TunTapDevice {
|
pub struct TunTapDevice {
|
||||||
fd: fs::File,
|
fd: fs::File,
|
||||||
ifname: String,
|
ifname: String,
|
||||||
type_: Type
|
type_: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,16 +94,18 @@ impl TunTapDevice {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// This method will return an error when the underlying system call fails. Common cases are:
|
/// This method will return an error when the underlying system call fails. Common cases are:
|
||||||
/// - The special device file `/dev/net/tun` does not exist or is not accessible by the current user.
|
/// - The special device file `/dev/net/tun` does not exist or is not accessible by the current
|
||||||
|
/// user.
|
||||||
/// - The interface name is invalid or already in use.
|
/// - The interface name is invalid or already in use.
|
||||||
/// - The current user does not have enough permissions to create tun/tap devices (this requires root permissions).
|
/// - The current user does not have enough permissions to create tun/tap devices (this
|
||||||
|
/// requires root permissions).
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// This method panics if the interface name is longer than 31 bytes.
|
/// This method panics if the interface name is longer than 31 bytes.
|
||||||
pub fn new(ifname: &str, type_: Type, path: Option<&str>) -> io::Result<Self> {
|
pub fn new(ifname: &str, type_: Type, path: Option<&str>) -> io::Result<Self> {
|
||||||
let path = path.unwrap_or_else(|| Self::default_path(type_));
|
let path = path.unwrap_or_else(|| Self::default_path(type_));
|
||||||
if type_ == Type::Dummy {
|
if type_ == Type::Dummy {
|
||||||
return Self::dummy(ifname, path, type_)
|
return Self::dummy(ifname, path, type_);
|
||||||
}
|
}
|
||||||
let fd = fs::OpenOptions::new().read(true).write(true).open(path)?;
|
let fd = fs::OpenOptions::new().read(true).write(true).open(path)?;
|
||||||
// Add trailing \0 to interface name
|
// Add trailing \0 to interface name
|
||||||
|
@ -125,7 +126,7 @@ impl TunTapDevice {
|
||||||
ifname_c.pop();
|
ifname_c.pop();
|
||||||
}
|
}
|
||||||
Ok(Self{fd, ifname: String::from_utf8(ifname_c).unwrap(), type_})
|
Ok(Self{fd, ifname: String::from_utf8(ifname_c).unwrap(), type_})
|
||||||
}
|
},
|
||||||
_ => Err(IoError::last_os_error())
|
_ => Err(IoError::last_os_error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,15 +168,9 @@ impl TunTapDevice {
|
||||||
(start, read)
|
(start, read)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(target_os = "bitrig", target_os = "dragonfly",
|
||||||
target_os = "bitrig",
|
target_os = "freebsd", target_os = "ios", target_os = "macos",
|
||||||
target_os = "dragonfly",
|
target_os = "netbsd", target_os = "openbsd"))]
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "ios",
|
|
||||||
target_os = "macos",
|
|
||||||
target_os = "netbsd",
|
|
||||||
target_os = "openbsd"
|
|
||||||
))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn correct_data_after_read(&mut self, buffer: &mut [u8], start: usize, read: usize) -> (usize, usize) {
|
fn correct_data_after_read(&mut self, buffer: &mut [u8], start: usize, read: usize) -> (usize, usize) {
|
||||||
if self.type_ == Type::Tun {
|
if self.type_ == Type::Tun {
|
||||||
|
@ -193,22 +188,15 @@ impl TunTapDevice {
|
||||||
start
|
start
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(target_os = "bitrig", target_os = "dragonfly",
|
||||||
target_os = "bitrig",
|
target_os = "freebsd", target_os = "ios", target_os = "macos",
|
||||||
target_os = "dragonfly",
|
target_os = "netbsd", target_os = "openbsd"))]
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "ios",
|
|
||||||
target_os = "macos",
|
|
||||||
target_os = "netbsd",
|
|
||||||
target_os = "openbsd"
|
|
||||||
))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn correct_data_before_write(&mut self, buffer: &mut [u8], start: usize) -> usize {
|
fn correct_data_before_write(&mut self, buffer: &mut [u8], start: usize) -> usize {
|
||||||
if self.type_ == Type::Tun {
|
if self.type_ == Type::Tun {
|
||||||
// BSD-based systems add a 4-byte header containing the Ethertype for TUN
|
// BSD-based systems add a 4-byte header containing the Ethertype for TUN
|
||||||
assert!(start>=4);
|
assert!(start>=4);
|
||||||
match buffer[start] >> 4 {
|
match buffer[start] >> 4 { // IP version
|
||||||
// IP version
|
|
||||||
4 => buffer[start-4..start].copy_from_slice(&[0x00, 0x00, 0x08, 0x00]),
|
4 => buffer[start-4..start].copy_from_slice(&[0x00, 0x00, 0x08, 0x00]),
|
||||||
6 => buffer[start-4..start].copy_from_slice(&[0x00, 0x00, 0x86, 0xdd]),
|
6 => buffer[start-4..start].copy_from_slice(&[0x00, 0x00, 0x86, 0xdd]),
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
|
@ -242,6 +230,7 @@ impl Device for TunTapDevice {
|
||||||
Err(e) => Err(Error::TunTapDev("Write error", e))
|
Err(e) => Err(Error::TunTapDev("Write error", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRawFd for TunTapDevice {
|
impl AsRawFd for TunTapDevice {
|
||||||
|
|
|
@ -2,20 +2,17 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use std::{
|
use std::net::SocketAddr;
|
||||||
collections::{hash_map::Entry, HashMap},
|
use std::collections::HashMap;
|
||||||
hash::BuildHasherDefault,
|
use std::collections::hash_map::Entry;
|
||||||
io::{self, Write},
|
use std::hash::BuildHasherDefault;
|
||||||
marker::PhantomData,
|
use std::io::{self, Write};
|
||||||
net::SocketAddr
|
use std::marker::PhantomData;
|
||||||
};
|
|
||||||
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
|
|
||||||
use super::{
|
use super::types::{Error, Table, Protocol, Address};
|
||||||
types::{Address, Error, Protocol, Table},
|
use super::util::{TimeSource, Time, Duration};
|
||||||
util::{Duration, Time, TimeSource}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An ethernet frame dissector
|
/// An ethernet frame dissector
|
||||||
///
|
///
|
||||||
|
@ -32,7 +29,7 @@ impl Protocol for Frame {
|
||||||
/// This method will fail when the given data is not a valid ethernet frame.
|
/// This method will fail when the given data is not a valid ethernet frame.
|
||||||
fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
|
fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
|
||||||
if data.len() < 14 {
|
if data.len() < 14 {
|
||||||
return Err(Error::Parse("Frame is too short"))
|
return Err(Error::Parse("Frame is too short"));
|
||||||
}
|
}
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
let dst_data = &data[pos..pos+6];
|
let dst_data = &data[pos..pos+6];
|
||||||
|
@ -42,14 +39,12 @@ impl Protocol for Frame {
|
||||||
if data[pos] == 0x81 && data[pos+1] == 0x00 {
|
if data[pos] == 0x81 && data[pos+1] == 0x00 {
|
||||||
pos += 2;
|
pos += 2;
|
||||||
if data.len() < pos + 2 {
|
if data.len() < pos + 2 {
|
||||||
return Err(Error::Parse("Vlan frame is too short"))
|
return Err(Error::Parse("Vlan frame is too short"));
|
||||||
}
|
}
|
||||||
let mut src = [0; 16];
|
let mut src = [0; 16];
|
||||||
let mut dst = [0; 16];
|
let mut dst = [0; 16];
|
||||||
src[0] = data[pos];
|
src[0] = data[pos]; src[1] = data[pos+1];
|
||||||
src[1] = data[pos + 1];
|
dst[0] = data[pos]; dst[1] = data[pos+1];
|
||||||
dst[0] = data[pos];
|
|
||||||
dst[1] = data[pos + 1];
|
|
||||||
src[2..8].copy_from_slice(src_data);
|
src[2..8].copy_from_slice(src_data);
|
||||||
dst[2..8].copy_from_slice(dst_data);
|
dst[2..8].copy_from_slice(dst_data);
|
||||||
Ok((Address{data: src, len: 8}, Address{data: dst, len: 8}))
|
Ok((Address{data: src, len: 8}, Address{data: dst, len: 8}))
|
||||||
|
@ -125,7 +120,7 @@ impl<TS: TimeSource> Table for SwitchTable<TS> {
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
entry.insert(SwitchTableValue{address: addr, timeout: deadline});
|
entry.insert(SwitchTableValue{address: addr, timeout: deadline});
|
||||||
info!("Learned address {} => {}", key, addr);
|
info!("Learned address {} => {}", key, addr);
|
||||||
}
|
},
|
||||||
Entry::Occupied(mut entry) => {
|
Entry::Occupied(mut entry) => {
|
||||||
let mut entry = entry.get_mut();
|
let mut entry = entry.get_mut();
|
||||||
if entry.timeout + Time::from(self.protection_period) > deadline {
|
if entry.timeout + Time::from(self.protection_period) > deadline {
|
||||||
|
@ -168,9 +163,9 @@ impl<TS: TimeSource> Table for SwitchTable<TS> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)] use super::util::MockTimeSource;
|
|
||||||
#[cfg(test)] use std::net::ToSocketAddrs;
|
|
||||||
#[cfg(test)] use std::str::FromStr;
|
#[cfg(test)] use std::str::FromStr;
|
||||||
|
#[cfg(test)] use std::net::ToSocketAddrs;
|
||||||
|
#[cfg(test)] use super::util::MockTimeSource;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decode_frame_without_vlan() {
|
fn decode_frame_without_vlan() {
|
||||||
|
|
47
src/ip.rs
47
src/ip.rs
|
@ -2,16 +2,14 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use std::{
|
use std::net::SocketAddr;
|
||||||
collections::{hash_map, HashMap},
|
use std::collections::{hash_map, HashMap};
|
||||||
hash::BuildHasherDefault,
|
use std::hash::BuildHasherDefault;
|
||||||
io::{self, Write},
|
use std::io::{self, Write};
|
||||||
net::SocketAddr
|
|
||||||
};
|
|
||||||
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
|
|
||||||
use super::types::{Address, Error, Protocol, Table};
|
use super::types::{Protocol, Error, Table, Address};
|
||||||
|
|
||||||
|
|
||||||
/// An IP packet dissector
|
/// An IP packet dissector
|
||||||
|
@ -28,26 +26,26 @@ impl Protocol for Packet {
|
||||||
/// This method will fail when the given data is not a valid ipv4 and ipv6 packet.
|
/// This method will fail when the given data is not a valid ipv4 and ipv6 packet.
|
||||||
fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
|
fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
|
||||||
if data.is_empty() {
|
if data.is_empty() {
|
||||||
return Err(Error::Parse("Empty header"))
|
return Err(Error::Parse("Empty header"));
|
||||||
}
|
}
|
||||||
let version = data[0] >> 4;
|
let version = data[0] >> 4;
|
||||||
match version {
|
match version {
|
||||||
4 => {
|
4 => {
|
||||||
if data.len() < 20 {
|
if data.len() < 20 {
|
||||||
return Err(Error::Parse("Truncated IPv4 header"))
|
return Err(Error::Parse("Truncated IPv4 header"));
|
||||||
}
|
}
|
||||||
let src = Address::read_from_fixed(&data[12..], 4)?;
|
let src = Address::read_from_fixed(&data[12..], 4)?;
|
||||||
let dst = Address::read_from_fixed(&data[16..], 4)?;
|
let dst = Address::read_from_fixed(&data[16..], 4)?;
|
||||||
Ok((src, dst))
|
Ok((src, dst))
|
||||||
}
|
},
|
||||||
6 => {
|
6 => {
|
||||||
if data.len() < 40 {
|
if data.len() < 40 {
|
||||||
return Err(Error::Parse("Truncated IPv6 header"))
|
return Err(Error::Parse("Truncated IPv6 header"));
|
||||||
}
|
}
|
||||||
let src = Address::read_from_fixed(&data[8..], 16)?;
|
let src = Address::read_from_fixed(&data[8..], 16)?;
|
||||||
let dst = Address::read_from_fixed(&data[24..], 16)?;
|
let dst = Address::read_from_fixed(&data[24..], 16)?;
|
||||||
Ok((src, dst))
|
Ok((src, dst))
|
||||||
}
|
},
|
||||||
_ => Err(Error::Parse("Invalid version"))
|
_ => Err(Error::Parse("Invalid version"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,9 +95,7 @@ impl Table for RoutingTable {
|
||||||
// Add the entry to the routing table, creating a new list of the prefix group is empty.
|
// Add the entry to the routing table, creating a new list of the prefix group is empty.
|
||||||
match self.0.entry(group_bytes) {
|
match self.0.entry(group_bytes) {
|
||||||
hash_map::Entry::Occupied(mut entry) => entry.get_mut().push(routing_entry),
|
hash_map::Entry::Occupied(mut entry) => entry.get_mut().push(routing_entry),
|
||||||
hash_map::Entry::Vacant(entry) => {
|
hash_map::Entry::Vacant(entry) => { entry.insert(vec![routing_entry]); }
|
||||||
entry.insert(vec![routing_entry]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +126,7 @@ impl Table for RoutingTable {
|
||||||
match_len += 8;
|
match_len += 8;
|
||||||
} else {
|
} else {
|
||||||
match_len += b.leading_zeros();
|
match_len += b.leading_zeros();
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the full prefix matches and the match is longer than the longest prefix
|
// If the full prefix matches and the match is longer than the longest prefix
|
||||||
|
@ -178,8 +174,8 @@ impl Table for RoutingTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)] use std::net::ToSocketAddrs;
|
|
||||||
#[cfg(test)] use std::str::FromStr;
|
#[cfg(test)] use std::str::FromStr;
|
||||||
|
#[cfg(test)] use std::net::ToSocketAddrs;
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -192,10 +188,7 @@ fn decode_ipv4_packet() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decode_ipv6_packet() {
|
fn decode_ipv6_packet() {
|
||||||
let data = [
|
let data = [0x60,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,0,9,8,7,6,5,4,3,2,1,6,5,4,3,2,1];
|
||||||
0x60, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 6, 5,
|
|
||||||
4, 3, 2, 1
|
|
||||||
];
|
|
||||||
let (src, dst) = Packet::parse(&data).unwrap();
|
let (src, dst) = Packet::parse(&data).unwrap();
|
||||||
assert_eq!(src, Address{data: [1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6], len: 16});
|
assert_eq!(src, Address{data: [1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6], len: 16});
|
||||||
assert_eq!(dst, Address{data: [0,9,8,7,6,5,4,3,2,1,6,5,4,3,2,1], len: 16});
|
assert_eq!(dst, Address{data: [0,9,8,7,6,5,4,3,2,1,6,5,4,3,2,1], len: 16});
|
||||||
|
@ -204,11 +197,7 @@ fn decode_ipv6_packet() {
|
||||||
#[test]
|
#[test]
|
||||||
fn decode_invalid_packet() {
|
fn decode_invalid_packet() {
|
||||||
assert!(Packet::parse(&[0x40,0,0,0,0,0,0,0,0,0,0,0,192,168,1,1,192,168,1,2]).is_ok());
|
assert!(Packet::parse(&[0x40,0,0,0,0,0,0,0,0,0,0,0,192,168,1,1,192,168,1,2]).is_ok());
|
||||||
assert!(Packet::parse(&[
|
assert!(Packet::parse(&[0x60,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,0,9,8,7,6,5,4,3,2,1,6,5,4,3,2,1]).is_ok());
|
||||||
0x60, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 6, 5,
|
|
||||||
4, 3, 2, 1
|
|
||||||
])
|
|
||||||
.is_ok());
|
|
||||||
// no data
|
// no data
|
||||||
assert!(Packet::parse(&[]).is_err());
|
assert!(Packet::parse(&[]).is_err());
|
||||||
// wrong version
|
// wrong version
|
||||||
|
@ -216,11 +205,7 @@ fn decode_invalid_packet() {
|
||||||
// truncated ipv4
|
// truncated ipv4
|
||||||
assert!(Packet::parse(&[0x40,0,0,0,0,0,0,0,0,0,0,0,192,168,1,1,192,168,1]).is_err());
|
assert!(Packet::parse(&[0x40,0,0,0,0,0,0,0,0,0,0,0,192,168,1,1,192,168,1]).is_err());
|
||||||
// truncated ipv6
|
// truncated ipv6
|
||||||
assert!(Packet::parse(&[
|
assert!(Packet::parse(&[0x60,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,0,9,8,7,6,5,4,3,2,1,6,5,4,3,2]).is_err());
|
||||||
0x60, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 6, 5,
|
|
||||||
4, 3, 2
|
|
||||||
])
|
|
||||||
.is_err());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
134
src/main.rs
134
src/main.rs
|
@ -10,49 +10,42 @@
|
||||||
#[cfg(test)] extern crate tempfile;
|
#[cfg(test)] extern crate tempfile;
|
||||||
#[cfg(feature = "bench")] extern crate test;
|
#[cfg(feature = "bench")] extern crate test;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use] pub mod util;
|
||||||
pub mod util;
|
#[cfg(test)] #[macro_use] mod tests;
|
||||||
#[cfg(test)]
|
pub mod types;
|
||||||
#[macro_use]
|
|
||||||
mod tests;
|
|
||||||
pub mod beacon;
|
|
||||||
#[cfg(feature = "bench")] mod benches;
|
|
||||||
pub mod cloud;
|
|
||||||
pub mod config;
|
|
||||||
pub mod crypto;
|
pub mod crypto;
|
||||||
pub mod device;
|
pub mod udpmessage;
|
||||||
pub mod ethernet;
|
pub mod ethernet;
|
||||||
pub mod ip;
|
pub mod ip;
|
||||||
pub mod net;
|
pub mod cloud;
|
||||||
|
pub mod device;
|
||||||
pub mod poll;
|
pub mod poll;
|
||||||
|
pub mod config;
|
||||||
pub mod port_forwarding;
|
pub mod port_forwarding;
|
||||||
pub mod traffic;
|
pub mod traffic;
|
||||||
pub mod types;
|
pub mod beacon;
|
||||||
pub mod udpmessage;
|
pub mod net;
|
||||||
|
#[cfg(feature = "bench")] mod benches;
|
||||||
|
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
|
|
||||||
use std::{
|
use std::sync::Mutex;
|
||||||
fs::File,
|
use std::str::FromStr;
|
||||||
io::{self, Write},
|
use std::process::Command;
|
||||||
net::UdpSocket,
|
use std::fs::File;
|
||||||
path::Path,
|
use std::path::Path;
|
||||||
process::Command,
|
use std::io::{self, Write};
|
||||||
str::FromStr,
|
use std::net::UdpSocket;
|
||||||
sync::Mutex
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::device::{TunTapDevice, Device, Type};
|
||||||
cloud::GenericCloud,
|
use crate::ethernet::SwitchTable;
|
||||||
config::Config,
|
use crate::ip::RoutingTable;
|
||||||
crypto::{Crypto, CryptoMethod},
|
use crate::types::{Mode, Range, Protocol, HeaderMagic, Error};
|
||||||
device::{Device, TunTapDevice, Type},
|
use crate::cloud::GenericCloud;
|
||||||
ethernet::SwitchTable,
|
use crate::crypto::{Crypto, CryptoMethod};
|
||||||
ip::RoutingTable,
|
use crate::port_forwarding::PortForwarding;
|
||||||
port_forwarding::PortForwarding,
|
use crate::util::{Duration, SystemTimeSource};
|
||||||
types::{Error, HeaderMagic, Mode, Protocol, Range},
|
use crate::config::Config;
|
||||||
util::{Duration, SystemTimeSource}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const VERSION: u8 = 1;
|
const VERSION: u8 = 1;
|
||||||
|
@ -123,8 +116,7 @@ impl log::Log for DualLogger {
|
||||||
let mut file = self.file.lock().expect("Lock poisoned");
|
let mut file = self.file.lock().expect("Lock poisoned");
|
||||||
if let Some(ref mut file) = *file {
|
if let Some(ref mut file) = *file {
|
||||||
let time = time::strftime("%F %T", &time::now()).expect("Failed to format timestamp");
|
let time = time::strftime("%F %T", &time::now()).expect("Failed to format timestamp");
|
||||||
writeln!(file, "{} - {} - {}", time, record.level(), record.args())
|
writeln!(file, "{} - {} - {}", time, record.level(), record.args()).expect("Failed to write to logfile");
|
||||||
.expect("Failed to write to logfile");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,11 +135,9 @@ fn run_script(script: &str, ifname: &str) {
|
||||||
cmd.arg("-c").arg(&script).env("IFNAME", ifname);
|
cmd.arg("-c").arg(&script).env("IFNAME", ifname);
|
||||||
debug!("Running script: {:?}", cmd);
|
debug!("Running script: {:?}", cmd);
|
||||||
match cmd.status() {
|
match cmd.status() {
|
||||||
Ok(status) => {
|
Ok(status) => if !status.success() {
|
||||||
if !status.success() {
|
|
||||||
error!("Script returned with error: {:?}", status.code())
|
error!("Script returned with error: {:?}", status.code())
|
||||||
}
|
},
|
||||||
}
|
|
||||||
Err(e) => error!("Failed to execute script {:?}: {}", script, e)
|
Err(e) => error!("Failed to execute script {:?}: {}", script, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,36 +154,17 @@ enum AnyCloud<P: Protocol> {
|
||||||
|
|
||||||
impl<P: Protocol> AnyCloud<P> {
|
impl<P: Protocol> AnyCloud<P> {
|
||||||
#[allow(unknown_lints, clippy::too_many_arguments)]
|
#[allow(unknown_lints, clippy::too_many_arguments)]
|
||||||
fn new(
|
fn new(config: &Config, device: TunTapDevice, table: AnyTable,
|
||||||
config: &Config, device: TunTapDevice, table: AnyTable, learning: bool, broadcast: bool, addresses: Vec<Range>,
|
learning: bool, broadcast: bool, addresses: Vec<Range>,
|
||||||
crypto: Crypto, port_forwarding: Option<PortForwarding>
|
crypto: Crypto, port_forwarding: Option<PortForwarding>) -> Self {
|
||||||
) -> Self
|
|
||||||
{
|
|
||||||
match table {
|
match table {
|
||||||
AnyTable::Switch(t) => {
|
AnyTable::Switch(t) => AnyCloud::Switch(GenericCloud::<TunTapDevice, P, SwitchTable<SystemTimeSource>, UdpSocket, SystemTimeSource>::new(
|
||||||
AnyCloud::Switch(GenericCloud::<
|
config, device,t, learning, broadcast, addresses, crypto, port_forwarding
|
||||||
TunTapDevice,
|
)),
|
||||||
P,
|
AnyTable::Routing(t) => AnyCloud::Routing(GenericCloud::<TunTapDevice, P, RoutingTable, UdpSocket, SystemTimeSource>::new(
|
||||||
SwitchTable<SystemTimeSource>,
|
|
||||||
UdpSocket,
|
|
||||||
SystemTimeSource
|
|
||||||
>::new(
|
|
||||||
config, device,t, learning, broadcast, addresses, crypto, port_forwarding
|
config, device,t, learning, broadcast, addresses, crypto, port_forwarding
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
AnyTable::Routing(t) => {
|
|
||||||
AnyCloud::Routing(GenericCloud::<TunTapDevice, P, RoutingTable, UdpSocket, SystemTimeSource>::new(
|
|
||||||
config,
|
|
||||||
device,
|
|
||||||
t,
|
|
||||||
learning,
|
|
||||||
broadcast,
|
|
||||||
addresses,
|
|
||||||
crypto,
|
|
||||||
port_forwarding
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ifname(&self) -> &str {
|
fn ifname(&self) -> &str {
|
||||||
|
@ -227,12 +198,8 @@ impl<P: Protocol> AnyCloud<P> {
|
||||||
|
|
||||||
|
|
||||||
fn run<P: Protocol> (config: Config) {
|
fn run<P: Protocol> (config: Config) {
|
||||||
let device = try_fail!(
|
let device = try_fail!(TunTapDevice::new(&config.device_name, config.device_type, config.device_path.as_ref().map(|s| s as &str)),
|
||||||
TunTapDevice::new(&config.device_name, config.device_type, config.device_path.as_ref().map(|s| s as &str)),
|
"Failed to open virtual {} interface {}: {}", config.device_type, config.device_name);
|
||||||
"Failed to open virtual {} interface {}: {}",
|
|
||||||
config.device_type,
|
|
||||||
config.device_name
|
|
||||||
);
|
|
||||||
info!("Opened device {}", device.ifname());
|
info!("Opened device {}", device.ifname());
|
||||||
let mut ranges = Vec::with_capacity(config.subnets.len());
|
let mut ranges = Vec::with_capacity(config.subnets.len());
|
||||||
for s in &config.subnets {
|
for s in &config.subnets {
|
||||||
|
@ -240,13 +207,11 @@ fn run<P: Protocol>(config: Config) {
|
||||||
}
|
}
|
||||||
let dst_timeout = config.dst_timeout;
|
let dst_timeout = config.dst_timeout;
|
||||||
let (learning, broadcasting, table) = match config.mode {
|
let (learning, broadcasting, table) = match config.mode {
|
||||||
Mode::Normal => {
|
Mode::Normal => match config.device_type {
|
||||||
match config.device_type {
|
|
||||||
Type::Tap => (true, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))),
|
Type::Tap => (true, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))),
|
||||||
Type::Tun => (false, false, AnyTable::Routing(RoutingTable::new())),
|
Type::Tun => (false, false, AnyTable::Routing(RoutingTable::new())),
|
||||||
Type::Dummy => (false, false, AnyTable::Switch(SwitchTable::new(dst_timeout, 10)))
|
Type::Dummy => (false, false, AnyTable::Switch(SwitchTable::new(dst_timeout, 10)))
|
||||||
}
|
},
|
||||||
}
|
|
||||||
Mode::Router => (false, false, AnyTable::Routing(RoutingTable::new())),
|
Mode::Router => (false, false, AnyTable::Routing(RoutingTable::new())),
|
||||||
Mode::Switch => (true, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))),
|
Mode::Switch => (true, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))),
|
||||||
Mode::Hub => (false, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10)))
|
Mode::Hub => (false, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10)))
|
||||||
|
@ -255,7 +220,11 @@ fn run<P: Protocol>(config: Config) {
|
||||||
Some(ref key) => Crypto::from_shared_key(config.crypto, key),
|
Some(ref key) => Crypto::from_shared_key(config.crypto, key),
|
||||||
None => Crypto::None
|
None => Crypto::None
|
||||||
};
|
};
|
||||||
let port_forwarding = if config.port_forwarding { PortForwarding::new(config.port) } else { None };
|
let port_forwarding = if config.port_forwarding {
|
||||||
|
PortForwarding::new(config.port)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let mut cloud = AnyCloud::<P>::new(&config, device, table, learning,broadcasting,ranges, crypto, port_forwarding);
|
let mut cloud = AnyCloud::<P>::new(&config, device, table, learning,broadcasting,ranges, crypto, port_forwarding);
|
||||||
if let Some(script) = config.ifup {
|
if let Some(script) = config.ifup {
|
||||||
run_script(&script, cloud.ifname());
|
run_script(&script, cloud.ifname());
|
||||||
|
@ -287,19 +256,24 @@ fn run<P: Protocol>(config: Config) {
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());
|
let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());
|
||||||
if args.flag_version {
|
if args.flag_version {
|
||||||
println!("VpnCloud v{}, protocol version {}", env!("CARGO_PKG_VERSION"), VERSION);
|
println!("VpnCloud v{}, protocol version {}",
|
||||||
return
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
VERSION
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
let logger = try_fail!(DualLogger::new(args.flag_log_file.as_ref()), "Failed to open logfile: {}");
|
let logger = try_fail!(DualLogger::new(args.flag_log_file.as_ref()), "Failed to open logfile: {}");
|
||||||
log::set_boxed_logger(Box::new(logger)).unwrap();
|
log::set_boxed_logger(Box::new(logger)).unwrap();
|
||||||
assert!(!args.flag_verbose || !args.flag_quiet);
|
assert!(!args.flag_verbose || !args.flag_quiet);
|
||||||
log::set_max_level(if args.flag_verbose {
|
log::set_max_level(
|
||||||
|
if args.flag_verbose {
|
||||||
log::LevelFilter::Debug
|
log::LevelFilter::Debug
|
||||||
} else if args.flag_quiet {
|
} else if args.flag_quiet {
|
||||||
log::LevelFilter::Error
|
log::LevelFilter::Error
|
||||||
} else {
|
} else {
|
||||||
log::LevelFilter::Info
|
log::LevelFilter::Info
|
||||||
});
|
}
|
||||||
|
);
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
if let Some(ref file) = args.flag_config {
|
if let Some(ref file) = args.flag_config {
|
||||||
info!("Reading config file '{}'", file);
|
info!("Reading config file '{}'", file);
|
||||||
|
|
27
src/net.rs
27
src/net.rs
|
@ -1,9 +1,7 @@
|
||||||
use std::{
|
use std::os::unix::io::{RawFd, AsRawFd};
|
||||||
collections::VecDeque,
|
use std::net::{UdpSocket, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||||
io::{self, ErrorKind},
|
use std::io::{self, ErrorKind};
|
||||||
net::{SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket},
|
use std::collections::VecDeque;
|
||||||
os::unix::io::{AsRawFd, RawFd}
|
|
||||||
};
|
|
||||||
|
|
||||||
use net2::UdpBuilder;
|
use net2::UdpBuilder;
|
||||||
|
|
||||||
|
@ -18,20 +16,13 @@ pub trait Socket: AsRawFd + Sized {
|
||||||
|
|
||||||
impl Socket for UdpSocket {
|
impl Socket for UdpSocket {
|
||||||
fn listen_v4(host: &str, port: u16) -> Result<Self, io::Error> {
|
fn listen_v4(host: &str, port: u16) -> Result<Self, io::Error> {
|
||||||
UdpBuilder::new_v4()
|
UdpBuilder::new_v4().expect("Failed to obtain ipv4 socket builder")
|
||||||
.expect("Failed to obtain ipv4 socket builder")
|
.reuse_address(true).expect("Failed to set so_reuseaddr").bind((host, port))
|
||||||
.reuse_address(true)
|
|
||||||
.expect("Failed to set so_reuseaddr")
|
|
||||||
.bind((host, port))
|
|
||||||
}
|
}
|
||||||
fn listen_v6(host: &str, port: u16) -> Result<Self, io::Error> {
|
fn listen_v6(host: &str, port: u16) -> Result<Self, io::Error> {
|
||||||
UdpBuilder::new_v6()
|
UdpBuilder::new_v6().expect("Failed to obtain ipv4 socket builder")
|
||||||
.expect("Failed to obtain ipv4 socket builder")
|
.only_v6(true).expect("Failed to set only_v6")
|
||||||
.only_v6(true)
|
.reuse_address(true).expect("Failed to set so_reuseaddr").bind((host, port))
|
||||||
.expect("Failed to set only_v6")
|
|
||||||
.reuse_address(true)
|
|
||||||
.expect("Failed to set so_reuseaddr")
|
|
||||||
.bind((host, port))
|
|
||||||
}
|
}
|
||||||
fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error> {
|
fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error> {
|
||||||
self.recv_from(buffer)
|
self.recv_from(buffer)
|
||||||
|
|
|
@ -4,11 +4,13 @@
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::io;
|
||||||
use crate::device::Device;
|
use crate::device::Device;
|
||||||
use std::{io, os::unix::io::RawFd};
|
|
||||||
|
|
||||||
use super::WaitResult;
|
use super::WaitResult;
|
||||||
use crate::{device::Type, net::Socket};
|
use crate::device::Type;
|
||||||
|
use crate::net::Socket;
|
||||||
|
|
||||||
pub struct EpollWait {
|
pub struct EpollWait {
|
||||||
poll_fd: RawFd,
|
poll_fd: RawFd,
|
||||||
|
@ -16,25 +18,23 @@ pub struct EpollWait {
|
||||||
socketv4: RawFd,
|
socketv4: RawFd,
|
||||||
socketv6: RawFd,
|
socketv6: RawFd,
|
||||||
device: RawFd,
|
device: RawFd,
|
||||||
timeout: u32
|
timeout: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EpollWait {
|
impl EpollWait {
|
||||||
pub fn new<S: Socket>(socketv4: &S, socketv6: &S, device: &dyn Device, timeout: u32) -> io::Result<Self> {
|
pub fn new<S: Socket>(socketv4: &S, socketv6: &S, device: &Device, timeout: u32) -> io::Result<Self> {
|
||||||
Self::create(socketv4, socketv6, device, timeout, libc::EPOLLIN as u32)
|
Self::create(socketv4, socketv6, device, timeout, libc::EPOLLIN as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn testing<S: Socket>(socketv4: &S, socketv6: &S, device: &dyn Device, timeout: u32) -> io::Result<Self> {
|
pub fn testing<S: Socket>(socketv4: &S, socketv6: &S, device: &Device, timeout: u32) -> io::Result<Self> {
|
||||||
Self::create(socketv4, socketv6, device, timeout, ( libc::EPOLLIN | libc::EPOLLOUT ) as u32)
|
Self::create(socketv4, socketv6, device, timeout, ( libc::EPOLLIN | libc::EPOLLOUT ) as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create<S: Socket>(
|
fn create<S: Socket>(socketv4: &S, socketv6: &S, device: &Device, timeout: u32, flags: u32) -> io::Result<Self> {
|
||||||
socketv4: &S, socketv6: &S, device: &dyn Device, timeout: u32, flags: u32
|
|
||||||
) -> io::Result<Self> {
|
|
||||||
let mut event = libc::epoll_event{u64: 0, events: 0};
|
let mut event = libc::epoll_event{u64: 0, events: 0};
|
||||||
let poll_fd = unsafe { libc::epoll_create(3) };
|
let poll_fd = unsafe { libc::epoll_create(3) };
|
||||||
if poll_fd == -1 {
|
if poll_fd == -1 {
|
||||||
return Err(io::Error::last_os_error())
|
return Err(io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
let raw_fds = if device.get_type() != Type::Dummy {
|
let raw_fds = if device.get_type() != Type::Dummy {
|
||||||
vec![socketv4.as_raw_fd(), socketv6.as_raw_fd(), device.as_raw_fd()]
|
vec![socketv4.as_raw_fd(), socketv6.as_raw_fd(), device.as_raw_fd()]
|
||||||
|
@ -46,7 +46,7 @@ impl EpollWait {
|
||||||
event.events = flags;
|
event.events = flags;
|
||||||
let res = unsafe { libc::epoll_ctl(poll_fd, libc::EPOLL_CTL_ADD, fd, &mut event) };
|
let res = unsafe { libc::epoll_ctl(poll_fd, libc::EPOLL_CTL_ADD, fd, &mut event) };
|
||||||
if res == -1 {
|
if res == -1 {
|
||||||
return Err(io::Error::last_os_error())
|
return Err(io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -73,8 +73,7 @@ impl Iterator for EpollWait {
|
||||||
Some(match unsafe { libc::epoll_wait(self.poll_fd, &mut self.event, 1, self.timeout as i32) } {
|
Some(match unsafe { libc::epoll_wait(self.poll_fd, &mut self.event, 1, self.timeout as i32) } {
|
||||||
-1 => WaitResult::Error(io::Error::last_os_error()),
|
-1 => WaitResult::Error(io::Error::last_os_error()),
|
||||||
0 => WaitResult::Timeout,
|
0 => WaitResult::Timeout,
|
||||||
1 => {
|
1 => if self.event.u64 == self.socketv4 as u64 {
|
||||||
if self.event.u64 == self.socketv4 as u64 {
|
|
||||||
WaitResult::SocketV4
|
WaitResult::SocketV4
|
||||||
} else if self.event.u64 == self.socketv6 as u64 {
|
} else if self.event.u64 == self.socketv6 as u64 {
|
||||||
WaitResult::SocketV6
|
WaitResult::SocketV6
|
||||||
|
@ -82,9 +81,9 @@ impl Iterator for EpollWait {
|
||||||
WaitResult::Device
|
WaitResult::Device
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
},
|
||||||
}
|
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use std::{
|
use std::net::{SocketAddrV4, UdpSocket, SocketAddr};
|
||||||
io,
|
use std::io;
|
||||||
net::{SocketAddr, SocketAddrV4, UdpSocket}
|
|
||||||
};
|
|
||||||
|
|
||||||
use igd::*;
|
use igd::*;
|
||||||
|
|
||||||
|
@ -19,18 +17,17 @@ pub struct PortForwarding {
|
||||||
pub internal_addr: SocketAddrV4,
|
pub internal_addr: SocketAddrV4,
|
||||||
pub external_addr: SocketAddrV4,
|
pub external_addr: SocketAddrV4,
|
||||||
pub gateway: Gateway,
|
pub gateway: Gateway,
|
||||||
pub next_extension: Option<Time>
|
pub next_extension: Option<Time>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PortForwarding {
|
impl PortForwarding {
|
||||||
pub fn new(port: u16) -> Option<Self> {
|
pub fn new(port: u16) -> Option<Self> {
|
||||||
// Get the gateway
|
// Get the gateway
|
||||||
let gateway = match search_gateway(Default::default()) {
|
let gateway = match search_gateway() {
|
||||||
Ok(gateway) => gateway,
|
Ok(gateway) => gateway,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let SearchError::IoError(ref err) = err {
|
if let SearchError::IoError(ref err) = err {
|
||||||
if err.kind() == io::ErrorKind::WouldBlock {
|
if err.kind() == io::ErrorKind::WouldBlock { // Why this code?
|
||||||
// Why this code?
|
|
||||||
warn!("Port-forwarding: no router found");
|
warn!("Port-forwarding: no router found");
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
@ -63,49 +60,46 @@ impl PortForwarding {
|
||||||
// - If the port is used, request any port
|
// - If the port is used, request any port
|
||||||
// - If timeout is denied, try permanent forwarding
|
// - If timeout is denied, try permanent forwarding
|
||||||
info!("Port-forwarding: external IP is {}", external_ip);
|
info!("Port-forwarding: external IP is {}", external_ip);
|
||||||
let (external_addr, timeout) = match gateway.add_port(
|
let (external_addr, timeout) = match gateway.add_port(PortMappingProtocol::UDP, internal_addr.port(), internal_addr, LEASE_TIME, DESCRIPTION) {
|
||||||
PortMappingProtocol::UDP,
|
|
||||||
internal_addr.port(),
|
|
||||||
internal_addr,
|
|
||||||
LEASE_TIME,
|
|
||||||
DESCRIPTION
|
|
||||||
) {
|
|
||||||
Ok(()) => (SocketAddrV4::new(external_ip, internal_addr.port()), LEASE_TIME),
|
Ok(()) => (SocketAddrV4::new(external_ip, internal_addr.port()), LEASE_TIME),
|
||||||
Err(AddPortError::PortInUse) => {
|
Err(AddPortError::PortInUse) => match gateway.add_any_port(PortMappingProtocol::UDP, internal_addr, LEASE_TIME, DESCRIPTION) {
|
||||||
match gateway.add_any_port(PortMappingProtocol::UDP, internal_addr, LEASE_TIME, DESCRIPTION) {
|
|
||||||
Ok(port) => (SocketAddrV4::new(external_ip, port), LEASE_TIME),
|
Ok(port) => (SocketAddrV4::new(external_ip, port), LEASE_TIME),
|
||||||
Err(AddAnyPortError::OnlyPermanentLeasesSupported) => {
|
Err(AddAnyPortError::OnlyPermanentLeasesSupported) => match gateway.add_any_port(PortMappingProtocol::UDP, internal_addr, 0, DESCRIPTION) {
|
||||||
match gateway.add_any_port(PortMappingProtocol::UDP, internal_addr, 0, DESCRIPTION) {
|
|
||||||
Ok(port) => (SocketAddrV4::new(external_ip, port), 0),
|
Ok(port) => (SocketAddrV4::new(external_ip, port), 0),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Port-forwarding: failed to activate port forwarding: {}", err);
|
error!("Port-forwarding: failed to activate port forwarding: {}", err);
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Port-forwarding: failed to activate port forwarding: {}", err);
|
error!("Port-forwarding: failed to activate port forwarding: {}", err);
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
Err(AddPortError::OnlyPermanentLeasesSupported) => match gateway.add_port(PortMappingProtocol::UDP, internal_addr.port(), internal_addr, 0, DESCRIPTION) {
|
||||||
Err(AddPortError::OnlyPermanentLeasesSupported) => {
|
|
||||||
match gateway.add_port(PortMappingProtocol::UDP, internal_addr.port(), internal_addr, 0, DESCRIPTION) {
|
|
||||||
Ok(()) => (SocketAddrV4::new(external_ip, internal_addr.port()), 0),
|
Ok(()) => (SocketAddrV4::new(external_ip, internal_addr.port()), 0),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Port-forwarding: failed to activate port forwarding: {}", err);
|
error!("Port-forwarding: failed to activate port forwarding: {}", err);
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Port-forwarding: failed to activate port forwarding: {}", err);
|
error!("Port-forwarding: failed to activate port forwarding: {}", err);
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
info!("Port-forwarding: sucessfully activated port forward on {}, timeout: {}", external_addr, timeout);
|
info!("Port-forwarding: sucessfully activated port forward on {}, timeout: {}", external_addr, timeout);
|
||||||
let next_extension = if timeout > 0 { Some(SystemTimeSource::now() + Time::from(timeout) - 60) } else { None };
|
let next_extension = if timeout > 0 {
|
||||||
Some(PortForwarding { internal_addr, external_addr, gateway, next_extension })
|
Some(SystemTimeSource::now() + Time::from(timeout) - 60)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Some(PortForwarding {
|
||||||
|
internal_addr,
|
||||||
|
external_addr,
|
||||||
|
gateway,
|
||||||
|
next_extension
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_extend(&mut self) {
|
pub fn check_extend(&mut self) {
|
||||||
|
@ -116,13 +110,7 @@ impl PortForwarding {
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
match self.gateway.add_port(
|
match self.gateway.add_port(PortMappingProtocol::UDP, self.external_addr.port(), self.internal_addr, LEASE_TIME, DESCRIPTION) {
|
||||||
PortMappingProtocol::UDP,
|
|
||||||
self.external_addr.port(),
|
|
||||||
self.internal_addr,
|
|
||||||
LEASE_TIME,
|
|
||||||
DESCRIPTION
|
|
||||||
) {
|
|
||||||
Ok(()) => debug!("Port-forwarding: extended port forwarding"),
|
Ok(()) => debug!("Port-forwarding: extended port forwarding"),
|
||||||
Err(err) => error!("Port-forwarding: failed to extend port forwarding: {}", err)
|
Err(err) => error!("Port-forwarding: failed to extend port forwarding: {}", err)
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,26 +2,23 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use] mod helper;
|
||||||
mod helper;
|
|
||||||
mod payload;
|
|
||||||
mod peers;
|
mod peers;
|
||||||
|
mod payload;
|
||||||
|
|
||||||
pub use std::net::SocketAddr;
|
pub use std::net::SocketAddr;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{Ordering, AtomicUsize};
|
||||||
|
|
||||||
pub use super::{
|
pub use super::ethernet::{self, SwitchTable};
|
||||||
cloud::GenericCloud,
|
pub use super::util::MockTimeSource;
|
||||||
config::Config,
|
pub use super::net::MockSocket;
|
||||||
crypto::Crypto,
|
pub use super::device::MockDevice;
|
||||||
device::MockDevice,
|
pub use super::udpmessage::Message;
|
||||||
ethernet::{self, SwitchTable},
|
pub use super::config::Config;
|
||||||
ip::{self, RoutingTable},
|
pub use super::crypto::Crypto;
|
||||||
net::MockSocket,
|
pub use super::cloud::GenericCloud;
|
||||||
types::{Protocol, Range, Table},
|
pub use super::types::{Protocol, Table, Range};
|
||||||
udpmessage::Message,
|
pub use super::ip::{self, RoutingTable};
|
||||||
util::MockTimeSource
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
type TestNode<P, T> = GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource>;
|
type TestNode<P, T> = GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource>;
|
||||||
|
@ -41,7 +38,12 @@ fn create_tap_node() -> TapTestNode {
|
||||||
|
|
||||||
fn create_tap_node_with_config(mut config: Config) -> TapTestNode {
|
fn create_tap_node_with_config(mut config: Config) -> TapTestNode {
|
||||||
config.port = NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16;
|
config.port = NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16;
|
||||||
TestNode::new(&config, MockDevice::new(), SwitchTable::new(1800, 10), true, true, vec![], Crypto::None, None)
|
TestNode::new(
|
||||||
|
&config,
|
||||||
|
MockDevice::new(),
|
||||||
|
SwitchTable::new(1800, 10),
|
||||||
|
true, true, vec![], Crypto::None, None
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -50,11 +52,7 @@ fn create_tun_node(addresses: Vec<Range>) -> TunTestNode {
|
||||||
&Config { port: NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16, ..Config::default() },
|
&Config { port: NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16, ..Config::default() },
|
||||||
MockDevice::new(),
|
MockDevice::new(),
|
||||||
RoutingTable::new(),
|
RoutingTable::new(),
|
||||||
false,
|
false, false, addresses, Crypto::None, None
|
||||||
false,
|
|
||||||
addresses,
|
|
||||||
Crypto::None,
|
|
||||||
None
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,11 +97,9 @@ fn cross_connect() {
|
||||||
fn connect_via_beacons() {
|
fn connect_via_beacons() {
|
||||||
MockTimeSource::set_time(0);
|
MockTimeSource::set_time(0);
|
||||||
let beacon_path = "target/.vpncloud_test";
|
let beacon_path = "target/.vpncloud_test";
|
||||||
let mut node1 =
|
let mut node1 = create_tap_node_with_config(Config { beacon_store: Some(beacon_path.to_string()), ..Config::default()});
|
||||||
create_tap_node_with_config(Config { beacon_store: Some(beacon_path.to_string()), ..Config::default() });
|
|
||||||
let node1_addr = node1.address().unwrap().0;
|
let node1_addr = node1.address().unwrap().0;
|
||||||
let mut node2 =
|
let mut node2 = create_tap_node_with_config(Config { beacon_load: Some(beacon_path.to_string()), ..Config::default()});
|
||||||
create_tap_node_with_config(Config { beacon_load: Some(beacon_path.to_string()), ..Config::default() });
|
|
||||||
let node2_addr = addr!("2.2.2.2:2222");
|
let node2_addr = addr!("2.2.2.2:2222");
|
||||||
|
|
||||||
assert!(!node1.peers().contains_node(&node2.node_id()));
|
assert!(!node1.peers().contains_node(&node2.node_id()));
|
||||||
|
@ -166,14 +164,14 @@ fn lost_init1() {
|
||||||
simulate!(node1 => node1_addr, node2 => node2_addr);
|
simulate!(node1 => node1_addr, node2 => node2_addr);
|
||||||
|
|
||||||
assert_connected!(node1, node2);
|
assert_connected!(node1, node2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wrong_magic() {
|
fn wrong_magic() {
|
||||||
let mut node1 = create_tap_node();
|
let mut node1 = create_tap_node();
|
||||||
let node1_addr = addr!("1.2.3.4:5678");
|
let node1_addr = addr!("1.2.3.4:5678");
|
||||||
let mut node2 =
|
let mut node2 = create_tap_node_with_config(Config { magic: Some("hash:different".to_string()), ..Config::default()});
|
||||||
create_tap_node_with_config(Config { magic: Some("hash:different".to_string()), ..Config::default() });
|
|
||||||
let node2_addr = addr!("2.3.4.5:6789");
|
let node2_addr = addr!("2.3.4.5:6789");
|
||||||
node1.connect("2.3.4.5:6789").unwrap();
|
node1.connect("2.3.4.5:6789").unwrap();
|
||||||
|
|
||||||
|
@ -204,3 +202,4 @@ fn remove_dead_peers() {
|
||||||
fn update_primary_address() {
|
fn update_primary_address() {
|
||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
// Copyright (C) 2018-2019 Dennis Schwerdel
|
// Copyright (C) 2018-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use std::{
|
use std::net::SocketAddr;
|
||||||
collections::HashMap,
|
use std::collections::HashMap;
|
||||||
io::{self, Write},
|
use std::io::{self, Write};
|
||||||
net::SocketAddr
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{cloud::Hash, types::Address, util::Bytes};
|
use super::types::Address;
|
||||||
|
use super::cloud::Hash;
|
||||||
|
use super::util::Bytes;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -116,14 +116,7 @@ impl TrafficStats {
|
||||||
let mut payload: Vec<_> = self.get_payload_traffic().collect();
|
let mut payload: Vec<_> = self.get_payload_traffic().collect();
|
||||||
payload.sort_unstable_by_key(|(_, data)| (data.out_bytes + data.in_bytes));
|
payload.sort_unstable_by_key(|(_, data)| (data.out_bytes + data.in_bytes));
|
||||||
for ((remote, local), data) in payload.iter().rev() {
|
for ((remote, local), data) in payload.iter().rev() {
|
||||||
writeln!(
|
writeln!(out, " - {} <-> {}: in={}/s, out={}/s", remote, local, Bytes(data.in_bytes/60), Bytes(data.out_bytes/60))?;
|
||||||
out,
|
|
||||||
" - {} <-> {}: in={}/s, out={}/s",
|
|
||||||
remote,
|
|
||||||
local,
|
|
||||||
Bytes(data.in_bytes / 60),
|
|
||||||
Bytes(data.out_bytes / 60)
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
64
src/types.rs
64
src/types.rs
|
@ -2,13 +2,11 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use std::{
|
use std::net::{SocketAddr, Ipv4Addr, Ipv6Addr};
|
||||||
fmt,
|
use std::fmt;
|
||||||
hash::{Hash, Hasher},
|
use std::str::FromStr;
|
||||||
io::{self, Write},
|
use std::hash::{Hash, Hasher};
|
||||||
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
|
use std::io::{self, Write};
|
||||||
str::FromStr
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::util::{bytes_to_hex, Encoder};
|
use super::util::{bytes_to_hex, Encoder};
|
||||||
|
|
||||||
|
@ -28,7 +26,7 @@ impl Address {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_from(data: &[u8]) -> Result<(Address, usize), Error> {
|
pub fn read_from(data: &[u8]) -> Result<(Address, usize), Error> {
|
||||||
if data.is_empty() {
|
if data.is_empty() {
|
||||||
return Err(Error::Parse("Address too short"))
|
return Err(Error::Parse("Address too short"));
|
||||||
}
|
}
|
||||||
let len = data[0] as usize;
|
let len = data[0] as usize;
|
||||||
let addr = Address::read_from_fixed(&data[1..], len)?;
|
let addr = Address::read_from_fixed(&data[1..], len)?;
|
||||||
|
@ -38,10 +36,10 @@ impl Address {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_from_fixed(data: &[u8], len: usize) -> Result<Address, Error> {
|
pub fn read_from_fixed(data: &[u8], len: usize) -> Result<Address, Error> {
|
||||||
if len > 16 {
|
if len > 16 {
|
||||||
return Err(Error::Parse("Invalid address, too long"))
|
return Err(Error::Parse("Invalid address, too long"));
|
||||||
}
|
}
|
||||||
if data.len() < len {
|
if data.len() < len {
|
||||||
return Err(Error::Parse("Address too short"))
|
return Err(Error::Parse("Address too short"));
|
||||||
}
|
}
|
||||||
let mut bytes = [0; 16];
|
let mut bytes = [0; 16];
|
||||||
bytes[0..len].copy_from_slice(&data[0..len]);
|
bytes[0..len].copy_from_slice(&data[0..len]);
|
||||||
|
@ -109,7 +107,7 @@ impl FromStr for Address {
|
||||||
let ip = addr.octets();
|
let ip = addr.octets();
|
||||||
let mut res = [0; 16];
|
let mut res = [0; 16];
|
||||||
res[0..4].copy_from_slice(&ip);
|
res[0..4].copy_from_slice(&ip);
|
||||||
return Ok(Address { data: res, len: 4 })
|
return Ok(Address{data: res, len: 4});
|
||||||
}
|
}
|
||||||
if let Ok(addr) = Ipv6Addr::from_str(text) {
|
if let Ok(addr) = Ipv6Addr::from_str(text) {
|
||||||
let segments = addr.segments();
|
let segments = addr.segments();
|
||||||
|
@ -117,7 +115,7 @@ impl FromStr for Address {
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
Encoder::write_u16(segments[i], &mut res[2*i..]);
|
Encoder::write_u16(segments[i], &mut res[2*i..]);
|
||||||
}
|
}
|
||||||
return Ok(Address { data: res, len: 16 })
|
return Ok(Address{data: res, len: 16});
|
||||||
}
|
}
|
||||||
let parts: Vec<&str> = text.split(':').collect();
|
let parts: Vec<&str> = text.split(':').collect();
|
||||||
if parts.len() == 6 {
|
if parts.len() == 6 {
|
||||||
|
@ -125,7 +123,7 @@ impl FromStr for Address {
|
||||||
for i in 0..6 {
|
for i in 0..6 {
|
||||||
bytes[i] = u8::from_str_radix(parts[i], 16).map_err(|_| Error::Parse("Failed to parse mac"))?;
|
bytes[i] = u8::from_str_radix(parts[i], 16).map_err(|_| Error::Parse("Failed to parse mac"))?;
|
||||||
}
|
}
|
||||||
return Ok(Address { data: bytes, len: 6 })
|
return Ok(Address{data: bytes, len: 6});
|
||||||
}
|
}
|
||||||
Err(Error::Parse("Failed to parse address"))
|
Err(Error::Parse("Failed to parse address"))
|
||||||
}
|
}
|
||||||
|
@ -143,7 +141,7 @@ impl Range {
|
||||||
pub fn read_from(data: &[u8]) -> Result<(Range, usize), Error> {
|
pub fn read_from(data: &[u8]) -> Result<(Range, usize), Error> {
|
||||||
let (address, read) = Address::read_from(data)?;
|
let (address, read) = Address::read_from(data)?;
|
||||||
if data.len() < read + 1 {
|
if data.len() < read + 1 {
|
||||||
return Err(Error::Parse("Range too short"))
|
return Err(Error::Parse("Range too short"));
|
||||||
}
|
}
|
||||||
let prefix_len = data[read];
|
let prefix_len = data[read];
|
||||||
Ok((Range{base: address, prefix_len}, read + 1))
|
Ok((Range{base: address, prefix_len}, read + 1))
|
||||||
|
@ -166,7 +164,8 @@ impl FromStr for Range {
|
||||||
Some(pos) => pos,
|
Some(pos) => pos,
|
||||||
None => return Err(Error::Parse("Invalid range format"))
|
None => return Err(Error::Parse("Invalid range format"))
|
||||||
};
|
};
|
||||||
let prefix_len = u8::from_str(&text[pos + 1..]).map_err(|_| Error::Parse("Failed to parse prefix length"))?;
|
let prefix_len = u8::from_str(&text[pos+1..])
|
||||||
|
.map_err(|_| Error::Parse("Failed to parse prefix length"))?;
|
||||||
let base = Address::from_str(&text[..pos])?;
|
let base = Address::from_str(&text[..pos])?;
|
||||||
Ok(Range{base, prefix_len})
|
Ok(Range{base, prefix_len})
|
||||||
}
|
}
|
||||||
|
@ -202,7 +201,7 @@ impl fmt::Display for Mode {
|
||||||
Mode::Normal => write!(formatter, "normal"),
|
Mode::Normal => write!(formatter, "normal"),
|
||||||
Mode::Hub => write!(formatter, "hub"),
|
Mode::Hub => write!(formatter, "hub"),
|
||||||
Mode::Switch => write!(formatter, "switch"),
|
Mode::Switch => write!(formatter, "switch"),
|
||||||
Mode::Router => write!(formatter, "router")
|
Mode::Router => write!(formatter, "router"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,14 +250,8 @@ impl fmt::Display for Error {
|
||||||
fn address_parse_fmt() {
|
fn address_parse_fmt() {
|
||||||
assert_eq!(format!("{}", Address::from_str("120.45.22.5").unwrap()), "120.45.22.5");
|
assert_eq!(format!("{}", Address::from_str("120.45.22.5").unwrap()), "120.45.22.5");
|
||||||
assert_eq!(format!("{}", Address::from_str("78:2d:16:05:01:02").unwrap()), "78:2d:16:05:01:02");
|
assert_eq!(format!("{}", Address::from_str("78:2d:16:05:01:02").unwrap()), "78:2d:16:05:01:02");
|
||||||
assert_eq!(
|
assert_eq!(format!("{}", Address{data: [3,56,120,45,22,5,1,2,0,0,0,0,0,0,0,0], len: 8}), "vlan824/78:2d:16:05:01:02");
|
||||||
format!("{}", Address { data: [3, 56, 120, 45, 22, 5, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0], len: 8 }),
|
assert_eq!(format!("{}", Address::from_str("0001:0203:0405:0607:0809:0a0b:0c0d:0e0f").unwrap()), "0001:0203:0405:0607:0809:0a0b:0c0d:0e0f");
|
||||||
"vlan824/78:2d:16:05:01:02"
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
format!("{}", Address::from_str("0001:0203:0405:0607:0809:0a0b:0c0d:0e0f").unwrap()),
|
|
||||||
"0001:0203:0405:0607:0809:0a0b:0c0d:0e0f"
|
|
||||||
);
|
|
||||||
assert_eq!(format!("{:?}", Address{data: [1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0], len: 2}), "0102");
|
assert_eq!(format!("{:?}", Address{data: [1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0], len: 2}), "0102");
|
||||||
assert!(Address::from_str("").is_err()); // Failed to parse address
|
assert!(Address::from_str("").is_err()); // Failed to parse address
|
||||||
}
|
}
|
||||||
|
@ -285,29 +278,16 @@ fn address_decode_encode() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn address_eq() {
|
fn address_eq() {
|
||||||
assert_eq!(
|
assert_eq!(Address::read_from_fixed(&[1,2,3,4], 4).unwrap(), Address::read_from_fixed(&[1,2,3,4], 4).unwrap());
|
||||||
Address::read_from_fixed(&[1, 2, 3, 4], 4).unwrap(),
|
assert_ne!(Address::read_from_fixed(&[1,2,3,4], 4).unwrap(), Address::read_from_fixed(&[1,2,3,5], 4).unwrap());
|
||||||
Address::read_from_fixed(&[1, 2, 3, 4], 4).unwrap()
|
assert_eq!(Address::read_from_fixed(&[1,2,3,4], 3).unwrap(), Address::read_from_fixed(&[1,2,3,5], 3).unwrap());
|
||||||
);
|
assert_ne!(Address::read_from_fixed(&[1,2,3,4], 3).unwrap(), Address::read_from_fixed(&[1,2,3,4], 4).unwrap());
|
||||||
assert_ne!(
|
|
||||||
Address::read_from_fixed(&[1, 2, 3, 4], 4).unwrap(),
|
|
||||||
Address::read_from_fixed(&[1, 2, 3, 5], 4).unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Address::read_from_fixed(&[1, 2, 3, 4], 3).unwrap(),
|
|
||||||
Address::read_from_fixed(&[1, 2, 3, 5], 3).unwrap()
|
|
||||||
);
|
|
||||||
assert_ne!(
|
|
||||||
Address::read_from_fixed(&[1, 2, 3, 4], 3).unwrap(),
|
|
||||||
Address::read_from_fixed(&[1, 2, 3, 4], 4).unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn address_range_decode_encode() {
|
fn address_range_decode_encode() {
|
||||||
let mut buf = [0; 32];
|
let mut buf = [0; 32];
|
||||||
let range =
|
let range = Range{base: Address{data: [0,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}, prefix_len: 24};
|
||||||
Range { base: Address { data: [0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 4 }, prefix_len: 24 };
|
|
||||||
assert_eq!(range.write_to(&mut buf), 6);
|
assert_eq!(range.write_to(&mut buf), 6);
|
||||||
assert_eq!(&buf[0..6], &[4, 0, 1, 2, 3, 24]);
|
assert_eq!(&buf[0..6], &[4, 0, 1, 2, 3, 24]);
|
||||||
assert_eq!((range, 6), Range::read_from(&buf).unwrap());
|
assert_eq!((range, 6), Range::read_from(&buf).unwrap());
|
||||||
|
|
|
@ -2,16 +2,12 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use std::{
|
use std::fmt;
|
||||||
fmt,
|
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr};
|
||||||
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::types::{NodeId, HeaderMagic, Error, Range, NODE_ID_BYTES};
|
||||||
crypto::Crypto,
|
use super::util::{bytes_to_hex, Encoder};
|
||||||
types::{Error, HeaderMagic, NodeId, Range, NODE_ID_BYTES},
|
use super::crypto::Crypto;
|
||||||
util::{bytes_to_hex, Encoder}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
|
@ -31,7 +27,7 @@ impl TopHeader {
|
||||||
|
|
||||||
pub fn read_from(data: &[u8]) -> Result<(TopHeader, usize), Error> {
|
pub fn read_from(data: &[u8]) -> Result<(TopHeader, usize), Error> {
|
||||||
if data.len() < TopHeader::size() {
|
if data.len() < TopHeader::size() {
|
||||||
return Err(Error::Parse("Empty message"))
|
return Err(Error::Parse("Empty message"));
|
||||||
}
|
}
|
||||||
let mut header = TopHeader::default();
|
let mut header = TopHeader::default();
|
||||||
header.magic.copy_from_slice(&data[0..4]);
|
header.magic.copy_from_slice(&data[0..4]);
|
||||||
|
@ -56,7 +52,7 @@ pub enum Message<'a> {
|
||||||
Data(&'a mut[u8], usize, usize), // data, start, end
|
Data(&'a mut[u8], usize, usize), // data, start, end
|
||||||
Peers(Vec<SocketAddr>), // peers
|
Peers(Vec<SocketAddr>), // peers
|
||||||
Init(u8, NodeId, Vec<Range>), // step, node_id, ranges
|
Init(u8, NodeId, Vec<Range>), // step, node_id, ranges
|
||||||
Close
|
Close,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Message<'a> {
|
impl<'a> Message<'a> {
|
||||||
|
@ -85,11 +81,9 @@ impl<'a> fmt::Debug for Message<'a> {
|
||||||
write!(formatter, "{}", p)?;
|
write!(formatter, "{}", p)?;
|
||||||
}
|
}
|
||||||
write!(formatter, "]")
|
write!(formatter, "]")
|
||||||
}
|
},
|
||||||
Message::Init(stage, ref node_id, ref peers) => {
|
Message::Init(stage, ref node_id, ref peers) => write!(formatter, "Init(stage={}, node_id={}, {:?})", stage, bytes_to_hex(node_id), peers),
|
||||||
write!(formatter, "Init(stage={}, node_id={}, {:?})", stage, bytes_to_hex(node_id), peers)
|
Message::Close => write!(formatter, "Close"),
|
||||||
}
|
|
||||||
Message::Close => write!(formatter, "Close")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,15 +93,15 @@ pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &Crypto) -> Re
|
||||||
let mut end = data.len();
|
let mut end = data.len();
|
||||||
let (header, mut pos) = TopHeader::read_from(&data[..end])?;
|
let (header, mut pos) = TopHeader::read_from(&data[..end])?;
|
||||||
if header.magic != magic {
|
if header.magic != magic {
|
||||||
return Err(Error::WrongHeaderMagic(header.magic))
|
return Err(Error::WrongHeaderMagic(header.magic));
|
||||||
}
|
}
|
||||||
if header.crypto_method != crypto.method() {
|
if header.crypto_method != crypto.method() {
|
||||||
return Err(Error::Crypto("Wrong crypto method"))
|
return Err(Error::Crypto("Wrong crypto method"));
|
||||||
}
|
}
|
||||||
if crypto.method() > 0 {
|
if crypto.method() > 0 {
|
||||||
let len = crypto.nonce_bytes();
|
let len = crypto.nonce_bytes();
|
||||||
if end < pos + len {
|
if end < pos + len {
|
||||||
return Err(Error::Parse("Truncated crypto header"))
|
return Err(Error::Parse("Truncated crypto header"));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let (before, after) = data.split_at_mut(pos);
|
let (before, after) = data.split_at_mut(pos);
|
||||||
|
@ -121,14 +115,14 @@ pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &Crypto) -> Re
|
||||||
0 => Message::Data(data, pos, end),
|
0 => Message::Data(data, pos, end),
|
||||||
1 => {
|
1 => {
|
||||||
if end < pos + 1 {
|
if end < pos + 1 {
|
||||||
return Err(Error::Parse("Missing IPv4 count"))
|
return Err(Error::Parse("Missing IPv4 count"));
|
||||||
}
|
}
|
||||||
let mut peers = Vec::new();
|
let mut peers = Vec::new();
|
||||||
let count = data[pos];
|
let count = data[pos];
|
||||||
pos += 1;
|
pos += 1;
|
||||||
let len = count as usize * 6;
|
let len = count as usize * 6;
|
||||||
if end < pos + len {
|
if end < pos + len {
|
||||||
return Err(Error::Parse("IPv4 peer data too short"))
|
return Err(Error::Parse("IPv4 peer data too short"));
|
||||||
}
|
}
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let ip = &data[pos..];
|
let ip = &data[pos..];
|
||||||
|
@ -140,13 +134,13 @@ pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &Crypto) -> Re
|
||||||
peers.push(addr);
|
peers.push(addr);
|
||||||
}
|
}
|
||||||
if end < pos + 1 {
|
if end < pos + 1 {
|
||||||
return Err(Error::Parse("Missing IPv6 count"))
|
return Err(Error::Parse("Missing IPv6 count"));
|
||||||
}
|
}
|
||||||
let count = data[pos];
|
let count = data[pos];
|
||||||
pos += 1;
|
pos += 1;
|
||||||
let len = count as usize * 18;
|
let len = count as usize * 18;
|
||||||
if end < pos + len {
|
if end < pos + len {
|
||||||
return Err(Error::Parse("IPv6 peer data too short"))
|
return Err(Error::Parse("IPv6 peer data too short"));
|
||||||
}
|
}
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let mut ip = [0u16; 8];
|
let mut ip = [0u16; 8];
|
||||||
|
@ -156,19 +150,15 @@ pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &Crypto) -> Re
|
||||||
}
|
}
|
||||||
let port = Encoder::read_u16(&data[pos..]);
|
let port = Encoder::read_u16(&data[pos..]);
|
||||||
pos += 2;
|
pos += 2;
|
||||||
let addr = SocketAddr::V6(SocketAddrV6::new(
|
let addr = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(ip[0], ip[1], ip[2],
|
||||||
Ipv6Addr::new(ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]),
|
ip[3], ip[4], ip[5], ip[6], ip[7]), port, 0, 0));
|
||||||
port,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
));
|
|
||||||
peers.push(addr);
|
peers.push(addr);
|
||||||
}
|
}
|
||||||
Message::Peers(peers)
|
Message::Peers(peers)
|
||||||
}
|
},
|
||||||
2 => {
|
2 => {
|
||||||
if end < pos + 2 + NODE_ID_BYTES {
|
if end < pos + 2 + NODE_ID_BYTES {
|
||||||
return Err(Error::Parse("Init data too short"))
|
return Err(Error::Parse("Init data too short"));
|
||||||
}
|
}
|
||||||
let stage = data[pos];
|
let stage = data[pos];
|
||||||
pos += 1;
|
pos += 1;
|
||||||
|
@ -184,7 +174,7 @@ pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &Crypto) -> Re
|
||||||
addrs.push(range);
|
addrs.push(range);
|
||||||
}
|
}
|
||||||
Message::Init(stage, node_id, addrs)
|
Message::Init(stage, node_id, addrs)
|
||||||
}
|
},
|
||||||
3 => Message::Close,
|
3 => Message::Close,
|
||||||
_ => return Err(Error::Parse("Unknown message type"))
|
_ => return Err(Error::Parse("Unknown message type"))
|
||||||
};
|
};
|
||||||
|
@ -192,9 +182,7 @@ pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &Crypto) -> Re
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unknown_lints,clippy::needless_range_loop)]
|
#[allow(unknown_lints,clippy::needless_range_loop)]
|
||||||
pub fn encode<'a>(
|
pub fn encode<'a>(msg: &'a mut Message, mut buf: &'a mut [u8], magic: HeaderMagic, crypto: &mut Crypto) -> &'a mut [u8] {
|
||||||
msg: &'a mut Message, mut buf: &'a mut [u8], magic: HeaderMagic, crypto: &mut Crypto
|
|
||||||
) -> &'a mut [u8] {
|
|
||||||
let header_type = match msg {
|
let header_type = match msg {
|
||||||
Message::Data(_, _, _) => 0,
|
Message::Data(_, _, _) => 0,
|
||||||
Message::Peers(_) => 1,
|
Message::Peers(_) => 1,
|
||||||
|
@ -208,7 +196,7 @@ pub fn encode<'a>(
|
||||||
buf = data;
|
buf = data;
|
||||||
start = data_start;
|
start = data_start;
|
||||||
end = data_end;
|
end = data_end;
|
||||||
}
|
},
|
||||||
Message::Peers(ref peers) => {
|
Message::Peers(ref peers) => {
|
||||||
let mut v4addrs = Vec::new();
|
let mut v4addrs = Vec::new();
|
||||||
let mut v6addrs = Vec::new();
|
let mut v6addrs = Vec::new();
|
||||||
|
@ -230,7 +218,7 @@ pub fn encode<'a>(
|
||||||
pos += 4;
|
pos += 4;
|
||||||
Encoder::write_u16(addr.port(), &mut buf[pos..]);
|
Encoder::write_u16(addr.port(), &mut buf[pos..]);
|
||||||
pos += 2;
|
pos += 2;
|
||||||
}
|
};
|
||||||
buf[pos] = v6addrs.len() as u8;
|
buf[pos] = v6addrs.len() as u8;
|
||||||
pos += 1;
|
pos += 1;
|
||||||
for addr in v6addrs {
|
for addr in v6addrs {
|
||||||
|
@ -241,9 +229,9 @@ pub fn encode<'a>(
|
||||||
}
|
}
|
||||||
Encoder::write_u16(addr.port(), &mut buf[pos..]);
|
Encoder::write_u16(addr.port(), &mut buf[pos..]);
|
||||||
pos += 2;
|
pos += 2;
|
||||||
}
|
};
|
||||||
end = pos;
|
end = pos;
|
||||||
}
|
},
|
||||||
Message::Init(stage, ref node_id, ref ranges) => {
|
Message::Init(stage, ref node_id, ref ranges) => {
|
||||||
let mut pos = start;
|
let mut pos = start;
|
||||||
assert!(buf.len() >= pos + 2 + NODE_ID_BYTES);
|
assert!(buf.len() >= pos + 2 + NODE_ID_BYTES);
|
||||||
|
@ -258,8 +246,9 @@ pub fn encode<'a>(
|
||||||
pos += range.write_to(&mut buf[pos..]);
|
pos += range.write_to(&mut buf[pos..]);
|
||||||
}
|
}
|
||||||
end = pos;
|
end = pos;
|
||||||
|
},
|
||||||
|
Message::Close => {
|
||||||
}
|
}
|
||||||
Message::Close => {}
|
|
||||||
}
|
}
|
||||||
assert!(start >= 64);
|
assert!(start >= 64);
|
||||||
assert!(buf.len() >= end + 64);
|
assert!(buf.len() >= end + 64);
|
||||||
|
@ -282,56 +271,41 @@ pub fn encode<'a>(
|
||||||
&mut buf[start..end]
|
&mut buf[start..end]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> PartialEq for Message<'a> {
|
impl<'a> PartialEq for Message<'a> {
|
||||||
fn eq(&self, other: &Message) -> bool {
|
fn eq(&self, other: &Message) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
Message::Data(ref data1, start1, end1) => {
|
Message::Data(ref data1, start1, end1) => if let Message::Data(ref data2, start2, end2) = *other {
|
||||||
if let Message::Data(ref data2, start2, end2) = *other {
|
|
||||||
data1[start1..end1] == data2[start2..end2]
|
data1[start1..end1] == data2[start2..end2]
|
||||||
} else {
|
} else { false },
|
||||||
false
|
Message::Peers(ref peers1) => if let Message::Peers(ref peers2) = *other {
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::Peers(ref peers1) => {
|
|
||||||
if let Message::Peers(ref peers2) = *other {
|
|
||||||
peers1 == peers2
|
peers1 == peers2
|
||||||
} else {
|
} else { false },
|
||||||
false
|
Message::Init(step1, node_id1, ref ranges1) => if let Message::Init(step2, node_id2, ref ranges2) = *other {
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::Init(step1, node_id1, ref ranges1) => {
|
|
||||||
if let Message::Init(step2, node_id2, ref ranges2) = *other {
|
|
||||||
step1 == step2 && node_id1 == node_id2 && ranges1 == ranges2
|
step1 == step2 && node_id1 == node_id2 && ranges1 == ranges2
|
||||||
} else {
|
} else { false },
|
||||||
false
|
Message::Close => if let Message::Close = *other {
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::Close => {
|
|
||||||
if let Message::Close = *other {
|
|
||||||
true
|
true
|
||||||
} else {
|
} else { false }
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)] use super::crypto::CryptoMethod;
|
|
||||||
#[cfg(test)] use super::types::Address;
|
|
||||||
#[cfg(test)] use super::MAGIC;
|
|
||||||
#[cfg(test)] use std::str::FromStr;
|
#[cfg(test)] use std::str::FromStr;
|
||||||
|
#[cfg(test)] use super::MAGIC;
|
||||||
|
#[cfg(test)] use super::types::Address;
|
||||||
|
#[cfg(test)] use super::crypto::CryptoMethod;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
fn udpmessage_packet() {
|
fn udpmessage_packet() {
|
||||||
let mut crypto = Crypto::None;
|
let mut crypto = Crypto::None;
|
||||||
let mut payload = [
|
let mut payload = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0,
|
1,2,3,4,5,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
|
||||||
];
|
|
||||||
let mut msg = Message::Data(&mut payload, 64, 69);
|
let mut msg = Message::Data(&mut payload, 64, 69);
|
||||||
let mut buf = [0; 1024];
|
let mut buf = [0; 1024];
|
||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
|
@ -352,12 +326,11 @@ fn udpmessage_packet() {
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
fn udpmessage_encrypted() {
|
fn udpmessage_encrypted() {
|
||||||
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
|
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
|
||||||
let mut payload = [
|
let mut payload = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0,
|
1,2,3,4,5,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
|
||||||
];
|
|
||||||
let mut orig_payload = [0; 133];
|
let mut orig_payload = [0; 133];
|
||||||
for i in 0..payload.len() {
|
for i in 0..payload.len() {
|
||||||
orig_payload[i] = payload[i];
|
orig_payload[i] = payload[i];
|
||||||
|
@ -383,15 +356,9 @@ fn udpmessage_encrypted() {
|
||||||
fn udpmessage_peers() {
|
fn udpmessage_peers() {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
let mut crypto = Crypto::None;
|
let mut crypto = Crypto::None;
|
||||||
let mut msg = Message::Peers(vec![
|
let mut msg = Message::Peers(vec![SocketAddr::from_str("1.2.3.4:123").unwrap(), SocketAddr::from_str("5.6.7.8:12345").unwrap(), SocketAddr::from_str("[0001:0203:0405:0607:0809:0a0b:0c0d:0e0f]:6789").unwrap()]);
|
||||||
SocketAddr::from_str("1.2.3.4:123").unwrap(),
|
let mut should = [118,112,110,1,0,0,0,1,2,1,2,3,4,0,123,5,6,7,8,48,57,1,0,1,2,3,4,5,6,7,
|
||||||
SocketAddr::from_str("5.6.7.8:12345").unwrap(),
|
8,9,10,11,12,13,14,15,26,133];
|
||||||
SocketAddr::from_str("[0001:0203:0405:0607:0809:0a0b:0c0d:0e0f]:6789").unwrap(),
|
|
||||||
]);
|
|
||||||
let mut should = [
|
|
||||||
118, 112, 110, 1, 0, 0, 0, 1, 2, 1, 2, 3, 4, 0, 123, 5, 6, 7, 8, 48, 57, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
||||||
11, 12, 13, 14, 15, 26, 133
|
|
||||||
];
|
|
||||||
{
|
{
|
||||||
let mut buf = [0; 1024];
|
let mut buf = [0; 1024];
|
||||||
let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
||||||
|
@ -416,16 +383,11 @@ fn udpmessage_peers() {
|
||||||
fn udpmessage_init() {
|
fn udpmessage_init() {
|
||||||
use super::types::Address;
|
use super::types::Address;
|
||||||
let mut crypto = Crypto::None;
|
let mut crypto = Crypto::None;
|
||||||
let addrs = vec![
|
let addrs = vec![Range{base: Address{data: [0,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}, prefix_len: 24},
|
||||||
Range { base: Address { data: [0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 4 }, prefix_len: 24 },
|
Range{base: Address{data: [0,1,2,3,4,5,0,0,0,0,0,0,0,0,0,0], len: 6}, prefix_len: 16}];
|
||||||
Range { base: Address { data: [0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 6 }, prefix_len: 16 },
|
|
||||||
];
|
|
||||||
let node_id = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
|
let node_id = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
|
||||||
let mut msg = Message::Init(0, node_id, addrs);
|
let mut msg = Message::Init(0, node_id, addrs);
|
||||||
let mut should = [
|
let mut should = [118,112,110,1,0,0,0,2,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,2,4,0,1,2,3,24,6,0,1,2,3,4,5,16];
|
||||||
118, 112, 110, 1, 0, 0, 0, 2, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 2, 4, 0, 1, 2, 3, 24, 6,
|
|
||||||
0, 1, 2, 3, 4, 5, 16
|
|
||||||
];
|
|
||||||
{
|
{
|
||||||
let mut buf = [0; 1024];
|
let mut buf = [0; 1024];
|
||||||
let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
|
||||||
|
@ -476,35 +438,17 @@ fn udpmessage_invalid_crypto() {
|
||||||
assert!(decode(&mut [0x76,0x70,0x6e,1,1,0,0,0], MAGIC, &mut crypto).is_err());
|
assert!(decode(&mut [0x76,0x70,0x6e,1,1,0,0,0], MAGIC, &mut crypto).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_fmt() {
|
fn message_fmt() {
|
||||||
assert_eq!(format!("{:?}", Message::Data(&mut [1,2,3,4,5], 0, 5)), "Data(5 bytes)");
|
assert_eq!(format!("{:?}", Message::Data(&mut [1,2,3,4,5], 0, 5)), "Data(5 bytes)");
|
||||||
assert_eq!(
|
assert_eq!(format!("{:?}", Message::Peers(vec![SocketAddr::from_str("1.2.3.4:123").unwrap(),
|
||||||
format!(
|
|
||||||
"{:?}",
|
|
||||||
Message::Peers(vec![
|
|
||||||
SocketAddr::from_str("1.2.3.4:123").unwrap(),
|
|
||||||
SocketAddr::from_str("5.6.7.8:12345").unwrap(),
|
SocketAddr::from_str("5.6.7.8:12345").unwrap(),
|
||||||
SocketAddr::from_str("[0001:0203:0405:0607:0809:0a0b:0c0d:0e0f]:6789").unwrap()
|
SocketAddr::from_str("[0001:0203:0405:0607:0809:0a0b:0c0d:0e0f]:6789").unwrap()])),
|
||||||
])
|
"Peers [1.2.3.4:123, 5.6.7.8:12345, [1:203:405:607:809:a0b:c0d:e0f]:6789]");
|
||||||
),
|
assert_eq!(format!("{:?}", Message::Init(0, [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], vec![
|
||||||
"Peers [1.2.3.4:123, 5.6.7.8:12345, [1:203:405:607:809:a0b:c0d:e0f]:6789]"
|
Range{base: Address{data: [0,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}, prefix_len: 24},
|
||||||
);
|
Range{base: Address{data: [0,1,2,3,4,5,0,0,0,0,0,0,0,0,0,0], len: 6}, prefix_len: 16}
|
||||||
assert_eq!(
|
])), "Init(stage=0, node_id=000102030405060708090a0b0c0d0e0f, [0.1.2.3/24, 00:01:02:03:04:05/16])");
|
||||||
format!(
|
|
||||||
"{:?}",
|
|
||||||
Message::Init(0, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], vec![
|
|
||||||
Range {
|
|
||||||
base: Address { data: [0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 4 },
|
|
||||||
prefix_len: 24
|
|
||||||
},
|
|
||||||
Range {
|
|
||||||
base: Address { data: [0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 6 },
|
|
||||||
prefix_len: 16
|
|
||||||
}
|
|
||||||
])
|
|
||||||
),
|
|
||||||
"Init(stage=0, node_id=000102030405060708090a0b0c0d0e0f, [0.1.2.3/24, 00:01:02:03:04:05/16])"
|
|
||||||
);
|
|
||||||
assert_eq!(format!("{:?}", Message::Close), "Close");
|
assert_eq!(format!("{:?}", Message::Close), "Close");
|
||||||
}
|
}
|
||||||
|
|
56
src/util.rs
56
src/util.rs
|
@ -2,17 +2,17 @@
|
||||||
// Copyright (C) 2015-2019 Dennis Schwerdel
|
// Copyright (C) 2015-2019 Dennis Schwerdel
|
||||||
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
// This software is licensed under GPL-3 or newer (see LICENSE.md)
|
||||||
|
|
||||||
use std::{
|
use std::net::{SocketAddr, ToSocketAddrs};
|
||||||
fmt,
|
use std::fmt;
|
||||||
net::{SocketAddr, ToSocketAddrs},
|
use std::sync::atomic::{AtomicIsize, Ordering};
|
||||||
sync::atomic::{AtomicIsize, Ordering}
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::types::Error;
|
use super::types::Error;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")] use libc;
|
#[cfg(target_os = "linux")]
|
||||||
|
use libc;
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))] use time;
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
use time;
|
||||||
|
|
||||||
use signal::{trap::Trap, Signal};
|
use signal::{trap::Trap, Signal};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
@ -30,7 +30,9 @@ pub fn bytes_to_hex(bytes: &[u8]) -> String {
|
||||||
v.push(HEX_CHARS[(byte >> 4) as usize]);
|
v.push(HEX_CHARS[(byte >> 4) as usize]);
|
||||||
v.push(HEX_CHARS[(byte & 0xf) as usize]);
|
v.push(HEX_CHARS[(byte & 0xf) as usize]);
|
||||||
}
|
}
|
||||||
unsafe { String::from_utf8_unchecked(v) }
|
unsafe {
|
||||||
|
String::from_utf8_unchecked(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,7 +52,8 @@ impl Encoder {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_u32(data: &[u8]) -> u32 {
|
pub fn read_u32(data: &[u8]) -> u32 {
|
||||||
(u32::from(data[0]) << 24) | (u32::from(data[1]) << 16) | (u32::from(data[2]) << 8) | u32::from(data[3])
|
(u32::from(data[0]) << 24) | (u32::from(data[1]) << 16) |
|
||||||
|
(u32::from(data[2]) << 8) | u32::from(data[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -63,14 +66,10 @@ impl Encoder {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_u64(data: &[u8]) -> u64 {
|
pub fn read_u64(data: &[u8]) -> u64 {
|
||||||
(u64::from(data[0]) << 56)
|
(u64::from(data[0]) << 56) | (u64::from(data[1]) << 48) |
|
||||||
| (u64::from(data[1]) << 48)
|
(u64::from(data[2]) << 40) | (u64::from(data[3]) << 32) |
|
||||||
| (u64::from(data[2]) << 40)
|
(u64::from(data[4]) << 24) | (u64::from(data[5]) << 16) |
|
||||||
| (u64::from(data[3]) << 32)
|
(u64::from(data[6]) << 8) | u64::from(data[7])
|
||||||
| (u64::from(data[4]) << 24)
|
|
||||||
| (u64::from(data[5]) << 16)
|
|
||||||
| (u64::from(data[6]) << 8)
|
|
||||||
| u64::from(data[7])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -122,11 +121,9 @@ pub fn resolve<Addr: ToSocketAddrs + fmt::Debug>(addr: Addr) -> Result<Vec<Socke
|
||||||
// Remove duplicates in addrs (why are there duplicates???)
|
// Remove duplicates in addrs (why are there duplicates???)
|
||||||
let mut addrs = addrs.collect::<Vec<_>>();
|
let mut addrs = addrs.collect::<Vec<_>>();
|
||||||
// Try IPv4 first as it usually is faster
|
// Try IPv4 first as it usually is faster
|
||||||
addrs.sort_by_key(|addr| {
|
addrs.sort_by_key(|addr| match *addr {
|
||||||
match *addr {
|
|
||||||
SocketAddr::V4(_) => 4,
|
SocketAddr::V4(_) => 4,
|
||||||
SocketAddr::V6(_) => 6
|
SocketAddr::V6(_) => 6
|
||||||
}
|
|
||||||
});
|
});
|
||||||
addrs.dedup();
|
addrs.dedup();
|
||||||
Ok(addrs)
|
Ok(addrs)
|
||||||
|
@ -134,9 +131,11 @@ pub fn resolve<Addr: ToSocketAddrs + fmt::Debug>(addr: Addr) -> Result<Vec<Socke
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
macro_rules! addr {
|
macro_rules! addr {
|
||||||
($addr: expr) => {{
|
($addr: expr) => {
|
||||||
|
{
|
||||||
std::net::ToSocketAddrs::to_socket_addrs($addr).unwrap().next().unwrap()
|
std::net::ToSocketAddrs::to_socket_addrs($addr).unwrap().next().unwrap()
|
||||||
}};
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,28 +147,29 @@ impl fmt::Display for Bytes {
|
||||||
if size >= 512.0 {
|
if size >= 512.0 {
|
||||||
size /= 1024.0;
|
size /= 1024.0;
|
||||||
} else {
|
} else {
|
||||||
return write!(formatter, "{:.0} B", size)
|
return write!(formatter, "{:.0} B", size);
|
||||||
}
|
}
|
||||||
if size >= 512.0 {
|
if size >= 512.0 {
|
||||||
size /= 1024.0;
|
size /= 1024.0;
|
||||||
} else {
|
} else {
|
||||||
return write!(formatter, "{:.1} KiB", size)
|
return write!(formatter, "{:.1} KiB", size);
|
||||||
}
|
}
|
||||||
if size >= 512.0 {
|
if size >= 512.0 {
|
||||||
size /= 1024.0;
|
size /= 1024.0;
|
||||||
} else {
|
} else {
|
||||||
return write!(formatter, "{:.1} MiB", size)
|
return write!(formatter, "{:.1} MiB", size);
|
||||||
}
|
}
|
||||||
if size >= 512.0 {
|
if size >= 512.0 {
|
||||||
size /= 1024.0;
|
size /= 1024.0;
|
||||||
} else {
|
} else {
|
||||||
return write!(formatter, "{:.1} GiB", size)
|
return write!(formatter, "{:.1} GiB", size);
|
||||||
}
|
}
|
||||||
write!(formatter, "{:.1} TiB", size)
|
write!(formatter, "{:.1} TiB", size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub struct CtrlC {
|
pub struct CtrlC {
|
||||||
dummy_time: Instant,
|
dummy_time: Instant,
|
||||||
trap: Trap
|
trap: Trap
|
||||||
|
@ -205,9 +205,7 @@ impl TimeSource for SystemTimeSource {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn now() -> Time {
|
fn now() -> Time {
|
||||||
let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 };
|
let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 };
|
||||||
unsafe {
|
unsafe { libc::clock_gettime(6, &mut tv); }
|
||||||
libc::clock_gettime(6, &mut tv);
|
|
||||||
}
|
|
||||||
tv.tv_sec as Time
|
tv.tv_sec as Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"path": "."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {}
|
|
||||||
}
|
|
Loading…
Reference in New Issue