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 # 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 \ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends asciidoctor valgrind && 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 name: Test Suite
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: szenius/set-timezone@v1.0
with:
timezoneLinux: Europe/Berlin
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:

View File

@ -28,6 +28,7 @@ mkdir -p %{buildroot}/lib/systemd/system
mkdir -p %{buildroot}/usr/share/man/man1 mkdir -p %{buildroot}/usr/share/man/man1
cp %{buildroot}/../../../../../assets/example.net.disabled %{buildroot}/etc/vpncloud/example.net.disabled 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@.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}/../../../../../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 %{buildroot}/../../../../../target/vpncloud.1.gz %{buildroot}/usr/share/man/man1/vpncloud.1.gz
cp -a * %{buildroot} cp -a * %{buildroot}
@ -40,6 +41,7 @@ rm -rf %{buildroot}
/etc/vpncloud/example.net.disabled /etc/vpncloud/example.net.disabled
/usr/bin/vpncloud /usr/bin/vpncloud
/lib/systemd/system/vpncloud@.service /lib/systemd/system/vpncloud@.service
/lib/systemd/system/vpncloud.target
/lib/systemd/system/vpncloud-wsproxy.service /lib/systemd/system/vpncloud-wsproxy.service
/usr/share/man/man1/vpncloud.1.gz /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). 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] Added interactive configuration wizard
- [added] Support for (un-)installation - [added] Support for (un-)installation
- [added] Building static binaries - [added] Building static binaries
- [added] Building i686 rpm - [added] Building i686 rpm
- [changed] Restructured example config - [changed] Restructured example config
- [changed] Changed Rust version to 1.50.0 - [changed] Changed Rust version to 1.51.0
- [changed] Updated dependencies - [changed] Updated dependencies
- [changed] Change permissions of /etc/vpncloud - [changed] Change permissions of /etc/vpncloud

160
Cargo.lock generated
View File

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

View File

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

View File

@ -19,7 +19,7 @@ peers:
- REMOTE_HOST:PORT - 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 ### 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 vpncloud (2.1.0) stable; urgency=medium
* [added] Support for websocket proxy mode * [added] Support for websocket proxy mode
@ -9,7 +23,7 @@ vpncloud (2.1.0) stable; urgency=medium
* [fixed] Added missing peer address propagation * [fixed] Added missing peer address propagation
* [fixed] Fixed problem with peer addresses without port * [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 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' Description=VpnCloud network '%I'
After=network-online.target After=network-online.target
Wants=network-online.target Wants=network-online.target
PartOf=vpncloud.target
Documentation=man:vpncloud(1) Documentation=man:vpncloud(1)
[Service] [Service]

View File

@ -1,9 +1,13 @@
FROM ubuntu 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 /root/.asciinema
RUN mkdir /etc/vpncloud RUN mkdir /etc/vpncloud
WORKDIR /data WORKDIR /data
ADD config /root/.asciinema/config 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] [record]
command = /usr/bin/bash -l command = bash -l
idle_time_limit = 2.5 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 # Note: this script will run for ~8 minutes and incur costs of about $ 0.02
FILE = "../target/release/vpncloud" FILE = "../../target/release/vpncloud"
VERSION = "2.1.0" VERSION = "2.2.0"
REGION = "eu-central-1" REGION = "eu-central-1"
env = EC2Environment( env = EC2Environment(
@ -113,4 +113,4 @@ name = "measurements/{date}_{version}_perf.json".format(date=date.today().strfti
eprint('Storing results in {}'.format(name)) eprint('Storing results in {}'.format(name))
with open(name, 'w') as fp: with open(name, 'w') as fp:
json.dump(results, fp, indent=2) json.dump(results, fp, indent=2)
eprint("done.") eprint("done.")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,20 +1,20 @@
use super::{ use super::{
shared::{SharedPeerCrypto, SharedTable, SharedTraffic},
common::SPACE_BEFORE, common::SPACE_BEFORE,
shared::{SharedPeerCrypto, SharedTable, SharedTraffic},
}; };
use crate::{ use crate::{
beacon::BeaconSerializer, beacon::BeaconSerializer,
config::{DEFAULT_PEER_TIMEOUT, DEFAULT_PORT}, config::{DEFAULT_PEER_TIMEOUT, DEFAULT_PORT},
crypto::{is_init_message, InitResult, InitState, MessageResult, Crypto}, crypto::{is_init_message, Crypto, InitResult, InitState, MessageResult},
device::{Type, Device}, device::{Device, Type},
engine::common::{Hash, PeerData}, engine::common::{Hash, PeerData},
error::Error, error::Error,
messages::{ messages::{
AddrList, NodeInfo, PeerInfo, MESSAGE_TYPE_CLOSE, MESSAGE_TYPE_DATA, MESSAGE_TYPE_KEEPALIVE, AddrList, NodeInfo, PeerInfo, MESSAGE_TYPE_CLOSE, MESSAGE_TYPE_DATA, MESSAGE_TYPE_KEEPALIVE,
MESSAGE_TYPE_NODE_INFO, MESSAGE_TYPE_NODE_INFO,
}, },
net::{mapped_addr, Socket}, net::{mapped_addr, parse_listen, Socket},
port_forwarding::PortForwarding, port_forwarding::PortForwarding,
types::{Address, NodeId, Range, RangeList}, types::{Address, NodeId, Range, RangeList},
util::{addr_nice, resolve, MsgBuffer, StatsdMsg, Time, TimeSource}, 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> { async fn update_peer_info(&mut self, addr: SocketAddr, info: Option<NodeInfo>) -> Result<(), Error> {
if let Some(peer) = self.peers.get_mut(&addr) { if let Some(peer) = self.peers.get_mut(&addr) {
peer.last_seen = TS::now(); 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 { } else {
error!("Received peer update from non peer {}", addr_nice(addr)); error!("Received peer update from non peer {}", addr_nice(addr));
return Ok(()); 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 { 'outer: for peer in peers {
for addr in &peer.addrs { for addr in &peer.addrs {
if self.peers.contains_key(addr) { 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; continue 'outer;
} }
} }
@ -427,11 +443,6 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
pfw.check_extend(); pfw.check_extend();
} }
let now = TS::now(); 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 // Periodically send peer list to peers
if self.next_peers <= now { if self.next_peers <= now {
debug!("Send peer list to all peers"); 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.table.sync();
self.traffic.sync(); self.traffic.sync();
self.peer_crypto.store(&self.peers); 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()); assert!(self.buffer.is_empty());
Ok(()) 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<()> { async fn reset_own_addresses(&mut self) -> io::Result<()> {
self.own_addresses.clear(); 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 { if let Some(ref pfw) = self.port_forwarding {
self.own_addresses.push(pfw.get_internal_ip().into()); self.own_addresses.push(pfw.get_internal_ip().into());
self.own_addresses.push(pfw.get_external_ip().into()); self.own_addresses.push(pfw.get_external_ip().into());

View File

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

View File

@ -4,11 +4,12 @@ use std::{
fs::{self, File}, fs::{self, File},
io::Write, io::Write,
os::unix::fs::PermissionsExt, os::unix::fs::PermissionsExt,
process::Command process::Command,
}; };
const MANPAGE: &[u8] = include_bytes!("../target/vpncloud.1.gz"); const MANPAGE: &[u8] = include_bytes!("../target/vpncloud.1.gz");
const SERVICE_FILE: &[u8] = include_bytes!("../assets/vpncloud@.service"); 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 WS_PROXY_SERVICE_FILE: &[u8] = include_bytes!("../assets/vpncloud-wsproxy.service");
const EXAMPLE_CONFIG: &[u8] = include_bytes!("../assets/example.net.disabled"); const EXAMPLE_CONFIG: &[u8] = include_bytes!("../assets/example.net.disabled");
@ -22,10 +23,10 @@ pub fn install() -> Result<(), Error> {
env::current_exe() env::current_exe()
.and_then(|p| fs::copy(p, "/usr/bin/vpncloud")) .and_then(|p| fs::copy(p, "/usr/bin/vpncloud"))
.map_err(|e| Error::FileIo("Failed to copy binary", e))?; .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))?; .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::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))?; .map_err(|e| Error::FileIo("Failed to set permissions for config folder", e))?;
File::create("/etc/vpncloud/example.net.disabled") File::create("/etc/vpncloud/example.net.disabled")
.and_then(|mut f| f.write_all(EXAMPLE_CONFIG)) .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") File::create("/lib/systemd/system/vpncloud@.service")
.and_then(|mut f| f.write_all(SERVICE_FILE)) .and_then(|mut f| f.write_all(SERVICE_FILE))
.map_err(|e| Error::FileIo("Failed to create service file", e))?; .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") File::create("/lib/systemd/system/vpncloud-wsproxy.service")
.and_then(|mut f| f.write_all(WS_PROXY_SERVICE_FILE)) .and_then(|mut f| f.write_all(WS_PROXY_SERVICE_FILE))
.map_err(|e| Error::FileIo("Failed to create wsporxy service file", e))?; .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("/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") fs::remove_file("/lib/systemd/system/vpncloud@.service")
.map_err(|e| Error::FileIo("Failed to remove service file", e))?; .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") fs::remove_file("/lib/systemd/system/vpncloud-wsproxy.service")
.map_err(|e| Error::FileIo("Failed to remove wsproxy service file", e))?; .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))?; 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 serde;
#[macro_use] extern crate tokio; #[macro_use] extern crate tokio;
#[cfg(test)] extern crate tempfile; #[cfg(test)]
extern crate tempfile;
#[macro_use] #[macro_use]
pub mod util; pub mod util;
@ -19,6 +20,8 @@ pub mod config;
pub mod crypto; pub mod crypto;
pub mod device; pub mod device;
pub mod error; pub mod error;
#[cfg(feature = "installer")]
pub mod installer;
pub mod messages; pub mod messages;
pub mod net; pub mod net;
pub mod oldconfig; pub mod oldconfig;
@ -28,9 +31,10 @@ pub mod port_forwarding;
pub mod table; pub mod table;
pub mod traffic; pub mod traffic;
pub mod types; pub mod types;
#[cfg(feature = "wizard")] pub mod wizard; #[cfg(feature = "wizard")]
#[cfg(feature = "websocket")] pub mod wsproxy; pub mod wizard;
#[cfg(feature = "installer")] pub mod installer; #[cfg(feature = "websocket")]
pub mod wsproxy;
use structopt::StructOpt; use structopt::StructOpt;
@ -43,7 +47,7 @@ use std::{
process, process,
str::FromStr, str::FromStr,
sync::Mutex, sync::Mutex,
thread thread,
}; };
use crate::{ use crate::{
@ -61,7 +65,7 @@ use crate::{
use crate::wsproxy::ProxyConnection; use crate::wsproxy::ProxyConnection;
struct DualLogger { struct DualLogger {
file: Option<Mutex<File>> file: Option<Mutex<File>>,
} }
impl DualLogger { impl DualLogger {
@ -117,18 +121,18 @@ fn run_script(script: &str, ifname: &str) {
error!("Script returned with error: {:?}", status.code()) 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> { fn parse_ip_netmask(addr: &str) -> Result<(Ipv4Addr, Ipv4Addr), String> {
let (ip_str, len_str) = match addr.find('/') { let (ip_str, len_str) = match addr.find('/') {
Some(pos) => (&addr[..pos], &addr[pos + 1..]), 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))?; let prefix_len = u8::from_str(len_str).map_err(|_| format!("Invalid prefix length: {}", len_str))?;
if prefix_len > 32 { 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 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()); 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(); let args: Args = Args::from_args();
if args.version { if args.version {
println!("VpnCloud v{}", env!("CARGO_PKG_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: {}"); let logger = try_fail!(DualLogger::new(args.log_file.as_ref()), "Failed to open logfile: {}");
log::set_boxed_logger(Box::new(logger)).unwrap(); log::set_boxed_logger(Box::new(logger)).unwrap();
@ -294,7 +298,7 @@ async fn main() {
} }
} }
} }
return return;
} }
let mut config = Config::default(); let mut config = Config::default();
if let Some(ref file) = args.config { if let Some(ref file) = args.config {
@ -319,7 +323,7 @@ async fn main() {
debug!("Config: {:?}", config); debug!("Config: {:?}", config);
if config.crypto.password.is_none() && config.crypto.private_key.is_none() { 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"); error!("Either password or private key must be set in config or given as parameter");
return return;
} }
#[cfg(feature = "websocket")] #[cfg(feature = "websocket")]
if config.listen.starts_with("ws://") { if config.listen.starts_with("ws://") {
@ -328,7 +332,7 @@ async fn main() {
Type::Tap => run::<payload::Frame, _>(config, socket).await, Type::Tap => run::<payload::Frame, _>(config, socket).await,
Type::Tun => run::<payload::Packet, _>(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); let socket = try_fail!(NetSocket::listen(&config.listen).await, "Failed to open socket {}: {}", config.listen);
match config.device_type { match config.device_type {

View File

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

View File

@ -2,15 +2,15 @@
// Copyright (C) 2015-2021 Dennis Schwerdel // Copyright (C) 2015-2021 Dennis Schwerdel
// This software is licensed under GPL-3 or newer (see LICENSE.md) // 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::port_forwarding::PortForwarding;
use crate::util::{MockTimeSource, MsgBuffer, Time, TimeSource};
use async_trait::async_trait; use async_trait::async_trait;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
io::{self, ErrorKind}, io::{self, ErrorKind},
net::{IpAddr, Ipv6Addr, SocketAddr, UdpSocket}, net::{IpAddr, Ipv6Addr, SocketAddr, UdpSocket},
os::unix::io::AsRawFd,
sync::{ sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Arc,
@ -40,15 +40,17 @@ pub trait Socket: Sized + Clone + Send + Sync + 'static {
async fn create_port_forwarding(&self) -> Option<PortForwarding>; 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("*:") { if let Some(addr) = addr.strip_prefix("*:") {
let port = try_fail!(addr.parse::<u16>(), "Invalid port: {}"); let port = try_fail!(addr.parse::<u16>(), "Invalid port: {}");
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port) SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port)
} else if addr.contains(':') { } else if addr.contains(':') {
try_fail!(addr.parse::<SocketAddr>(), "Invalid address: {}: {}", addr) try_fail!(addr.parse::<SocketAddr>(), "Invalid address: {}: {}", addr)
} else { } else if let Ok(port) = addr.parse::<u16>() {
let port = try_fail!(addr.parse::<u16>(), "Invalid port: {}");
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port) 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] #[async_trait]
impl Socket for NetSocket { impl Socket for NetSocket {
async fn listen(addr: &str) -> Result<Self, io::Error> { 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)?)) Ok(NetSocket(UdpSocket::bind(addr)?))
} }
@ -142,11 +143,10 @@ impl MockSocket {
} }
} }
#[async_trait] #[async_trait]
impl Socket for MockSocket { impl Socket for MockSocket {
async fn listen(addr: &str) -> Result<Self, io::Error> { 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> { async fn receive(&mut self, buffer: &mut MsgBuffer) -> Result<SocketAddr, io::Error> {

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@ mod internal {
pub internal_addr: SocketAddrV4, pub internal_addr: SocketAddrV4,
pub external_addr: SocketAddrV4, pub external_addr: SocketAddrV4,
gateway: Gateway, gateway: Gateway,
pub next_extension: Option<Time> pub next_extension: Option<Time>,
} }
impl PortForwarding { impl PortForwarding {
@ -32,11 +32,11 @@ mod internal {
if err.kind() == io::ErrorKind::WouldBlock { if err.kind() == io::ErrorKind::WouldBlock {
// Why this code? // Why this code?
info!("Port-forwarding: no router found"); info!("Port-forwarding: no router found");
return None return None;
} }
} }
error!("Port-forwarding: failed to find router: {}", err); error!("Port-forwarding: failed to find router: {}", err);
return None return None;
} }
}; };
debug!("Port-forwarding: found router at {}", gateway.addr); debug!("Port-forwarding: found router at {}", gateway.addr);
@ -46,7 +46,7 @@ mod internal {
Ok(ip) => ip, Ok(ip) => ip,
Err(err) => { Err(err) => {
error!("Port-forwarding: failed to obtain external IP: {}", 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) { 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), ()> { fn get_any_forwarding(gateway: &Gateway, addr: SocketAddrV4, port: u16) -> Result<(u16, u32), ()> {
if let Ok(a) = Self::get_forwarding(gateway, addr, port) { 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) { if let Ok(a) = Self::get_forwarding(gateway, addr, 0) {
return Ok(a) return Ok(a);
} }
for i in 1..5 { for i in 1..5 {
if let Ok(a) = Self::get_forwarding(gateway, addr, port + i) { if let Ok(a) = Self::get_forwarding(gateway, addr, port + i) {
return Ok(a) return Ok(a);
} }
} }
for _ in 0..5 { for _ in 0..5 {
if let Ok(a) = Self::get_forwarding(gateway, addr, rand::random()) { if let Ok(a) = Self::get_forwarding(gateway, addr, rand::random()) {
return Ok(a) return Ok(a);
} }
} }
warn!("Failed to activate port forwarding"); warn!("Failed to activate port forwarding");
@ -125,20 +125,20 @@ mod internal {
pub fn check_extend(&mut self) { pub fn check_extend(&mut self) {
if let Some(deadline) = self.next_extension { if let Some(deadline) = self.next_extension {
if deadline > SystemTimeSource::now() { if deadline > SystemTimeSource::now() {
return return;
} }
} else { } else {
return return;
} }
match self.gateway.add_port( match self.gateway.add_port(
PortMappingProtocol::UDP, PortMappingProtocol::UDP,
self.external_addr.port(), self.external_addr.port(),
self.internal_addr, self.internal_addr,
LEASE_TIME, LEASE_TIME,
DESCRIPTION DESCRIPTION,
) { ) {
Ok(()) => debug!("Port-forwarding: extended port forwarding"), 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); self.next_extension = Some(SystemTimeSource::now() + Time::from(LEASE_TIME) - 60);
} }
@ -146,7 +146,7 @@ mod internal {
fn deactivate(&self) { fn deactivate(&self) {
match self.gateway.remove_port(PortMappingProtocol::UDP, self.external_addr.port()) { match self.gateway.remove_port(PortMappingProtocol::UDP, self.external_addr.port()) {
Ok(()) => info!("Port-forwarding: successfully deactivated port forwarding"), 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 fnv::FnvHasher;
use std::{ 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::{ use crate::{
types::{Address, Range, RangeList}, types::{Address, Range, RangeList},
util::{addr_nice, Duration, Time, TimeSource} util::{addr_nice, Duration, Time, TimeSource},
}; };
type Hash = BuildHasherDefault<FnvHasher>; type Hash = BuildHasherDefault<FnvHasher>;
struct CacheValue { struct CacheValue {
peer: SocketAddr, peer: SocketAddr,
timeout: Time timeout: Time,
} }
struct ClaimEntry { struct ClaimEntry {
peer: SocketAddr, peer: SocketAddr,
claim: Range, claim: Range,
timeout: Time timeout: Time,
} }
pub struct ClaimTable<TS: TimeSource> { pub struct ClaimTable<TS: TimeSource> {
@ -32,7 +30,7 @@ pub struct ClaimTable<TS: TimeSource> {
cache_timeout: Duration, cache_timeout: Duration,
claims: Vec<ClaimEntry>, claims: Vec<ClaimEntry>,
claim_timeout: Duration, claim_timeout: Duration,
_dummy: PhantomData<TS> _dummy: PhantomData<TS>,
} }
impl<TS: TimeSource> ClaimTable<TS> { impl<TS: TimeSource> ClaimTable<TS> {
@ -57,7 +55,7 @@ impl<TS: TimeSource> ClaimTable<TS> {
entry.timeout = TS::now() + self.claim_timeout as Time; entry.timeout = TS::now() + self.claim_timeout as Time;
claims.swap_remove(pos); claims.swap_remove(pos);
if claims.is_empty() { if claims.is_empty() {
break break;
} }
} else { } else {
entry.timeout = 0 entry.timeout = 0
@ -92,7 +90,7 @@ impl<TS: TimeSource> ClaimTable<TS> {
pub fn lookup(&mut self, addr: Address) -> Option<SocketAddr> { pub fn lookup(&mut self, addr: Address) -> Option<SocketAddr> {
// HOT PATH // HOT PATH
if let Some(entry) = self.cache.get(&addr) { if let Some(entry) = self.cache.get(&addr) {
return Some(entry.peer) return Some(entry.peer);
} }
// COLD PATH // COLD PATH
let mut found = None; let mut found = None;
@ -104,11 +102,11 @@ impl<TS: TimeSource> ClaimTable<TS> {
} }
} }
if let Some(entry) = found { if let Some(entry) = found {
self.cache.insert(addr, CacheValue { self.cache.insert(
peer: entry.peer, addr,
timeout: min(TS::now() + self.cache_timeout as Time, entry.timeout) CacheValue { peer: entry.peer, timeout: min(TS::now() + self.cache_timeout as Time, entry.timeout) },
}); );
return Some(entry.peer) return Some(entry.peer);
} }
None None
} }
@ -155,4 +153,4 @@ impl<TS: TimeSource> ClaimTable<TS> {
} }
} }
// TODO: test // TODO: test

View File

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

View File

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

View File

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

View File

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