From 62186152e4a2c9b05c1963b07171e0b9f20f76a6 Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Tue, 19 Feb 2019 18:42:50 +0100 Subject: [PATCH] Full beacon encode/decode --- Cargo.lock | 109 ++++++++- Cargo.toml | 2 +- src/beacon.rs | 368 +++++++++++++++++++---------- src/benches.rs | 5 - src/cloud.rs | 9 +- src/config.rs | 150 ++++++++++++ src/crypto.rs | 67 +++++- src/ethernet.rs | 47 ++++ src/ip.rs | 106 +++++++++ src/main.rs | 7 +- src/tests.rs | 591 ---------------------------------------------- src/types.rs | 53 ++++- src/udpmessage.rs | 182 ++++++++++++++ 13 files changed, 947 insertions(+), 749 deletions(-) delete mode 100644 src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index c176da3..2ef0b61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,39 @@ name = "autocfg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "backtrace" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base-62" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "base64" version = "0.9.3" @@ -30,11 +63,6 @@ name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "bs58" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "byteorder" version = "1.3.1" @@ -83,6 +111,26 @@ name = "dtoa" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "failure" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fnv" version = "1.0.6" @@ -214,6 +262,28 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-bigint" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "num_cpus" version = "1.10.0" @@ -431,6 +501,11 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustc-demangle" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustc_version" version = "0.2.3" @@ -525,6 +600,17 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synstructure" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.6" @@ -629,8 +715,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "vpncloud" version = "0.9.1" dependencies = [ + "base-62 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", "daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -697,10 +783,12 @@ dependencies = [ [metadata] "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" +"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" +"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum base-62 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f28ebd71b3e708e895b83ec2d35c6e2ef96e34945706bf4d73826354e84f89b2" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0de79cfb98e7aa9988188784d8664b4b5dad6eaaa0863b91d9a4ed871d4f7a42" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4390a3b5f4f6bce9c1d0c00128379df433e53777fdd30e92f16a529332baec4e" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" @@ -708,6 +796,8 @@ dependencies = [ "checksum daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4093d27eb267d617f03c2ee25d4c3ca525b89a76154001954a11984508ffbde5" "checksum docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db2906c2579b5b7207fc1e328796a9a8835dc44e22dbe8e460b1d636f9a7b225" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" +"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" @@ -725,6 +815,9 @@ dependencies = [ "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" +"checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" +"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" @@ -749,6 +842,7 @@ dependencies = [ "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" "checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861" "checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" +"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" @@ -762,6 +856,7 @@ dependencies = [ "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" +"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" diff --git a/Cargo.toml b/Cargo.toml index b00af14..9f75cd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ igd = "0.6" # Do not update, 0.7 has problems with exit by ctrl-c siphasher = "0.3" daemonize = "0.3" ring = "0.14" -bs58 = "*" +base-62 = "0.1" [build-dependencies] cc = "^1" diff --git a/src/beacon.rs b/src/beacon.rs index 474de54..e8b372a 100644 --- a/src/beacon.rs +++ b/src/beacon.rs @@ -1,164 +1,270 @@ -use bs58; +use base_62; use ring::digest; -use std::str::FromStr; +use std::num::Wrapping; use super::util::{now, Encoder}; use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr}; -fn base58_encode(data: &[u8]) -> String { - bs58::encode(data).into_string() -} - -fn base58_decode(data: &str) -> Vec { - bs58::decode(data).into_vec().unwrap() +fn base_62_sanitize(data: &str) -> String { + data.chars().filter(|c| c.is_ascii_alphanumeric()).collect() } fn sha512(data: &[u8]) -> Vec { - digest::digest(&digest::SHA512, data).as_ref().iter().map(|b| *b).collect() + digest::digest(&digest::SHA512, data).as_ref().iter().map(|b| *b).collect() } +fn now_hour_16() -> u16 { + ((now() / 3600) & 0xffff) as u16 +} pub struct BeaconSerializer { - magic: Vec, - shared_key: String + magic: Vec, + shared_key: Vec } impl BeaconSerializer { - pub fn new(magic: &[u8], shared_key: &str) -> Self { - BeaconSerializer { - magic: magic.to_owned(), - shared_key: shared_key.to_string() + pub fn new(magic: &[u8], shared_key: &[u8]) -> Self { + BeaconSerializer { + magic: magic.to_owned(), + shared_key: shared_key.to_owned() + } } - } - fn seed(&self, key: &str) -> Vec { - let mut data = Vec::new(); - data.extend_from_slice(key.as_bytes()); - data.extend_from_slice(&self.magic); - data.extend_from_slice(self.shared_key.as_bytes()); - sha512(&data) - } + fn seed(&self, key: &str) -> Vec { + let mut data = Vec::new(); + data.extend_from_slice(key.as_bytes()); + data.extend_from_slice(&self.magic); + data.extend_from_slice(&self.shared_key); + sha512(&data) + } - fn mask_with_seed(&self, data: &[u8], key: &str) -> Vec { - let mask = self.seed(key); - let mut output = Vec::with_capacity(data.len()); - for i in 0..data.len() { - output.push(data[i] ^ mask[i]); + fn mask_with_seed(&self, data: &[u8], key: &str) -> Vec { + let mask = self.seed(key); + let mut output = Vec::with_capacity(data.len()); + for i in 0..data.len() { + output.push(data[i] ^ mask[i]); + } + output } - output - } - fn begin(&self) -> String { - base58_encode(&self.seed("begin"))[0..5].to_string() - } + fn begin(&self) -> String { + base_62::encode(&self.seed("begin"))[0..5].to_string() + } - fn end(&self) -> String { - base58_encode(&self.seed("end"))[0..5].to_string() - } + fn end(&self) -> String { + base_62::encode(&self.seed("end"))[0..5].to_string() + } - fn peerlist_encode(&self, peers: &[SocketAddr]) -> String { - let mut data = Vec::new(); - // Add timestamp - data.append(&mut self.mask_with_seed(&(now() as u32).to_be_bytes(), "time")); - // Split addresses into v4 and v6 - let mut v4addrs = Vec::new(); - let mut v6addrs = Vec::new(); - for p in peers { - match *p { - SocketAddr::V4(addr) => v4addrs.push(addr), - SocketAddr::V6(addr) => v6addrs.push(addr) - } + fn peerlist_encode(&self, peers: &[SocketAddr], now_hour: u16) -> String { + let mut data = Vec::new(); + // Add timestamp + data.append(&mut self.mask_with_seed(&now_hour.to_be_bytes(), "time")); + // Split addresses into v4 and v6 + let mut v4addrs = Vec::new(); + let mut v6addrs = Vec::new(); + for p in peers { + match *p { + SocketAddr::V4(addr) => v4addrs.push(addr), + SocketAddr::V6(addr) => v6addrs.push(addr) + } + } + // Add count of v4 addresses + data.append(&mut self.mask_with_seed(&[v4addrs.len() as u8], "v4count")); + // Add v4 addresses + for addr in v4addrs { + let mut dat = [0u8; 6]; + dat[0..4].copy_from_slice(&addr.ip().octets()); + Encoder::write_u16(addr.port(), &mut dat[4..]); + data.append(&mut self.mask_with_seed(&dat, "peer")); + } + // Add v6 addresses + for addr in v6addrs { + let mut dat = [0u8; 18]; + let ip = addr.ip().segments(); + Encoder::write_u16(ip[0], &mut dat[0..]); + Encoder::write_u16(ip[1], &mut dat[2..]); + Encoder::write_u16(ip[2], &mut dat[4..]); + Encoder::write_u16(ip[3], &mut dat[6..]); + Encoder::write_u16(ip[4], &mut dat[8..]); + Encoder::write_u16(ip[5], &mut dat[10..]); + Encoder::write_u16(ip[6], &mut dat[12..]); + Encoder::write_u16(ip[7], &mut dat[14..]); + Encoder::write_u16(addr.port(), &mut dat[16..]); + data.append(&mut self.mask_with_seed(&dat, "peer")); + } + let mut parity = 0; + for b in &data { + parity ^= b; + } + data.push(parity); + base_62::encode(&data) } - // Add count of v4 addresses - data.append(&mut self.mask_with_seed(&[v4addrs.len() as u8], "v4count")); - // Add v4 addresses - for addr in v4addrs { - let mut dat = [0u8; 6]; - dat[0..4].copy_from_slice(&addr.ip().octets()); - Encoder::write_u16(addr.port(), &mut dat[4..]); - data.append(&mut self.mask_with_seed(&dat, "peer")); - } - // Add v6 addresses - for addr in v6addrs { - let mut dat = [0u8; 18]; - let ip = addr.ip().segments(); - Encoder::write_u16(ip[0], &mut dat[0..]); - Encoder::write_u16(ip[1], &mut dat[2..]); - Encoder::write_u16(ip[2], &mut dat[4..]); - Encoder::write_u16(ip[3], &mut dat[6..]); - Encoder::write_u16(ip[4], &mut dat[8..]); - Encoder::write_u16(ip[5], &mut dat[10..]); - Encoder::write_u16(ip[6], &mut dat[12..]); - Encoder::write_u16(ip[7], &mut dat[14..]); - Encoder::write_u16(addr.port(), &mut dat[16..]); - data.append(&mut self.mask_with_seed(&dat, "peer")); - } - base58_encode(&data) - } - fn peerlist_decode(&self, data: &str) -> Vec { - let data = base58_decode(data); - let mut peers = Vec::new(); - //TODO: decode time - let mut pos = 4; - let v4count = self.mask_with_seed(&[data[pos]], "v4count")[0]; - pos += 1; - for _ in 0..v4count { - assert!(data.len() >= pos + 6); - let dat = self.mask_with_seed(&data[pos..pos+6], "peer"); - pos += 6; - let port = Encoder::read_u16(&dat[4..]); - let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(dat[0], dat[1], dat[2], dat[3]), port)); - peers.push(addr); + fn peerlist_decode(&self, data: &str, ttl_hours: Option, now_hour: u16) -> Vec { + let mut data = base_62::decode(data).expect("Invalid input"); + let mut peers = Vec::new(); + let mut pos = 0; + if data.len() < 4 { + return peers + } + let mut parity = data.pop().unwrap(); + for b in &data { + parity ^= b; + } + if parity != 0 { + return peers + } + let then = Wrapping(Encoder::read_u16(&self.mask_with_seed(&data[pos..=pos+1], "time"))); + if let Some(ttl) = ttl_hours { + let now = Wrapping(now_hour); + if now - then > Wrapping(ttl) && then - now > Wrapping(ttl) { + return peers + } + } + pos += 2; + let v4count = self.mask_with_seed(&[data[pos]], "v4count")[0] as usize; + pos += 1; + if v4count * 6 > data.len() - pos || (data.len() - pos - v4count * 6) % 18 > 0 { + return peers + } + for _ in 0..v4count { + assert!(data.len() >= pos + 6); + let dat = self.mask_with_seed(&data[pos..pos+6], "peer"); + pos += 6; + let port = Encoder::read_u16(&dat[4..]); + let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(dat[0], dat[1], dat[2], dat[3]), port)); + peers.push(addr); + } + let v6count = (data.len() - pos)/18; + for _ in 0..v6count { + assert!(data.len() >= pos + 18); + let dat = self.mask_with_seed(&data[pos..pos+18], "peer"); + pos += 18; + let mut ip = [0u16; 8]; + for i in 0..8 { + ip[i] = Encoder::read_u16(&dat[i*2..i*2+2]); + } + let port = Encoder::read_u16(&dat[16..]); + let addr = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(ip[0], ip[1], ip[2], + ip[3], ip[4], ip[5], ip[6], ip[7]), port, 0, 0)); + peers.push(addr); + } + peers } - let v6count = (data.len() - pos)/18; - for _ in 0..v6count { - assert!(data.len() >= pos + 18); - let dat = self.mask_with_seed(&data[pos..pos+18], "peer"); - pos += 18; - let mut ip = [0u16; 8]; - for i in 0..8 { - ip[i] = Encoder::read_u16(&dat[i*2..i*2+2]); - } - let port = Encoder::read_u16(&dat[16..]); - let addr = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(ip[0], ip[1], ip[2], - ip[3], ip[4], ip[5], ip[6], ip[7]), port, 0, 0)); - peers.push(addr); - } - peers - } - fn decode(&self, data: &str) -> Vec { - //TODO: remove anything that is not base58 - let mut peers = Vec::new(); - let begin = self.begin(); - let end = self.end(); - let mut pos = 0; - while let Some(found) = data[pos..].find(&begin) { - pos += found; - let start_pos = pos + begin.len(); - if let Some(found) = data[pos..].find(&end) { - let end_pos = pos + found; - peers.append(&mut self.peerlist_decode(&data[start_pos..end_pos])); - pos = end_pos + end.len(); - } else { - pos += begin.len(); - } + fn encode_internal(&self, peers: &[SocketAddr], now_hour: u16) -> String { + format!("{}{}{}", self.begin(), self.peerlist_encode(peers, now_hour), self.end()) + } + + pub fn encode(&self, peers: &[SocketAddr]) -> String { + self.encode_internal(peers, now_hour_16()) + } + + fn decode_internal(&self, data: &str, ttl_hours: Option, now_hour: u16) -> Vec { + let data = base_62_sanitize(data); + let mut peers = Vec::new(); + let begin = self.begin(); + let end = self.end(); + let mut pos = 0; + while let Some(found) = data[pos..].find(&begin) { + pos += found; + let start_pos = pos + begin.len(); + if let Some(found) = data[pos..].find(&end) { + let end_pos = pos + found; + peers.append(&mut self.peerlist_decode(&data[start_pos..end_pos], ttl_hours, now_hour)); + pos = start_pos + } else { + break + } + } + peers + } + + pub fn decode(&self, data: &str, ttl_hours: Option) -> Vec { + self.decode_internal(data, ttl_hours, now_hour_16()) } - peers - } } -pub fn test() { - let beacon = BeaconSerializer::new(b"vpnc", "mysecretkey"); - 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("[::1]:5678").unwrap()); - let string = format!("{}{}{}", beacon.begin(), beacon.peerlist_encode(&peers), beacon.end()); - println!("{}", string); - println!("{:?}", beacon.decode(&string)); +#[cfg(test)] use std::str::FromStr; + +#[test] +fn encode() { + let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + let mut peers = Vec::new(); + peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); + peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); + assert_eq!("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", ser.encode_internal(&peers, 2000)); + peers.push(SocketAddr::from_str("[::1]:5678").unwrap()); + assert_eq!("JHEiL4gZk5Jq5R3IRnwJiIqgGzBPgXJrhO1hrmeSRCNLaw26VcVSGriv", ser.encode_internal(&peers, 2000)); } + +#[test] +fn decode() { + let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + let mut peers = Vec::new(); + peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); + peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", None, 2000))); + peers.push(SocketAddr::from_str("[::1]:5678").unwrap()); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("JHEiL4gZk5Jq5R3IRnwJiIqgGzBPgXJrhO1hrmeSRCNLaw26VcVSGriv", None, 2000))); +} + +#[test] +fn decode_split() { + let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + let mut peers = Vec::new(); + peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); + peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("JHEiL-pS.FT:n8 R4\tPI\nQ1(mu)Dy[5t]Y3ülEäSGriv", None, 2000))); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("J -, \nHE--iLpSFTn8R4PIQ1muDy5tY3lES(G}rÖÄÜ\niv", None, 2000))); +} + +#[test] +fn decode_offset() { + let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + let mut peers = Vec::new(); + peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); + peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("Hello World: JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv! End of the World", None, 2000))); +} + +#[test] +fn decode_multiple() { + let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + let mut peers = Vec::new(); + peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); + peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); + assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode_internal("JHEiL4dGxY6nwSaDoRBSGriv JHEiL7c3Y6ptTMaDoRBSGriv", None, 2000))); +} + +#[test] +fn decode_ttl() { + let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + let mut peers = Vec::new(); + peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); + peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap()); + assert_eq!(2, ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", None, 2000).len()); + assert_eq!(2, ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", None, 2100).len()); + assert_eq!(2, ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", None, 2005).len()); + assert_eq!(2, ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", None, 1995).len()); + assert_eq!(2, ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", Some(24), 2000).len()); + assert_eq!(2, ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", Some(24), 1995).len()); + assert_eq!(2, ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", Some(24), 2005).len()); + assert_eq!(0, ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", Some(24), 2100).len()); + assert_eq!(0, ser.decode_internal("JHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", Some(24), 1900).len()); +} + +#[test] +fn decode_invalid() { + let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey"); + assert_eq!(0, ser.decode_internal("", None, 2000).len()); + assert_eq!(0, ser.decode_internal("JHEiLSGriv", None, 2000).len()); + assert_eq!(0, ser.decode_internal("JHEiL--", None, 2000).len()); + assert_eq!(0, ser.decode_internal("--SGriv", None, 2000).len()); + assert_eq!(0, ser.decode_internal("JHEiLpSFTn8R4PIQ1nuDy5tY3lESGriv", None, 2000).len()); + assert_eq!(2, ser.decode_internal("SGrivJHEiLpSFTn8R4PIQ1muDy5tY3lESGrivJHEiL", None, 2000).len()); + assert_eq!(2, ser.decode_internal("JHEiLJHEiLpSFTn8R4PIQ1muDy5tY3lESGriv", None, 2000).len()); +} \ No newline at end of file diff --git a/src/benches.rs b/src/benches.rs index f7289ce..e152435 100644 --- a/src/benches.rs +++ b/src/benches.rs @@ -22,7 +22,6 @@ use super::poll::{Poll, Flags}; #[bench] fn crypto_chacha20(b: &mut Bencher) { - Crypto::init(); let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); let mut payload = [0; 1500]; let header = [0; 8]; @@ -36,10 +35,6 @@ fn crypto_chacha20(b: &mut Bencher) { #[bench] fn crypto_aes256(b: &mut Bencher) { - Crypto::init(); - if !Crypto::aes256_available() { - return - } let mut crypto = Crypto::from_shared_key(CryptoMethod::AES256, "test"); let mut payload = [0; 1500]; let header = [0; 8]; diff --git a/src/cloud.rs b/src/cloud.rs index 5a20f7f..4a004df 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -29,6 +29,7 @@ use super::port_forwarding::PortForwarding; use super::util::{now, Time, Duration, resolve}; use super::poll::{Poll, Flags}; use super::traffic::TrafficStats; +use super::beacon::BeaconSerializer; pub type Hash = BuildHasherDefault; @@ -222,6 +223,7 @@ pub struct GenericCloud { port_forwarding: Option, traffic: TrafficStats, stats_file: Option, + beacon_serializer: BeaconSerializer, _dummy_p: PhantomData

, } @@ -254,7 +256,6 @@ impl GenericCloud { socket4, socket6, device, - crypto, next_peerlist: now(), update_freq: config.get_keepalive(), buffer_out: [0; 64*1024], @@ -263,6 +264,8 @@ impl GenericCloud { port_forwarding, traffic: TrafficStats::default(), stats_file: config.stats_file.clone(), + beacon_serializer: BeaconSerializer::new(&config.get_magic(), crypto.get_key()), + crypto, _dummy_p: PhantomData, } } @@ -423,6 +426,10 @@ impl GenericCloud { // ...and send them to all peers let mut msg = Message::Peers(peers); try!(self.broadcast_msg(&mut msg)); + // Output beacon + let beacon = self.beacon_serializer.encode(&self.peers.subset(3)); + //TODO: publish beacon + debug!("Beacon: {}", beacon); // Reschedule for next update self.next_peerlist = now + Time::from(self.update_freq); } diff --git a/src/config.rs b/src/config.rs index 6df7d91..28c25e2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -242,3 +242,153 @@ pub struct ConfigFile { pub user: Option, pub group: Option, } + + +#[test] +fn config_file() { + let config_file = " +device_type: tun +device_name: vpncloud%d +device_path: /dev/net/tun +magic: 0123ABCD +ifup: ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up +ifdown: 'true' +crypto: aes256 +shared_key: mysecret +port: 3210 +peers: + - remote.machine.foo:3210 + - remote.machine.bar:3210 +peer_timeout: 1800 +keepalive: 840 +dst_timeout: 300 +mode: normal +subnets: + - 10.0.1.0/24 +port_forwarding: true +user: nobody +group: nogroup +pid_file: /run/vpncloud.run +stats_file: /var/log/vpncloud.stats + "; + assert_eq!(serde_yaml::from_str::(config_file).unwrap(), ConfigFile{ + device_type: Some(Type::Tun), + device_name: Some("vpncloud%d".to_string()), + device_path: Some("/dev/net/tun".to_string()), + ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()), + ifdown: Some("true".to_string()), + crypto: Some(CryptoMethod::AES256), + shared_key: Some("mysecret".to_string()), + magic: Some("0123ABCD".to_string()), + port: Some(3210), + peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]), + peer_timeout: Some(1800), + keepalive: Some(840), + mode: Some(Mode::Normal), + dst_timeout: Some(300), + subnets: Some(vec!["10.0.1.0/24".to_string()]), + port_forwarding: Some(true), + user: Some("nobody".to_string()), + group: Some("nogroup".to_string()), + pid_file: Some("/run/vpncloud.run".to_string()), + stats_file: Some("/var/log/vpncloud.stats".to_string()) + }) +} + +#[test] +fn config_merge() { + let mut config = Config::default(); + config.merge_file(ConfigFile{ + device_type: Some(Type::Tun), + device_name: Some("vpncloud%d".to_string()), + device_path: None, + ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()), + ifdown: Some("true".to_string()), + crypto: Some(CryptoMethod::AES256), + shared_key: Some("mysecret".to_string()), + magic: Some("0123ABCD".to_string()), + port: Some(3210), + peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]), + peer_timeout: Some(1800), + keepalive: Some(840), + mode: Some(Mode::Normal), + dst_timeout: Some(300), + subnets: Some(vec!["10.0.1.0/24".to_string()]), + port_forwarding: Some(true), + user: Some("nobody".to_string()), + group: Some("nogroup".to_string()), + pid_file: Some("/run/vpncloud.run".to_string()), + stats_file: Some("/var/log/vpncloud.stats".to_string()) + }); + assert_eq!(config, Config{ + device_type: Type::Tun, + device_name: "vpncloud%d".to_string(), + device_path: None, + ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()), + ifdown: Some("true".to_string()), + magic: Some("0123ABCD".to_string()), + crypto: CryptoMethod::AES256, + shared_key: Some("mysecret".to_string()), + port: 3210, + peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()], + peer_timeout: 1800, + keepalive: Some(840), + dst_timeout: 300, + mode: Mode::Normal, + port_forwarding: true, + subnets: vec!["10.0.1.0/24".to_string()], + user: Some("nobody".to_string()), + group: Some("nogroup".to_string()), + pid_file: Some("/run/vpncloud.run".to_string()), + stats_file: Some("/var/log/vpncloud.stats".to_string()), + ..Default::default() + }); + config.merge_args(Args{ + flag_type: Some(Type::Tap), + flag_device: Some("vpncloud0".to_string()), + flag_device_path: Some("/dev/null".to_string()), + flag_ifup: Some("ifconfig $IFNAME 10.0.1.2/16 mtu 1400 up".to_string()), + flag_ifdown: Some("ifconfig $IFNAME down".to_string()), + flag_crypto: Some(CryptoMethod::ChaCha20), + flag_shared_key: Some("anothersecret".to_string()), + flag_magic: Some("hash:mynet".to_string()), + flag_listen: Some(3211), + flag_peer_timeout: Some(1801), + flag_keepalive: Some(850), + flag_dst_timeout: Some(301), + flag_mode: Some(Mode::Switch), + flag_subnet: vec![], + flag_connect: vec!["another:3210".to_string()], + flag_no_port_forwarding: true, + flag_daemon: true, + flag_pid_file: Some("/run/vpncloud-mynet.run".to_string()), + flag_stats_file: Some("/var/log/vpncloud-mynet.stats".to_string()), + flag_user: Some("root".to_string()), + flag_group: Some("root".to_string()), + ..Default::default() + }); + assert_eq!(config, Config{ + device_type: Type::Tap, + device_name: "vpncloud0".to_string(), + device_path: Some("/dev/null".to_string()), + ifup: Some("ifconfig $IFNAME 10.0.1.2/16 mtu 1400 up".to_string()), + ifdown: Some("ifconfig $IFNAME down".to_string()), + magic: Some("hash:mynet".to_string()), + crypto: CryptoMethod::ChaCha20, + shared_key: Some("anothersecret".to_string()), + port: 3211, + peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string(), "another:3210".to_string()], + peer_timeout: 1801, + keepalive: Some(850), + dst_timeout: 301, + mode: Mode::Switch, + port_forwarding: false, + subnets: vec!["10.0.1.0/24".to_string()], + user: Some("root".to_string()), + group: Some("root".to_string()), + pid_file: Some("/run/vpncloud-mynet.run".to_string()), + stats_file: Some("/var/log/vpncloud-mynet.stats".to_string()), + daemonize: true, + ..Default::default() + }); +} diff --git a/src/crypto.rs b/src/crypto.rs index 70b1992..a5f28bf 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -27,7 +27,8 @@ pub enum CryptoMethod { pub struct CryptoData { sealing_key: SealingKey, opening_key: OpeningKey, - nonce: Vec + nonce: Vec, + key: Vec } #[allow(unknown_lints, clippy::large_enum_variant)] @@ -52,10 +53,6 @@ fn inc_nonce(nonce: &mut [u8]) { impl Crypto { - #[inline] - pub fn init() { - } - #[inline] pub fn method(&self) -> u8 { match *self { @@ -73,6 +70,14 @@ impl Crypto { } } + #[inline] + pub fn get_key(&self) -> &[u8] { + match *self { + Crypto::None => &[], + Crypto::ChaCha20Poly1305(ref data) | Crypto::AES256GCM(ref data) => &data.key + } + } + #[inline] #[allow(unknown_lints,clippy::match_same_arms)] pub fn additional_bytes(&self) -> usize { @@ -117,7 +122,7 @@ impl Crypto { if SystemRandom::new().fill(&mut nonce[1..]).is_err() { fail!("Randomizing nonce failed"); } - let data = CryptoData { sealing_key, opening_key, nonce }; + let data = CryptoData { sealing_key, opening_key, nonce, key }; match method { CryptoMethod::ChaCha20 => Crypto::ChaCha20Poly1305(data), CryptoMethod::AES256 => Crypto::AES256GCM(data) @@ -153,3 +158,53 @@ impl Crypto { } } } + + + +#[test] +fn encrypt_decrypt_chacha20poly1305() { + let mut sender = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); + let receiver = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); + let msg = "HelloWorld0123456789"; + let msg_bytes = msg.as_bytes(); + let mut buffer = [0u8; 1024]; + let header = [0u8; 8]; + for i in 0..msg_bytes.len() { + buffer[i] = msg_bytes[i]; + } + let mut nonce1 = [0u8; 12]; + let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce1, &header); + assert_eq!(size, msg_bytes.len() + sender.additional_bytes()); + assert!(msg_bytes != &buffer[..msg_bytes.len()] as &[u8]); + receiver.decrypt(&mut buffer[..size], &nonce1, &header).unwrap(); + assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]); + let mut nonce2 = [0u8; 12]; + let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce2, &header); + assert!(nonce1 != nonce2); + receiver.decrypt(&mut buffer[..size], &nonce2, &header).unwrap(); + assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]); +} + +#[test] +fn encrypt_decrypt_aes256() { + let mut sender = Crypto::from_shared_key(CryptoMethod::AES256, "test"); + let receiver = Crypto::from_shared_key(CryptoMethod::AES256, "test"); + let msg = "HelloWorld0123456789"; + let msg_bytes = msg.as_bytes(); + let mut buffer = [0u8; 1024]; + let header = [0u8; 8]; + for i in 0..msg_bytes.len() { + buffer[i] = msg_bytes[i]; + } + let mut nonce1 = [0u8; 12]; + let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce1, &header); + assert_eq!(size, msg_bytes.len() + sender.additional_bytes()); + assert!(msg_bytes != &buffer[..msg_bytes.len()] as &[u8]); + receiver.decrypt(&mut buffer[..size], &nonce1, &header).unwrap(); + assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]); + let mut nonce2 = [0u8; 12]; + let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce2, &header); + assert!(nonce1 != nonce2); + receiver.decrypt(&mut buffer[..size], &nonce2, &header).unwrap(); + assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]); +} diff --git a/src/ethernet.rs b/src/ethernet.rs index 0ff4343..fb3e621 100644 --- a/src/ethernet.rs +++ b/src/ethernet.rs @@ -159,3 +159,50 @@ impl Table for SwitchTable { } } } + + +#[cfg(test)] use std::str::FromStr; +#[cfg(test)] use std::net::ToSocketAddrs; +#[cfg(test)] use std::thread; + +#[test] +fn decode_frame_without_vlan() { + let data = [6,5,4,3,2,1,1,2,3,4,5,6,1,2,3,4,5,6,7,8]; + let (src, dst) = Frame::parse(&data).unwrap(); + assert_eq!(src, Address{data: [1,2,3,4,5,6,0,0,0,0,0,0,0,0,0,0], len: 6}); + assert_eq!(dst, Address{data: [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], len: 6}); +} + +#[test] +fn decode_frame_with_vlan() { + let data = [6,5,4,3,2,1,1,2,3,4,5,6,0x81,0,4,210,1,2,3,4,5,6,7,8]; + let (src, dst) = Frame::parse(&data).unwrap(); + assert_eq!(src, Address{data: [4,210,1,2,3,4,5,6,0,0,0,0,0,0,0,0], len: 8}); + assert_eq!(dst, Address{data: [4,210,6,5,4,3,2,1,0,0,0,0,0,0,0,0], len: 8}); +} + +#[test] +fn decode_invalid_frame() { + assert!(Frame::parse(&[6,5,4,3,2,1,1,2,3,4,5,6,1,2,3,4,5,6,7,8]).is_ok()); + // truncated frame + assert!(Frame::parse(&[]).is_err()); + // truncated vlan frame + assert!(Frame::parse(&[6,5,4,3,2,1,1,2,3,4,5,6,0x81,0x00]).is_err()); +} + +#[test] +fn switch() { + let mut table = SwitchTable::new(10, 1); + let addr = Address::from_str("12:34:56:78:90:ab").unwrap(); + let peer = "1.2.3.4:5678".to_socket_addrs().unwrap().next().unwrap(); + let peer2 = "1.2.3.5:7890".to_socket_addrs().unwrap().next().unwrap(); + assert!(table.lookup(&addr).is_none()); + table.learn(addr.clone(), None, peer.clone()); + assert_eq!(table.lookup(&addr), Some(peer)); + // Do not override within 1 seconds + table.learn(addr.clone(), None, peer2.clone()); + assert_eq!(table.lookup(&addr), Some(peer)); + thread::sleep(std::time::Duration::from_secs(1)); + table.learn(addr.clone(), None, peer2.clone()); + assert_eq!(table.lookup(&addr), Some(peer2)); +} diff --git a/src/ip.rs b/src/ip.rs index a715f21..338493b 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -172,3 +172,109 @@ impl Table for RoutingTable { } } } + + +#[cfg(test)] use std::str::FromStr; +#[cfg(test)] use std::net::ToSocketAddrs; + + +#[test] +fn decode_ipv4_packet() { + let data = [0x40,0,0,0,0,0,0,0,0,0,0,0,192,168,1,1,192,168,1,2]; + let (src, dst) = Packet::parse(&data).unwrap(); + assert_eq!(src, Address{data: [192,168,1,1,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}); + assert_eq!(dst, Address{data: [192,168,1,2,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}); +} + +#[test] +fn decode_ipv6_packet() { + 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]; + 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!(dst, Address{data: [0,9,8,7,6,5,4,3,2,1,6,5,4,3,2,1], len: 16}); +} + +#[test] +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(&[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 + assert!(Packet::parse(&[]).is_err()); + // wrong version + assert!(Packet::parse(&[0x20]).is_err()); + // 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()); + // truncated ipv6 + 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()); +} + + +#[test] +fn routing_table_ipv4() { + let mut table = RoutingTable::new(); + let peer1 = "1.2.3.4:1".to_socket_addrs().unwrap().next().unwrap(); + let peer2 = "1.2.3.4:2".to_socket_addrs().unwrap().next().unwrap(); + let peer3 = "1.2.3.4:3".to_socket_addrs().unwrap().next().unwrap(); + assert!(table.lookup(&Address::from_str("192.168.1.1").unwrap()).is_none()); + table.learn(Address::from_str("192.168.1.1").unwrap(), Some(32), peer1.clone()); + assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); + table.learn(Address::from_str("192.168.1.2").unwrap(), None, peer2.clone()); + assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2)); + table.learn(Address::from_str("192.168.1.0").unwrap(), Some(24), peer3.clone()); + assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2)); + assert_eq!(table.lookup(&Address::from_str("192.168.1.3").unwrap()), Some(peer3)); + table.learn(Address::from_str("192.168.0.0").unwrap(), Some(16), peer1.clone()); + assert_eq!(table.lookup(&Address::from_str("192.168.2.1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2)); + assert_eq!(table.lookup(&Address::from_str("192.168.1.3").unwrap()), Some(peer3)); + table.learn(Address::from_str("0.0.0.0").unwrap(), Some(0), peer2.clone()); + assert_eq!(table.lookup(&Address::from_str("192.168.2.1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2)); + assert_eq!(table.lookup(&Address::from_str("192.168.1.3").unwrap()), Some(peer3)); + assert_eq!(table.lookup(&Address::from_str("1.2.3.4").unwrap()), Some(peer2)); + table.learn(Address::from_str("192.168.2.0").unwrap(), Some(27), peer3.clone()); + assert_eq!(table.lookup(&Address::from_str("192.168.2.31").unwrap()), Some(peer3)); + assert_eq!(table.lookup(&Address::from_str("192.168.2.32").unwrap()), Some(peer1)); + table.learn(Address::from_str("192.168.2.0").unwrap(), Some(28), peer3.clone()); + assert_eq!(table.lookup(&Address::from_str("192.168.2.1").unwrap()), Some(peer3)); +} + +#[test] +fn routing_table_ipv6() { + let mut table = RoutingTable::new(); + let peer1 = "::1:1".to_socket_addrs().unwrap().next().unwrap(); + let peer2 = "::1:2".to_socket_addrs().unwrap().next().unwrap(); + let peer3 = "::1:3".to_socket_addrs().unwrap().next().unwrap(); + assert!(table.lookup(&Address::from_str("::1").unwrap()).is_none()); + table.learn(Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap(), Some(128), peer1.clone()); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); + table.learn(Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap(), None, peer2.clone()); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap()), Some(peer2)); + table.learn(Address::from_str("dead:beef:dead:beef::").unwrap(), Some(64), peer3.clone()); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap()), Some(peer2)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:3").unwrap()), Some(peer3)); + table.learn(Address::from_str("dead:beef:dead:be00::").unwrap(), Some(56), peer1.clone()); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:1::").unwrap()), Some(peer3)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:be01::").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap()), Some(peer2)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:3").unwrap()), Some(peer3)); + table.learn(Address::from_str("::").unwrap(), Some(0), peer2.clone()); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:1::").unwrap()), Some(peer3)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:be01::").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap()), Some(peer2)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:3").unwrap()), Some(peer3)); + assert_eq!(table.lookup(&Address::from_str("::1").unwrap()), Some(peer2)); + table.learn(Address::from_str("dead:beef:dead:beef:dead:beef:dead:be00").unwrap(), Some(123), peer2.clone()); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:be1f").unwrap()), Some(peer2)); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:be20").unwrap()), Some(peer3)); + table.learn(Address::from_str("dead:beef:dead:beef:dead:beef:dead:be00").unwrap(), Some(124), peer3.clone()); + assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:be01").unwrap()), Some(peer3)); +} diff --git a/src/main.rs b/src/main.rs index 56937ac..e43e88a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ extern crate igd; extern crate siphasher; extern crate daemonize; extern crate ring; -extern crate bs58; +extern crate base_62; #[cfg(feature = "bench")] extern crate test; #[macro_use] pub mod util; @@ -37,7 +37,6 @@ pub mod config; pub mod port_forwarding; pub mod traffic; pub mod beacon; -#[cfg(test)] mod tests; #[cfg(feature = "bench")] mod benches; use docopt::Docopt; @@ -225,7 +224,6 @@ fn run (config: Config) { Mode::Switch => (true, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))), Mode::Hub => (false, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))) }; - Crypto::init(); let crypto = match config.shared_key { Some(ref key) => Crypto::from_shared_key(config.crypto, key), None => Crypto::None @@ -264,11 +262,8 @@ fn run (config: Config) { } fn main() { - beacon::test(); - return; let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit()); if args.flag_version { - Crypto::init(); println!("VpnCloud v{}, protocol version {}", env!("CARGO_PKG_VERSION"), VERSION diff --git a/src/tests.rs b/src/tests.rs deleted file mode 100644 index 2f5e74b..0000000 --- a/src/tests.rs +++ /dev/null @@ -1,591 +0,0 @@ -// VpnCloud - Peer-to-Peer VPN -// Copyright (C) 2015-2017 Dennis Schwerdel -// This software is licensed under GPL-3 or newer (see LICENSE.md) - -use std::net::{ToSocketAddrs, SocketAddr}; -use std::str::FromStr; -use std::thread; -use std::time::Duration; - -use serde_yaml; - -use super::MAGIC; -use super::ethernet::{Frame, SwitchTable}; -use super::ip::{RoutingTable, Packet}; -use super::device::Type; -use super::types::{Protocol, Address, Range, Table, Mode}; -use super::udpmessage::{Message, decode, encode}; -use super::crypto::{Crypto, CryptoMethod}; -use super::config::{Config, ConfigFile}; -use super::Args; - - -impl<'a> PartialEq for Message<'a> { - fn eq(&self, other: &Message) -> bool { - match self { - &Message::Data(ref data1, start1, end1) => if let &Message::Data(ref data2, start2, end2) = other { - data1[start1..end1] == data2[start2..end2] - } else { false }, - &Message::Peers(ref peers1) => if let &Message::Peers(ref peers2) = other { - peers1 == peers2 - } else { false }, - &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 - } else { false }, - &Message::Close => if let &Message::Close = other { - true - } else { false } - } - } -} - -#[test] -#[allow(unused_assignments)] -fn udpmessage_packet() { - let mut crypto = Crypto::None; - 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, - 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]; - let mut msg = Message::Data(&mut payload, 64, 69); - let mut buf = [0; 1024]; - let mut len = 0; - { - let res = encode(&mut msg, &mut [], MAGIC, &mut crypto); - assert_eq!(res.len(), 13); - assert_eq!(&res[..8], &[118,112,110,1,0,0,0,0]); - for i in 0..res.len() { - buf[i] = res[i]; - } - len = res.len(); - } - let msg2 = decode(&mut buf[..len], MAGIC, &mut crypto).unwrap(); - assert_eq!(msg, msg2); -} - -#[test] -#[allow(unused_assignments)] -fn udpmessage_encrypted() { - let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); - 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, - 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]; - let mut orig_payload = [0; 133]; - for i in 0..payload.len() { - orig_payload[i] = payload[i]; - } - let orig_msg = Message::Data(&mut orig_payload, 64, 69); - let mut msg = Message::Data(&mut payload, 64, 69); - let mut buf = [0; 1024]; - let mut len = 0; - { - let res = encode(&mut msg, &mut [], MAGIC, &mut crypto); - assert_eq!(res.len(), 41); - assert_eq!(&res[..8], &[118,112,110,1,1,0,0,0]); - for i in 0..res.len() { - buf[i] = res[i]; - } - len = res.len(); - } - let msg2 = decode(&mut buf[..len], MAGIC, &mut crypto).unwrap(); - assert_eq!(orig_msg, msg2); -} - -#[test] -fn udpmessage_peers() { - use std::str::FromStr; - let mut crypto = Crypto::None; - 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()]); - 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 res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto); - assert_eq!(res.len(), 40); - for i in 0..res.len() { - assert_eq!(res[i], should[i]); - } - } - let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap(); - assert_eq!(msg, msg2); - // Missing IPv4 count - assert!(decode(&mut[118,112,110,1,0,0,0,1], MAGIC, &mut crypto).is_err()); - // Truncated IPv4 - assert!(decode(&mut[118,112,110,1,0,0,0,1,1], MAGIC, &mut crypto).is_err()); - // Missing IPv6 count - assert!(decode(&mut[118,112,110,1,0,0,0,1,1,1,2,3,4,0,0], MAGIC, &mut crypto).is_err()); - // Truncated IPv6 - assert!(decode(&mut[118,112,110,1,0,0,0,1,1,1,2,3,4,0,0,1], MAGIC, &mut crypto).is_err()); -} - -#[test] -fn udpmessage_init() { - use super::types::Address; - let mut crypto = Crypto::None; - 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,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 mut msg = Message::Init(0, node_id, addrs); - 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]; - { - let mut buf = [0; 1024]; - let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto); - assert_eq!(res.len(), 40); - for i in 0..res.len() { - assert_eq!(res[i], should[i]); - } - } - let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap(); - assert_eq!(msg, msg2); -} - -#[test] -fn udpmessage_close() { - let mut crypto = Crypto::None; - let mut msg = Message::Close; - let mut should = [118,112,110,1,0,0,0,3]; - { - let mut buf = [0; 1024]; - let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto); - assert_eq!(res.len(), 8); - assert_eq!(&res, &should); - } - let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap(); - assert_eq!(msg, msg2); -} - -#[test] -fn udpmessage_invalid() { - let mut crypto = Crypto::None; - assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0], MAGIC, &mut crypto).is_ok()); - // too short - assert!(decode(&mut [], MAGIC, &mut crypto).is_err()); - // invalid protocol - assert!(decode(&mut [0,1,2,0,0,0,0,0], MAGIC, &mut crypto).is_err()); - // invalid version - assert!(decode(&mut [0x76,0x70,0x6e,0xaa,0,0,0,0], MAGIC, &mut crypto).is_err()); - // invalid crypto - assert!(decode(&mut [0x76,0x70,0x6e,1,0xaa,0,0,0], MAGIC, &mut crypto).is_err()); - // invalid msg type - assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0xaa], MAGIC, &mut crypto).is_err()); -} - -#[test] -fn udpmessage_invalid_crypto() { - let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); - // truncated crypto - assert!(decode(&mut [0x76,0x70,0x6e,1,1,0,0,0], MAGIC, &mut crypto).is_err()); -} - - -#[test] -fn decode_frame_without_vlan() { - let data = [6,5,4,3,2,1,1,2,3,4,5,6,1,2,3,4,5,6,7,8]; - let (src, dst) = Frame::parse(&data).unwrap(); - assert_eq!(src, Address{data: [1,2,3,4,5,6,0,0,0,0,0,0,0,0,0,0], len: 6}); - assert_eq!(dst, Address{data: [6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0], len: 6}); -} - -#[test] -fn decode_frame_with_vlan() { - let data = [6,5,4,3,2,1,1,2,3,4,5,6,0x81,0,4,210,1,2,3,4,5,6,7,8]; - let (src, dst) = Frame::parse(&data).unwrap(); - assert_eq!(src, Address{data: [4,210,1,2,3,4,5,6,0,0,0,0,0,0,0,0], len: 8}); - assert_eq!(dst, Address{data: [4,210,6,5,4,3,2,1,0,0,0,0,0,0,0,0], len: 8}); -} - -#[test] -fn decode_invalid_frame() { - assert!(Frame::parse(&[6,5,4,3,2,1,1,2,3,4,5,6,1,2,3,4,5,6,7,8]).is_ok()); - // truncated frame - assert!(Frame::parse(&[]).is_err()); - // truncated vlan frame - assert!(Frame::parse(&[6,5,4,3,2,1,1,2,3,4,5,6,0x81,0x00]).is_err()); -} - - -#[test] -fn decode_ipv4_packet() { - let data = [0x40,0,0,0,0,0,0,0,0,0,0,0,192,168,1,1,192,168,1,2]; - let (src, dst) = Packet::parse(&data).unwrap(); - assert_eq!(src, Address{data: [192,168,1,1,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}); - assert_eq!(dst, Address{data: [192,168,1,2,0,0,0,0,0,0,0,0,0,0,0,0], len: 4}); -} - -#[test] -fn decode_ipv6_packet() { - 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]; - 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!(dst, Address{data: [0,9,8,7,6,5,4,3,2,1,6,5,4,3,2,1], len: 16}); -} - -#[test] -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(&[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 - assert!(Packet::parse(&[]).is_err()); - // wrong version - assert!(Packet::parse(&[0x20]).is_err()); - // 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()); - // truncated ipv6 - 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()); -} - - -#[test] -fn switch() { - let mut table = SwitchTable::new(10, 1); - let addr = Address::from_str("12:34:56:78:90:ab").unwrap(); - let peer = "1.2.3.4:5678".to_socket_addrs().unwrap().next().unwrap(); - let peer2 = "1.2.3.5:7890".to_socket_addrs().unwrap().next().unwrap(); - assert!(table.lookup(&addr).is_none()); - table.learn(addr.clone(), None, peer.clone()); - assert_eq!(table.lookup(&addr), Some(peer)); - // Do not override within 1 seconds - table.learn(addr.clone(), None, peer2.clone()); - assert_eq!(table.lookup(&addr), Some(peer)); - thread::sleep(Duration::from_secs(1)); - table.learn(addr.clone(), None, peer2.clone()); - assert_eq!(table.lookup(&addr), Some(peer2)); -} - -#[test] -fn routing_table_ipv4() { - let mut table = RoutingTable::new(); - let peer1 = "1.2.3.4:1".to_socket_addrs().unwrap().next().unwrap(); - let peer2 = "1.2.3.4:2".to_socket_addrs().unwrap().next().unwrap(); - let peer3 = "1.2.3.4:3".to_socket_addrs().unwrap().next().unwrap(); - assert!(table.lookup(&Address::from_str("192.168.1.1").unwrap()).is_none()); - table.learn(Address::from_str("192.168.1.1").unwrap(), Some(32), peer1.clone()); - assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); - table.learn(Address::from_str("192.168.1.2").unwrap(), None, peer2.clone()); - assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2)); - table.learn(Address::from_str("192.168.1.0").unwrap(), Some(24), peer3.clone()); - assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2)); - assert_eq!(table.lookup(&Address::from_str("192.168.1.3").unwrap()), Some(peer3)); - table.learn(Address::from_str("192.168.0.0").unwrap(), Some(16), peer1.clone()); - assert_eq!(table.lookup(&Address::from_str("192.168.2.1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2)); - assert_eq!(table.lookup(&Address::from_str("192.168.1.3").unwrap()), Some(peer3)); - table.learn(Address::from_str("0.0.0.0").unwrap(), Some(0), peer2.clone()); - assert_eq!(table.lookup(&Address::from_str("192.168.2.1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("192.168.1.1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("192.168.1.2").unwrap()), Some(peer2)); - assert_eq!(table.lookup(&Address::from_str("192.168.1.3").unwrap()), Some(peer3)); - assert_eq!(table.lookup(&Address::from_str("1.2.3.4").unwrap()), Some(peer2)); - table.learn(Address::from_str("192.168.2.0").unwrap(), Some(27), peer3.clone()); - assert_eq!(table.lookup(&Address::from_str("192.168.2.31").unwrap()), Some(peer3)); - assert_eq!(table.lookup(&Address::from_str("192.168.2.32").unwrap()), Some(peer1)); - table.learn(Address::from_str("192.168.2.0").unwrap(), Some(28), peer3.clone()); - assert_eq!(table.lookup(&Address::from_str("192.168.2.1").unwrap()), Some(peer3)); -} - -#[test] -fn routing_table_ipv6() { - let mut table = RoutingTable::new(); - let peer1 = "::1:1".to_socket_addrs().unwrap().next().unwrap(); - let peer2 = "::1:2".to_socket_addrs().unwrap().next().unwrap(); - let peer3 = "::1:3".to_socket_addrs().unwrap().next().unwrap(); - assert!(table.lookup(&Address::from_str("::1").unwrap()).is_none()); - table.learn(Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap(), Some(128), peer1.clone()); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); - table.learn(Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap(), None, peer2.clone()); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap()), Some(peer2)); - table.learn(Address::from_str("dead:beef:dead:beef::").unwrap(), Some(64), peer3.clone()); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap()), Some(peer2)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:3").unwrap()), Some(peer3)); - table.learn(Address::from_str("dead:beef:dead:be00::").unwrap(), Some(56), peer1.clone()); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:1::").unwrap()), Some(peer3)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:be01::").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap()), Some(peer2)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:3").unwrap()), Some(peer3)); - table.learn(Address::from_str("::").unwrap(), Some(0), peer2.clone()); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:1::").unwrap()), Some(peer3)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:be01::").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:1").unwrap()), Some(peer1)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:2").unwrap()), Some(peer2)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:3").unwrap()), Some(peer3)); - assert_eq!(table.lookup(&Address::from_str("::1").unwrap()), Some(peer2)); - table.learn(Address::from_str("dead:beef:dead:beef:dead:beef:dead:be00").unwrap(), Some(123), peer2.clone()); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:be1f").unwrap()), Some(peer2)); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:be20").unwrap()), Some(peer3)); - table.learn(Address::from_str("dead:beef:dead:beef:dead:beef:dead:be00").unwrap(), Some(124), peer3.clone()); - assert_eq!(table.lookup(&Address::from_str("dead:beef:dead:beef:dead:beef:dead:be01").unwrap()), Some(peer3)); -} - -#[test] -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("78:2d:16:05:01:02").unwrap()), "78:2d:16:05:01:02"); - 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"); - 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!(Address::from_str("").is_err()); // Failed to parse address -} - -#[test] -fn address_decode_encode() { - let mut buf = [0; 32]; - let addr = Address::from_str("120.45.22.5").unwrap(); - assert_eq!(addr.write_to(&mut buf), 5); - assert_eq!(&buf[0..5], &[4, 120, 45, 22, 5]); - assert_eq!((addr, 5), Address::read_from(&buf).unwrap()); - assert_eq!(addr, Address::read_from_fixed(&buf[1..], 4).unwrap()); - let addr = Address::from_str("78:2d:16:05:01:02").unwrap(); - assert_eq!(addr.write_to(&mut buf), 7); - assert_eq!(&buf[0..7], &[6, 0x78, 0x2d, 0x16, 0x05, 0x01, 0x02]); - assert_eq!((addr, 7), Address::read_from(&buf).unwrap()); - assert_eq!(addr, Address::read_from_fixed(&buf[1..], 6).unwrap()); - assert!(Address::read_from(&buf[0..0]).is_err()); // Address too short - buf[0] = 100; - assert!(Address::read_from(&buf).is_err()); // Invalid address, too long - buf[0] = 5; - assert!(Address::read_from(&buf[0..4]).is_err()); // Address too short -} - -#[test] -fn address_eq() { - assert!(Address::read_from_fixed(&[1,2,3,4], 4).unwrap() == Address::read_from_fixed(&[1,2,3,4], 4).unwrap()); - assert!(Address::read_from_fixed(&[1,2,3,4], 4).unwrap() != Address::read_from_fixed(&[1,2,3,5], 4).unwrap()); - assert!(Address::read_from_fixed(&[1,2,3,4], 3).unwrap() == Address::read_from_fixed(&[1,2,3,5], 3).unwrap()); - assert!(Address::read_from_fixed(&[1,2,3,4], 3).unwrap() != Address::read_from_fixed(&[1,2,3,4], 4).unwrap()); -} - -#[test] -fn address_range_decode_encode() { - let mut buf = [0; 32]; - 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}; - assert_eq!(range.write_to(&mut buf), 6); - assert_eq!(&buf[0..6], &[4, 0, 1, 2, 3, 24]); - assert_eq!((range, 6), Range::read_from(&buf).unwrap()); - assert!(Range::read_from(&buf[..5]).is_err()); // Missing prefix length - buf[0] = 17; - assert!(Range::read_from(&buf).is_err()); -} - -#[test] -fn message_fmt() { - assert_eq!(format!("{:?}", Message::Data(&mut [1,2,3,4,5], 0, 5)), "Data(5 bytes)"); - assert_eq!(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("[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![ - 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"); -} - -#[test] -fn encrypt_decrypt_chacha20poly1305() { - let mut sender = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); - let receiver = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); - let msg = "HelloWorld0123456789"; - let msg_bytes = msg.as_bytes(); - let mut buffer = [0u8; 1024]; - let header = [0u8; 8]; - for i in 0..msg_bytes.len() { - buffer[i] = msg_bytes[i]; - } - let mut nonce1 = [0u8; 12]; - let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce1, &header); - assert_eq!(size, msg_bytes.len() + sender.additional_bytes()); - assert!(msg_bytes != &buffer[..msg_bytes.len()] as &[u8]); - receiver.decrypt(&mut buffer[..size], &nonce1, &header).unwrap(); - assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]); - let mut nonce2 = [0u8; 12]; - let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce2, &header); - assert!(nonce1 != nonce2); - receiver.decrypt(&mut buffer[..size], &nonce2, &header).unwrap(); - assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]); -} - -#[test] -fn encrypt_decrypt_aes256() { - Crypto::init(); - if ! Crypto::aes256_available() { - return - } - let mut sender = Crypto::from_shared_key(CryptoMethod::AES256, "test"); - let receiver = Crypto::from_shared_key(CryptoMethod::AES256, "test"); - let msg = "HelloWorld0123456789"; - let msg_bytes = msg.as_bytes(); - let mut buffer = [0u8; 1024]; - let header = [0u8; 8]; - for i in 0..msg_bytes.len() { - buffer[i] = msg_bytes[i]; - } - let mut nonce1 = [0u8; 12]; - let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce1, &header); - assert_eq!(size, msg_bytes.len() + sender.additional_bytes()); - assert!(msg_bytes != &buffer[..msg_bytes.len()] as &[u8]); - receiver.decrypt(&mut buffer[..size], &nonce1, &header).unwrap(); - assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]); - let mut nonce2 = [0u8; 12]; - let size = sender.encrypt(&mut buffer, msg_bytes.len(), &mut nonce2, &header); - assert!(nonce1 != nonce2); - receiver.decrypt(&mut buffer[..size], &nonce2, &header).unwrap(); - assert_eq!(msg_bytes, &buffer[..msg_bytes.len()] as &[u8]); -} - -#[test] -fn config_file() { - let config_file = " -device_type: tun -device_name: vpncloud%d -device_path: /dev/net/tun -magic: 0123ABCD -ifup: ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up -ifdown: 'true' -crypto: aes256 -shared_key: mysecret -port: 3210 -peers: - - remote.machine.foo:3210 - - remote.machine.bar:3210 -peer_timeout: 1800 -keepalive: 840 -dst_timeout: 300 -mode: normal -subnets: - - 10.0.1.0/24 -port_forwarding: true -user: nobody -group: nogroup -pid_file: /run/vpncloud.run -stats_file: /var/log/vpncloud.stats - "; - assert_eq!(serde_yaml::from_str::(config_file).unwrap(), ConfigFile{ - device_type: Some(Type::Tun), - device_name: Some("vpncloud%d".to_string()), - device_path: Some("/dev/net/tun".to_string()), - ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()), - ifdown: Some("true".to_string()), - crypto: Some(CryptoMethod::AES256), - shared_key: Some("mysecret".to_string()), - magic: Some("0123ABCD".to_string()), - port: Some(3210), - peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]), - peer_timeout: Some(1800), - keepalive: Some(840), - mode: Some(Mode::Normal), - dst_timeout: Some(300), - subnets: Some(vec!["10.0.1.0/24".to_string()]), - port_forwarding: Some(true), - user: Some("nobody".to_string()), - group: Some("nogroup".to_string()), - pid_file: Some("/run/vpncloud.run".to_string()), - stats_file: Some("/var/log/vpncloud.stats".to_string()) - }) -} - -#[test] -fn config_merge() { - let mut config = Config::default(); - config.merge_file(ConfigFile{ - device_type: Some(Type::Tun), - device_name: Some("vpncloud%d".to_string()), - device_path: None, - ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()), - ifdown: Some("true".to_string()), - crypto: Some(CryptoMethod::AES256), - shared_key: Some("mysecret".to_string()), - magic: Some("0123ABCD".to_string()), - port: Some(3210), - peers: Some(vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()]), - peer_timeout: Some(1800), - keepalive: Some(840), - mode: Some(Mode::Normal), - dst_timeout: Some(300), - subnets: Some(vec!["10.0.1.0/24".to_string()]), - port_forwarding: Some(true), - user: Some("nobody".to_string()), - group: Some("nogroup".to_string()), - pid_file: Some("/run/vpncloud.run".to_string()), - stats_file: Some("/var/log/vpncloud.stats".to_string()) - }); - assert_eq!(config, Config{ - device_type: Type::Tun, - device_name: "vpncloud%d".to_string(), - device_path: None, - ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()), - ifdown: Some("true".to_string()), - magic: Some("0123ABCD".to_string()), - crypto: CryptoMethod::AES256, - shared_key: Some("mysecret".to_string()), - port: 3210, - peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()], - peer_timeout: 1800, - keepalive: Some(840), - dst_timeout: 300, - mode: Mode::Normal, - port_forwarding: true, - subnets: vec!["10.0.1.0/24".to_string()], - user: Some("nobody".to_string()), - group: Some("nogroup".to_string()), - pid_file: Some("/run/vpncloud.run".to_string()), - stats_file: Some("/var/log/vpncloud.stats".to_string()), - ..Default::default() - }); - config.merge_args(Args{ - flag_type: Some(Type::Tap), - flag_device: Some("vpncloud0".to_string()), - flag_device_path: Some("/dev/null".to_string()), - flag_ifup: Some("ifconfig $IFNAME 10.0.1.2/16 mtu 1400 up".to_string()), - flag_ifdown: Some("ifconfig $IFNAME down".to_string()), - flag_crypto: Some(CryptoMethod::ChaCha20), - flag_shared_key: Some("anothersecret".to_string()), - flag_magic: Some("hash:mynet".to_string()), - flag_listen: Some(3211), - flag_peer_timeout: Some(1801), - flag_keepalive: Some(850), - flag_dst_timeout: Some(301), - flag_mode: Some(Mode::Switch), - flag_subnet: vec![], - flag_connect: vec!["another:3210".to_string()], - flag_no_port_forwarding: true, - flag_daemon: true, - flag_pid_file: Some("/run/vpncloud-mynet.run".to_string()), - flag_stats_file: Some("/var/log/vpncloud-mynet.stats".to_string()), - flag_user: Some("root".to_string()), - flag_group: Some("root".to_string()), - ..Default::default() - }); - assert_eq!(config, Config{ - device_type: Type::Tap, - device_name: "vpncloud0".to_string(), - device_path: Some("/dev/null".to_string()), - ifup: Some("ifconfig $IFNAME 10.0.1.2/16 mtu 1400 up".to_string()), - ifdown: Some("ifconfig $IFNAME down".to_string()), - magic: Some("hash:mynet".to_string()), - crypto: CryptoMethod::ChaCha20, - shared_key: Some("anothersecret".to_string()), - port: 3211, - peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string(), "another:3210".to_string()], - peer_timeout: 1801, - keepalive: Some(850), - dst_timeout: 301, - mode: Mode::Switch, - port_forwarding: false, - subnets: vec!["10.0.1.0/24".to_string()], - user: Some("root".to_string()), - group: Some("root".to_string()), - pid_file: Some("/run/vpncloud-mynet.run".to_string()), - stats_file: Some("/var/log/vpncloud-mynet.stats".to_string()), - daemonize: true, - ..Default::default() - }); -} diff --git a/src/types.rs b/src/types.rs index c1e7ca1..a3ca926 100644 --- a/src/types.rs +++ b/src/types.rs @@ -241,4 +241,55 @@ impl fmt::Display for Error { Error::File(msg, ref err) => write!(formatter, "{}: {:?}", msg, err) } } -} \ No newline at end of file +} + + +#[test] +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("78:2d:16:05:01:02").unwrap()), "78:2d:16:05:01:02"); + 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"); + 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!(Address::from_str("").is_err()); // Failed to parse address +} + +#[test] +fn address_decode_encode() { + let mut buf = [0; 32]; + let addr = Address::from_str("120.45.22.5").unwrap(); + assert_eq!(addr.write_to(&mut buf), 5); + assert_eq!(&buf[0..5], &[4, 120, 45, 22, 5]); + assert_eq!((addr, 5), Address::read_from(&buf).unwrap()); + assert_eq!(addr, Address::read_from_fixed(&buf[1..], 4).unwrap()); + let addr = Address::from_str("78:2d:16:05:01:02").unwrap(); + assert_eq!(addr.write_to(&mut buf), 7); + assert_eq!(&buf[0..7], &[6, 0x78, 0x2d, 0x16, 0x05, 0x01, 0x02]); + assert_eq!((addr, 7), Address::read_from(&buf).unwrap()); + assert_eq!(addr, Address::read_from_fixed(&buf[1..], 6).unwrap()); + assert!(Address::read_from(&buf[0..0]).is_err()); // Address too short + buf[0] = 100; + assert!(Address::read_from(&buf).is_err()); // Invalid address, too long + buf[0] = 5; + assert!(Address::read_from(&buf[0..4]).is_err()); // Address too short +} + +#[test] +fn address_eq() { + assert!(Address::read_from_fixed(&[1,2,3,4], 4).unwrap() == Address::read_from_fixed(&[1,2,3,4], 4).unwrap()); + assert!(Address::read_from_fixed(&[1,2,3,4], 4).unwrap() != Address::read_from_fixed(&[1,2,3,5], 4).unwrap()); + assert!(Address::read_from_fixed(&[1,2,3,4], 3).unwrap() == Address::read_from_fixed(&[1,2,3,5], 3).unwrap()); + assert!(Address::read_from_fixed(&[1,2,3,4], 3).unwrap() != Address::read_from_fixed(&[1,2,3,4], 4).unwrap()); +} + +#[test] +fn address_range_decode_encode() { + let mut buf = [0; 32]; + 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}; + assert_eq!(range.write_to(&mut buf), 6); + assert_eq!(&buf[0..6], &[4, 0, 1, 2, 3, 24]); + assert_eq!((range, 6), Range::read_from(&buf).unwrap()); + assert!(Range::read_from(&buf[..5]).is_err()); // Missing prefix length + buf[0] = 17; + assert!(Range::read_from(&buf).is_err()); +} diff --git a/src/udpmessage.rs b/src/udpmessage.rs index 30f0df8..0014083 100644 --- a/src/udpmessage.rs +++ b/src/udpmessage.rs @@ -258,3 +258,185 @@ pub fn encode<'a>(msg: &'a mut Message, mut buf: &'a mut [u8], magic: HeaderMagi } &mut buf[start..end] } + + +impl<'a> PartialEq for Message<'a> { + fn eq(&self, other: &Message) -> bool { + match self { + &Message::Data(ref data1, start1, end1) => if let &Message::Data(ref data2, start2, end2) = other { + data1[start1..end1] == data2[start2..end2] + } else { false }, + &Message::Peers(ref peers1) => if let &Message::Peers(ref peers2) = other { + peers1 == peers2 + } else { false }, + &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 + } else { false }, + &Message::Close => if let &Message::Close = other { + true + } else { false } + } + } +} + + +#[cfg(test)] use std::str::FromStr; +#[cfg(test)] use super::MAGIC; +#[cfg(test)] use super::types::Address; +#[cfg(test)] use super::crypto::CryptoMethod; + +#[test] +#[allow(unused_assignments)] +fn udpmessage_packet() { + let mut crypto = Crypto::None; + 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, + 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]; + let mut msg = Message::Data(&mut payload, 64, 69); + let mut buf = [0; 1024]; + let mut len = 0; + { + let res = encode(&mut msg, &mut [], MAGIC, &mut crypto); + assert_eq!(res.len(), 13); + assert_eq!(&res[..8], &[118,112,110,1,0,0,0,0]); + for i in 0..res.len() { + buf[i] = res[i]; + } + len = res.len(); + } + let msg2 = decode(&mut buf[..len], MAGIC, &mut crypto).unwrap(); + assert_eq!(msg, msg2); +} + +#[test] +#[allow(unused_assignments)] +fn udpmessage_encrypted() { + let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); + 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, + 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]; + let mut orig_payload = [0; 133]; + for i in 0..payload.len() { + orig_payload[i] = payload[i]; + } + let orig_msg = Message::Data(&mut orig_payload, 64, 69); + let mut msg = Message::Data(&mut payload, 64, 69); + let mut buf = [0; 1024]; + let mut len = 0; + { + let res = encode(&mut msg, &mut [], MAGIC, &mut crypto); + assert_eq!(res.len(), 41); + assert_eq!(&res[..8], &[118,112,110,1,1,0,0,0]); + for i in 0..res.len() { + buf[i] = res[i]; + } + len = res.len(); + } + let msg2 = decode(&mut buf[..len], MAGIC, &mut crypto).unwrap(); + assert_eq!(orig_msg, msg2); +} + +#[test] +fn udpmessage_peers() { + use std::str::FromStr; + let mut crypto = Crypto::None; + 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()]); + 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 res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto); + assert_eq!(res.len(), 40); + for i in 0..res.len() { + assert_eq!(res[i], should[i]); + } + } + let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap(); + assert_eq!(msg, msg2); + // Missing IPv4 count + assert!(decode(&mut[118,112,110,1,0,0,0,1], MAGIC, &mut crypto).is_err()); + // Truncated IPv4 + assert!(decode(&mut[118,112,110,1,0,0,0,1,1], MAGIC, &mut crypto).is_err()); + // Missing IPv6 count + assert!(decode(&mut[118,112,110,1,0,0,0,1,1,1,2,3,4,0,0], MAGIC, &mut crypto).is_err()); + // Truncated IPv6 + assert!(decode(&mut[118,112,110,1,0,0,0,1,1,1,2,3,4,0,0,1], MAGIC, &mut crypto).is_err()); +} + +#[test] +fn udpmessage_init() { + use super::types::Address; + let mut crypto = Crypto::None; + 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,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 mut msg = Message::Init(0, node_id, addrs); + 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]; + { + let mut buf = [0; 1024]; + let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto); + assert_eq!(res.len(), 40); + for i in 0..res.len() { + assert_eq!(res[i], should[i]); + } + } + let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap(); + assert_eq!(msg, msg2); +} + +#[test] +fn udpmessage_close() { + let mut crypto = Crypto::None; + let mut msg = Message::Close; + let mut should = [118,112,110,1,0,0,0,3]; + { + let mut buf = [0; 1024]; + let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto); + assert_eq!(res.len(), 8); + assert_eq!(&res, &should); + } + let msg2 = decode(&mut should, MAGIC, &mut crypto).unwrap(); + assert_eq!(msg, msg2); +} + +#[test] +fn udpmessage_invalid() { + let mut crypto = Crypto::None; + assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0], MAGIC, &mut crypto).is_ok()); + // too short + assert!(decode(&mut [], MAGIC, &mut crypto).is_err()); + // invalid protocol + assert!(decode(&mut [0,1,2,0,0,0,0,0], MAGIC, &mut crypto).is_err()); + // invalid version + assert!(decode(&mut [0x76,0x70,0x6e,0xaa,0,0,0,0], MAGIC, &mut crypto).is_err()); + // invalid crypto + assert!(decode(&mut [0x76,0x70,0x6e,1,0xaa,0,0,0], MAGIC, &mut crypto).is_err()); + // invalid msg type + assert!(decode(&mut [0x76,0x70,0x6e,1,0,0,0,0xaa], MAGIC, &mut crypto).is_err()); +} + +#[test] +fn udpmessage_invalid_crypto() { + let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); + // truncated crypto + assert!(decode(&mut [0x76,0x70,0x6e,1,1,0,0,0], MAGIC, &mut crypto).is_err()); +} + + +#[test] +fn message_fmt() { + assert_eq!(format!("{:?}", Message::Data(&mut [1,2,3,4,5], 0, 5)), "Data(5 bytes)"); + assert_eq!(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("[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![ + 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"); +}