Merge branch 'master' into threading

This commit is contained in:
Dennis Schwerdel 2021-04-11 21:49:39 +02:00
commit a7a7ab3a1f
39 changed files with 582 additions and 449 deletions

View File

@ -1,6 +1,18 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/rust/.devcontainer/base.Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/rust:0-1
FROM mcr.microsoft.com/vscode/devcontainers/rust:1
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends asciidoctor valgrind
RUN rm /etc/localtime && ln -s /usr/share/zoneinfo/Europe/Berlin /etc/localtime
RUN chown vscode: -R /usr/local/rustup /usr/local/cargo
USER vscode
RUN rustup default 1.51.0 \
&& rustup component add clippy rust-src rustfmt
RUN cargo install cargo-outdated cargo-cache \
&& cargo cache -a

View File

@ -18,6 +18,9 @@ jobs:
name: Test Suite
runs-on: ubuntu-latest
steps:
- uses: szenius/set-timezone@v1.0
with:
timezoneLinux: Europe/Berlin
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:

View File

@ -28,6 +28,7 @@ mkdir -p %{buildroot}/lib/systemd/system
mkdir -p %{buildroot}/usr/share/man/man1
cp %{buildroot}/../../../../../assets/example.net.disabled %{buildroot}/etc/vpncloud/example.net.disabled
cp %{buildroot}/../../../../../assets/vpncloud@.service %{buildroot}/lib/systemd/system/vpncloud@.service
cp %{buildroot}/../../../../../assets/vpncloud.target %{buildroot}/lib/systemd/system/vpncloud.target
cp %{buildroot}/../../../../../assets/vpncloud-wsproxy.service %{buildroot}/lib/systemd/system/vpncloud-wsproxy.service
cp %{buildroot}/../../../../../target/vpncloud.1.gz %{buildroot}/usr/share/man/man1/vpncloud.1.gz
cp -a * %{buildroot}
@ -40,6 +41,7 @@ rm -rf %{buildroot}
/etc/vpncloud/example.net.disabled
/usr/bin/vpncloud
/lib/systemd/system/vpncloud@.service
/lib/systemd/system/vpncloud.target
/lib/systemd/system/vpncloud-wsproxy.service
/usr/share/man/man1/vpncloud.1.gz

View File

@ -1,12 +0,0 @@
{
"scanSettings": {
"baseBranches": []
},
"checkRunSettings": {
"vulnerableCheckRunConclusionLevel": "failure",
"displayMode": "diff"
},
"issueSettings": {
"minSeverityLevel": "LOW"
}
}

View File

@ -2,14 +2,15 @@
This project follows [semantic versioning](http://semver.org).
### UNRELEASED
### v2.2.0 (2021-04-06)
- [added] Service target file (thanks to mnhauke)
- [added] Added interactive configuration wizard
- [added] Support for (un-)installation
- [added] Building static binaries
- [added] Building i686 rpm
- [changed] Restructured example config
- [changed] Changed Rust version to 1.50.0
- [changed] Changed Rust version to 1.51.0
- [changed] Updated dependencies
- [changed] Change permissions of /etc/vpncloud

160
Cargo.lock generated
View File

@ -102,9 +102,9 @@ checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
[[package]]
name = "byteorder"
version = "1.4.2"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
@ -171,9 +171,9 @@ dependencies = [
[[package]]
name = "const_fn"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
checksum = "076a6803b0dacd6a88cfe64deba628b01533ff5ef265687e6938280c1afd0a28"
[[package]]
name = "cpuid-bool"
@ -240,35 +240,33 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d60ab4a8dba064f2fbb5aa270c28da5cf4bbd0e72dae1140a6b0353a779dbe00"
checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
"lazy_static",
"loom",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bae8f328835f8f5a6ceb6a7842a7f2d0c03692adb5c889347235d59194731fe3"
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"lazy_static",
"loom",
]
[[package]]
name = "csv"
version = "1.1.5"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d58633299b24b515ac72a3f869f8b91306a3cec616a602843a383acd6f9e97"
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
dependencies = [
"bstr",
"csv-core",
@ -325,9 +323,9 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "dtoa"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "either"
@ -357,19 +355,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "generator"
version = "0.6.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9fed24fd1e18827652b4d55652899a1e9da8e54d91624dc3437a5bc3a9f9a9c"
dependencies = [
"cc",
"libc",
"log",
"rustversion",
"winapi",
]
[[package]]
name = "generic-array"
version = "0.14.4"
@ -506,9 +491,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "js-sys"
version = "0.3.47"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65"
checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c"
dependencies = [
"wasm-bindgen",
]
@ -521,9 +506,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.86"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "linked-hash-map"
@ -549,17 +534,6 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "loom"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d44c73b4636e497b4917eb21c33539efa3816741a2d3ff26c6316f1b529481a4"
dependencies = [
"cfg-if 1.0.0",
"generator",
"scoped-tls",
]
[[package]]
name = "matches"
version = "0.1.8"
@ -574,9 +548,9 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "memoffset"
version = "0.6.1"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d"
dependencies = [
"autocfg",
]
@ -659,9 +633,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.6.0"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ad167a2f54e832b82dbe003a046280dceffe5227b5f79e08e363a29638cfddd"
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
[[package]]
name = "oorandom"
@ -788,9 +762,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.24"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid",
]
@ -880,9 +854,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.4.3"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
dependencies = [
"regex-syntax",
]
@ -898,9 +872,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.6.22"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
[[package]]
name = "remove_dir_all"
@ -935,12 +909,6 @@ dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd"
[[package]]
name = "ryu"
version = "1.0.5"
@ -956,12 +924,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -985,9 +947,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.123"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
dependencies = [
"serde_derive",
]
@ -1004,9 +966,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.123"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote",
@ -1015,9 +977,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.62"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
dependencies = [
"itoa",
"ryu",
@ -1099,9 +1061,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "standback"
version = "0.2.15"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2beb4d1860a61f571530b3f855a1b538d0200f7871c63331ecd6f17b1f014f8"
checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
dependencies = [
"version_check",
]
@ -1187,9 +1149,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.60"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
dependencies = [
"proc-macro2",
"quote",
@ -1289,9 +1251,9 @@ dependencies = [
[[package]]
name = "tinytemplate"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ada8616fad06a2d0c455adc530de4ef57605a8120cc65da9653e0e9623ca74"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
@ -1299,9 +1261,9 @@ dependencies = [
[[package]]
name = "tinyvec"
version = "1.1.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
dependencies = [
"tinyvec_macros",
]
@ -1365,9 +1327,9 @@ dependencies = [
[[package]]
name = "typenum"
version = "1.12.0"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "unicode-bidi"
@ -1437,9 +1399,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "void"
@ -1449,7 +1411,7 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "vpncloud"
version = "2.1.0"
version = "2.2.0"
dependencies = [
"async-trait",
"byteorder",
@ -1481,9 +1443,9 @@ dependencies = [
[[package]]
name = "walkdir"
version = "2.3.1"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
@ -1498,9 +1460,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
version = "0.2.70"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
@ -1508,9 +1470,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.70"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7"
checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae"
dependencies = [
"bumpalo",
"lazy_static",
@ -1523,9 +1485,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.70"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c"
checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1533,9 +1495,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.70"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385"
checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c"
dependencies = [
"proc-macro2",
"quote",
@ -1546,15 +1508,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.70"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489"
[[package]]
name = "web-sys"
version = "0.3.47"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3"
checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -1562,9 +1524,9 @@ dependencies = [
[[package]]
name = "wildmatch"
version = "1.0.13"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2399814fda0d6999a6bfe9b5c7660d836334deb162a09db8b73d5b38fd8c40a"
checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a"
[[package]]
name = "winapi"

View File

@ -1,6 +1,6 @@
[package]
name = "vpncloud"
version = "2.1.0"
version = "2.2.0"
authors = ["Dennis Schwerdel <schwerdel@googlemail.com>"]
build = "build.rs"
license = "GPL-3.0"
@ -12,7 +12,7 @@ readme = "README.md"
edition = "2018"
[package.metadata]
toolchain = "1.50.0"
toolchain = "1.51.0"
upx_version = "3.96"
[dependencies]
@ -86,6 +86,7 @@ assets = [
["target/release/vpncloud", "/usr/bin/vpncloud", "755"],
["assets/example.net.disabled", "/etc/vpncloud/example.net.disabled", "600"],
["assets/vpncloud@.service", "/lib/systemd/system/vpncloud@.service", "644"],
["assets/vpncloud.target", "/lib/systemd/system/vpncloud.target", "644"],
["assets/vpncloud-wsproxy.service", "/lib/systemd/system/vpncloud-wsproxy.service", "644"],
["target/vpncloud.1.gz", "/usr/share/man/man1/vpncloud.1.gz", "644"]
]

View File

@ -19,7 +19,7 @@ peers:
- REMOTE_HOST:PORT
```
For more information, please see the [Website](https://vpncloud.ddswd.de) or the [Forum](https://groups.google.com/forum/#!forum/vpncloud).
For more information, please see the [Website](https://vpncloud.ddswd.de) or the [Discussions group](https://github.com/dswd/vpncloud/discussions).
### Project Status

View File

@ -1,3 +1,17 @@
vpncloud (2.2.0) stable; urgency=medium
* [added] Service target file (thanks to mnhauke)
* [added] Added interactive configuration wizard
* [added] Support for (un-)installation
* [added] Building static binaries
* [added] Building i686 rpm
* [changed] Restructured example config
* [changed] Changed Rust version to 1.51.0
* [changed] Updated dependencies
* [changed] Change permissions of /etc/vpncloud
-- Dennis Schwerdel <schwerdel+vpncloud@googlemail.com> Tue, 06 Apr 2021 12:27:00 +0200
vpncloud (2.1.0) stable; urgency=medium
* [added] Support for websocket proxy mode
@ -9,7 +23,7 @@ vpncloud (2.1.0) stable; urgency=medium
* [fixed] Added missing peer address propagation
* [fixed] Fixed problem with peer addresses without port
-- Dennis Schwerdel <schwerdel@googlemail.com> Sat, 06 Feb 2020 13:13:00 +0100
-- Dennis Schwerdel <schwerdel@googlemail.com> Sat, 06 Feb 2021 13:13:00 +0100
vpncloud (2.0.1) stable; urgency=medium

2
assets/vpncloud.target Normal file
View File

@ -0,0 +1,2 @@
[Unit]
Description=VpnCloud target allowing to start/stop all vpncloud@.service instances at once

View File

@ -2,6 +2,7 @@
Description=VpnCloud network '%I'
After=network-online.target
Wants=network-online.target
PartOf=vpncloud.target
Documentation=man:vpncloud(1)
[Service]

View File

@ -1,9 +1,13 @@
FROM ubuntu
RUN apt-get update && apt-get install -y asciinema
RUN apt-get update && apt-get install -y asciinema locales bash iputils-ping
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
RUN mkdir /root/.asciinema
RUN mkdir /etc/vpncloud
WORKDIR /data
ADD config /root/.asciinema/config
RUN echo 'PS1="\[\e[00;34m\]\[\e[01;31m\]\u\[\e[00;01;34m\]@\[\e[00;34m\]node\[\e[01;31m\]:\[\e[00;34m\]\w\[\e[01;31m\]> \[\e[00m\]"' >> /root/.bashrc
RUN echo 'PS1="\[\e[00;34m\]\[\e[01;31m\]\u\[\e[00;01;34m\]@\[\e[00;34m\]node\[\e[01;31m\]:\[\e[00;34m\]\w\[\e[01;31m\]> \[\e[00m\]"' >> /root/.bashrc

View File

@ -1,3 +1,3 @@
[record]
command = /usr/bin/bash -l
idle_time_limit = 2.5
command = bash -l
idle_time_limit = 2.5

View File

@ -1,6 +0,0 @@
#!/bin/bash
set -e
docker build -t asciinema-recorder .
docker run -it --rm --network host -v $(pwd)/../../target/release/:/usr/local/bin/ -v $(pwd):/data asciinema-recorder asciinema "$@"

View File

@ -0,0 +1,11 @@
#!/bin/bash
set -e
cd $(dirname $0)
docker build -t asciinema-recorder .
docker run -it --rm --network host \
-v $(pwd):/data \
-v /etc/hosts:/etc/hosts \
asciinema-recorder

View File

@ -0,0 +1,165 @@
{
"meta": {
"region": "eu-central-1",
"instance_type": "m5.large",
"ami": "ami-0db9040eb3ab74509",
"version": "2.2.0",
"duration": 623.0307722091675
},
"native": {
"iperf": {
"throughput": 9680235000.0,
"cpu_sender": 12.015535,
"cpu_receiver": 71.982452
},
"ping_100": {
"rtt_min": 0.049,
"rtt_max": 0.219,
"rtt_avg": 0.058,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.053,
"rtt_max": 0.247,
"rtt_avg": 0.059,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.053,
"rtt_max": 0.189,
"rtt_avg": 0.06,
"pkt_loss": 0.0
}
},
"plain": {
"iperf": {
"throughput": 5790600000.0,
"cpu_sender": 14.109763,
"cpu_receiver": 69.727033
},
"ping_100": {
"rtt_min": 0.079,
"rtt_max": 0.291,
"rtt_avg": 0.094,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.079,
"rtt_max": 0.304,
"rtt_avg": 0.096,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.082,
"rtt_max": 0.367,
"rtt_avg": 0.097,
"pkt_loss": 0.0
}
},
"aes256": {
"iperf": {
"throughput": 3917767000.0,
"cpu_sender": 6.439156,
"cpu_receiver": 64.267206
},
"ping_100": {
"rtt_min": 0.081,
"rtt_max": 0.206,
"rtt_avg": 0.097,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.088,
"rtt_max": 0.206,
"rtt_avg": 0.1,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.089,
"rtt_max": 0.319,
"rtt_avg": 0.103,
"pkt_loss": 0.0
}
},
"aes128": {
"iperf": {
"throughput": 3697142000.0,
"cpu_sender": 7.417808,
"cpu_receiver": 59.433831
},
"ping_100": {
"rtt_min": 0.083,
"rtt_max": 0.265,
"rtt_avg": 0.097,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.081,
"rtt_max": 0.369,
"rtt_avg": 0.102,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.086,
"rtt_max": 0.448,
"rtt_avg": 0.102,
"pkt_loss": 0.0
}
},
"chacha20": {
"iperf": {
"throughput": 3194412000.0,
"cpu_sender": 6.12856,
"cpu_receiver": 61.223349
},
"ping_100": {
"rtt_min": 0.081,
"rtt_max": 0.28,
"rtt_avg": 0.098,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.088,
"rtt_max": 0.264,
"rtt_avg": 0.103,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.092,
"rtt_max": 0.204,
"rtt_avg": 0.106,
"pkt_loss": 0.0
}
},
"results": {
"throughput_mbits": {
"native": 9680.235,
"plain": 5790.6,
"aes256": 3917.767,
"aes128": 3697.142,
"chacha20": 3194.412
},
"latency_us": {
"plain": {
"100": 18.0,
"500": 18.500000000000004,
"1000": 18.500000000000004
},
"aes256": {
"100": 19.5,
"500": 20.500000000000004,
"1000": 21.5
},
"aes128": {
"100": 19.5,
"500": 21.5,
"1000": 20.999999999999996
},
"chacha20": {
"100": 20.0,
"500": 22.0,
"1000": 23.0
}
}
}
}

View File

@ -7,8 +7,8 @@ from datetime import date
# Note: this script will run for ~8 minutes and incur costs of about $ 0.02
FILE = "../target/release/vpncloud"
VERSION = "2.1.0"
FILE = "../../target/release/vpncloud"
VERSION = "2.2.0"
REGION = "eu-central-1"
env = EC2Environment(
@ -113,4 +113,4 @@ name = "measurements/{date}_{version}_perf.json".format(date=date.today().strfti
eprint('Storing results in {}'.format(name))
with open(name, 'w') as fp:
json.dump(results, fp, indent=2)
eprint("done.")
eprint("done.")

View File

@ -15,16 +15,15 @@ use std::{
process::{Command, Stdio},
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex
Arc, Mutex,
},
thread
thread,
};
use super::util::{from_base62, to_base62, Encoder, TimeSource};
use smallvec::SmallVec;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
const TYPE_BEGIN: u8 = 0;
const TYPE_END: u8 = 1;
const TYPE_DATA: u8 = 2;
@ -40,14 +39,14 @@ fn sha512(data: &[u8]) -> SmallVec<[u8; 64]> {
struct FutureResult<T> {
has_result: AtomicBool,
result: Mutex<T>
result: Mutex<T>,
}
#[derive(Clone)]
pub struct BeaconSerializer<TS> {
shared_key: Vec<u8>,
future_peers: Arc<FutureResult<Vec<SocketAddr>>>,
_dummy_ts: PhantomData<TS>
_dummy_ts: PhantomData<TS>,
}
impl<TS: TimeSource> BeaconSerializer<TS> {
@ -55,7 +54,7 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
Self {
shared_key: shared_key.to_owned(),
future_peers: Arc::new(FutureResult { has_result: AtomicBool::new(false), result: Mutex::new(Vec::new()) }),
_dummy_ts: PhantomData
_dummy_ts: PhantomData,
}
}
@ -105,7 +104,7 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
fn decrypt_data(&self, data: &mut Vec<u8>) -> bool {
if data.is_empty() {
return false
return false;
}
let seed = data.pop().unwrap() ^ self.get_keystream(TYPE_SEED, 0, 0)[0];
self.mask_with_keystream(data as &mut [u8], TYPE_DATA, seed);
@ -122,7 +121,7 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
for p in peers {
match *p {
SocketAddr::V4(addr) => v4addrs.push(addr),
SocketAddr::V6(addr) => v6addrs.push(addr)
SocketAddr::V6(addr) => v6addrs.push(addr),
}
}
// Add count of v4 addresses
@ -158,23 +157,23 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
let mut peers = Vec::new();
let mut pos = 0;
if data.len() < 4 {
return peers
return peers;
}
if !self.decrypt_data(&mut data) {
return peers
return peers;
}
let then = Wrapping(Encoder::read_u16(&data[pos..=pos + 1]));
if let Some(ttl) = ttl_hours {
let now = Wrapping(Self::now_hour_16());
if now - then > Wrapping(ttl) && then - now > Wrapping(ttl) {
return peers
return peers;
}
}
pos += 2;
let v4count = data[pos] as usize;
pos += 1;
if v4count * 6 > data.len() - pos || (data.len() - pos - v4count * 6) % 18 > 0 {
return peers
return peers;
}
for _ in 0..v4count {
assert!(data.len() >= pos + 6);
@ -198,7 +197,7 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
Ipv6Addr::new(ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]),
port,
0,
0
0,
));
peers.push(addr);
}
@ -262,14 +261,14 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
peers.append(&mut self.peerlist_decode(&data[start_pos..end_pos], ttl_hours));
pos = start_pos
} else {
break
break;
}
}
peers
}
pub fn read_from_file<P: AsRef<Path>>(
&self, path: P, ttl_hours: Option<u16>
&self, path: P, ttl_hours: Option<u16>,
) -> Result<Vec<SocketAddr>, io::Error> {
let mut f = File::open(&path)?;
let mut contents = String::new();
@ -316,26 +315,22 @@ impl<TS: TimeSource> BeaconSerializer<TS> {
}
}
#[cfg(test)] use crate::util::MockTimeSource;
#[cfg(test)] use std::str::FromStr;
#[cfg(test)] use std::time::Duration;
#[cfg(test)]
use crate::util::MockTimeSource;
#[cfg(test)]
use std::str::FromStr;
#[cfg(test)]
use std::time::Duration;
#[test]
async fn encode() {
MockTimeSource::set_time(2000 * 3600);
let ser = BeaconSerializer::<MockTimeSource>::new(b"mysecretkey");
let mut peers = vec![
SocketAddr::from_str("1.2.3.4:5678").unwrap(),
SocketAddr::from_str("6.6.6.6:53").unwrap()
];
let mut peers = vec![SocketAddr::from_str("1.2.3.4:5678").unwrap(), SocketAddr::from_str("6.6.6.6:53").unwrap()];
assert_eq!("WsHI31EWDMBYxvITiILIrm2k9gEik22E", ser.encode(&peers));
peers.push(SocketAddr::from_str("[::1]:5678").unwrap());
assert_eq!("WsHI3GXKaXCveo6uejmZizZ72kR6Y0L9T7h49TXONp1ugfKvvvEik22E", ser.encode(&peers));
let peers = vec![
SocketAddr::from_str("1.2.3.4:5678").unwrap(),
SocketAddr::from_str("6.6.6.6:54").unwrap()
];
let peers = vec![SocketAddr::from_str("1.2.3.4:5678").unwrap(), SocketAddr::from_str("6.6.6.6:54").unwrap()];
assert_eq!("WsHI32gm9eMSHP3Lm1GXcdP7rD3ik22E", ser.encode(&peers));
}
@ -343,10 +338,7 @@ async fn encode() {
async fn decode() {
MockTimeSource::set_time(2000 * 3600);
let ser = BeaconSerializer::<MockTimeSource>::new(b"mysecretkey");
let mut peers = vec![
SocketAddr::from_str("1.2.3.4:5678").unwrap(),
SocketAddr::from_str("6.6.6.6:53").unwrap()
];
let mut peers = vec![SocketAddr::from_str("1.2.3.4:5678").unwrap(), SocketAddr::from_str("6.6.6.6:53").unwrap()];
assert_eq!(format!("{:?}", peers), format!("{:?}", ser.decode("WsHI31EWDMBYxvITiILIrm2k9gEik22E", None)));
peers.push(SocketAddr::from_str("[::1]:5678").unwrap());
assert_eq!(
@ -359,10 +351,7 @@ async fn decode() {
async fn decode_split() {
MockTimeSource::set_time(2000 * 3600);
let ser = BeaconSerializer::<MockTimeSource>::new(b"mysecretkey");
let peers = vec![
SocketAddr::from_str("1.2.3.4:5678").unwrap(),
SocketAddr::from_str("6.6.6.6:53").unwrap()
];
let peers = vec![SocketAddr::from_str("1.2.3.4:5678").unwrap(), SocketAddr::from_str("6.6.6.6:53").unwrap()];
assert_eq!(
format!("{:?}", peers),
format!("{:?}", ser.decode("WsHI3-1E.WD:MB Yx\tvI\nTi(IL)Ir[m2]k9ügEäik22E", None))
@ -377,10 +366,7 @@ async fn decode_split() {
async fn decode_offset() {
MockTimeSource::set_time(2000 * 3600);
let ser = BeaconSerializer::<MockTimeSource>::new(b"mysecretkey");
let peers = vec![
SocketAddr::from_str("1.2.3.4:5678").unwrap(),
SocketAddr::from_str("6.6.6.6:53").unwrap()
];
let peers = vec![SocketAddr::from_str("1.2.3.4:5678").unwrap(), SocketAddr::from_str("6.6.6.6:53").unwrap()];
assert_eq!(
format!("{:?}", peers),
format!("{:?}", ser.decode("Hello World: WsHI31EWDMBYxvITiILIrm2k9gEik22E! End of the World", None))
@ -391,10 +377,7 @@ async fn decode_offset() {
async fn decode_multiple() {
MockTimeSource::set_time(2000 * 3600);
let ser = BeaconSerializer::<MockTimeSource>::new(b"mysecretkey");
let peers = vec![
SocketAddr::from_str("1.2.3.4:5678").unwrap(),
SocketAddr::from_str("6.6.6.6:53").unwrap()
];
let peers = vec![SocketAddr::from_str("1.2.3.4:5678").unwrap(), SocketAddr::from_str("6.6.6.6:53").unwrap()];
assert_eq!(
format!("{:?}", peers),
format!("{:?}", ser.decode("WsHI31HVpqxFNMNSPrvik22E WsHI34yOBcZIulKdtn2ik22E", None))
@ -438,15 +421,11 @@ async fn decode_invalid() {
assert_eq!(2, ser.decode("WsHI3WsHI31EWDMBYxvITiILIrm2k9gEik22Eik22E", None).len());
}
#[test]
async fn encode_decode() {
MockTimeSource::set_time(2000 * 3600);
let ser = BeaconSerializer::<MockTimeSource>::new(b"mysecretkey");
let peers = vec![
SocketAddr::from_str("1.2.3.4:5678").unwrap(),
SocketAddr::from_str("6.6.6.6:53").unwrap()
];
let peers = vec![SocketAddr::from_str("1.2.3.4:5678").unwrap(), SocketAddr::from_str("6.6.6.6:53").unwrap()];
let data = ser.encode(&peers);
let peers2 = ser.decode(&data, None);
assert_eq!(format!("{:?}", peers), format!("{:?}", peers2));
@ -456,10 +435,7 @@ async fn encode_decode() {
async fn encode_decode_file() {
MockTimeSource::set_time(2000 * 3600);
let ser = BeaconSerializer::<MockTimeSource>::new(b"mysecretkey");
let peers = vec![
SocketAddr::from_str("1.2.3.4:5678").unwrap(),
SocketAddr::from_str("6.6.6.6:53").unwrap()
];
let peers = vec![SocketAddr::from_str("1.2.3.4:5678").unwrap(), SocketAddr::from_str("6.6.6.6:53").unwrap()];
let file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
assert!(ser.write_to_file(&peers, file.path()).is_ok());
let peers2 = ser.read_from_file(file.path(), None);
@ -471,10 +447,7 @@ async fn encode_decode_file() {
async fn encode_decode_cmd() {
MockTimeSource::set_time(2000 * 3600);
let ser = BeaconSerializer::<MockTimeSource>::new(b"mysecretkey");
let peers = vec![
SocketAddr::from_str("1.2.3.4:5678").unwrap(),
SocketAddr::from_str("6.6.6.6:53").unwrap()
];
let peers = vec![SocketAddr::from_str("1.2.3.4:5678").unwrap(), SocketAddr::from_str("6.6.6.6:53").unwrap()];
let file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
assert!(ser.write_to_cmd(&peers, &format!("echo $beacon > {}", file.path().display())).is_ok());
thread::sleep(Duration::from_millis(100));

View File

@ -2,22 +2,15 @@
// Copyright (C) 2015-2021 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
use super::{device::Type, types::Mode, util::Duration, util::run_cmd};
use super::{device::Type, types::Mode, util::run_cmd, util::Duration};
pub use crate::crypto::Config as CryptoConfig;
use std::{
cmp::max,
collections::HashMap,
ffi::OsStr,
process,
thread
};
use std::{cmp::max, collections::HashMap, ffi::OsStr, process, thread};
use structopt::{clap::Shell, StructOpt};
pub const DEFAULT_PEER_TIMEOUT: u16 = 300;
pub const DEFAULT_PORT: u16 = 3210;
#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct Config {
pub device_type: Type,
@ -27,6 +20,7 @@ pub struct Config {
pub fix_rp_filter: bool,
pub ip: Option<String>,
pub advertise_addresses: Vec<String>,
pub ifup: Option<String>,
pub ifdown: Option<String>,
@ -53,7 +47,7 @@ pub struct Config {
pub user: Option<String>,
pub group: Option<String>,
pub hook: Option<String>,
pub hooks: HashMap<String, String>
pub hooks: HashMap<String, String>,
}
impl Default for Config {
@ -65,6 +59,7 @@ impl Default for Config {
device_mtu: None,
fix_rp_filter: false,
ip: None,
advertise_addresses: vec![],
ifup: None,
ifdown: None,
crypto: CryptoConfig::default(),
@ -89,7 +84,7 @@ impl Default for Config {
user: None,
group: None,
hook: None,
hooks: HashMap::new()
hooks: HashMap::new(),
}
}
}
@ -117,6 +112,9 @@ impl Config {
if let Some(val) = file.ip {
self.ip = Some(val);
}
if let Some(mut val) = file.advertise_addresses {
self.advertise_addresses.append(&mut val);
}
if let Some(val) = file.ifup {
self.ifup = Some(val);
}
@ -227,6 +225,7 @@ impl Config {
if let Some(val) = args.ifup {
self.ifup = Some(val);
}
self.advertise_addresses.append(&mut args.advertise_addresses);
if let Some(val) = args.ifdown {
self.ifdown = Some(val);
}
@ -303,7 +302,7 @@ impl Config {
if s.contains(':') {
let pos = s.find(':').unwrap();
let name = &s[..pos];
let hook = &s[pos+1..];
let hook = &s[pos + 1..];
self.hooks.insert(name.to_string(), hook.to_string());
} else {
self.hook = Some(s);
@ -319,14 +318,14 @@ impl Config {
store: self.beacon_store,
load: self.beacon_load,
interval: Some(self.beacon_interval),
password: self.beacon_password
password: self.beacon_password,
}),
device: Some(ConfigFileDevice {
name: Some(self.device_name),
path: self.device_path,
mtu: self.device_mtu,
type_: Some(self.device_type),
fix_rp_filter: Some(self.fix_rp_filter)
fix_rp_filter: Some(self.fix_rp_filter),
}),
crypto: self.crypto,
group: self.group,
@ -334,6 +333,7 @@ impl Config {
ifup: self.ifup,
ifdown: self.ifdown,
ip: self.ip,
advertise_addresses: Some(self.advertise_addresses),
keepalive: self.keepalive,
listen: Some(self.listen),
mode: Some(self.mode),
@ -342,13 +342,10 @@ impl Config {
pid_file: self.pid_file,
port_forwarding: Some(self.port_forwarding),
stats_file: self.stats_file,
statsd: Some(ConfigFileStatsd {
server: self.statsd_server,
prefix: self.statsd_prefix
}),
statsd: Some(ConfigFileStatsd { server: self.statsd_server, prefix: self.statsd_prefix }),
switch_timeout: Some(self.switch_timeout),
hook: self.hook,
hooks: self.hooks
hooks: self.hooks,
}
}
@ -388,7 +385,7 @@ impl Config {
}
pub fn call_hook(
&self, event: &'static str, envs: impl IntoIterator<Item = (&'static str, impl AsRef<OsStr>)>, detach: bool
&self, event: &'static str, envs: impl IntoIterator<Item = (&'static str, impl AsRef<OsStr>)>, detach: bool,
) {
let mut script = None;
if let Some(ref s) = self.hook {
@ -398,7 +395,7 @@ impl Config {
script = Some(s);
}
if script.is_none() {
return
return;
}
let script = script.unwrap();
let mut cmd = process::Command::new("sh");
@ -518,6 +515,10 @@ pub struct Args {
#[structopt(long)]
pub ip: Option<String>,
/// A list of IP Addresses to advertise as our external address(s)
#[structopt(long = "advertise_addresses", use_delimiter = true)]
pub advertise_addresses: Vec<String>,
/// A command to setup the network interface
#[structopt(long)]
pub ifup: Option<String>,
@ -589,8 +590,8 @@ pub enum Command {
#[structopt(alias = "wsproxy")]
WsProxy {
/// Websocket listen address IP:PORT
#[structopt(long, short, default_value="3210")]
listen: String
#[structopt(long, short, default_value = "3210")]
listen: String,
},
/// Migrate an old config file
@ -604,8 +605,8 @@ pub enum Command {
/// Generate shell completions
Completion {
/// Shell to create completions for
#[structopt(long, default_value="bash")]
shell: Shell
#[structopt(long, default_value = "bash")]
shell: Shell,
},
/// Edit the config of a network
@ -613,7 +614,7 @@ pub enum Command {
Config {
/// Name of the network
#[structopt(short, long)]
name: Option<String>
name: Option<String>,
},
/// Install required utility files
@ -621,8 +622,8 @@ pub enum Command {
Install {
/// Remove installed files again
#[structopt(long)]
uninstall: bool
}
uninstall: bool,
},
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
@ -658,6 +659,7 @@ pub struct ConfigFile {
pub device: Option<ConfigFileDevice>,
pub ip: Option<String>,
pub advertise_addresses: Option<Vec<String>>,
pub ifup: Option<String>,
pub ifdown: Option<String>,
@ -679,7 +681,7 @@ pub struct ConfigFile {
pub user: Option<String>,
pub group: Option<String>,
pub hook: Option<String>,
pub hooks: HashMap<String, String>
pub hooks: HashMap<String, String>,
}
#[test]
@ -691,6 +693,9 @@ device:
path: /dev/net/tun
mtu: 1400
ip: 10.0.1.1/16
advertise-addresses:
- 192.168.0.1
- 192.168.1.1
ifup: ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up
ifdown: 'true'
peers:
@ -727,6 +732,7 @@ statsd:
fix_rp_filter: None
}),
ip: Some("10.0.1.1/16".to_string()),
advertise_addresses: Some(vec!["192.168.0.1".to_string(), "192.168.1.1".to_string()]),
ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()),
ifdown: Some("true".to_string()),
crypto: CryptoConfig::default(),
@ -776,6 +782,7 @@ async fn config_merge() {
fix_rp_filter: None,
}),
ip: None,
advertise_addresses: Some(vec![]),
ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()),
ifdown: Some("true".to_string()),
crypto: CryptoConfig::default(),
@ -803,35 +810,39 @@ async fn config_merge() {
prefix: Some("prefix".to_string()),
}),
hook: None,
hooks: HashMap::new()
});
assert_eq!(config, Config {
device_type: Type::Tun,
device_name: "vpncloud%d".to_string(),
device_path: None,
ip: None,
ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()),
ifdown: Some("true".to_string()),
listen: "3210".to_string(),
peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()],
peer_timeout: 600,
keepalive: Some(840),
switch_timeout: 300,
beacon_store: Some("/run/vpncloud.beacon.out".to_string()),
beacon_load: Some("/run/vpncloud.beacon.in".to_string()),
beacon_interval: 7200,
beacon_password: Some("test123".to_string()),
mode: Mode::Normal,
port_forwarding: true,
claims: 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()),
statsd_server: Some("example.com:1234".to_string()),
statsd_prefix: Some("prefix".to_string()),
..Default::default()
hooks: HashMap::new(),
});
assert_eq!(
config,
Config {
device_type: Type::Tun,
device_name: "vpncloud%d".to_string(),
device_path: None,
ip: None,
advertise_addresses: vec![],
ifup: Some("ifconfig $IFNAME 10.0.1.1/16 mtu 1400 up".to_string()),
ifdown: Some("true".to_string()),
listen: "3210".to_string(),
peers: vec!["remote.machine.foo:3210".to_string(), "remote.machine.bar:3210".to_string()],
peer_timeout: 600,
keepalive: Some(840),
switch_timeout: 300,
beacon_store: Some("/run/vpncloud.beacon.out".to_string()),
beacon_load: Some("/run/vpncloud.beacon.in".to_string()),
beacon_interval: 7200,
beacon_password: Some("test123".to_string()),
mode: Mode::Normal,
port_forwarding: true,
claims: 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()),
statsd_server: Some("example.com:1234".to_string()),
statsd_prefix: Some("prefix".to_string()),
..Default::default()
}
);
config.merge_args(Args {
type_: Some(Type::Tap),
device: Some("vpncloud0".to_string()),
@ -866,6 +877,7 @@ async fn config_merge() {
device_path: Some("/dev/null".to_string()),
device_mtu: None,
fix_rp_filter: false,
advertise_addresses: vec![],
ip: None,
ifup: Some("ifconfig $IFNAME 10.0.1.2/16 mtu 1400 up".to_string()),
ifdown: Some("ifconfig $IFNAME down".to_string()),

View File

@ -44,7 +44,7 @@
use byteorder::{ReadBytesExt, WriteBytesExt};
use ring::{
aead::{self, LessSafeKey, UnboundKey},
rand::{SecureRandom, SystemRandom}
rand::{SecureRandom, SystemRandom},
};
use std::cell::UnsafeCell;
@ -57,12 +57,10 @@ use std::{
use crate::{error::Error, util::MsgBuffer};
const NONCE_LEN: usize = 12;
pub const TAG_LEN: usize = 16;
pub const EXTRA_LEN: usize = 8;
fn random_data(size: usize) -> Vec<u8> {
let rand = SystemRandom::new();
let mut data = vec![0; size];
@ -98,7 +96,7 @@ impl Nonce {
num = num.wrapping_add(1);
self.0[i] = num;
if num > 0 {
return
return;
}
}
}
@ -109,7 +107,7 @@ struct CryptoKey {
send_nonce: Nonce,
min_nonce: Nonce,
next_min_nonce: Nonce,
seen_nonce: Nonce
seen_nonce: Nonce,
}
impl CryptoKey {
@ -121,7 +119,7 @@ impl CryptoKey {
send_nonce,
min_nonce: Nonce::zero(),
next_min_nonce: Nonce::zero(),
seen_nonce: Nonce::zero()
seen_nonce: Nonce::zero(),
}
}
@ -190,7 +188,7 @@ impl CryptoCore {
fn decrypt_with_key(key: &mut CryptoKey, nonce: Nonce, data_and_tag: &mut [u8]) -> Result<(), Error> {
if nonce < key.min_nonce {
return Err(Error::Crypto("Old nonce rejected"))
return Err(Error::Crypto("Old nonce rejected"));
}
// decrypt
let crypto_nonce = aead::Nonce::assume_unique_for_key(*nonce.as_bytes());
@ -273,7 +271,6 @@ pub fn test_speed(algo: &'static aead::Algorithm, max_time: &Duration) -> f64 {
data as f64 / duration / 1_000_000.0
}
#[cfg(test)]
mod tests {
use super::*;
@ -316,7 +313,6 @@ mod tests {
test_encrypt_decrypt(&aead::CHACHA20_POLY1305)
}
fn test_tampering(algo: &'static aead::Algorithm) {
let (sender, receiver) = create_dummy_pair(algo);
let plain = random_data(1000);
@ -447,7 +443,6 @@ mod tests {
test_key_rotation(&aead::CHACHA20_POLY1305);
}
#[test]
async fn test_core_size() {
assert_eq!(2400, mem::size_of::<CryptoCore>());

View File

@ -7,5 +7,6 @@ mod core;
mod init;
mod rotate;
pub use self::core::{EXTRA_LEN, TAG_LEN};
pub use common::*;
pub use self::core::{EXTRA_LEN, TAG_LEN};

View File

@ -34,20 +34,18 @@ use crate::{error::Error, util::MsgBuffer};
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
use ring::{
agreement::{agree_ephemeral, EphemeralPrivateKey, UnparsedPublicKey, X25519},
rand::SystemRandom
rand::SystemRandom,
};
use smallvec::{smallvec, SmallVec};
use std::io::{self, Cursor, Read, Write};
type EcdhPublicKey = UnparsedPublicKey<SmallVec<[u8; 96]>>;
type EcdhPrivateKey = EphemeralPrivateKey;
pub struct RotationMessage {
message_id: u64,
propose: EcdhPublicKey,
confirm: Option<EcdhPublicKey>
confirm: Option<EcdhPublicKey>,
}
impl RotationMessage {
@ -91,13 +89,13 @@ pub struct RotationState {
pending: Option<(Key, EcdhPublicKey)>, // sent by remote, to be confirmed
proposed: Option<EcdhPrivateKey>, // my own, proposed but not confirmed
message_id: u64,
timeout: bool
timeout: bool,
}
pub struct RotatedKey {
pub key: Key,
pub id: u64,
pub use_for_sending: bool
pub use_for_sending: bool,
}
impl RotationState {
@ -155,7 +153,7 @@ impl RotationState {
pub fn process_message(&mut self, msg: RotationMessage) -> Option<RotatedKey> {
if msg.message_id <= self.message_id {
return None
return None;
}
debug!("Received rotation message with id {}", msg.message_id);
self.timeout = false;
@ -167,7 +165,7 @@ impl RotationState {
if let Some(peer_key) = msg.confirm {
if let Some(private_key) = self.proposed.take() {
let key = Self::derive_key(private_key, peer_key);
return Some(RotatedKey { key, id: msg.message_id, use_for_sending: true })
return Some(RotatedKey { key, id: msg.message_id, use_for_sending: true });
}
}
None
@ -183,7 +181,7 @@ impl RotationState {
// Reconfirm last confirmed key
Self::send(
&RotationMessage { confirm: Some(confirmed_key.clone()), propose: proposed_key, message_id },
out
out,
);
} else {
// First message has been lost
@ -202,7 +200,7 @@ impl RotationState {
self.proposed = Some(private_key);
self.confirmed = Some((confirm_key.clone(), message_id));
Self::send(&RotationMessage { confirm: Some(confirm_key), propose: propose_key, message_id }, out);
return Some(RotatedKey { key, id: message_id, use_for_sending: false })
return Some(RotatedKey { key, id: message_id, use_for_sending: false });
} else {
// Nothing pending nor proposed, still waiting to receive message 1
// Do nothing, peer will retry
@ -221,7 +219,7 @@ mod tests {
impl MsgBuffer {
fn msg(&mut self) -> Option<RotationMessage> {
if self.is_empty() {
return None
return None;
}
let msg = RotationMessage::read_from(Cursor::new(self.message())).unwrap();
self.set_length(0);

View File

@ -6,7 +6,6 @@ use thiserror::Error;
use std::io;
#[derive(Error, Debug)]
pub enum Error {
/// Crypto init error, this is recoverable
@ -52,5 +51,5 @@ pub enum Error {
Parse(&'static str),
#[error("Name can not be resolved: {0}")]
NameUnresolvable(String)
NameUnresolvable(String),
}

View File

@ -4,11 +4,12 @@ use std::{
fs::{self, File},
io::Write,
os::unix::fs::PermissionsExt,
process::Command
process::Command,
};
const MANPAGE: &[u8] = include_bytes!("../target/vpncloud.1.gz");
const SERVICE_FILE: &[u8] = include_bytes!("../assets/vpncloud@.service");
const TARGET_FILE: &[u8] = include_bytes!("../assets/vpncloud.target");
const WS_PROXY_SERVICE_FILE: &[u8] = include_bytes!("../assets/vpncloud-wsproxy.service");
const EXAMPLE_CONFIG: &[u8] = include_bytes!("../assets/example.net.disabled");
@ -22,10 +23,10 @@ pub fn install() -> Result<(), Error> {
env::current_exe()
.and_then(|p| fs::copy(p, "/usr/bin/vpncloud"))
.map_err(|e| Error::FileIo("Failed to copy binary", e))?;
fs::set_permissions("/usr/bin/vpncloud", fs::Permissions::from_mode(755))
fs::set_permissions("/usr/bin/vpncloud", fs::Permissions::from_mode(0o755))
.map_err(|e| Error::FileIo("Failed to set permissions for binary", e))?;
fs::create_dir_all("/etc/vpncloud").map_err(|e| Error::FileIo("Failed to create config folder", e))?;
fs::set_permissions("/etc/vpncloud", fs::Permissions::from_mode(700))
fs::set_permissions("/etc/vpncloud", fs::Permissions::from_mode(0o700))
.map_err(|e| Error::FileIo("Failed to set permissions for config folder", e))?;
File::create("/etc/vpncloud/example.net.disabled")
.and_then(|mut f| f.write_all(EXAMPLE_CONFIG))
@ -36,6 +37,9 @@ pub fn install() -> Result<(), Error> {
File::create("/lib/systemd/system/vpncloud@.service")
.and_then(|mut f| f.write_all(SERVICE_FILE))
.map_err(|e| Error::FileIo("Failed to create service file", e))?;
File::create("/lib/systemd/system/vpncloud.target")
.and_then(|mut f| f.write_all(TARGET_FILE))
.map_err(|e| Error::FileIo("Failed to create service target file", e))?;
File::create("/lib/systemd/system/vpncloud-wsproxy.service")
.and_then(|mut f| f.write_all(WS_PROXY_SERVICE_FILE))
.map_err(|e| Error::FileIo("Failed to create wsporxy service file", e))?;
@ -49,6 +53,8 @@ pub fn uninstall() -> Result<(), Error> {
fs::remove_file("/usr/share/man/man1/vpncloud.1.gz").map_err(|e| Error::FileIo("Failed to remove manpage", e))?;
fs::remove_file("/lib/systemd/system/vpncloud@.service")
.map_err(|e| Error::FileIo("Failed to remove service file", e))?;
fs::remove_file("/lib/systemd/system/vpncloud.target")
.map_err(|e| Error::FileIo("Failed to remove service target file", e))?;
fs::remove_file("/lib/systemd/system/vpncloud-wsproxy.service")
.map_err(|e| Error::FileIo("Failed to remove wsproxy service file", e))?;
fs::remove_file("/usr/bin/vpncloud").map_err(|e| Error::FileIo("Failed to remove binary", e))?;

View File

@ -6,7 +6,8 @@
#[macro_use] extern crate serde;
#[macro_use] extern crate tokio;
#[cfg(test)] extern crate tempfile;
#[cfg(test)]
extern crate tempfile;
#[macro_use]
pub mod util;
@ -19,6 +20,8 @@ pub mod config;
pub mod crypto;
pub mod device;
pub mod error;
#[cfg(feature = "installer")]
pub mod installer;
pub mod messages;
pub mod net;
pub mod oldconfig;
@ -28,9 +31,10 @@ pub mod port_forwarding;
pub mod table;
pub mod traffic;
pub mod types;
#[cfg(feature = "wizard")] pub mod wizard;
#[cfg(feature = "websocket")] pub mod wsproxy;
#[cfg(feature = "installer")] pub mod installer;
#[cfg(feature = "wizard")]
pub mod wizard;
#[cfg(feature = "websocket")]
pub mod wsproxy;
use structopt::StructOpt;
@ -43,7 +47,7 @@ use std::{
process,
str::FromStr,
sync::Mutex,
thread
thread,
};
use crate::{
@ -61,7 +65,7 @@ use crate::{
use crate::wsproxy::ProxyConnection;
struct DualLogger {
file: Option<Mutex<File>>
file: Option<Mutex<File>>,
}
impl DualLogger {
@ -117,18 +121,18 @@ fn run_script(script: &str, ifname: &str) {
error!("Script returned with error: {:?}", status.code())
}
}
Err(e) => error!("Failed to execute script {:?}: {}", script, e)
Err(e) => error!("Failed to execute script {:?}: {}", script, e),
}
}
fn parse_ip_netmask(addr: &str) -> Result<(Ipv4Addr, Ipv4Addr), String> {
let (ip_str, len_str) = match addr.find('/') {
Some(pos) => (&addr[..pos], &addr[pos + 1..]),
None => (addr, "24")
None => (addr, "24"),
};
let prefix_len = u8::from_str(len_str).map_err(|_| format!("Invalid prefix length: {}", len_str))?;
if prefix_len > 32 {
return Err(format!("Invalid prefix length: {}", prefix_len))
return Err(format!("Invalid prefix length: {}", prefix_len));
}
let ip = Ipv4Addr::from_str(ip_str).map_err(|_| format!("Invalid ip address: {}", ip_str))?;
let netmask = Ipv4Addr::from(u32::max_value().checked_shl(32 - prefix_len as u32).unwrap());
@ -233,7 +237,7 @@ async fn main() {
let args: Args = Args::from_args();
if args.version {
println!("VpnCloud v{}", env!("CARGO_PKG_VERSION"));
return
return;
}
let logger = try_fail!(DualLogger::new(args.log_file.as_ref()), "Failed to open logfile: {}");
log::set_boxed_logger(Box::new(logger)).unwrap();
@ -294,7 +298,7 @@ async fn main() {
}
}
}
return
return;
}
let mut config = Config::default();
if let Some(ref file) = args.config {
@ -319,7 +323,7 @@ async fn main() {
debug!("Config: {:?}", config);
if config.crypto.password.is_none() && config.crypto.private_key.is_none() {
error!("Either password or private key must be set in config or given as parameter");
return
return;
}
#[cfg(feature = "websocket")]
if config.listen.starts_with("ws://") {
@ -328,7 +332,7 @@ async fn main() {
Type::Tap => run::<payload::Frame, _>(config, socket).await,
Type::Tun => run::<payload::Packet, _>(config, socket).await
}
return
return;
}
let socket = try_fail!(NetSocket::listen(&config.listen).await, "Failed to open socket {}: {}", config.listen);
match config.device_type {

View File

@ -6,30 +6,27 @@ use crate::{
crypto::Payload,
error::Error,
types::{NodeId, Range, RangeList, NODE_ID_BYTES},
util::MsgBuffer
util::MsgBuffer,
};
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
use smallvec::{smallvec, SmallVec};
use std::{
io::{self, Cursor, Read, Seek, SeekFrom, Take, Write},
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
};
pub const MESSAGE_TYPE_DATA: u8 = 0;
pub const MESSAGE_TYPE_NODE_INFO: u8 = 1;
pub const MESSAGE_TYPE_KEEPALIVE: u8 = 2;
pub const MESSAGE_TYPE_CLOSE: u8 = 0xff;
pub type AddrList = SmallVec<[SocketAddr; 4]>;
pub type PeerList = SmallVec<[PeerInfo; 16]>;
#[derive(Debug, PartialEq)]
pub struct PeerInfo {
pub node_id: Option<NodeId>,
pub addrs: AddrList
pub addrs: AddrList,
}
#[derive(Debug, PartialEq)]
@ -38,7 +35,7 @@ pub struct NodeInfo {
pub peers: PeerList,
pub claims: RangeList,
pub peer_timeout: Option<u16>,
pub addrs: AddrList
pub addrs: AddrList,
}
impl NodeInfo {
@ -53,7 +50,7 @@ impl NodeInfo {
let flags = r.read_u8()?;
Self::read_addr_list_inner(r, flags)
}
fn read_addr_list_inner<R: Read>(r: &mut Take<R>, flags: u8) -> Result<AddrList, io::Error> {
let num_ipv4_addrs = (flags & 0x07) as usize;
let num_ipv6_addrs = (flags & 0x38) as usize / 8;
@ -74,7 +71,7 @@ impl NodeInfo {
}
Ok(addrs)
}
fn decode_peer_list_part<R: Read>(r: &mut Take<R>) -> Result<PeerList, io::Error> {
let mut peers = smallvec![];
while r.limit() > 0 {
@ -109,7 +106,7 @@ impl NodeInfo {
loop {
let part = r.read_u8().map_err(|_| Error::Message("Truncated message"))?;
if part == Self::PART_END {
break
break;
}
let part_len = r.read_u16::<NetworkEndian>().map_err(|_| Error::Message("Truncated message"))? as usize;
let mut rp = r.take(part_len as u64);
@ -139,7 +136,7 @@ impl NodeInfo {
}
let node_id = match node_id {
Some(node_id) => node_id,
None => return Err(Error::Message("Payload without node_id"))
None => return Err(Error::Message("Payload without node_id")),
};
Ok(Self { node_id, peers, claims, peer_timeout, addrs })
}
@ -155,7 +152,7 @@ impl NodeInfo {
for a in &p.addrs {
match a {
SocketAddr::V4(addr) => addr_ipv4.push(*addr),
SocketAddr::V6(addr) => addr_ipv6.push(*addr)
SocketAddr::V6(addr) => addr_ipv6.push(*addr),
}
}
while addr_ipv4.len() >= 8 {
@ -190,7 +187,7 @@ impl NodeInfo {
for a in &self.addrs {
match a {
SocketAddr::V4(addr) => addr_ipv4.push(*addr),
SocketAddr::V6(addr) => addr_ipv6.push(*addr)
SocketAddr::V6(addr) => addr_ipv6.push(*addr),
}
}
while addr_ipv4.len() >= 8 {
@ -213,7 +210,7 @@ impl NodeInfo {
}
fn encode_part<F: FnOnce(&mut Cursor<&mut [u8]>) -> Result<(), io::Error>>(
cursor: &mut Cursor<&mut [u8]>, part: u8, f: F
cursor: &mut Cursor<&mut [u8]>, part: u8, f: F,
) -> Result<(), io::Error> {
cursor.write_u8(part)?;
cursor.write_u16::<NetworkEndian>(0)?;
@ -257,7 +254,6 @@ impl NodeInfo {
}
}
impl Payload for NodeInfo {
fn write_to(&self, buffer: &mut MsgBuffer) {
self.encode(buffer)

View File

@ -40,15 +40,17 @@ pub trait Socket: Sized + Clone + Send + Sync + 'static {
async fn create_port_forwarding(&self) -> Option<PortForwarding>;
}
pub fn parse_listen(addr: &str) -> SocketAddr {
pub fn parse_listen(addr: &str, default_port: u16) -> SocketAddr {
if let Some(addr) = addr.strip_prefix("*:") {
let port = try_fail!(addr.parse::<u16>(), "Invalid port: {}");
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)
} else if addr.contains(':') {
try_fail!(addr.parse::<SocketAddr>(), "Invalid address: {}: {}", addr)
} else {
let port = try_fail!(addr.parse::<u16>(), "Invalid port: {}");
} else if let Ok(port) = addr.parse::<u16>() {
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)
} else {
let ip = try_fail!(addr.parse::<IpAddr>(), "Invalid addr: {}");
SocketAddr::new(ip, default_port)
}
}

View File

@ -13,7 +13,7 @@ pub enum OldCryptoMethod {
#[serde(rename = "aes256")]
AES256,
#[serde(rename = "aes128")]
AES128
AES128,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
@ -57,7 +57,7 @@ pub struct OldConfigFile {
#[serde(alias = "statsd-prefix")]
pub statsd_prefix: Option<String>,
pub user: Option<String>,
pub group: Option<String>
pub group: Option<String>,
}
impl OldConfigFile {
@ -89,7 +89,7 @@ impl OldConfigFile {
interval: self.beacon_interval,
load: self.beacon_load,
store: self.beacon_store,
password: self.shared_key.clone()
password: self.shared_key.clone(),
}),
claims: self.subnets,
crypto: CryptoConfig {
@ -97,7 +97,7 @@ impl OldConfigFile {
password: Some(self.shared_key.unwrap_or_else(|| "none".to_string())),
private_key: None,
public_key: None,
trusted_keys: vec![]
trusted_keys: vec![],
},
device: Some(ConfigFileDevice {
fix_rp_filter: None,
@ -110,6 +110,7 @@ impl OldConfigFile {
ifdown: self.ifdown,
ifup: self.ifup,
ip: None,
advertise_addresses: None,
keepalive: self.keepalive,
listen: self.listen.or(self.port.map(|p| format!("{}", p))),
mode: self.mode,
@ -122,7 +123,7 @@ impl OldConfigFile {
switch_timeout: self.dst_timeout,
user: self.user,
hook: None,
hooks: HashMap::new()
hooks: HashMap::new(),
}
}
}

View File

@ -43,7 +43,7 @@ impl Protocol for Frame {
// treat vlan id 0x000 as untagged
src.copy_within(2..8, 0);
dst.copy_within(2..8, 0);
return Ok((Address { data: src, len: 6 }, Address { data: dst, len: 6 }))
return Ok((Address { data: src, len: 6 }, Address { data: dst, len: 6 }));
}
Ok((Address { data: src, len: 8 }, Address { data: dst, len: 8 }))
} else {
@ -52,7 +52,6 @@ impl Protocol for Frame {
}
}
#[test]
async 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];
@ -93,13 +92,13 @@ impl Protocol for Packet {
fn parse(data: &[u8]) -> Result<(Address, Address), Error> {
// HOT PATH
if data.is_empty() {
return Err(Error::Parse("Empty header"))
return Err(Error::Parse("Empty header"));
}
let version = data[0] >> 4;
match version {
4 => {
if data.len() < 20 {
return Err(Error::Parse("Truncated IPv4 header"))
return Err(Error::Parse("Truncated IPv4 header"));
}
let src = Address::read_from_fixed(&data[12..], 4)?;
let dst = Address::read_from_fixed(&data[16..], 4)?;
@ -107,18 +106,17 @@ impl Protocol for Packet {
}
6 => {
if data.len() < 40 {
return Err(Error::Parse("Truncated IPv6 header"))
return Err(Error::Parse("Truncated IPv6 header"));
}
let src = Address::read_from_fixed(&data[8..], 16)?;
let dst = Address::read_from_fixed(&data[24..], 16)?;
Ok((src, dst))
}
_ => Err(Error::Parse("Invalid IP protocol version"))
_ => Err(Error::Parse("Invalid IP protocol version")),
}
}
}
#[test]
async 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];
@ -131,7 +129,7 @@ async fn decode_ipv4_packet() {
async 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
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 });
@ -158,4 +156,4 @@ async fn decode_invalid_packet() {
4, 3, 2
])
.is_err());
}
}

View File

@ -11,7 +11,7 @@ pub struct EpollWait {
event: libc::epoll_event,
socket: RawFd,
device: RawFd,
timeout: u32
timeout: u32,
}
impl EpollWait {
@ -27,14 +27,14 @@ impl EpollWait {
let mut event = libc::epoll_event { u64: 0, events: 0 };
let poll_fd = unsafe { libc::epoll_create(3) };
if poll_fd == -1 {
return Err(io::Error::last_os_error())
return Err(io::Error::last_os_error());
}
for fd in &[socket, device] {
event.u64 = *fd as u64;
event.events = flags;
let res = unsafe { libc::epoll_ctl(poll_fd, libc::EPOLL_CTL_ADD, *fd, &mut event) };
if res == -1 {
return Err(io::Error::last_os_error())
return Err(io::Error::last_os_error());
}
}
Ok(Self { poll_fd, event, socket, device, timeout })
@ -63,7 +63,7 @@ impl Iterator for EpollWait {
unreachable!()
}
}
_ => unreachable!()
_ => unreachable!(),
})
}
}

View File

@ -8,12 +8,11 @@ mod epoll;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::epoll::EpollWait as WaitImpl;
use std::io;
pub enum WaitResult {
Timeout,
Socket,
Device,
Error(io::Error)
Error(io::Error),
}

View File

@ -19,7 +19,7 @@ mod internal {
pub internal_addr: SocketAddrV4,
pub external_addr: SocketAddrV4,
gateway: Gateway,
pub next_extension: Option<Time>
pub next_extension: Option<Time>,
}
impl PortForwarding {
@ -32,11 +32,11 @@ mod internal {
if err.kind() == io::ErrorKind::WouldBlock {
// Why this code?
info!("Port-forwarding: no router found");
return None
return None;
}
}
error!("Port-forwarding: failed to find router: {}", err);
return None
return None;
}
};
debug!("Port-forwarding: found router at {}", gateway.addr);
@ -46,7 +46,7 @@ mod internal {
Ok(ip) => ip,
Err(err) => {
error!("Port-forwarding: failed to obtain external IP: {}", err);
return None
return None;
}
};
if let Ok((port, timeout)) = Self::get_any_forwarding(&gateway, internal_addr, port) {
@ -64,19 +64,19 @@ mod internal {
fn get_any_forwarding(gateway: &Gateway, addr: SocketAddrV4, port: u16) -> Result<(u16, u32), ()> {
if let Ok(a) = Self::get_forwarding(gateway, addr, port) {
return Ok(a)
return Ok(a);
}
if let Ok(a) = Self::get_forwarding(gateway, addr, 0) {
return Ok(a)
return Ok(a);
}
for i in 1..5 {
if let Ok(a) = Self::get_forwarding(gateway, addr, port + i) {
return Ok(a)
return Ok(a);
}
}
for _ in 0..5 {
if let Ok(a) = Self::get_forwarding(gateway, addr, rand::random()) {
return Ok(a)
return Ok(a);
}
}
warn!("Failed to activate port forwarding");
@ -125,20 +125,20 @@ mod internal {
pub fn check_extend(&mut self) {
if let Some(deadline) = self.next_extension {
if deadline > SystemTimeSource::now() {
return
return;
}
} else {
return
return;
}
match self.gateway.add_port(
PortMappingProtocol::UDP,
self.external_addr.port(),
self.internal_addr,
LEASE_TIME,
DESCRIPTION
DESCRIPTION,
) {
Ok(()) => debug!("Port-forwarding: extended port forwarding"),
Err(err) => debug!("Port-forwarding: failed to extend port forwarding: {}", err)
Err(err) => debug!("Port-forwarding: failed to extend port forwarding: {}", err),
};
self.next_extension = Some(SystemTimeSource::now() + Time::from(LEASE_TIME) - 60);
}
@ -146,7 +146,7 @@ mod internal {
fn deactivate(&self) {
match self.gateway.remove_port(PortMappingProtocol::UDP, self.external_addr.port()) {
Ok(()) => info!("Port-forwarding: successfully deactivated port forwarding"),
Err(err) => debug!("Port-forwarding: failed to deactivate port forwarding: {}", err)
Err(err) => debug!("Port-forwarding: failed to deactivate port forwarding: {}", err),
}
}

View File

@ -4,27 +4,25 @@
use fnv::FnvHasher;
use std::{
cmp::min, collections::HashMap, hash::BuildHasherDefault, io, io::Write, marker::PhantomData, net::SocketAddr
cmp::min, collections::HashMap, hash::BuildHasherDefault, io, io::Write, marker::PhantomData, net::SocketAddr,
};
use crate::{
types::{Address, Range, RangeList},
util::{addr_nice, Duration, Time, TimeSource}
util::{addr_nice, Duration, Time, TimeSource},
};
type Hash = BuildHasherDefault<FnvHasher>;
struct CacheValue {
peer: SocketAddr,
timeout: Time
timeout: Time,
}
struct ClaimEntry {
peer: SocketAddr,
claim: Range,
timeout: Time
timeout: Time,
}
pub struct ClaimTable<TS: TimeSource> {
@ -32,7 +30,7 @@ pub struct ClaimTable<TS: TimeSource> {
cache_timeout: Duration,
claims: Vec<ClaimEntry>,
claim_timeout: Duration,
_dummy: PhantomData<TS>
_dummy: PhantomData<TS>,
}
impl<TS: TimeSource> ClaimTable<TS> {
@ -57,7 +55,7 @@ impl<TS: TimeSource> ClaimTable<TS> {
entry.timeout = TS::now() + self.claim_timeout as Time;
claims.swap_remove(pos);
if claims.is_empty() {
break
break;
}
} else {
entry.timeout = 0
@ -92,7 +90,7 @@ impl<TS: TimeSource> ClaimTable<TS> {
pub fn lookup(&mut self, addr: Address) -> Option<SocketAddr> {
// HOT PATH
if let Some(entry) = self.cache.get(&addr) {
return Some(entry.peer)
return Some(entry.peer);
}
// COLD PATH
let mut found = None;
@ -104,11 +102,11 @@ impl<TS: TimeSource> ClaimTable<TS> {
}
}
if let Some(entry) = found {
self.cache.insert(addr, CacheValue {
peer: entry.peer,
timeout: min(TS::now() + self.cache_timeout as Time, entry.timeout)
});
return Some(entry.peer)
self.cache.insert(
addr,
CacheValue { peer: entry.peer, timeout: min(TS::now() + self.cache_timeout as Time, entry.timeout) },
);
return Some(entry.peer);
}
None
}
@ -155,4 +153,4 @@ impl<TS: TimeSource> ClaimTable<TS> {
}
}
// TODO: test
// TODO: test

View File

@ -8,8 +8,8 @@ use std::{
net::SocketAddr,
sync::{
atomic::{AtomicUsize, Ordering},
Once
}
Once,
},
};
pub use crate::{
@ -19,10 +19,9 @@ pub use crate::{
net::MockSocket,
payload::{Frame, Packet, Protocol},
types::Range,
util::{MockTimeSource, Time, TimeSource}
util::{MockTimeSource, Time, TimeSource},
};
static INIT_LOGGER: Once = Once::new();
pub fn init_debug_logger() {
@ -61,20 +60,18 @@ impl log::Log for DebugLogger {
}
}
type TestNode<P> = GenericCloud<MockDevice, P, MockSocket, MockTimeSource>;
pub struct Simulator<P: Protocol> {
next_port: u16,
nodes: HashMap<SocketAddr, TestNode<P>>,
messages: VecDeque<(SocketAddr, SocketAddr, Vec<u8>)>
messages: VecDeque<(SocketAddr, SocketAddr, Vec<u8>)>,
}
pub type TapSimulator = Simulator<Frame>;
#[allow(dead_code)]
pub type TunSimulator = Simulator<Packet>;
impl<P: Protocol> Simulator<P> {
pub fn new() -> Self {
init_debug_logger();

View File

@ -5,4 +5,9 @@
mod common;
mod nat;
mod payload;
mod peers;
mod peers;
#[test]
fn test_time_format() {
assert!(time::OffsetDateTime::try_now_local().is_ok());
}

View File

@ -6,13 +6,13 @@ use std::{
collections::HashMap,
io::{self, Write},
net::SocketAddr,
ops::AddAssign
ops::AddAssign,
};
use super::{
engine::common::{Hash, STATS_INTERVAL},
types::Address,
util::{addr_nice, Bytes}
util::{addr_nice, Bytes},
};
@ -26,7 +26,7 @@ pub struct TrafficEntry {
pub in_packets_total: usize,
pub in_bytes: u64,
pub in_packets: usize,
pub idle_periods: usize
pub idle_periods: usize,
}
impl AddAssign<&TrafficEntry> for TrafficEntry {
@ -72,12 +72,11 @@ impl TrafficEntry {
}
}
#[derive(Default)]
pub struct TrafficStats {
peers: HashMap<SocketAddr, TrafficEntry, Hash>,
payload: HashMap<(Address, Address), TrafficEntry, Hash>,
pub dropped: TrafficEntry
pub dropped: TrafficEntry,
}
impl TrafficStats {

View File

@ -4,7 +4,7 @@
use crate::{
error::Error,
util::{bytes_to_hex, Encoder}
util::{bytes_to_hex, Encoder},
};
use byteorder::{ReadBytesExt, WriteBytesExt};
use smallvec::SmallVec;
@ -13,18 +13,17 @@ use std::{
hash::{Hash, Hasher},
io::{Read, Write},
net::{Ipv4Addr, Ipv6Addr},
str::FromStr
str::FromStr,
};
pub const NODE_ID_BYTES: usize = 16;
pub type NodeId = [u8; NODE_ID_BYTES];
#[derive(Eq, Clone, Copy)]
pub struct Address {
pub data: [u8; 16],
pub len: u8
pub len: u8,
}
impl Address {
@ -37,7 +36,7 @@ impl Address {
#[inline]
pub fn read_from_fixed<R: Read>(mut r: R, len: u8) -> Result<Address, Error> {
if len > 16 {
return Err(Error::Parse("Invalid address, too long"))
return Err(Error::Parse("Invalid address, too long"));
}
let mut data = [0; 16];
r.read_exact(&mut data[..len as usize]).map_err(|_| Error::Parse("Address too short"))?;
@ -57,7 +56,6 @@ impl Address {
}
}
impl PartialEq for Address {
#[inline]
fn eq(&self, rhs: &Self) -> bool {
@ -65,7 +63,6 @@ impl PartialEq for Address {
}
}
impl Hash for Address {
#[inline]
fn hash<H: Hasher>(&self, hasher: &mut H) {
@ -73,7 +70,6 @@ impl Hash for Address {
}
}
impl fmt::Display for Address {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let d = &self.data;
@ -108,7 +104,7 @@ impl FromStr for Address {
let ip = addr.octets();
let mut res = [0; 16];
res[0..4].copy_from_slice(&ip);
return Ok(Address { data: res, len: 4 })
return Ok(Address { data: res, len: 4 });
}
if let Ok(addr) = Ipv6Addr::from_str(text) {
let segments = addr.segments();
@ -116,7 +112,7 @@ impl FromStr for Address {
for i in 0..8 {
Encoder::write_u16(segments[i], &mut res[2 * i..]);
}
return Ok(Address { data: res, len: 16 })
return Ok(Address { data: res, len: 16 });
}
let parts: SmallVec<[&str; 10]> = text.split(':').collect();
if parts.len() == 6 {
@ -124,17 +120,16 @@ impl FromStr for Address {
for i in 0..6 {
bytes[i] = u8::from_str_radix(parts[i], 16).map_err(|_| Error::Parse("Failed to parse mac"))?;
}
return Ok(Address { data: bytes, len: 6 })
return Ok(Address { data: bytes, len: 6 });
}
Err(Error::Parse("Failed to parse address"))
}
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct Range {
pub base: Address,
pub prefix_len: u8
pub prefix_len: u8,
}
pub type RangeList = SmallVec<[Range; 4]>;
@ -142,14 +137,14 @@ pub type RangeList = SmallVec<[Range; 4]>;
impl Range {
pub fn matches(&self, addr: Address) -> bool {
if self.base.len != addr.len {
return false
return false;
}
let mut match_len = 0;
for i in 0..addr.len as usize {
let m = addr.data[i] ^ self.base.data[i];
match_len += m.leading_zeros() as u8;
if m != 0 {
break
break;
}
}
match_len >= self.prefix_len
@ -175,7 +170,7 @@ impl FromStr for Range {
fn from_str(text: &str) -> Result<Self, Self::Err> {
let pos = match text.find('/') {
Some(pos) => pos,
None => return Err(Error::Parse("Invalid range format"))
None => return Err(Error::Parse("Invalid range format")),
};
let prefix_len = u8::from_str(&text[pos + 1..]).map_err(|_| Error::Parse("Failed to parse prefix length"))?;
let base = Address::from_str(&text[..pos])?;
@ -195,7 +190,6 @@ impl fmt::Debug for Range {
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
pub enum Mode {
#[serde(rename = "normal")]
@ -205,7 +199,7 @@ pub enum Mode {
#[serde(rename = "switch")]
Switch,
#[serde(rename = "router")]
Router
Router,
}
impl fmt::Display for Mode {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
@ -213,7 +207,7 @@ impl fmt::Display for Mode {
Mode::Normal => write!(formatter, "normal"),
Mode::Hub => write!(formatter, "hub"),
Mode::Switch => write!(formatter, "switch"),
Mode::Router => write!(formatter, "router")
Mode::Router => write!(formatter, "router"),
}
}
}
@ -226,12 +220,11 @@ impl FromStr for Mode {
"hub" => Self::Hub,
"switch" => Self::Switch,
"router" => Self::Router,
_ => return Err("Unknown mode")
_ => return Err("Unknown mode"),
})
}
}
#[cfg(test)]
mod tests {

View File

@ -32,7 +32,7 @@ fn configure_connectivity(config: &mut Config, mode: usize, theme: &ColorfulThem
Input::with_theme(theme)
.with_prompt("Peer addresses (comma separated)")
.default(config.peers.join(","))
.interact_text()?
.interact_text()?,
);
if mode >= MODE_ADVANCED {
config.port_forwarding = Confirm::with_theme(theme)
@ -41,6 +41,12 @@ fn configure_connectivity(config: &mut Config, mode: usize, theme: &ColorfulThem
.interact()?;
}
if mode == MODE_EXPERT {
config.advertise_addresses = str_list(
Input::with_theme(theme)
.with_prompt("Advertise addresses (comma separated)")
.default(config.advertise_addresses.join(","))
.interact_text()?,
);
config.peer_timeout = Input::with_theme(theme)
.with_prompt("Peer timeout (in seconds)")
.default(config.peer_timeout)
@ -54,12 +60,11 @@ fn configure_connectivity(config: &mut Config, mode: usize, theme: &ColorfulThem
Ok(())
}
fn configure_crypto(config: &mut Config, mode: usize, theme: &ColorfulTheme) -> Result<(), io::Error> {
if (config.crypto.password.is_some() || config.crypto.private_key.is_some())
&& !Confirm::with_theme(theme).with_prompt("Create new crypto config?").default(false).interact()?
{
return Ok(())
return Ok(());
}
let mut use_password = true;
if mode >= MODE_ADVANCED {
@ -75,7 +80,7 @@ fn configure_crypto(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
Password::with_theme(theme)
.with_prompt("Password")
.with_confirmation("Confirm password", "Passwords do not match")
.interact()?
.interact()?,
);
config.crypto.private_key = None;
config.crypto.public_key = None;
@ -113,13 +118,13 @@ fn configure_crypto(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
info!("Public key: {}", pub_key);
(priv_key, pub_key)
}
_ => unreachable!()
_ => unreachable!(),
};
config.crypto.trusted_keys = str_list(
Input::with_theme(theme)
.with_prompt("Trusted keys (public keys, comma separated)")
.default(pub_key.clone())
.interact_text()?
.interact_text()?,
);
config.crypto.private_key = Some(priv_key);
config.crypto.public_key = Some(pub_key);
@ -133,7 +138,7 @@ fn configure_crypto(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
("Unencrypted (dangerous)", unencrypted),
("AES-128 in GCM mode", allowed_algos.contains(&&aead::AES_128_GCM)),
("AES-256 in GCM mode", allowed_algos.contains(&&aead::AES_256_GCM)),
("ChaCha20-Poly1305 (RFC 7539)", allowed_algos.contains(&&aead::CHACHA20_POLY1305))
("ChaCha20-Poly1305 (RFC 7539)", allowed_algos.contains(&&aead::CHACHA20_POLY1305)),
])
.interact()?;
config.crypto.algorithms = vec![];
@ -156,7 +161,7 @@ fn configure_device(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
{
0 => device::Type::Tun,
1 => device::Type::Tap,
_ => unreachable!()
_ => unreachable!(),
}
}
if mode == MODE_EXPERT {
@ -166,7 +171,7 @@ fn configure_device(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
Input::with_theme(theme)
.with_prompt("Device path (empty for default)")
.default(config.device_path.as_ref().cloned().unwrap_or_default())
.interact_text()?
.interact_text()?,
);
config.fix_rp_filter = Confirm::with_theme(theme)
.with_prompt("Automatically fix insecure rp_filter settings")
@ -179,7 +184,7 @@ fn configure_device(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
Mode::Normal => 0,
Mode::Router => 1,
Mode::Switch => 2,
Mode::Hub => 3
Mode::Hub => 3,
})
.interact()?
{
@ -187,7 +192,7 @@ fn configure_device(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
1 => Mode::Router,
2 => Mode::Switch,
3 => Mode::Hub,
_ => unreachable!()
_ => unreachable!(),
};
if config.mode == Mode::Switch {
config.switch_timeout = Input::with_theme(theme)
@ -205,7 +210,7 @@ fn configure_addresses(config: &mut Config, mode: usize, theme: &ColorfulTheme)
.with_prompt("Virtual IP address (e.g. 10.0.0.1, leave empty for none)")
.allow_empty(true)
.default(config.ip.as_ref().cloned().unwrap_or_default())
.interact_text()?
.interact_text()?,
);
if config.device_type == device::Type::Tun {
if mode >= MODE_ADVANCED {
@ -220,7 +225,7 @@ fn configure_addresses(config: &mut Config, mode: usize, theme: &ColorfulTheme)
.with_prompt("Claim additional addresses (e.g. 10.0.0.0/24, comma separated, leave empty for none)")
.allow_empty(true)
.default(config.claims.join(","))
.interact_text()?
.interact_text()?,
);
}
} else {
@ -232,14 +237,14 @@ fn configure_addresses(config: &mut Config, mode: usize, theme: &ColorfulTheme)
.with_prompt("Interface setup command (leave empty for none)")
.allow_empty(true)
.default(config.ifup.as_ref().cloned().unwrap_or_default())
.interact_text()?
.interact_text()?,
);
config.ifdown = str_opt(
Input::with_theme(theme)
.with_prompt("Interface tear down command (leave empty for none)")
.allow_empty(true)
.default(config.ifdown.as_ref().cloned().unwrap_or_default())
.interact_text()?
.interact_text()?,
);
}
Ok(())
@ -267,24 +272,20 @@ fn configure_beacon(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
.interact()?
{
0 => None,
1 => {
Some(
Input::with_theme(theme)
.with_prompt("File path")
.default(config.beacon_store.clone().unwrap_or_default())
.interact_text()?
)
}
2 => {
Some(format!(
"|{}",
Input::<String>::with_theme(theme)
.with_prompt("Command")
.default(config.beacon_store.clone().unwrap_or_default().trim_start_matches('|').to_string())
.interact_text()?
))
}
_ => unreachable!()
1 => Some(
Input::with_theme(theme)
.with_prompt("File path")
.default(config.beacon_store.clone().unwrap_or_default())
.interact_text()?,
),
2 => Some(format!(
"|{}",
Input::<String>::with_theme(theme)
.with_prompt("Command")
.default(config.beacon_store.clone().unwrap_or_default().trim_start_matches('|').to_string())
.interact_text()?
)),
_ => unreachable!(),
};
config.beacon_load = match Select::with_theme(theme)
.with_prompt("How to load beacons")
@ -301,24 +302,20 @@ fn configure_beacon(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
.interact()?
{
0 => None,
1 => {
Some(
Input::with_theme(theme)
.with_prompt("File path")
.default(config.beacon_load.clone().unwrap_or_default())
.interact_text()?
)
}
2 => {
Some(format!(
"|{}",
Input::<String>::with_theme(theme)
.with_prompt("Command")
.default(config.beacon_load.clone().unwrap_or_default().trim_start_matches('|').to_string())
.interact_text()?
))
}
_ => unreachable!()
1 => Some(
Input::with_theme(theme)
.with_prompt("File path")
.default(config.beacon_load.clone().unwrap_or_default())
.interact_text()?,
),
2 => Some(format!(
"|{}",
Input::<String>::with_theme(theme)
.with_prompt("Command")
.default(config.beacon_load.clone().unwrap_or_default().trim_start_matches('|').to_string())
.interact_text()?
)),
_ => unreachable!(),
};
config.beacon_interval = Input::with_theme(theme)
.with_prompt("Beacon interval (in seconds)")
@ -329,7 +326,7 @@ fn configure_beacon(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
.with_prompt("Beacon password (leave empty for none)")
.with_confirmation("Confirm password", "Passwords do not match")
.allow_empty_password(true)
.interact()?
.interact()?,
);
}
Ok(())
@ -342,7 +339,7 @@ fn configure_stats(config: &mut Config, mode: usize, theme: &ColorfulTheme) -> R
.with_prompt("Write stats to file (empty to disable)")
.default(config.stats_file.clone().unwrap_or_default())
.allow_empty(true)
.interact_text()?
.interact_text()?,
);
}
if mode == MODE_EXPERT {
@ -356,14 +353,14 @@ fn configure_stats(config: &mut Config, mode: usize, theme: &ColorfulTheme) -> R
.with_prompt("Statsd server URL")
.default(config.statsd_server.clone().unwrap_or_default())
.allow_empty(true)
.interact_text()?
.interact_text()?,
);
config.statsd_prefix = str_opt(
Input::with_theme(theme)
.with_prompt("Statsd prefix")
.default(config.statsd_prefix.clone().unwrap_or_default())
.allow_empty(true)
.interact_text()?
.interact_text()?,
);
} else {
config.statsd_server = None;
@ -379,21 +376,21 @@ fn configure_process(config: &mut Config, mode: usize, theme: &ColorfulTheme) ->
.with_prompt("Run as different user (empty to disable)")
.default(config.user.clone().unwrap_or_default())
.allow_empty(true)
.interact_text()?
.interact_text()?,
);
config.group = str_opt(
Input::with_theme(theme)
.with_prompt("Run as different group (empty to disable)")
.default(config.group.clone().unwrap_or_default())
.allow_empty(true)
.interact_text()?
.interact_text()?,
);
config.pid_file = str_opt(
Input::with_theme(theme)
.with_prompt("Write process id to file (empty to disable)")
.default(config.pid_file.clone().unwrap_or_default())
.allow_empty(true)
.interact_text()?
.interact_text()?,
);
}
Ok(())
@ -411,7 +408,7 @@ fn configure_hooks(config: &mut Config, mode: usize, theme: &ColorfulTheme) -> R
.with_prompt("Command to execute for all events (empty to disable)")
.default(config.hook.clone().unwrap_or_default())
.allow_empty(true)
.interact_text()?
.interact_text()?,
);
let mut hooks: HashMap<String, String> = Default::default();
for event in &[
@ -421,14 +418,14 @@ fn configure_hooks(config: &mut Config, mode: usize, theme: &ColorfulTheme) -> R
"device_setup",
"device_configured",
"vpn_started",
"vpn_shutdown"
"vpn_shutdown",
] {
if let Some(cmd) = str_opt(
Input::with_theme(theme)
.with_prompt(format!("Command to execute for event '{}' (empty to disable)", event))
.default(config.hooks.get(*event).cloned().unwrap_or_default())
.allow_empty(true)
.interact_text()?
.interact_text()?,
) {
hooks.insert(event.to_string(), cmd);
}
@ -470,7 +467,7 @@ pub fn configure(name: Option<String>) -> Result<(), io::Error> {
config.merge_file(config_file);
}
if file.parent().unwrap().metadata()?.permissions().readonly() {
return Err(io::Error::new(io::ErrorKind::PermissionDenied, "Config file not writable"))
return Err(io::Error::new(io::ErrorKind::PermissionDenied, "Config file not writable"));
}
loop {
@ -489,7 +486,7 @@ pub fn configure(name: Option<String>) -> Result<(), io::Error> {
configure_process(&mut config, mode, &theme)?;
configure_hooks(&mut config, mode, &theme)?;
if Confirm::with_theme(&theme).with_prompt("Finish configuration?").default(true).interact()? {
break
break;
}
}
@ -498,7 +495,7 @@ pub fn configure(name: Option<String>) -> Result<(), io::Error> {
let f = fs::File::create(&file)?;
serde_yaml::to_writer(f, &config_file)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Failed to parse config file"))?;
fs::set_permissions(file, fs::Permissions::from_mode(600))?;
fs::set_permissions(file, fs::Permissions::from_mode(0o600))?;
println!();
println!("Use the following commands to control your VPN:");
println!(" start the VPN: sudo service vpncloud@{0} start", name);

View File

@ -94,7 +94,7 @@ fn serve_proxy_connection(stream: TcpStream) -> Result<(), io::Error> {
}
pub fn run_proxy(listen: &str) -> Result<(), io::Error> {
let addr = parse_listen(listen);
let addr = parse_listen(listen, 8080);
let server = TcpListener::bind(addr)?;
info!("Listening on ws://{}", server.local_addr()?);
for stream in server.incoming() {
@ -121,7 +121,7 @@ impl ProxyConnection {
unimplemented!();
/*
if let Message::Binary(data) = io_error!(self.socket.read_message(), "Failed to read from ws proxy: {}")? {
return Ok(data)
return Ok(data);
}
*/
}