Compare commits

...

35 Commits

Author SHA1 Message Date
Dennis Schwerdel 66bef5cd21 Fixes 2021-04-11 21:50:02 +02:00
Dennis Schwerdel a7a7ab3a1f Merge branch 'master' into threading 2021-04-11 21:49:39 +02:00
Dennis Schwerdel 8c55e6c076 Finish advertise address & exchange own addresses 2021-04-09 22:01:35 +02:00
Dennis Schwerdel 7427be31c8 Merge branch 'master' of github.com:dswd/vpncloud 2021-04-09 20:31:16 +02:00
Dennis Schwerdel 5609a61ddb Add performance for 2.2.0 2021-04-09 20:31:13 +02:00
Jeffrey Schiller 0f9a0d8f91
Add ability to configure “own” addresses (#185)
* Add ability to configure “own” addresses

This configuration option permits the declaration of external or public
addresses instead of attempting to learn them from port forwarding or
interfaces. This is useful in situations where it isn’t possible to
accurately obtain the correct external addresses that peers should use.

* Update args and use better parse listen address

Add the --advertise_addresses control argument to accompany the new
configuration option. Also parse the listen address/port to extract the
port to advertise with advertise_addresses instead of assuming it is
just a port.
2021-04-09 20:30:16 +02:00
dswd 7a55529cb9
Merge pull request #184 from dswd/dependabot/cargo/libc-0.2.93
Bump libc from 0.2.92 to 0.2.93
2021-04-07 18:56:38 +02:00
dependabot[bot] eef6e528e2
Bump libc from 0.2.92 to 0.2.93
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.92 to 0.2.93.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.92...0.2.93)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-07 05:18:23 +00:00
Dennis Schwerdel 4fb92a36a6 Prepare for release 2021-04-06 12:28:31 +02:00
Dennis Schwerdel 665b190257 Fix permissions 2021-04-06 00:18:33 +02:00
Dennis Schwerdel ff75845dfb Fix rpm build 2021-04-05 22:50:35 +02:00
Dennis Schwerdel 97e168c856 Fix check action 2021-04-05 17:06:19 +00:00
Dennis Schwerdel 3b96380b42 Update deps 2021-04-05 17:01:40 +00:00
Dennis Schwerdel 6d4591f685 Install .target file 2021-04-05 16:55:09 +00:00
dswd 41e8ecd962
Merge pull request #182 from mnhauke/master
systemd: add file vpncloud.target
2021-04-05 18:51:21 +02:00
Dennis Schwerdel ea3b2e22a6 Merge branch 'master' of github.com:dswd/vpncloud 2021-04-05 16:50:22 +00:00
Martin Hauke 151ca445c3 systemd: add file vpncloud.target
Add file vpncloud.target, which allows you to stop or restart all
instances.
2021-04-04 22:02:39 +02:00
dswd b745735af8
Merge pull request #181 from dswd/dependabot/cargo/libc-0.2.92
Bump libc from 0.2.91 to 0.2.92
2021-04-02 10:58:28 +02:00
dependabot[bot] 07d65df6ee
Bump libc from 0.2.91 to 0.2.92
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.91 to 0.2.92.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.91...0.2.92)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-31 05:05:49 +00:00
Dennis Schwerdel d7d06941e5 Test for broken time crate 2021-03-26 08:16:23 +01:00
Dennis Schwerdel 62def2de17 Merge branch 'master' of github.com:dswd/vpncloud 2021-03-23 08:05:39 +00:00
dswd 8df6214fd7
Merge pull request #178 from dswd/dependabot/cargo/libc-0.2.91
Bump libc from 0.2.88 to 0.2.91
2021-03-23 09:04:25 +01:00
dswd 940707333c
Merge pull request #179 from dswd/dependabot/cargo/serde-1.0.125
Bump serde from 1.0.124 to 1.0.125
2021-03-23 09:03:38 +01:00
dependabot[bot] 8675fdee87
Bump serde from 1.0.124 to 1.0.125
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.124 to 1.0.125.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.124...v1.0.125)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-23 05:16:35 +00:00
dependabot[bot] 5053bfa559
Bump libc from 0.2.88 to 0.2.91
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.88 to 0.2.91.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.88...0.2.91)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-23 05:16:30 +00:00
dswd bcc029afea
Merge pull request #172 from dswd/dependabot/cargo/dialoguer-0.8.0
Bump dialoguer from 0.7.1 to 0.8.0
2021-03-15 08:43:19 +01:00
dswd 35ac0339e7
Merge pull request #171 from dswd/dependabot/cargo/byteorder-1.4.3
Bump byteorder from 1.4.2 to 1.4.3
2021-03-15 08:43:09 +01:00
dependabot[bot] 186f3fc215
Bump dialoguer from 0.7.1 to 0.8.0
Bumps [dialoguer](https://github.com/mitsuhiko/dialoguer) from 0.7.1 to 0.8.0.
- [Release notes](https://github.com/mitsuhiko/dialoguer/releases)
- [Changelog](https://github.com/mitsuhiko/dialoguer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/dialoguer/compare/0.7.1...0.8.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-15 05:27:35 +00:00
dependabot[bot] e643217671
Bump byteorder from 1.4.2 to 1.4.3
Bumps [byteorder](https://github.com/BurntSushi/byteorder) from 1.4.2 to 1.4.3.
- [Release notes](https://github.com/BurntSushi/byteorder/releases)
- [Changelog](https://github.com/BurntSushi/byteorder/blob/master/CHANGELOG.md)
- [Commits](https://github.com/BurntSushi/byteorder/compare/1.4.2...1.4.3)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-11 05:17:50 +00:00
dswd 1e53e98c58
Merge pull request #170 from dswd/dependabot/cargo/serde-1.0.124
Bump serde from 1.0.123 to 1.0.124
2021-03-08 22:11:56 +01:00
dswd dd4b38e274
Merge pull request #169 from dswd/dependabot/cargo/libc-0.2.88
Bump libc from 0.2.87 to 0.2.88
2021-03-08 22:11:35 +01:00
dependabot[bot] a427ce70bd
Bump serde from 1.0.123 to 1.0.124
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.123 to 1.0.124.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.123...v1.0.124)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-08 05:29:10 +00:00
dependabot[bot] 2b79496e2c
Bump libc from 0.2.87 to 0.2.88
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.87 to 0.2.88.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.87...0.2.88)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-08 05:29:07 +00:00
dswd 2f69450af7
Merge pull request #168 from dswd/dependabot/cargo/libc-0.2.87
Bump libc from 0.2.86 to 0.2.87
2021-03-07 18:09:14 +01:00
dependabot[bot] c8192928dc
Bump libc from 0.2.86 to 0.2.87
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.86 to 0.2.87.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.86...0.2.87)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-03 05:06:20 +00:00
41 changed files with 621 additions and 469 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

@ -8,7 +8,6 @@ use crate::{
types::NodeId,
util::{from_base62, to_base62, MsgBuffer},
};
use libc::BPF_FS_MAGIC;
use ring::{
aead::{self, Algorithm, LessSafeKey, UnboundKey},
agreement::{EphemeralPrivateKey, UnparsedPublicKey},
@ -253,8 +252,8 @@ impl PeerCrypto {
if msg.stage() == STAGE_PONG {
buffer.set_length(last_init_message.len());
buffer.message_mut().copy_from_slice(last_init_message);
return Ok(MessageResult::Reply)
}
return Ok(MessageResult::Reply)
}
}
Ok(MessageResult::None)

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,5 @@ mod core;
mod init;
mod rotate;
pub use common::*;
pub use self::core::{EXTRA_LEN, TAG_LEN};
pub use common::*;

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

@ -1,20 +1,20 @@
use super::{
shared::{SharedPeerCrypto, SharedTable, SharedTraffic},
common::SPACE_BEFORE,
shared::{SharedPeerCrypto, SharedTable, SharedTraffic},
};
use crate::{
beacon::BeaconSerializer,
config::{DEFAULT_PEER_TIMEOUT, DEFAULT_PORT},
crypto::{is_init_message, InitResult, InitState, MessageResult, Crypto},
device::{Type, Device},
crypto::{is_init_message, Crypto, InitResult, InitState, MessageResult},
device::{Device, Type},
engine::common::{Hash, PeerData},
error::Error,
messages::{
AddrList, NodeInfo, PeerInfo, MESSAGE_TYPE_CLOSE, MESSAGE_TYPE_DATA, MESSAGE_TYPE_KEEPALIVE,
MESSAGE_TYPE_NODE_INFO,
},
net::{mapped_addr, Socket},
net::{mapped_addr, parse_listen, Socket},
port_forwarding::PortForwarding,
types::{Address, NodeId, Range, RangeList},
util::{addr_nice, resolve, MsgBuffer, StatsdMsg, Time, TimeSource},
@ -236,7 +236,17 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
async fn update_peer_info(&mut self, addr: SocketAddr, info: Option<NodeInfo>) -> Result<(), Error> {
if let Some(peer) = self.peers.get_mut(&addr) {
peer.last_seen = TS::now();
peer.timeout = TS::now() + self.config.peer_timeout as Time
peer.timeout = TS::now() + self.config.peer_timeout as Time;
if let Some(info) = &info {
// Update peer addresses, always add seen address
peer.addrs.clear();
peer.addrs.push(addr);
for addr in &info.addrs {
if !peer.addrs.contains(addr) {
peer.addrs.push(*addr);
}
}
}
} else {
error!("Received peer update from non peer {}", addr_nice(addr));
return Ok(());
@ -281,6 +291,12 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
'outer: for peer in peers {
for addr in &peer.addrs {
if self.peers.contains_key(addr) {
// Check addresses and add addresses that we don't know to own addresses
for addr in &peer.addrs {
if !self.own_addresses.contains(addr) {
self.own_addresses.push(*addr)
}
}
continue 'outer;
}
}
@ -427,11 +443,6 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
pfw.check_extend();
}
let now = TS::now();
// Periodically reset own peers
if self.next_own_address_reset <= now {
self.reset_own_addresses().await.map_err(|err| Error::SocketIo("Failed to get own addresses", err))?;
self.next_own_address_reset = now + OWN_ADDRESS_RESET_INTERVAL;
}
// Periodically send peer list to peers
if self.next_peers <= now {
debug!("Send peer list to all peers");
@ -465,6 +476,11 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
self.table.sync();
self.traffic.sync();
self.peer_crypto.store(&self.peers);
// Periodically reset own peers
if self.next_own_address_reset <= now {
self.reset_own_addresses().await.map_err(|err| Error::SocketIo("Failed to get own addresses", err))?;
self.next_own_address_reset = now + OWN_ADDRESS_RESET_INTERVAL;
}
assert!(self.buffer.is_empty());
Ok(())
}
@ -497,7 +513,14 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
async fn reset_own_addresses(&mut self) -> io::Result<()> {
self.own_addresses.clear();
self.own_addresses.push(self.socket.address().await.map(mapped_addr)?);
let socket_addr = self.socket.address().await.map(mapped_addr)?;
// 1) Specified advertise addresses
for addr in &self.config.advertise_addresses {
self.own_addresses.push(parse_listen(addr, socket_addr.port()));
}
// 2) Address of UDP socket
self.own_addresses.push(socket_addr);
// 3) Addresses from port forwarding
if let Some(ref pfw) = self.port_forwarding {
self.own_addresses.push(pfw.get_internal_ip().into());
self.own_addresses.push(pfw.get_external_ip().into());

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

@ -2,15 +2,15 @@
// Copyright (C) 2015-2021 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md)
use super::util::{MockTimeSource, MsgBuffer, Time, TimeSource};
use crate::config::DEFAULT_PORT;
use crate::port_forwarding::PortForwarding;
use crate::util::{MockTimeSource, MsgBuffer, Time, TimeSource};
use async_trait::async_trait;
use parking_lot::Mutex;
use std::{
collections::{HashMap, VecDeque},
io::{self, ErrorKind},
net::{IpAddr, Ipv6Addr, SocketAddr, UdpSocket},
os::unix::io::AsRawFd,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
@ -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)
}
}
@ -60,11 +62,10 @@ impl Clone for NetSocket {
}
}
#[async_trait]
impl Socket for NetSocket {
async fn listen(addr: &str) -> Result<Self, io::Error> {
let addr = parse_listen(addr);
let addr = parse_listen(addr, DEFAULT_PORT);
Ok(NetSocket(UdpSocket::bind(addr)?))
}
@ -142,11 +143,10 @@ impl MockSocket {
}
}
#[async_trait]
impl Socket for MockSocket {
async fn listen(addr: &str) -> Result<Self, io::Error> {
Ok(Self::new(parse_listen(addr)))
Ok(Self::new(parse_listen(addr, DEFAULT_PORT)))
}
async fn receive(&mut self, buffer: &mut MsgBuffer) -> Result<SocketAddr, io::Error> {

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]
async 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);
}
*/
}