Compare commits

...

5 Commits

Author SHA1 Message Date
Dennis Schwerdel 50058e60d0 Fixed wrong base62 encoding 2019-12-04 16:33:42 +01:00
Dennis Schwerdel 229db241de Fixed potential startup dependency issue 2019-12-04 13:43:38 +01:00
Dennis Schwerdel 63dc1e3ce1 Tests for NAT 2019-12-04 13:09:20 +01:00
Dennis Schwerdel 9cd7e53880 Reducing published peer timeout to 5 min on NAT 2019-12-04 10:34:08 +01:00
Dennis Schwerdel ee8542307f Exchange peer_timeout and adapt keepalive 2019-12-04 10:04:42 +01:00
19 changed files with 440 additions and 237 deletions

View File

@ -2,10 +2,15 @@
This project follows [semantic versioning](http://semver.org).
### Unreleased
### v1.1.0 (2019-12-04)
- [changed] Rust version 1.41.0
- [added] Exchange peer timeout and adapt keepalive accordingly
- [added] Reducing published peer timeout to 5 min when NAT is detected
- [added] Added more tests
- [changed] Rust version 1.39.0
- [changed] Updated dependencies
- [fixed] Fixed potential startup dependency issue
- [fixed] Fixed wrong base62 encoding
### v1.0.0 (2019-03-21)

113
Cargo.lock generated
View File

@ -23,42 +23,6 @@ dependencies = [
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "autocfg"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (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.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.2.1"
@ -131,26 +95,6 @@ name = "dtoa"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "failure"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure_derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fnv"
version = "1.0.6"
@ -295,33 +239,6 @@ dependencies = [
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-bigint"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "percent-encoding"
version = "1.0.1"
@ -466,11 +383,6 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.103"
@ -544,17 +456,6 @@ dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tempfile"
version = "3.1.0"
@ -639,9 +540,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vpncloud"
version = "1.0.0"
version = "1.1.0"
dependencies = [
"base-62 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
"daemonize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -799,10 +699,6 @@ dependencies = [
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum anyhow 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9267dff192e68f3399525901e709a48c1d3982c9c072fa32f2127a0cb0babf14"
"checksum attohttpc 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4da2f0d9a8eb4e0aabc162278968eaf753999aa4aed173b753f9fa8da4cb86"
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea"
"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491"
"checksum base-62 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f28ebd71b3e708e895b83ec2d35c6e2ef96e34945706bf4d73826354e84f89b2"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum boxfnonce 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5988cb1d626264ac94100be357308f29ff7cbdd3b36bda27f450a4ee3f713426"
"checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708"
@ -814,8 +710,6 @@ dependencies = [
"checksum daemonize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70c24513e34f53b640819f0ac9f705b673fcf4006d7aab8778bee72ebfc89815"
"checksum docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f525a586d310c87df72ebcd98009e57f1cc030c8c268305287a476beb653969"
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08"
"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 getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
@ -835,9 +729,6 @@ dependencies = [
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a"
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
@ -856,7 +747,6 @@ dependencies = [
"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac"
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
"checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702"
"checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0"
"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
@ -867,7 +757,6 @@ dependencies = [
"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
"checksum strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "032c03039aae92b350aad2e3779c352e104d919cb192ba2fabbd7b831ce4f0f6"
"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
"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"

View File

@ -1,6 +1,6 @@
[package]
name = "vpncloud"
version = "1.0.0"
version = "1.1.0"
authors = ["Dennis Schwerdel <schwerdel@googlemail.com>"]
build = "build.rs"
license = "GPL-3.0"
@ -28,7 +28,6 @@ igd = "0.9"
siphasher = "0.3"
daemonize = "0.4"
ring = "0.16"
base-62 = "0.1"
[build-dependencies]
cc = "^1"

View File

@ -102,6 +102,4 @@ contributions are very welcome:
### Semantic Versioning
This project uses [semantic versioning](http://semver.org). Currently that means
that everything can change between versions before 1.0 is finally released.
However I am considering to release 1.0 soon.
This project uses [semantic versioning](http://semver.org).

View File

@ -1,3 +1,15 @@
vpncloud (1.1.0) stable; urgency=medium
* [added] Exchange peer timeout and adapt keepalive accordingly
* [added] Reducing published peer timeout to 5 min when NAT is detected
* [added] Added more tests
* [changed] Rust version 1.41.0
* [changed] Updated dependencies
* [fixed] Fixed potential startup dependency issue
* [fixed] Fixed wrong base62 encoding
-- Dennis Schwerdel <schwerdel@googlemail.com> Thu, 04 Dec 2019 19:01:34 +0100
vpncloud (1.0.0) stable; urgency=medium
* [added] Added ability to publish small beacons for rendezvous

View File

@ -1,6 +1,7 @@
[Unit]
Description=VpnCloud network '%I'
Before=network-online.target
After=network-online.target
Wants=network-online.target
[Service]
Type=forking

View File

@ -9,7 +9,7 @@ RUN useradd -ms /bin/bash user
USER user
WORKDIR /home/user
ENV RUST=1.33.0
ENV RUST=1.39.0
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain ${RUST}

View File

@ -6,7 +6,7 @@ RUN useradd -ms /bin/bash user
USER user
WORKDIR /home/user
ENV RUST=1.33.0
ENV RUST=1.39.0
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain ${RUST}

View File

@ -2,7 +2,6 @@
// Copyright (C) 2019-2019 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
use base_62;
use ring::digest;
use std::{
@ -21,7 +20,7 @@ use std::{
thread
};
use super::util::{Encoder, TimeSource};
use super::util::{from_base62, to_base62, Encoder, TimeSource};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
@ -90,11 +89,11 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
}
fn begin(&self) -> String {
base_62::encode(&self.get_keystream(TYPE_BEGIN, 0, 0))[0..5].to_string()
to_base62(&self.get_keystream(TYPE_BEGIN, 0, 0))[0..5].to_string()
}
fn end(&self) -> String {
base_62::encode(&self.get_keystream(TYPE_END, 0, 0))[0..5].to_string()
to_base62(&self.get_keystream(TYPE_END, 0, 0))[0..5].to_string()
}
fn encrypt_data(&self, data: &mut Vec<u8>) {
// Note: the 1 byte seed is only meant to protect from random changes,
@ -152,11 +151,11 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
data.extend_from_slice(&dat);
}
self.encrypt_data(&mut data);
base_62::encode(&data)
to_base62(&data)
}
fn peerlist_decode(&self, data: &str, ttl_hours: Option<u16>) -> Vec<SocketAddr> {
let mut data = base_62::decode(data).expect("Invalid input");
let mut data = from_base62(data).expect("Invalid input");
let mut peers = Vec::new();
let mut pos = 0;
if data.len() < 4 {
@ -327,13 +326,13 @@ fn encode() {
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!("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", ser.encode(&peers));
assert_eq!("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", ser.encode(&peers));
peers.push(SocketAddr::from_str("[::1]:5678").unwrap());
assert_eq!("juWwKjF5qZG7PE5imnpi5XARaXnP3UsMsGBLxM4FNFDzvjlKt1SO55LN", ser.encode(&peers));
assert_eq!("3hRD8BKvg7jotek0FGLeYtIc1zj7jzPRyQscQAe9tCqnFJ0vyVfIxYMB", ser.encode(&peers));
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:54").unwrap());
assert_eq!("juWwKIgSqTammVFRNoIVzLPO0BEO55LN", ser.encode(&peers));
assert_eq!("3hRD86NwMC5dPp8bh5idzhMal4AIxYMB", ser.encode(&peers));
}
#[test]
@ -343,11 +342,11 @@ fn decode() {
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("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None)));
assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", None)));
peers.push(SocketAddr::from_str("[::1]:5678").unwrap());
assert_eq!(
format!("{:?}", peers),
format!("{:?}", ser.decode("juWwKjF5qZG7PE5imnpi5XARaXnP3UsMsGBLxM4FNFDzvjlKt1SO55LN", None))
format!("{:?}", ser.decode("3hRD8BKvg7jotek0FGLeYtIc1zj7jzPRyQscQAe9tCqnFJ0vyVfIxYMB", None))
);
}
@ -360,11 +359,11 @@ fn decode_split() {
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
assert_eq!(
format!("{:?}", peers),
format!("{:?}", ser.decode("juWwK-hj.VT:Yj bw\tJj\ntY(AZ)lM[fE]j7üIDäO55LN", None))
format!("{:?}", ser.decode("3hRD8-5V.3h:1P 0g\t5U\nn9(ZW)no[qR]Doü7ZäIxYMB", None))
);
assert_eq!(
format!("{:?}", peers),
format!("{:?}", ser.decode("j -, \nuW--wKhjVTYjbwJjtYAZlMfEj7IDO(5}5ÖÄÜ\nLN", None))
format!("{:?}", ser.decode("3 -, \nhR--D85V3h1P0g5Un9ZWnoqRDo7ZI(x}YÖÄÜ\nMB", None))
);
}
@ -377,7 +376,7 @@ fn decode_offset() {
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
assert_eq!(
format!("{:?}", peers),
format!("{:?}", ser.decode("Hello World: juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN! End of the World", None))
format!("{:?}", ser.decode("Hello World: 3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB! End of the World", None))
);
}
@ -390,7 +389,7 @@ fn decode_multiple() {
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
assert_eq!(
format!("{:?}", peers),
format!("{:?}", ser.decode("juWwKkBEVBp9SsDiN3BO55LN juWwKtGGPQz1gXIBd68O55LN", None))
format!("{:?}", ser.decode("3hRD850fTOmqFffvcJEIxYMB 3hRD823uwTS47pupeONIxYMB", None))
);
}
@ -402,23 +401,23 @@ fn decode_ttl() {
peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap());
peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
MockTimeSource::set_time(2000 * 3600);
assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len());
assert_eq!(2, ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", None).len());
MockTimeSource::set_time(2100 * 3600);
assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len());
assert_eq!(2, ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", None).len());
MockTimeSource::set_time(2005 * 3600);
assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len());
assert_eq!(2, ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", None).len());
MockTimeSource::set_time(1995 * 3600);
assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len());
assert_eq!(2, ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", None).len());
MockTimeSource::set_time(2000 * 3600);
assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len());
assert_eq!(2, ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", Some(24)).len());
MockTimeSource::set_time(1995 * 3600);
assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len());
assert_eq!(2, ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", Some(24)).len());
MockTimeSource::set_time(2005 * 3600);
assert_eq!(2, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len());
assert_eq!(2, ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", Some(24)).len());
MockTimeSource::set_time(2100 * 3600);
assert_eq!(0, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len());
assert_eq!(0, ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", Some(24)).len());
MockTimeSource::set_time(1900 * 3600);
assert_eq!(0, ser.decode("juWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", Some(24)).len());
assert_eq!(0, ser.decode("3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB", Some(24)).len());
}
#[test]
@ -426,12 +425,12 @@ fn decode_invalid() {
MockTimeSource::set_time(2000 * 3600);
let ser = BeaconSerializer::<MockTimeSource>::new(b"vpnc", b"mysecretkey");
assert_eq!(0, ser.decode("", None).len());
assert_eq!(0, ser.decode("juWwKO55LN", None).len());
assert_eq!(0, ser.decode("juWwK--", None).len());
assert_eq!(0, ser.decode("--O55LN", None).len());
assert_eq!(0, ser.decode("juWwKhjVTYjbwJjtYAZXMfEj7IDO55LN", None).len());
assert_eq!(2, ser.decode("SGrivjuWwKhjVTYjbwJjtYAZlMfEj7IDO55LNjuWwK", None).len());
assert_eq!(2, ser.decode("juWwKjuWwKhjVTYjbwJjtYAZlMfEj7IDO55LN", None).len());
assert_eq!(0, ser.decode("3hRD8IxYMB", None).len());
assert_eq!(0, ser.decode("3hRD8--", None).len());
assert_eq!(0, ser.decode("--IxYMB", None).len());
assert_eq!(0, ser.decode("3hRD85V3h1P0g5Un8ZWnoqRDo7ZIxYMB", None).len());
assert_eq!(2, ser.decode("IxYMB3hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMB3hRD8", None).len());
assert_eq!(2, ser.decode("3hRD83hRD85V3h1P0g5Un9ZWnoqRDo7ZIxYMBIxYMB", None).len());
}

View File

@ -40,6 +40,7 @@ pub const STATS_INTERVAL: Time = 60;
struct PeerData {
timeout: Time,
peer_timeout: u16,
node_id: NodeId,
alt_addrs: Vec<SocketAddr>
}
@ -84,6 +85,10 @@ impl<TS: TimeSource> PeerList<TS> {
del
}
pub fn min_peer_timeout(&self) -> u16 {
self.peers.iter().map(|p| p.1.peer_timeout).min().unwrap_or(1800)
}
#[inline]
pub fn contains_addr(&self, addr: &SocketAddr) -> bool {
self.addresses.contains_key(addr)
@ -106,13 +111,14 @@ impl<TS: TimeSource> PeerList<TS> {
#[inline]
fn add(&mut self, node_id: NodeId, addr: SocketAddr) {
fn add(&mut self, node_id: NodeId, addr: SocketAddr, peer_timeout: u16) {
if self.nodes.insert(node_id, addr).is_none() {
info!("New peer: {}", addr);
self.peers.insert(addr, PeerData {
timeout: TS::now() + Time::from(self.timeout),
node_id,
alt_addrs: vec![]
alt_addrs: vec![],
peer_timeout
});
self.addresses.insert(addr, node_id);
}
@ -221,7 +227,8 @@ pub struct GenericCloud<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSou
device: D,
crypto: Crypto,
next_peerlist: Time,
update_freq: Duration,
peer_timeout_publish: u16,
update_freq: u16,
buffer_out: [u8; 64 * 1024],
next_housekeep: Time,
next_stats_out: Time,
@ -249,6 +256,12 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
Err(err) => fail!("Failed to open ipv6 address ::{}: {}", config.port, err)
};
let now = TS::now();
let peer_timeout_publish = if socket4.detect_nat() {
info!("Private IP detected, setting published peer timeout to 300s");
300
} else {
config.peer_timeout as u16
};
let mut res = GenericCloud {
magic: config.get_magic(),
node_id: random(),
@ -258,12 +271,13 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
broadcast,
reconnect_peers: Vec::new(),
own_addresses: Vec::new(),
peer_timeout_publish,
table,
socket4,
socket6,
device,
next_peerlist: now,
update_freq: config.get_keepalive(),
update_freq: config.get_keepalive() as u16,
buffer_out: [0; 64 * 1024],
next_housekeep: now,
next_stats_out: now + STATS_INTERVAL,
@ -408,7 +422,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
// Send a message to each resolved address
for a in resolve(&addr)? {
// Ignore error this time
let mut msg = Message::Init(0, node_id, subnets.clone());
let mut msg = Message::Init(0, node_id, subnets.clone(), self.peer_timeout_publish);
self.send_msg(a, &mut msg).ok();
}
Ok(())
@ -429,7 +443,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
debug!("Connecting to {:?}", addr);
let subnets = self.addresses.clone();
let node_id = self.node_id;
let mut msg = Message::Init(0, node_id, subnets.clone());
let mut msg = Message::Init(0, node_id, subnets.clone(), self.peer_timeout_publish);
self.send_msg(addr, &mut msg)
}
@ -466,6 +480,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
let mut msg = Message::Peers(peers);
self.broadcast_msg(&mut msg)?;
// Reschedule for next update
self.update_freq = min(self.config.get_keepalive() as u16, self.peers.min_peer_timeout());
self.next_peerlist = now + Time::from(self.update_freq);
}
// Connect to those reconnect_peers that are due
@ -697,7 +712,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
// Refresh peer
self.peers.refresh(&peer);
}
Message::Init(stage, node_id, ranges) => {
Message::Init(stage, node_id, ranges, peer_timeout) => {
// Avoid connecting to self
if node_id == self.node_id {
self.own_addresses.push(peer);
@ -707,7 +722,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
if self.peers.contains_node(&node_id) {
self.peers.make_primary(node_id, peer);
} else {
self.peers.add(node_id, peer);
self.peers.add(node_id, peer, peer_timeout);
for range in ranges {
self.table.learn(range.base, Some(range.prefix_len), peer);
}
@ -716,7 +731,10 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
if stage == 0 {
let own_addrs = self.addresses.clone();
let own_node_id = self.node_id;
self.send_msg(peer, &mut Message::Init(stage + 1, own_node_id, own_addrs))?;
self.send_msg(
peer,
&mut Message::Init(stage + 1, own_node_id, own_addrs, self.peer_timeout_publish)
)?;
}
// Send peers in any case
let peers = self.peers.as_vec();

View File

@ -1,10 +1,13 @@
use std::{
collections::VecDeque,
collections::{HashMap, VecDeque},
io::{self, ErrorKind},
net::{SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket},
os::unix::io::{AsRawFd, RawFd}
os::unix::io::{AsRawFd, RawFd},
sync::atomic::{AtomicBool, Ordering}
};
use super::util::{get_internal_ip, MockTimeSource, Time, TimeSource};
use net2::UdpBuilder;
@ -14,6 +17,7 @@ pub trait Socket: AsRawFd + Sized {
fn receive(&mut self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), io::Error>;
fn send(&mut self, data: &[u8], addr: SocketAddr) -> Result<usize, io::Error>;
fn address(&self) -> Result<SocketAddr, io::Error>;
fn detect_nat(&self) -> bool;
}
impl Socket for UdpSocket {
@ -42,10 +46,18 @@ impl Socket for UdpSocket {
fn address(&self) -> Result<SocketAddr, io::Error> {
self.local_addr()
}
fn detect_nat(&self) -> bool {
get_internal_ip().is_private()
}
}
thread_local! {
static MOCK_SOCKET_NAT: AtomicBool = AtomicBool::new(false);
}
pub struct MockSocket {
nat: bool,
nat_peers: HashMap<SocketAddr, Time>,
address: SocketAddr,
outbound: VecDeque<(SocketAddr, Vec<u8>)>,
inbound: VecDeque<(SocketAddr, Vec<u8>)>
@ -53,11 +65,36 @@ pub struct MockSocket {
impl MockSocket {
pub fn new(address: SocketAddr) -> Self {
Self { address, outbound: VecDeque::new(), inbound: VecDeque::new() }
Self {
nat: Self::get_nat(),
nat_peers: HashMap::new(),
address,
outbound: VecDeque::new(),
inbound: VecDeque::new()
}
}
pub fn put_inbound(&mut self, from: SocketAddr, data: Vec<u8>) {
self.inbound.push_back((from, data))
pub fn set_nat(nat: bool) {
MOCK_SOCKET_NAT.with(|t| t.store(nat, Ordering::SeqCst))
}
pub fn get_nat() -> bool {
MOCK_SOCKET_NAT.with(|t| t.load(Ordering::SeqCst))
}
pub fn put_inbound(&mut self, from: SocketAddr, data: Vec<u8>) -> bool {
if !self.nat {
self.inbound.push_back((from, data));
return true
}
if let Some(timeout) = self.nat_peers.get(&from) {
if *timeout >= MockTimeSource::now() {
self.inbound.push_back((from, data));
return true
}
}
warn!("Sender {:?} is filtered out by NAT", from);
false
}
pub fn pop_outbound(&mut self) -> Option<(SocketAddr, Vec<u8>)> {
@ -85,14 +122,20 @@ impl Socket for MockSocket {
buffer[0..data.len()].copy_from_slice(&data);
Ok((data.len(), addr))
} else {
Err(io::Error::from(ErrorKind::UnexpectedEof))
Err(io::Error::new(ErrorKind::Other, "nothing in queue"))
}
}
fn send(&mut self, data: &[u8], addr: SocketAddr) -> Result<usize, io::Error> {
self.outbound.push_back((addr, data.to_owned()));
if self.nat {
self.nat_peers.insert(addr, MockTimeSource::now() + 300);
}
Ok(data.len())
}
fn address(&self) -> Result<SocketAddr, io::Error> {
Ok(self.address)
}
fn detect_nat(&self) -> bool {
self.nat
}
}

View File

@ -2,16 +2,13 @@
// Copyright (C) 2015-2019 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
use std::{
io,
net::{SocketAddr, SocketAddrV4, UdpSocket}
};
use std::{io, net::SocketAddrV4};
use igd::*;
use super::util::{SystemTimeSource, Time, TimeSource};
use super::util::{get_internal_ip, SystemTimeSource, Time, TimeSource};
const LEASE_TIME: u32 = 300;
const LEASE_TIME: u32 = 1800;
const DESCRIPTION: &str = "VpnCloud";
@ -40,16 +37,7 @@ impl PortForwarding {
}
};
info!("Port-forwarding: found router at {}", gateway.addr);
// Get the internal address (this trick gets the address by opening a UDP connection which
// does not really open anything but returns the correct address)
let dummy_sock = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind");
dummy_sock.connect(gateway.addr).expect("Failed to connect");
let internal_addr;
if let SocketAddr::V4(addr) = dummy_sock.local_addr().expect("Failed to get local address") {
internal_addr = SocketAddrV4::new(*addr.ip(), port);
} else {
unreachable!()
}
let internal_addr = SocketAddrV4::new(get_internal_ip(), port);
// Query the external address
let external_ip = match gateway.get_external_ip() {
Ok(ip) => ip,

View File

@ -39,6 +39,20 @@ macro_rules! simulate {
};
}
macro_rules! simulate_time {
($time:expr, $($node: expr => $addr: expr),*) => {
for _ in 0..$time {
use crate::util::{MockTimeSource, TimeSource};
MockTimeSource::set_time(MockTimeSource::now()+1);
$(
$node.trigger_housekeep();
)*
simulate(&mut [$((&mut $node, $addr)),*]);
}
};
}
macro_rules! assert_connected {
($($node:expr),*) => {
for node1 in [$(&$node),*].iter() {

View File

@ -4,11 +4,18 @@
#[macro_use]
mod helper;
mod nat;
mod payload;
mod peers;
pub use std::net::SocketAddr;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{
io::Write,
sync::{
atomic::{AtomicUsize, Ordering},
Once
}
};
pub use super::{
cloud::GenericCloud,
@ -24,6 +31,37 @@ pub use super::{
};
static INIT_LOGGER: Once = Once::new();
pub fn init_debug_logger() {
INIT_LOGGER.call_once(|| {
log::set_boxed_logger(Box::new(DebugLogger)).unwrap();
log::set_max_level(log::LevelFilter::Debug);
})
}
struct DebugLogger;
impl log::Log for DebugLogger {
#[inline]
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
#[inline]
fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) {
eprintln!("{} - {}", record.level(), record.args());
}
}
#[inline]
fn flush(&self) {
std::io::stderr().flush().expect("Failed to flush")
}
}
type TestNode<P, T> = GenericCloud<MockDevice, P, T, MockSocket, MockTimeSource>;
type TapTestNode = TestNode<ethernet::Frame, SwitchTable<MockTimeSource>>;
@ -35,17 +73,19 @@ thread_local! {
static NEXT_PORT: AtomicUsize = AtomicUsize::new(1);
}
fn create_tap_node() -> TapTestNode {
create_tap_node_with_config(Config::default())
fn create_tap_node(nat: bool) -> TapTestNode {
create_tap_node_with_config(nat, Config::default())
}
fn create_tap_node_with_config(mut config: Config) -> TapTestNode {
fn create_tap_node_with_config(nat: bool, mut config: Config) -> TapTestNode {
MockSocket::set_nat(nat);
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)
}
#[allow(dead_code)]
fn create_tun_node(addresses: Vec<Range>) -> TunTestNode {
fn create_tun_node(nat: bool, addresses: Vec<Range>) -> TunTestNode {
MockSocket::set_nat(nat);
TestNode::new(
&Config { port: NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16, ..Config::default() },
MockDevice::new(),
@ -73,13 +113,15 @@ fn msg6_get<P: Protocol, T: Table>(node: &mut TestNode<P, T>) -> (SocketAddr, Ve
}
fn msg4_put<P: Protocol, T: Table>(node: &mut TestNode<P, T>, from: SocketAddr, msg: Vec<u8>) {
node.socket4().put_inbound(from, msg);
node.trigger_socket_v4_event();
if node.socket4().put_inbound(from, msg) {
node.trigger_socket_v4_event();
}
}
fn msg6_put<P: Protocol, T: Table>(node: &mut TestNode<P, T>, from: SocketAddr, msg: Vec<u8>) {
node.socket6().put_inbound(from, msg);
node.trigger_socket_v6_event();
if node.socket6().put_inbound(from, msg) {
node.trigger_socket_v6_event();
}
}
fn simulate<P: Protocol, T: Table>(nodes: &mut [(&mut TestNode<P, T>, SocketAddr)]) {

86
src/tests/nat.rs Normal file
View File

@ -0,0 +1,86 @@
// VpnCloud - Peer-to-Peer VPN
// Copyright (C) 2015-2019 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
use super::*;
#[test]
fn connect_nat_2_peers() {
init_debug_logger();
MockTimeSource::set_time(0);
let mut node1 = create_tap_node(true);
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node(false);
let node2_addr = addr!("2.3.4.5:6789");
node2.connect("1.2.3.4:5678").unwrap();
simulate!(node1 => node1_addr, node2 => node2_addr);
assert!(!node1.peers().contains_node(&node2.node_id()));
assert!(!node2.peers().contains_node(&node1.node_id()));
node1.connect("2.3.4.5:6789").unwrap();
simulate!(node1 => node1_addr, node2 => node2_addr);
assert_connected!(node1, node2);
}
#[test]
fn connect_nat_3_peers() {
init_debug_logger();
MockTimeSource::set_time(0);
let mut node1 = create_tap_node(true);
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node(false);
let node2_addr = addr!("2.3.4.5:6789");
let mut node3 = create_tap_node(false);
let node3_addr = addr!("3.4.5.6:7890");
node2.connect("1.2.3.4:5678").unwrap();
node3.connect("1.2.3.4:5678").unwrap();
simulate!(node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
assert!(!node1.peers().contains_node(&node2.node_id()));
assert!(!node2.peers().contains_node(&node1.node_id()));
assert!(!node3.peers().contains_node(&node1.node_id()));
assert!(!node3.peers().contains_node(&node2.node_id()));
assert!(!node1.peers().contains_node(&node3.node_id()));
assert!(!node2.peers().contains_node(&node3.node_id()));
node1.connect("3.4.5.6:7890").unwrap();
node2.connect("3.4.5.6:7890").unwrap();
simulate_time!(1000, node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
assert_connected!(node1, node3);
assert_connected!(node2, node3);
assert_connected!(node1, node2);
}
#[test]
fn nat_keepalive() {
init_debug_logger();
MockTimeSource::set_time(0);
let mut node1 = create_tap_node(true);
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node(false);
let node2_addr = addr!("2.3.4.5:6789");
let mut node3 = create_tap_node(false);
let node3_addr = addr!("3.4.5.6:7890");
node1.connect("3.4.5.6:7890").unwrap();
node2.connect("3.4.5.6:7890").unwrap();
simulate_time!(1000, node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
assert_connected!(node1, node3);
assert_connected!(node2, node3);
assert_connected!(node1, node2);
simulate_time!(10000, node1 => node1_addr, node2 => node2_addr, node3 => node3_addr);
assert_connected!(node1, node3);
assert_connected!(node2, node3);
assert_connected!(node1, node2);
}

View File

@ -6,9 +6,9 @@ use super::*;
#[test]
fn ethernet_delivers() {
let mut node1 = create_tap_node();
let mut node1 = create_tap_node(false);
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node();
let mut node2 = create_tap_node(false);
let node2_addr = addr!("2.3.4.5:6789");
node1.connect("2.3.4.5:6789").unwrap();
@ -28,11 +28,11 @@ fn ethernet_delivers() {
#[test]
fn switch_learns() {
let mut node1 = create_tap_node();
let mut node1 = create_tap_node(false);
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node();
let mut node2 = create_tap_node(false);
let node2_addr = addr!("2.3.4.5:6789");
let mut node3 = create_tap_node();
let mut node3 = create_tap_node(false);
let node3_addr = addr!("3.4.5.6:7890");
node1.connect("2.3.4.5:6789").unwrap();

View File

@ -6,9 +6,9 @@ use super::*;
#[test]
fn connect_v4() {
let mut node1 = create_tap_node();
let mut node1 = create_tap_node(false);
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node();
let mut node2 = create_tap_node(false);
let node2_addr = addr!("2.3.4.5:6789");
assert_clean!(node1, node2);
assert!(!node1.peers().contains_node(&node2.node_id()));
@ -17,24 +17,24 @@ fn connect_v4() {
node1.connect("2.3.4.5:6789").unwrap();
// Node 1 -> Node 2: Init 0
assert_message4!(node1, node1_addr, node2, node2_addr, Message::Init(0, node1.node_id(), vec![]));
assert_message4!(node1, node1_addr, node2, node2_addr, Message::Init(0, node1.node_id(), vec![], 1800));
assert_clean!(node1);
assert!(node2.peers().contains_node(&node1.node_id()));
// Node 2 -> Node 1: Init 1 | Node 2 -> Node 1: Peers
assert_message4!(node2, node2_addr, node1, node1_addr, Message::Init(1, node2.node_id(), vec![]));
assert_message4!(node2, node2_addr, node1, node1_addr, Message::Init(1, node2.node_id(), vec![], 1800));
assert!(node1.peers().contains_node(&node2.node_id()));
assert_message4!(node2, node2_addr, node1, node1_addr, Message::Peers(vec![node1_addr]));
assert_clean!(node2);
// Node 1 -> Node 2: Peers | Node 1 -> Node 1: Init 0
assert_message4!(node1, node1_addr, node2, node2_addr, Message::Peers(vec![node2_addr]));
assert_message4!(node1, node1_addr, node1, node1_addr, Message::Init(0, node1.node_id(), vec![]));
assert_message4!(node1, node1_addr, node1, node1_addr, Message::Init(0, node1.node_id(), vec![], 1800));
assert!(node1.own_addresses().contains(&node1_addr));
assert_clean!(node1);
// Node 2 -> Node 2: Init 0
assert_message4!(node2, node2_addr, node2, node2_addr, Message::Init(0, node2.node_id(), vec![]));
assert_message4!(node2, node2_addr, node2, node2_addr, Message::Init(0, node2.node_id(), vec![], 1800));
assert_clean!(node2);
assert!(node2.own_addresses().contains(&node2_addr));
@ -43,9 +43,9 @@ fn connect_v4() {
#[test]
fn connect_v6() {
let mut node1 = create_tap_node();
let mut node1 = create_tap_node(false);
let node1_addr = addr!("[::1]:5678");
let mut node2 = create_tap_node();
let mut node2 = create_tap_node(false);
let node2_addr = addr!("[::2]:6789");
node1.connect("[::2]:6789").unwrap();
@ -57,13 +57,13 @@ fn connect_v6() {
#[test]
fn cross_connect() {
let mut node1 = create_tap_node();
let mut node1 = create_tap_node(false);
let node1_addr = addr!("1.1.1.1:1111");
let mut node2 = create_tap_node();
let mut node2 = create_tap_node(false);
let node2_addr = addr!("2.2.2.2:2222");
let mut node3 = create_tap_node();
let mut node3 = create_tap_node(false);
let node3_addr = addr!("3.3.3.3:3333");
let mut node4 = create_tap_node();
let mut node4 = create_tap_node(false);
let node4_addr = addr!("4.4.4.4:4444");
node1.connect("2.2.2.2:2222").unwrap();
@ -98,10 +98,10 @@ fn connect_via_beacons() {
MockTimeSource::set_time(0);
let beacon_path = "target/.vpncloud_test";
let mut node1 =
create_tap_node_with_config(Config { beacon_store: Some(beacon_path.to_string()), ..Config::default() });
create_tap_node_with_config(false, Config { beacon_store: Some(beacon_path.to_string()), ..Config::default() });
let node1_addr = node1.address().unwrap().0;
let mut node2 =
create_tap_node_with_config(Config { beacon_load: Some(beacon_path.to_string()), ..Config::default() });
create_tap_node_with_config(false, Config { beacon_load: Some(beacon_path.to_string()), ..Config::default() });
let node2_addr = addr!("2.2.2.2:2222");
assert!(!node1.peers().contains_node(&node2.node_id()));
@ -122,9 +122,9 @@ fn connect_via_beacons() {
#[test]
fn reconnect_after_timeout() {
MockTimeSource::set_time(0);
let mut node1 = create_tap_node();
let mut node1 = create_tap_node(false);
let node1_addr = addr!("1.1.1.1:1111");
let mut node2 = create_tap_node();
let mut node2 = create_tap_node(false);
let node2_addr = addr!("2.2.2.2:2222");
node1.add_reconnect_peer("2.2.2.2:2222".to_string());
@ -148,15 +148,15 @@ fn reconnect_after_timeout() {
#[test]
fn lost_init1() {
let mut node1 = create_tap_node();
let mut node1 = create_tap_node(false);
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 = create_tap_node();
let mut node2 = create_tap_node(false);
let node2_addr = addr!("2.3.4.5:6789");
node1.connect("2.3.4.5:6789").unwrap();
// Node 1 -> Node 2: Init 0
assert_message4!(node1, node1_addr, node2, node2_addr, Message::Init(0, node1.node_id(), vec![]));
assert_message4!(node1, node1_addr, node2, node2_addr, Message::Init(0, node1.node_id(), vec![], 1800));
assert_clean!(node1);
// Node 2 -> Node 1: Init 1 | Node 2 -> Node 1: Peers
@ -170,14 +170,14 @@ fn lost_init1() {
#[test]
fn wrong_magic() {
let mut node1 = create_tap_node();
let mut node1 = create_tap_node(false);
let node1_addr = addr!("1.2.3.4:5678");
let mut node2 =
create_tap_node_with_config(Config { magic: Some("hash:different".to_string()), ..Config::default() });
create_tap_node_with_config(false, Config { magic: Some("hash:different".to_string()), ..Config::default() });
let node2_addr = addr!("2.3.4.5:6789");
node1.connect("2.3.4.5:6789").unwrap();
assert_message4!(node1, node1_addr, node2, node2_addr, Message::Init(0, node1.node_id(), vec![]));
assert_message4!(node1, node1_addr, node2, node2_addr, Message::Init(0, node1.node_id(), vec![], 1800));
assert_clean!(node1, node2);
@ -204,3 +204,8 @@ fn remove_dead_peers() {
fn update_primary_address() {
// TODO
}
#[test]
fn automatic_peer_timeout() {
// TODO
}

View File

@ -53,9 +53,9 @@ impl TopHeader {
}
pub enum Message<'a> {
Data(&'a mut [u8], usize, usize), // data, start, end
Peers(Vec<SocketAddr>), // peers
Init(u8, NodeId, Vec<Range>), // step, node_id, ranges
Data(&'a mut [u8], usize, usize), // data, start, end
Peers(Vec<SocketAddr>), // peers
Init(u8, NodeId, Vec<Range>, u16), // step, node_id, ranges
Close
}
@ -64,7 +64,7 @@ impl<'a> Message<'a> {
match self {
Message::Data(_, start, end) => Message::Data(&mut [], start, end),
Message::Peers(peers) => Message::Peers(peers),
Message::Init(step, node_id, ranges) => Message::Init(step, node_id, ranges),
Message::Init(step, node_id, ranges, timeout) => Message::Init(step, node_id, ranges, timeout),
Message::Close => Message::Close
}
}
@ -86,8 +86,15 @@ impl<'a> fmt::Debug for Message<'a> {
}
write!(formatter, "]")
}
Message::Init(stage, ref node_id, ref peers) => {
write!(formatter, "Init(stage={}, node_id={}, {:?})", stage, bytes_to_hex(node_id), peers)
Message::Init(stage, ref node_id, ref peers, ref peer_timeout) => {
write!(
formatter,
"Init(stage={}, node_id={}, peer_timeout={}, {:?})",
stage,
bytes_to_hex(node_id),
peer_timeout,
peers
)
}
Message::Close => write!(formatter, "Close")
}
@ -183,7 +190,12 @@ pub fn decode<'a>(data: &'a mut [u8], magic: HeaderMagic, crypto: &Crypto) -> Re
pos += read;
addrs.push(range);
}
Message::Init(stage, node_id, addrs)
let mut peer_timeout = 1800;
if data.len() >= pos + 2 {
peer_timeout = Encoder::read_u16(&data[pos..]);
// pos += 2; never read
}
Message::Init(stage, node_id, addrs, peer_timeout)
}
3 => Message::Close,
_ => return Err(Error::Parse("Unknown message type"))
@ -198,7 +210,7 @@ pub fn encode<'a>(
let header_type = match msg {
Message::Data(_, _, _) => 0,
Message::Peers(_) => 1,
Message::Init(_, _, _) => 2,
Message::Init(_, _, _, _) => 2,
Message::Close => 3
};
let mut start = 64;
@ -244,7 +256,7 @@ pub fn encode<'a>(
}
end = pos;
}
Message::Init(stage, ref node_id, ref ranges) => {
Message::Init(stage, ref node_id, ref ranges, peer_timeout) => {
let mut pos = start;
assert!(buf.len() >= pos + 2 + NODE_ID_BYTES);
buf[pos] = stage;
@ -257,6 +269,8 @@ pub fn encode<'a>(
for range in ranges {
pos += range.write_to(&mut buf[pos..]);
}
Encoder::write_u16(peer_timeout, &mut buf[pos..]);
pos += 2;
end = pos;
}
Message::Close => {}
@ -299,9 +313,9 @@ impl<'a> PartialEq for Message<'a> {
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
Message::Init(step1, node_id1, ref ranges1, peer_timeout1) => {
if let Message::Init(step2, node_id2, ref ranges2, peer_timeout2) = *other {
step1 == step2 && node_id1 == node_id2 && ranges1 == ranges2 && peer_timeout1 == peer_timeout2
} else {
false
}
@ -421,15 +435,15 @@ fn udpmessage_init() {
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 msg = Message::Init(0, node_id, addrs, 1800);
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
0, 1, 2, 3, 4, 5, 16, 7, 8
];
{
let mut buf = [0; 1024];
let res = encode(&mut msg, &mut buf[..], MAGIC, &mut crypto);
assert_eq!(res.len(), 40);
assert_eq!(res.len(), 42);
for i in 0..res.len() {
assert_eq!(res[i], should[i]);
}
@ -502,9 +516,9 @@ fn message_fmt() {
base: Address { data: [0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], len: 6 },
prefix_len: 16
}
])
], 1800)
),
"Init(stage=0, node_id=000102030405060708090a0b0c0d0e0f, [0.1.2.3/24, 00:01:02:03:04:05/16])"
"Init(stage=0, node_id=000102030405060708090a0b0c0d0e0f, peer_timeout=1800, [0.1.2.3/24, 00:01:02:03:04:05/16])"
);
assert_eq!(format!("{:?}", Message::Close), "Close");
}

View File

@ -4,7 +4,7 @@
use std::{
fmt,
net::{SocketAddr, ToSocketAddrs},
net::{Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket},
sync::atomic::{AtomicIsize, Ordering}
};
@ -91,11 +91,13 @@ macro_rules! fail {
($format:expr) => ( {
use std::process;
error!($format);
log::logger().flush();
process::exit(-1);
} );
($format:expr, $( $arg:expr ),+) => ( {
use std::process;
error!($format, $( $arg ),+ );
log::logger().flush();
process::exit(-1);
} );
}
@ -115,6 +117,18 @@ macro_rules! try_fail {
} );
}
pub fn get_internal_ip() -> Ipv4Addr {
// Get the internal address (this trick gets the address by opening a UDP connection which
// does not really open anything but returns the correct address)
let dummy_sock = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind");
dummy_sock.connect("8.8.8.8:53").expect("Failed to connect");
if let SocketAddr::V4(addr) = dummy_sock.local_addr().expect("Failed to get local address") {
*addr.ip()
} else {
unreachable!()
}
}
#[allow(unknown_lints, clippy::needless_pass_by_value)]
pub fn resolve<Addr: ToSocketAddrs + fmt::Debug>(addr: Addr) -> Result<Vec<SocketAddr>, Error> {
@ -235,3 +249,79 @@ impl TimeSource for MockTimeSource {
MOCK_TIME.with(|t| t.load(Ordering::SeqCst) as Time)
}
}
/// Helper function that multiplies the base62 data in buf[0..buflen] by 16 and adds m to it
fn base62_add_mult_16(buf: &mut [u8], mut buflen: usize, m: u8) -> usize {
let mut d: usize = m as usize;
for i in 0..buflen {
d += buf[i] as usize * 16;
buf[i] = (d % 62) as u8;
d /= 62;
}
assert!(d < 62);
if d > 0 {
buf[buflen] = d as u8;
buflen += 1;
}
buflen
}
const BASE62: [char; 62] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
];
pub fn to_base62(data: &[u8]) -> String {
let l = data.len();
let mut buf = vec![0; l * 2];
let mut buflen = 0;
for b in data {
buflen = base62_add_mult_16(&mut buf, buflen, b / 16);
buflen = base62_add_mult_16(&mut buf, buflen, b % 16);
}
buf[0..buflen].reverse();
let mut result = String::with_capacity(buflen);
for b in &buf[0..buflen] {
result.push(BASE62[*b as usize]);
}
result
}
pub fn from_base62(data: &str) -> Result<Vec<u8>, char> {
let mut buf = Vec::with_capacity(data.len() / 2 + data.len() / 4);
for c in data.chars() {
let mut val = match c {
'0'..='9' => ((c as usize) % ('0' as usize)),
'A'..='Z' => ((c as usize) % ('A' as usize)) + 10,
'a'..='z' => ((c as usize) % ('a' as usize)) + 36,
_ => return Err(c)
};
for i in 0..buf.len() {
val += buf[i] as usize * 62;
buf[i] = (val % 256) as u8;
val /= 256;
}
if val > 0 {
buf.push(val as u8);
}
}
buf.reverse();
Ok(buf)
}
#[test]
fn base62() {
assert_eq!("", to_base62(&[0]));
assert_eq!("z", to_base62(&[61]));
assert_eq!("10", to_base62(&[62]));
assert_eq!("48", to_base62(&[1, 0]));
assert_eq!("1Xp7Ke", to_base62(b"Test"));
assert!(from_base62("").unwrap().is_empty());
assert_eq!(vec![61], from_base62("z").unwrap());
assert_eq!(vec![62], from_base62("10").unwrap());
assert_eq!(vec![1, 0], from_base62("48").unwrap());
assert_eq!(b"Test".to_vec(), from_base62("1Xp7Ke").unwrap());
}