Full beacon encode/decode

This commit is contained in:
Dennis Schwerdel 2019-02-19 18:42:50 +01:00
parent 0dd694c708
commit 62186152e4
13 changed files with 947 additions and 749 deletions

109
Cargo.lock generated
View File

@ -11,6 +11,39 @@ name = "autocfg"
version = "0.1.2" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "base64" name = "base64"
version = "0.9.3" version = "0.9.3"
@ -30,11 +63,6 @@ name = "bitflags"
version = "1.0.4" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.3.1" version = "1.3.1"
@ -83,6 +111,26 @@ name = "dtoa"
version = "0.4.3" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.6" version = "1.0.6"
@ -214,6 +262,28 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.10.0" version = "1.10.0"
@ -431,6 +501,11 @@ dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.2.3" version = "0.2.3"
@ -525,6 +600,17 @@ dependencies = [
"unicode-xid 0.1.0 (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 = "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]] [[package]]
name = "thread_local" name = "thread_local"
version = "0.3.6" version = "0.3.6"
@ -629,8 +715,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "vpncloud" name = "vpncloud"
version = "0.9.1" version = "0.9.1"
dependencies = [ 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)", "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)", "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)", "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)", "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -697,10 +783,12 @@ dependencies = [
[metadata] [metadata]
"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "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 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 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 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 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 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 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" "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 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 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 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 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 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" "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 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 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 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 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 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" "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.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 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 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 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 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" "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 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 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 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 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 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" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"

View File

@ -28,7 +28,7 @@ igd = "0.6" # Do not update, 0.7 has problems with exit by ctrl-c
siphasher = "0.3" siphasher = "0.3"
daemonize = "0.3" daemonize = "0.3"
ring = "0.14" ring = "0.14"
bs58 = "*" base-62 = "0.1"
[build-dependencies] [build-dependencies]
cc = "^1" cc = "^1"

View File

@ -1,164 +1,270 @@
use bs58; use base_62;
use ring::digest; use ring::digest;
use std::str::FromStr; use std::num::Wrapping;
use super::util::{now, Encoder}; use super::util::{now, Encoder};
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr};
fn base58_encode(data: &[u8]) -> String { fn base_62_sanitize(data: &str) -> String {
bs58::encode(data).into_string() data.chars().filter(|c| c.is_ascii_alphanumeric()).collect()
}
fn base58_decode(data: &str) -> Vec<u8> {
bs58::decode(data).into_vec().unwrap()
} }
fn sha512(data: &[u8]) -> Vec<u8> { fn sha512(data: &[u8]) -> Vec<u8> {
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 { pub struct BeaconSerializer {
magic: Vec<u8>, magic: Vec<u8>,
shared_key: String shared_key: Vec<u8>
} }
impl BeaconSerializer { impl BeaconSerializer {
pub fn new(magic: &[u8], shared_key: &str) -> Self { pub fn new(magic: &[u8], shared_key: &[u8]) -> Self {
BeaconSerializer { BeaconSerializer {
magic: magic.to_owned(), magic: magic.to_owned(),
shared_key: shared_key.to_string() shared_key: shared_key.to_owned()
}
} }
}
fn seed(&self, key: &str) -> Vec<u8> { fn seed(&self, key: &str) -> Vec<u8> {
let mut data = Vec::new(); let mut data = Vec::new();
data.extend_from_slice(key.as_bytes()); data.extend_from_slice(key.as_bytes());
data.extend_from_slice(&self.magic); data.extend_from_slice(&self.magic);
data.extend_from_slice(self.shared_key.as_bytes()); data.extend_from_slice(&self.shared_key);
sha512(&data) sha512(&data)
} }
fn mask_with_seed(&self, data: &[u8], key: &str) -> Vec<u8> { fn mask_with_seed(&self, data: &[u8], key: &str) -> Vec<u8> {
let mask = self.seed(key); let mask = self.seed(key);
let mut output = Vec::with_capacity(data.len()); let mut output = Vec::with_capacity(data.len());
for i in 0..data.len() { for i in 0..data.len() {
output.push(data[i] ^ mask[i]); output.push(data[i] ^ mask[i]);
}
output
} }
output
}
fn begin(&self) -> String { fn begin(&self) -> String {
base58_encode(&self.seed("begin"))[0..5].to_string() base_62::encode(&self.seed("begin"))[0..5].to_string()
} }
fn end(&self) -> String { fn end(&self) -> String {
base58_encode(&self.seed("end"))[0..5].to_string() base_62::encode(&self.seed("end"))[0..5].to_string()
} }
fn peerlist_encode(&self, peers: &[SocketAddr]) -> String { fn peerlist_encode(&self, peers: &[SocketAddr], now_hour: u16) -> String {
let mut data = Vec::new(); let mut data = Vec::new();
// Add timestamp // Add timestamp
data.append(&mut self.mask_with_seed(&(now() as u32).to_be_bytes(), "time")); data.append(&mut self.mask_with_seed(&now_hour.to_be_bytes(), "time"));
// Split addresses into v4 and v6 // Split addresses into v4 and v6
let mut v4addrs = Vec::new(); let mut v4addrs = Vec::new();
let mut v6addrs = Vec::new(); let mut v6addrs = Vec::new();
for p in peers { for p in peers {
match *p { match *p {
SocketAddr::V4(addr) => v4addrs.push(addr), SocketAddr::V4(addr) => v4addrs.push(addr),
SocketAddr::V6(addr) => v6addrs.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<SocketAddr> { fn peerlist_decode(&self, data: &str, ttl_hours: Option<u16>, now_hour: u16) -> Vec<SocketAddr> {
let data = base58_decode(data); let mut data = base_62::decode(data).expect("Invalid input");
let mut peers = Vec::new(); let mut peers = Vec::new();
//TODO: decode time let mut pos = 0;
let mut pos = 4; if data.len() < 4 {
let v4count = self.mask_with_seed(&[data[pos]], "v4count")[0]; return peers
pos += 1; }
for _ in 0..v4count { let mut parity = data.pop().unwrap();
assert!(data.len() >= pos + 6); for b in &data {
let dat = self.mask_with_seed(&data[pos..pos+6], "peer"); parity ^= b;
pos += 6; }
let port = Encoder::read_u16(&dat[4..]); if parity != 0 {
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(dat[0], dat[1], dat[2], dat[3]), port)); return peers
peers.push(addr); }
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<SocketAddr> { fn encode_internal(&self, peers: &[SocketAddr], now_hour: u16) -> String {
//TODO: remove anything that is not base58 format!("{}{}{}", self.begin(), self.peerlist_encode(peers, now_hour), self.end())
let mut peers = Vec::new(); }
let begin = self.begin();
let end = self.end(); pub fn encode(&self, peers: &[SocketAddr]) -> String {
let mut pos = 0; self.encode_internal(peers, now_hour_16())
while let Some(found) = data[pos..].find(&begin) { }
pos += found;
let start_pos = pos + begin.len(); fn decode_internal(&self, data: &str, ttl_hours: Option<u16>, now_hour: u16) -> Vec<SocketAddr> {
if let Some(found) = data[pos..].find(&end) { let data = base_62_sanitize(data);
let end_pos = pos + found; let mut peers = Vec::new();
peers.append(&mut self.peerlist_decode(&data[start_pos..end_pos])); let begin = self.begin();
pos = end_pos + end.len(); let end = self.end();
} else { let mut pos = 0;
pos += begin.len(); 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<u16>) -> Vec<SocketAddr> {
self.decode_internal(data, ttl_hours, now_hour_16())
} }
peers
}
} }
pub fn test() { #[cfg(test)] use std::str::FromStr;
let beacon = BeaconSerializer::new(b"vpnc", "mysecretkey");
let mut peers = Vec::new(); #[test]
peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); fn encode() {
peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); let ser = BeaconSerializer::new(b"vpnc", b"mysecretkey");
peers.push(SocketAddr::from_str("[::1]:5678").unwrap()); let mut peers = Vec::new();
let string = format!("{}{}{}", beacon.begin(), beacon.peerlist_encode(&peers), beacon.end()); peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap());
println!("{}", string); peers.push(SocketAddr::from_str("6.6.6.6:53").unwrap());
println!("{:?}", beacon.decode(&string)); 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());
} }

View File

@ -22,7 +22,6 @@ use super::poll::{Poll, Flags};
#[bench] #[bench]
fn crypto_chacha20(b: &mut Bencher) { fn crypto_chacha20(b: &mut Bencher) {
Crypto::init();
let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test"); let mut crypto = Crypto::from_shared_key(CryptoMethod::ChaCha20, "test");
let mut payload = [0; 1500]; let mut payload = [0; 1500];
let header = [0; 8]; let header = [0; 8];
@ -36,10 +35,6 @@ fn crypto_chacha20(b: &mut Bencher) {
#[bench] #[bench]
fn crypto_aes256(b: &mut Bencher) { 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 crypto = Crypto::from_shared_key(CryptoMethod::AES256, "test");
let mut payload = [0; 1500]; let mut payload = [0; 1500];
let header = [0; 8]; let header = [0; 8];

View File

@ -29,6 +29,7 @@ use super::port_forwarding::PortForwarding;
use super::util::{now, Time, Duration, resolve}; use super::util::{now, Time, Duration, resolve};
use super::poll::{Poll, Flags}; use super::poll::{Poll, Flags};
use super::traffic::TrafficStats; use super::traffic::TrafficStats;
use super::beacon::BeaconSerializer;
pub type Hash = BuildHasherDefault<FnvHasher>; pub type Hash = BuildHasherDefault<FnvHasher>;
@ -222,6 +223,7 @@ pub struct GenericCloud<P: Protocol, T: Table> {
port_forwarding: Option<PortForwarding>, port_forwarding: Option<PortForwarding>,
traffic: TrafficStats, traffic: TrafficStats,
stats_file: Option<String>, stats_file: Option<String>,
beacon_serializer: BeaconSerializer,
_dummy_p: PhantomData<P>, _dummy_p: PhantomData<P>,
} }
@ -254,7 +256,6 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
socket4, socket4,
socket6, socket6,
device, device,
crypto,
next_peerlist: now(), next_peerlist: now(),
update_freq: config.get_keepalive(), update_freq: config.get_keepalive(),
buffer_out: [0; 64*1024], buffer_out: [0; 64*1024],
@ -263,6 +264,8 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
port_forwarding, port_forwarding,
traffic: TrafficStats::default(), traffic: TrafficStats::default(),
stats_file: config.stats_file.clone(), stats_file: config.stats_file.clone(),
beacon_serializer: BeaconSerializer::new(&config.get_magic(), crypto.get_key()),
crypto,
_dummy_p: PhantomData, _dummy_p: PhantomData,
} }
} }
@ -423,6 +426,10 @@ impl<P: Protocol, T: Table> GenericCloud<P, T> {
// ...and send them to all peers // ...and send them to all peers
let mut msg = Message::Peers(peers); let mut msg = Message::Peers(peers);
try!(self.broadcast_msg(&mut msg)); 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 // Reschedule for next update
self.next_peerlist = now + Time::from(self.update_freq); self.next_peerlist = now + Time::from(self.update_freq);
} }

View File

@ -242,3 +242,153 @@ pub struct ConfigFile {
pub user: Option<String>, pub user: Option<String>,
pub group: Option<String>, pub group: Option<String>,
} }
#[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::<ConfigFile>(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()
});
}

View File

@ -27,7 +27,8 @@ pub enum CryptoMethod {
pub struct CryptoData { pub struct CryptoData {
sealing_key: SealingKey, sealing_key: SealingKey,
opening_key: OpeningKey, opening_key: OpeningKey,
nonce: Vec<u8> nonce: Vec<u8>,
key: Vec<u8>
} }
#[allow(unknown_lints, clippy::large_enum_variant)] #[allow(unknown_lints, clippy::large_enum_variant)]
@ -52,10 +53,6 @@ fn inc_nonce(nonce: &mut [u8]) {
impl Crypto { impl Crypto {
#[inline]
pub fn init() {
}
#[inline] #[inline]
pub fn method(&self) -> u8 { pub fn method(&self) -> u8 {
match *self { 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] #[inline]
#[allow(unknown_lints,clippy::match_same_arms)] #[allow(unknown_lints,clippy::match_same_arms)]
pub fn additional_bytes(&self) -> usize { pub fn additional_bytes(&self) -> usize {
@ -117,7 +122,7 @@ impl Crypto {
if SystemRandom::new().fill(&mut nonce[1..]).is_err() { if SystemRandom::new().fill(&mut nonce[1..]).is_err() {
fail!("Randomizing nonce failed"); fail!("Randomizing nonce failed");
} }
let data = CryptoData { sealing_key, opening_key, nonce }; let data = CryptoData { sealing_key, opening_key, nonce, key };
match method { match method {
CryptoMethod::ChaCha20 => Crypto::ChaCha20Poly1305(data), CryptoMethod::ChaCha20 => Crypto::ChaCha20Poly1305(data),
CryptoMethod::AES256 => Crypto::AES256GCM(data) CryptoMethod::AES256 => Crypto::AES256GCM(data)
@ -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]);
}

View File

@ -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));
}

106
src/ip.rs
View File

@ -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));
}

View File

@ -21,7 +21,7 @@ extern crate igd;
extern crate siphasher; extern crate siphasher;
extern crate daemonize; extern crate daemonize;
extern crate ring; extern crate ring;
extern crate bs58; extern crate base_62;
#[cfg(feature = "bench")] extern crate test; #[cfg(feature = "bench")] extern crate test;
#[macro_use] pub mod util; #[macro_use] pub mod util;
@ -37,7 +37,6 @@ pub mod config;
pub mod port_forwarding; pub mod port_forwarding;
pub mod traffic; pub mod traffic;
pub mod beacon; pub mod beacon;
#[cfg(test)] mod tests;
#[cfg(feature = "bench")] mod benches; #[cfg(feature = "bench")] mod benches;
use docopt::Docopt; use docopt::Docopt;
@ -225,7 +224,6 @@ fn run<P: Protocol> (config: Config) {
Mode::Switch => (true, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))), Mode::Switch => (true, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))),
Mode::Hub => (false, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10))) Mode::Hub => (false, true, AnyTable::Switch(SwitchTable::new(dst_timeout, 10)))
}; };
Crypto::init();
let crypto = match config.shared_key { let crypto = match config.shared_key {
Some(ref key) => Crypto::from_shared_key(config.crypto, key), Some(ref key) => Crypto::from_shared_key(config.crypto, key),
None => Crypto::None None => Crypto::None
@ -264,11 +262,8 @@ fn run<P: Protocol> (config: Config) {
} }
fn main() { fn main() {
beacon::test();
return;
let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit()); let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());
if args.flag_version { if args.flag_version {
Crypto::init();
println!("VpnCloud v{}, protocol version {}", println!("VpnCloud v{}, protocol version {}",
env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_VERSION"),
VERSION VERSION

View File

@ -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::<ConfigFile>(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()
});
}

View File

@ -242,3 +242,54 @@ impl fmt::Display for Error {
} }
} }
} }
#[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());
}

View File

@ -258,3 +258,185 @@ pub fn encode<'a>(msg: &'a mut Message, mut buf: &'a mut [u8], magic: HeaderMagi
} }
&mut buf[start..end] &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");
}