Compare commits

...

24 Commits

Author SHA1 Message Date
Travis Burtrum c8497a6188 Abandon travis-ci for jenkins
moparisthebest/wireguard-proxy/pipeline/head This commit looks good Details
2020-11-26 18:51:57 -05:00
Travis Burtrum 2e0ccc1d0e Fix up CI jobs 2020-07-23 15:14:17 -04:00
Travis Burtrum f3a78b2863 Add async feature implemented with tokio and rustls, make it default 2020-07-22 23:37:55 -04:00
Travis Burtrum 225d71d885 More documentation in example server.conf 2019-12-23 02:19:38 -05:00
Travis Burtrum c59cef666a Add openssl cert generation examples to readme 2019-12-23 01:27:37 -05:00
Travis Burtrum d026f700a5 Add -V, --version argument, env variables only apply for long options 2019-12-22 23:59:42 -05:00
Travis Burtrum 2c902bdddf Add example hardened systemd template unit 2019-12-22 01:54:52 -05:00
Travis Burtrum efc858f852 udp-test: determine current executable path better 2019-12-22 01:45:01 -05:00
Travis Burtrum 7be828efdf Add support for reading key/cert from stdin, better OpenSSL error messages 2019-12-22 01:15:23 -05:00
Travis Burtrum 1650cefa0d Support falling back to environment variables if command line arguments don't exist 2019-12-21 01:48:48 -05:00
Travis Burtrum 327a23d82d Put verbose byte count output behind verbose feature 2019-12-17 01:33:42 -05:00
Travis Burtrum da86f8da72 Make udp-test bi-directional 2019-12-17 01:28:43 -05:00
Travis Burtrum 1e7fa84a42 Added TLS --pinnedpubkey support 2019-12-17 01:22:26 -05:00
Travis Burtrum 9d37f3d929 CI: stop releasing archives, just release binaries 2019-12-16 22:33:26 -05:00
Travis Burtrum 2cd69aaf15 Enable TLS testing from udp-test, enable on CI 2019-12-16 21:44:07 -05:00
Travis Burtrum 7316bb5341 Add --tls-hostname argument to wireguard-proxy 2019-12-16 21:15:25 -05:00
Travis Burtrum b363f30298 update readme 2019-12-16 19:29:08 -05:00
Travis Burtrum d46c440b4e CI: depending on platform build without, with system, or with vendored TLS 2019-12-16 19:12:02 -05:00
Travis Burtrum 9b2866c5b4 Implement TLS server side, refactor to allow conditional TLS support at compile time 2019-12-16 11:52:17 -05:00
Travis Burtrum 4c6f4258b4 First (unsafe) go at tls support 2019-12-16 02:21:16 -05:00
Travis Burtrum 5280c038b7 Disable broken freebsd builds 2019-12-15 19:28:32 -05:00
Travis Burtrum 5e394bca1e Merge proxy/proxyd into single binary, make command line parsing more robust 2019-12-15 18:44:56 -05:00
Travis Burtrum 152bb991d4 Remove 'udp_target' argument from wireguard-proxy, set it dynamically from first UDP packet recieved 2019-12-15 13:50:19 -05:00
Travis Burtrum a9f7daf823 Add more tests to test.sh 2019-12-15 13:47:13 -05:00
27 changed files with 2254 additions and 520 deletions

42
.ci/Jenkinsfile vendored Normal file
View File

@ -0,0 +1,42 @@
properties(
[
disableConcurrentBuilds()
]
)
node('linux && docker') {
try {
stage('Checkout') {
//branch name from Jenkins environment variables
echo "My branch is: ${env.BRANCH_NAME}"
// this doesn't grab tags pointing to this branch
//checkout scm
// this hack does... https://issues.jenkins.io/browse/JENKINS-45164
checkout([
$class: 'GitSCM',
branches: [[name: 'refs/heads/'+env.BRANCH_NAME]],
extensions: [[$class: 'CloneOption', noTags: false, shallow: false, depth: 0, reference: '']],
userRemoteConfigs: scm.userRemoteConfigs,
])
sh '''
set -euxo pipefail
git checkout "$BRANCH_NAME" --
git reset --hard "origin/$BRANCH_NAME"
'''
}
stage('Build + Deploy') {
sh 'curl --compressed -sL https://code.moparisthebest.com/moparisthebest/self-ci/raw/branch/master/build-ci.sh | bash'
}
currentBuild.result = 'SUCCESS'
} catch (Exception err) {
currentBuild.result = 'FAILURE'
} finally {
stage('Email') {
step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: 'admin.jenkins@moparisthebest.com', sendToIndividuals: true])
}
deleteDir()
}
}

64
.ci/build.sh Executable file
View File

@ -0,0 +1,64 @@
#!/bin/bash
set -exo pipefail
echo "starting build for TARGET $TARGET"
export CRATE_NAME=wireguard-proxy
export OPENSSL_STATIC=1
export CARGO_FEATURES=async
DISABLE_TESTS=${DISABLE_TESTS:-0}
SUFFIX=""
# wine blows up in testing with async build
echo "$TARGET" | grep -E '^x86_64-pc-windows-gnu$' >/dev/null && DISABLE_TESTS=1 && SUFFIX=".exe"
# these only support openssl_vendored, not async
if echo "$TARGET" | grep -E '^(s390x|powerpc|mips)' >/dev/null
then
CARGO_FEATURES=openssl_vendored
fi
# these don't support any TLS at all
if echo "$TARGET" | grep -E '(^riscv64gc|solaris$)' >/dev/null
then
CARGO_FEATURES=verbose
fi
cross rustc --bin wireguard-proxy --target $TARGET --release --no-default-features --features $CARGO_FEATURES
cross rustc --bin udp-test --target $TARGET --release --no-default-features --features $CARGO_FEATURES
# to check how they are built
file "target/$TARGET/release/wireguard-proxy$SUFFIX" "target/$TARGET/release/udp-test$SUFFIX"
if [ $DISABLE_TESTS -ne 1 ]
then
# first make sure udp-test succeeds running against itself
cross run --target $TARGET --release --no-default-features --features $CARGO_FEATURES --bin udp-test
# now run udp-test through proxy/proxyd
cross run --target $TARGET --release --no-default-features --features $CARGO_FEATURES --bin udp-test -- -is
if [ $CARGO_FEATURES != "verbose" ]; then
# run TLS tests then too
cross run --target $TARGET --release --no-default-features --features $CARGO_FEATURES --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem
# now pubkey tests
# one that should fail (wrong pinnedpubkey lowercase e at end instead of uppercase E)
cross run --target $TARGET --release --no-default-features --features $CARGO_FEATURES --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4de= && exit 1 || true
# and one that should pass
cross run --target $TARGET --release --no-default-features --features $CARGO_FEATURES --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4dE=
fi
fi
# if this commit has a tag, upload artifact to release
strip "target/$TARGET/release/wireguard-proxy$SUFFIX" || true # if strip fails, it's fine
mkdir -p release
mv "target/$TARGET/release/wireguard-proxy$SUFFIX" "release/wireguard-proxy-$TARGET$SUFFIX"
echo 'build success!'
exit 0

View File

@ -1,133 +0,0 @@
# Based on the "trust" template v0.1.2
# https://github.com/japaric/trust/tree/v0.1.2
dist: trusty
language: rust
services: docker
sudo: required
# TODO Rust builds on stable by default, this can be
# overridden on a case by case basis down below.
env:
global:
# TODO Update this to match the name of your project.
- CRATE_NAME=wireguard-proxy
matrix:
# TODO These are all the build jobs. Adjust as necessary. Comment out what you
# don't need
include:
# Android
- env: TARGET=aarch64-linux-android DISABLE_TESTS=1
- env: TARGET=arm-linux-androideabi DISABLE_TESTS=1
- env: TARGET=armv7-linux-androideabi DISABLE_TESTS=1
- env: TARGET=i686-linux-android DISABLE_TESTS=1
- env: TARGET=x86_64-linux-android DISABLE_TESTS=1
# iOS
- env: TARGET=aarch64-apple-ios DISABLE_TESTS=1
os: osx
- env: TARGET=armv7-apple-ios DISABLE_TESTS=1
os: osx
- env: TARGET=armv7s-apple-ios DISABLE_TESTS=1
os: osx
- env: TARGET=i386-apple-ios DISABLE_TESTS=1
os: osx
- env: TARGET=x86_64-apple-ios DISABLE_TESTS=1
os: osx
# Linux
- env: TARGET=aarch64-unknown-linux-gnu
- env: TARGET=arm-unknown-linux-gnueabi
- env: TARGET=armv7-unknown-linux-gnueabihf
- env: TARGET=i686-unknown-linux-gnu
- env: TARGET=i686-unknown-linux-musl
- env: TARGET=mips-unknown-linux-gnu
- env: TARGET=mips64-unknown-linux-gnuabi64
- env: TARGET=mips64el-unknown-linux-gnuabi64
- env: TARGET=mipsel-unknown-linux-gnu
- env: TARGET=powerpc-unknown-linux-gnu
- env: TARGET=powerpc64-unknown-linux-gnu
- env: TARGET=powerpc64le-unknown-linux-gnu
- env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1
- env: TARGET=x86_64-unknown-linux-gnu
- env: TARGET=x86_64-unknown-linux-musl
# OSX
- env: TARGET=i686-apple-darwin
os: osx
- env: TARGET=x86_64-apple-darwin
os: osx
# *BSD
- env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1
- env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1
- env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1
# Windows
- env: TARGET=x86_64-pc-windows-gnu
# Bare metal
# These targets don't support std and as such are likely not suitable for
# most crates.
# - env: TARGET=thumbv6m-none-eabi
# - env: TARGET=thumbv7em-none-eabi
# - env: TARGET=thumbv7em-none-eabihf
# - env: TARGET=thumbv7m-none-eabi
# Testing other channels
- env: TARGET=x86_64-unknown-linux-gnu
rust: nightly
- env: TARGET=x86_64-apple-darwin
os: osx
rust: nightly
before_install:
- set -e
- rustup self update
install:
- sh ci/install.sh
- source ~/.cargo/env || true
script:
- bash ci/script.sh
after_script: set +e
before_deploy:
- sh ci/before_deploy.sh
deploy:
# TODO update `api_key.secure`
# - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new
# - Encrypt it: `travis encrypt 0123456789012345678901234567890123456789
# - Paste the output down here
api_key:
secure: $GITHUB_OAUTH
file_glob: true
file: $CRATE_NAME-$TRAVIS_TAG-$TARGET.*
on:
# TODO Here you can pick which targets will generate binary releases
# In this example, there are some targets that are tested using the stable
# and nightly channels. This condition makes sure there is only one release
# for such targets and that's generated using the stable channel
condition: $TRAVIS_RUST_VERSION = stable
tags: true
provider: releases
skip_cleanup: true
cache: cargo
before_cache:
# Travis can't cache files that are not readable by "others"
- chmod -R a+r $HOME/.cargo
branches:
only:
# release tags
- /^v\d+\.\d+\.\d+.*$/
- master
- travis
- ci

525
Cargo.lock generated
View File

@ -1,6 +1,531 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "autocfg"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "base64"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bumpalo"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytes"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "futures-core"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hermit-abi"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "js-sys"
version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"wasm-bindgen 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "mio"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio-uds"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miow"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "net2"
version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "once_cell"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "openssl"
version = "0.10.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "openssl-src"
version = "111.6.1+1.1.1d"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "openssl-sys"
version = "0.9.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-src 111.6.1+1.1.1d (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
"vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pin-project-lite"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pkg-config"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ring"
version = "0.16.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
"once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"web-sys 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustls"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.16.15 (registry+https://github.com/rust-lang/crates.io-index)",
"sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sct"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ring 0.16.15 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-project-lite 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-macros 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-macros"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-rustls"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rustls 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vcpkg"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasm-bindgen"
version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-macro 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bumpalo 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-shared 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-macro-support 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-backend 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-shared 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "web-sys"
version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"js-sys 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "webpki"
version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ring 0.16.15 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wireguard-proxy"
version = "0.1.0"
dependencies = [
"base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.16.15 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-rustls 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum bumpalo 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
"checksum bytes 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
"checksum cc 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)" = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
"checksum hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
"checksum js-sys 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "52732a3d3ad72c58ad2dc70624f9c17b46ecd0943b9a4f1ee37c4c18c5d983e2"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.73 (registry+https://github.com/rust-lang/crates.io-index)" = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"
"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
"checksum mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
"checksum mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
"checksum once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
"checksum openssl 0.10.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585"
"checksum openssl-src 111.6.1+1.1.1d (registry+https://github.com/rust-lang/crates.io-index)" = "c91b04cb43c1a8a90e934e0cd612e2a5715d976d2d6cff4490278a0cddf35005"
"checksum openssl-sys 0.9.53 (registry+https://github.com/rust-lang/crates.io-index)" = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f"
"checksum pin-project-lite 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
"checksum ring 0.16.15 (registry+https://github.com/rust-lang/crates.io-index)" = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4"
"checksum rustls 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac94b333ee2aac3284c5b8a1b7fb4dd11cba88c244e3fe33cdbd047af0eb693"
"checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
"checksum syn 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0"
"checksum tokio 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd"
"checksum tokio-macros 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
"checksum tokio-rustls 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "228139ddd4fea3fa345a29233009635235833e52807af7ea6448ead03890d6a9"
"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
"checksum untrusted 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
"checksum wasm-bindgen 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "f3edbcc9536ab7eababcc6d2374a0b7bfe13a2b6d562c5e07f370456b1a8f33d"
"checksum wasm-bindgen-backend 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "89ed2fb8c84bfad20ea66b26a3743f3e7ba8735a69fe7d95118c33ec8fc1244d"
"checksum wasm-bindgen-macro 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "eb071268b031a64d92fc6cf691715ca5a40950694d8f683c5bb43db7c730929e"
"checksum wasm-bindgen-macro-support 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "cf592c807080719d1ff2f245a687cbadb3ed28b2077ed7084b47aba8b691f2c6"
"checksum wasm-bindgen-shared 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "72b6c0220ded549d63860c78c38f3bcc558d1ca3f4efa74942c536ddbbb55e87"
"checksum web-sys 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "8be2398f326b7ba09815d0b403095f34dd708579220d099caae89be0b32137b2"
"checksum webpki 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"

View File

@ -18,3 +18,20 @@ include = [
"LICENSE-*",
"*.md",
]
[features]
default = ["async"]
tls = ["openssl"]
openssl_vendored = ["openssl/vendored"]
verbose = []
async = ["tokio", "tokio-rustls", "ring", "base64"]
[dependencies]
# only for non-async build with TLS support
openssl = { version = "0.10.26", optional = true }
# the rest of these are only required for async build
tokio = { version = "0.2", features = [ "macros", "net", "udp", "io-std", "io-util", "rt-threaded" ], optional = true }
tokio-rustls = { version = "0.14", features = ["dangerous_configuration"], optional = true }
# probably should try to keep ring the exact same version as rustls, same features too
ring = { version = "0.16.11", optional = true }
base64 = { version = "0.12.3", optional = true }

121
README.md
View File

@ -1,16 +1,91 @@
# wireguard-proxy
[![Build Status](https://ci.moparisthe.best/job/moparisthebest/job/wireguard-proxy/job/master/badge/icon%3Fstyle=plastic)](https://ci.moparisthe.best/job/moparisthebest/job/wireguard-proxy/job/master/)
[![Build status](https://ci.appveyor.com/api/projects/status/vl8c9xdhvgn997d2/branch/master?svg=true)](https://ci.appveyor.com/project/moparisthebest/wireguard-proxy)
[![crates.io](https://img.shields.io/crates/v/wireguard-proxy.svg)](https://crates.io/crates/wireguard-proxy)
Proxy wireguard UDP packets over TCP/TLS
`wireguard-proxyd` is a server-side daemon to accept TCP connections from multiple clients and pipe data to and from the specified UDP port
`wireguard-proxy` is a client-side daemon that accepts UDP packets on a local port from a single client, connects to a single remote TCP port, and pipes data between them
`wireguard-proxy` has 2 modes:
- server-side daemon to accept TCP/TLS connections from multiple clients and pipe data to and from the specified UDP port
- client-side daemon that accepts UDP packets on a local port from a single client, connects to a single remote TCP/TLS port, and pipes data between them
```
$ wireguard-proxy -h
usage: wireguard-proxy [options...]
Client Mode (requires --tcp-target):
-tt, --tcp-target <ip:port> TCP target to send packets to, where
wireguard-proxy server is running
-uh, --udp-host <ip:port> UDP host to listen on, point wireguard
client here, default: 127.0.0.1:51820
--tls use TLS when connecting to tcp-target
WARNING: authenticates/verifies nothing
without --pinnedpubkey below!!
--pinnedpubkey <sha256_hashes> Public key to verify peer against,
format is any number of base64 encoded
sha256 hashes preceded by "sha256//"
and separated by ";". Identical to curl's
--pinnedpubkey and CURLOPT_PINNEDPUBLICKEY
--tls-hostname send this in SNI instead of host
from --tcp-target, useful for avoiding
DNS lookup on connect
Server Mode (requires --tcp-host):
-th, --tcp-host <ip:port> TCP host to listen on
-ut, --udp-target <ip:port> UDP target to send packets to, where
wireguard server is running,
default: 127.0.0.1:51820
-ur, --udp-bind-host-range <ip:low-high> UDP host and port range to bind to,
one port per TCP connection, to
listen on for UDP packets to send
back over the TCP connection,
default: 127.0.0.1:30000-40000
-tk, --tls-key <ip:port> TLS key to listen with,
requires --tls-cert also
-tc, --tls-cert <ip:port> TLS cert to listen with,
requires --tls-key also
Note: with both --tls-key and --tls-cert,
- means stdin,
also the same file can work for both if you combine them into
one pem file
Common Options:
-h, --help print this usage text
-V, --version Show version number and TLS support then quit
-st, --socket-timeout <seconds> Socket timeout (time to wait for data)
before terminating, default: 0
Environment variable support:
For every long command line option (starting with --), if you replace the
leading -- with WGP_, and replace all remaining - with _, and uppercase
the whole thing, if you don't specify that command line option we will
read that environment variable for the argument. boolean arguments are
true if anything but unset, empty, 0, or false.
Examples:
--tcp-target ARG is WGP_TCP_TARGET=ARG
--socket-timeout 5 is WGP_SOCKET_TIMEOUT=5
--tls is WGP_TLS=1 or WGP_TLS=true
WGP_TLS=0 or WGP_TLS=false would be like not sending --tls
```
Binaries:
- [releases](https://github.com/moparisthebest/wireguard-proxy/releases) has static builds for most platforms performed by [self-ci](https://github.com/moparisthebest/self-ci) and appveyor courtesy of [trust](https://github.com/japaric/trust)
- Arch Linux AUR [wireguard-proxy](https://aur.archlinux.org/packages/wireguard-proxy/) and [wireguard-proxy-git](https://aur.archlinux.org/packages/wireguard-proxy-git/)
Building:
- `cargo build --release` - async build with TLS support supplied by rustls
- `cargo build --release --no-default-features ` - minimal build without TLS support, no dependencies
- `cargo build --release --no-default-features --feature tls` - links to system openssl
- `cargo build --release --no-default-features --feature openssl_vendored` - compiles vendored openssl and link to it
Testing:
`udp-test` is a utility to send a UDP packet and then receive a UDP packet and ensure they are the same, this verifies packets sent through proxy/proxyd are unmolested
`test.sh` runs udp-test against itself and then through proxyd/proxy
`udp-test -s` runs udp-test against itself through proxyd/proxy by spawning actual binaries
`udp-test -is` runs udp-test against itself through proxyd/proxy in same executable by using library, so does not test command line parsing etc
- `udp-test` is a utility to send a UDP packet and then receive a UDP packet and ensure they are the same, this verifies packets sent through proxy server/client are unmolested
- `udp-test -s` runs udp-test against itself through proxy server/client by spawning actual binaries
- `udp-test -is` runs udp-test against itself through proxy server/client in same executable by using library, so does not test command line parsing etc
- `test.sh` runs udp-test against itself, the udp-test self tests above, and through proxy server/client in the shell script
Testing with GNU netcat:
@ -20,6 +95,40 @@ Testing with GNU netcat:
- `nc 127.0.0.1 5555` connect directly to local tcp wireguard-proxy port to send/recieve data
- so to test through wireguard-proxy run first and last command while it's running, type in both places
# OpenSSL cert generation
Quick commands to generate your own certificate to use with wireguard-proxy, note if you are actually only sending
wireguard packets over this, the TLS layer doesn't really need to provide any security or authentication, only obfuscation
Currently the only authentication performed is optional and via --pinnedpubkey only if supplied
```sh
# single command self signed RSA cert
openssl req -new -x509 -sha256 -days 3650 -nodes -subj "/C=US/CN=example.org" -newkey rsa:2048 -out cert.pem -keyout key.pem
# customize key type
# more info: https://github.com/openssl/openssl/blob/master/doc/man1/openssl-genpkey.pod
# ordered roughly starting from oldest/worst/most supported (rsa) to newest/best/least supported (ed448) order
# run one of these only to generate the preferred key type
openssl genpkey -algorithm RSA -out key.pem -pkeyopt rsa_keygen_bits:1024
openssl genpkey -algorithm RSA -out key.pem -pkeyopt rsa_keygen_bits:2048
openssl genpkey -algorithm RSA -out key.pem -pkeyopt rsa_keygen_bits:4096
openssl genpkey -algorithm EC -out key.pem -pkeyopt ec_paramgen_curve:P-256 -pkeyopt ec_param_enc:named_curve
openssl genpkey -algorithm EC -out key.pem -pkeyopt ec_paramgen_curve:P-384 -pkeyopt ec_param_enc:named_curve
openssl genpkey -algorithm EC -out key.pem -pkeyopt ec_paramgen_curve:P-521 -pkeyopt ec_param_enc:named_curve
openssl genpkey -algorithm ED25519 -out key.pem
openssl genpkey -algorithm ED448 -out key.pem
# then run this to generate and self-sign a cert with the above key
openssl req -new -x509 -sha256 -days 3650 -nodes -subj "/C=US/CN=example.org" -out cert.pem -key key.pem
# optionally (but recommended) extract pinnedpubkey hash from the above generated cert like so:
# openssl x509 -in cert.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
# optionally run this to see human readable info about the cert
openssl x509 -in cert.pem -noout -text
```
# License
This project is licensed under either of

View File

@ -9,6 +9,9 @@ environment:
# TODO Update this to match the name of your project.
CRATE_NAME: wireguard-proxy
# gnu fails to compile with any openssl combo for some reason
OPENSSL_STATIC: 1
CARGO_FEATURES: async
# TODO These are all the build jobs. Adjust as necessary. Comment out what you
# don't need
@ -44,20 +47,23 @@ install:
test_script:
# we don't run the "test phase" when doing deploys
- if [%APPVEYOR_REPO_TAG%]==[false] (
cargo build --target %TARGET% &&
cargo build --target %TARGET% --release &&
cargo run --target %TARGET% --release --bin udp-test &&
cargo run --target %TARGET% --release --bin udp-test -- -is
cargo build --target %TARGET% --release --features %CARGO_FEATURES% &&
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test &&
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test -- -is
)
# todo: should run --pinnedpubkey test here where we expect failure, but unsure how to do that with windows, help?
- if [%CARGO_FEATURES%]==[async] (
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem &&
cargo run --target %TARGET% --release --features %CARGO_FEATURES% --bin udp-test -- -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4dE=
)
before_deploy:
# TODO Update this to build the artifacts that matter to you
- cargo rustc --target %TARGET% --release --bin wireguard-proxy -- -C lto
- cargo rustc --target %TARGET% --release --bin wireguard-proxyd -- -C lto
- cargo rustc --target %TARGET% --release --features %CARGO_FEATURES% --bin wireguard-proxy -- -C lto
- ps: ci\before_deploy.ps1
deploy:
artifact: /.*\.zip/
artifact: /wireguard-proxy-.*\.exe/
# TODO update `auth_token.secure`
# - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new
# - Encrypt it. Go to https://ci.appveyor.com/tools/encrypt
@ -85,6 +91,7 @@ branches:
- master
- appveyor
- ci
- openssl
notifications:
- provider: Email

85
benchmark.sh Executable file
View File

@ -0,0 +1,85 @@
#!/bin/sh
#set -x
# cert created with:
# cd ci && echo -e '\n\n\n\n\n\n\n' | openssl req -new -x509 -days 3650 -nodes -out cert.pem -keyout cert.key
export PATH="$(pwd)/target/release:$PATH"
run_tests() {
client_arg="$1"
shift
# now run proxyd pointing to nc
wireguard-proxy -th 127.0.0.1:5555 -ut 127.0.0.1:51822 "$@" &
proxyd_pid=$!
# wait for ports to be set up, this is fragile...
sleep 5
# proxy pointing to proxyd
wireguard-proxy -tt 127.0.0.1:5555 "$client_arg" &
proxy_pid=$!
# wait for ports to be set up, this is fragile...
sleep 1
# nc running through wireguard-proxy's above
nc -lup 51822 >/dev/null &
nc_listen_pid=$!
wireguard-proxy -V
dd if=/dev/zero bs=128M count=10 | nc -u 127.0.0.1 51820 &
nc_connect_pid=$!
sleep 5
kill $nc_listen_pid $nc_connect_pid $proxyd_pid $proxy_pid
}
# first no-network baseline
dd if=/dev/zero bs=128M count=10 | cat >/dev/null
# now openbsd netcat for network baseline
nc -lup 51822 >/dev/null &
nc_listen_pid=$!
dd if=/dev/zero bs=128M count=10 | nc -u 127.0.0.1 51822 &
nc_connect_pid=$!
sleep 5
kill $nc_listen_pid $nc_connect_pid
# first run without TLS
#cargo clean
cargo build --release --no-default-features 2>/dev/null || exit 1
run_tests || exit 1
# third run with async+rustls
#cargo clean
cargo build --release --no-default-features --features async 2>/dev/null || exit 1
# first plaintext tests
run_tests || exit 1
# then TLS tests
run_tests --tls --tls-key ci/cert.key --tls-cert ci/cert.pem || exit 1
exit 0
# first run with non-vendored tls
#cargo clean
cargo build --release --no-default-features --features tls 2>/dev/null || exit 1
# first plaintext tests
run_tests || exit 1
# then TLS tests
run_tests --tls --tls-key ci/cert.key --tls-cert ci/cert.pem || exit 1
# second run with vendored tls
#cargo clean
cargo build --release --no-default-features --features openssl_vendored 2>/dev/null || exit 1
# first plaintext tests
run_tests || exit 1
# then TLS tests
run_tests --tls --tls-key ci/cert.key --tls-cert ci/cert.pem || exit 1
exit 0

View File

@ -1,24 +0,0 @@
# This script takes care of packaging the build artifacts that will go in the
# release zipfile
$SRC_DIR = $PWD.Path
$STAGE = [System.Guid]::NewGuid().ToString()
Set-Location $ENV:Temp
New-Item -Type Directory -Name $STAGE
Set-Location $STAGE
$ZIP = "$SRC_DIR\$($Env:CRATE_NAME)-$($Env:APPVEYOR_REPO_TAG_NAME)-$($Env:TARGET).zip"
# TODO Update this to package the right artifacts
Copy-Item "$SRC_DIR\target\$($Env:TARGET)\release\wireguard-proxyd.exe" '.\'
Copy-Item "$SRC_DIR\target\$($Env:TARGET)\release\wireguard-proxy.exe" '.\'
7z a "$ZIP" *
Push-AppveyorArtifact "$ZIP"
Remove-Item *.* -Force
Set-Location ..
Remove-Item $STAGE
Set-Location $SRC_DIR

View File

@ -1,43 +0,0 @@
# This script takes care of building your crate and packaging it for release
set -ex
main() {
local src=$(pwd) \
stage=
case $TRAVIS_OS_NAME in
linux)
stage=$(mktemp -d)
;;
osx)
stage=$(mktemp -d -t tmp)
;;
esac
test -f Cargo.lock || cargo generate-lockfile
# TODO Update this to build the artifacts that matter to you
cross rustc --bin wireguard-proxy --target $TARGET --release -- -C lto
cross rustc --bin wireguard-proxyd --target $TARGET --release -- -C lto
# TODO Update this to package the right artifacts, this needs to handle .exe too...
case $TARGET in
x86_64-pc-windows-gnu)
strip target/$TARGET/release/wireguard-proxy.exe target/$TARGET/release/wireguard-proxyd.exe || echo 'strip failed, ignoring...'
cp target/$TARGET/release/wireguard-proxy.exe target/$TARGET/release/wireguard-proxyd.exe $stage/
;;
*)
strip target/$TARGET/release/wireguard-proxy target/$TARGET/release/wireguard-proxyd || echo 'strip failed, ignoring...'
cp target/$TARGET/release/wireguard-proxy target/$TARGET/release/wireguard-proxyd $stage/
;;
esac
cd $stage
tar czf $src/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz *
cd $src
rm -rf $stage
}
main

28
ci/cert.key Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCrvZ+p0/2i/p1s
xzg3ydOS4z5S3QD0WAD1gdBTUBPjqmeGNbtXl5XLGPTYJeBqNNGRv6louB+jL8U+
TSBsyyARVZfJ9EDU3iG1fOQzz6sK8yhVL15bL4wJHobDGkL1zc0//ozDbr9iH5Xi
5Xh1q9lEyLMviASNjZbWcdGWxxKQRceluzcTyowesBr7K9nQaQF7cSmetCYaA1L9
JXokHY3P8pPEOhfo8SI7Lkt7XzKrnI0RBEIQBDF5F/XsKHO2Iso8bVq1huNr37MH
QDEDo+D8803oFS+89j9SFDv2QgITZgl0gtY5w911qbNtyz0hWYloXzmlPH2QF/+Z
XThI2Kd/AgMBAAECggEARmvN4Xxsv346HRWfhri6ibumnaHDt22yjvj47ICkdzEz
nAPCWwtsP8hu9Yaqe8JGwMXfeHIvfuGitoY3qoSsFI+NWyFNyDuBhQK+LESWNTo5
qpxuy2M2v7KFvdCx7krCQ+Bj5esujNS4yD4h49Zgk+TcHLxgaY7KcAphz7q3cPKP
hJPkwSvFmLMdqomyLJfBTWPx6Ue3ioAfKxM62hbaYlBth1ch5YqOhL84YCMnPmbq
hL/iTTlPcXeZoCodEHoOac/t8Nvv4fRetrBqk9uMZXZ1Bm9VfKbVDysfiKp9W+np
uvtYUht/TdlrzjE01h3QHNnkYgJA+yuK/qjL0nvoYQKBgQDjN4HdZqA7ZBPvQUtq
LVfpm2jy/8Sf3ewUUx7Pwselft7FOpKzkouVhdyWY8BweN97zwaYPJo7OtfMzT3Y
NPO5Vz7nxMcvwZXir0VV514lLIYjqkZkDY94thGayFaF8DmyqfIfTuyVPrigRfOR
8+dbYJrSVn9hnP4i9cxoIu39TwKBgQDBfxAFp2gJ1G3U2YAjNt4qrbsdPZvsX6CI
A8T5EuiPanAqaVWbQnkPFQq077qBSlCI/zUunw01I0pyuR74paY7PaiKT3hLkovh
v2VlOFEMaA7K/TTjv6tZS5P8DXuM/r34h5XRVsaKXKPNBGoAcbvanJ/96N42j+E1
G4+R8FMG0QKBgFMmKfkKqFJzojPpEh8N7uEHRVW/sYXLYaxiaqEfJ45xqjZE5BCg
7UHPldTXNkIyiZ42ObSWYN6R/wzsgthPMG2/9r48LaRVVHN7LoVsQPCbpY8BrfbJ
W5qSDkk1TSyAp6yxMnCwojVPmaLVVngv6Jdw99dHXiAronjKuH3XYn5TAoGAUUpd
Y9Kx3bdWMR7zO1gYvBtiyeURNZvzKFFVFkMAWwgfeWHpaiHiFBkF93/jfd/Ht9Zn
9F8zwEhERbBKN7H4BVlhDkJWyoEVrVCoe37OZgTtehAogSoMBaa/1Buh9VksXFYx
9dGb9ZL36fDZy7f8cNpuSNDlUkzeE16x0WECsJECgYBdekYJFJWppHjQ0ID5Jt1T
GHQIovXFsHfACIKC1lyytqgtEdSaWQD0SfFsg/s6BYcW7bfs/tejTnbJySvcp3rC
+FCznrEfk6wVFuj/nrgB/MpmxbsG2N5EmchRX5YJHVDRtWpQxPkNgRvdNBPkPY54
5zSUEJh+lLSg+ZjuM52eOg==
-----END PRIVATE KEY-----

21
ci/cert.pem Normal file
View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUfeb7Ocg4fLv5BEiXLhLS5/fQGm0wDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xOTEyMTcwMjE2MDhaFw0yOTEy
MTQwMjE2MDhaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCrvZ+p0/2i/p1sxzg3ydOS4z5S3QD0WAD1gdBTUBPj
qmeGNbtXl5XLGPTYJeBqNNGRv6louB+jL8U+TSBsyyARVZfJ9EDU3iG1fOQzz6sK
8yhVL15bL4wJHobDGkL1zc0//ozDbr9iH5Xi5Xh1q9lEyLMviASNjZbWcdGWxxKQ
RceluzcTyowesBr7K9nQaQF7cSmetCYaA1L9JXokHY3P8pPEOhfo8SI7Lkt7XzKr
nI0RBEIQBDF5F/XsKHO2Iso8bVq1huNr37MHQDEDo+D8803oFS+89j9SFDv2QgIT
Zgl0gtY5w911qbNtyz0hWYloXzmlPH2QF/+ZXThI2Kd/AgMBAAGjUzBRMB0GA1Ud
DgQWBBSxoWXwmMEmKrsRsii2l1/IBhlvMzAfBgNVHSMEGDAWgBSxoWXwmMEmKrsR
sii2l1/IBhlvMzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB3
NIlwmAL3FBrqHmg5M+zh+xKkNl/O8SK4bJwhPoTYR+DHsDlEQWvwSVaGV5HGyqy2
cv39kHJ6OisSuOitESV4gdOUZvm/WCSV5xHpokJpGlztRSKi4iwFNONn0LUi4lnF
gkYgjS4OfOCjVJ0YgAkYaBYALM3PTY3VpG32vaz62A7mIzO5Jn/kMtEIgFT+32Be
BE/8E+pcOhgkvoE1xwv0STbrnM8dGN8/zyXvb1wt4b2ijkBlT5Wsqs0yvPa31SD0
FDqc+4/H3bJXjwfBGDbf18sTY1UQEPyQdNC7vhiy/w2AgjVNjVpNBI9nvj+9rkZ5
8m8sP3ldEkdIqSRCl95o
-----END CERTIFICATE-----

View File

@ -1,47 +0,0 @@
set -ex
main() {
local target=
if [ $TRAVIS_OS_NAME = linux ]; then
target=x86_64-unknown-linux-musl
sort=sort
else
target=x86_64-apple-darwin
sort=gsort # for `sort --sort-version`, from brew's coreutils.
fi
# Builds for iOS are done on OSX, but require the specific target to be
# installed.
case $TARGET in
aarch64-apple-ios)
rustup target install aarch64-apple-ios
;;
armv7-apple-ios)
rustup target install armv7-apple-ios
;;
armv7s-apple-ios)
rustup target install armv7s-apple-ios
;;
i386-apple-ios)
rustup target install i386-apple-ios
;;
x86_64-apple-ios)
rustup target install x86_64-apple-ios
;;
esac
# This fetches latest stable release
local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \
| cut -d/ -f3 \
| grep -E '^v[0.1.0-9.]+$' \
| $sort --version-sort \
| tail -n1)
curl -LSfs https://japaric.github.io/trust/install.sh | \
sh -s -- \
--force \
--git japaric/cross \
--tag $tag \
--target $target
}
main

View File

@ -1,24 +0,0 @@
# This script takes care of testing your crate
set -ex
# TODO This is the "test phase", tweak it as you see fit
main() {
cross build --target $TARGET
cross build --target $TARGET --release
if [ ! -z $DISABLE_TESTS ]; then
return
fi
# first make sure udp-test succeeds running against itself
cross run --target $TARGET --release --bin udp-test
# now run udp-test through proxy/proxyd
cross run --target $TARGET --release --bin udp-test -- -is
}
# we don't run the "test phase" when doing deploys
if [ -z $TRAVIS_TAG ]; then
main
fi

304
src/asyncmod.rs Normal file
View File

@ -0,0 +1,304 @@
use tokio::net::UdpSocket;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::runtime::Runtime;
use tokio_rustls::webpki::DNSNameRef;
use crate::error;
use crate::error::Result;
use crate::*;
pub struct TcpUdpPipe<T: AsyncReadExt + AsyncWriteExt + std::marker::Unpin + std::marker::Send + 'static> {
buf: [u8; 2050], // 2048 + 2 for len
tcp_stream: T,
udp_socket: UdpSocket,
}
impl<T: AsyncReadExt + AsyncWriteExt + std::marker::Unpin + std::marker::Send + 'static> TcpUdpPipe<T> {
pub fn new(tcp_stream: T, udp_socket: UdpSocket) -> TcpUdpPipe<T> {
TcpUdpPipe {
tcp_stream,
udp_socket,
buf: [0u8; 2050],
}
}
pub async fn shuffle_after_first_udp(mut self) -> Result<usize> {
let (len, src_addr) = self.udp_socket.recv_from(&mut self.buf[2..]).await?;
println!("first packet from {}, connecting to that", src_addr);
self.udp_socket.connect(src_addr).await?;
send_udp(&mut self.buf, &mut self.tcp_stream, len).await?;
self.shuffle().await
}
pub async fn shuffle(self) -> Result<usize> {
// todo: investigate https://docs.rs/tokio/0.2.22/tokio/net/struct.TcpStream.html#method.into_split
let (mut tcp_rd, mut tcp_wr) = tokio::io::split(self.tcp_stream);
let (mut udp_rd, mut udp_wr) = self.udp_socket.split();
let mut recv_buf = self.buf.clone(); // or zeroed or?
tokio::spawn(async move {
loop {
let len = udp_rd.recv(&mut recv_buf[2..]).await?;
send_udp(&mut recv_buf, &mut tcp_wr, len).await?;
}
// Sometimes, the rust type inferencer needs
// a little help
#[allow(unreachable_code)]
{
unsafe { std::hint::unreachable_unchecked(); }
Ok::<_, error::Error>(())
}
});
let mut send_buf = self.buf.clone(); // or zeroed or?
loop {
tcp_rd.read_exact(&mut send_buf[..2]).await?;
let len = ((send_buf[0] as usize) << 8) + send_buf[1] as usize;
#[cfg(feature = "verbose")]
println!("tcp expecting len: {}", len);
tcp_rd.read_exact(&mut send_buf[..len]).await?;
#[cfg(feature = "verbose")]
println!("tcp got len: {}", len);
udp_wr.send(&send_buf[..len]).await?;
}
#[allow(unreachable_code)]
{
unsafe { std::hint::unreachable_unchecked(); }
Ok(0)
}
}
}
async fn send_udp<T: AsyncWriteExt + std::marker::Unpin + 'static>(buf: &mut [u8; 2050], tcp_stream: &mut T, len: usize) -> Result<()> {
#[cfg(feature = "verbose")]
println!("udp got len: {}", len);
buf[0] = ((len >> 8) & 0xFF) as u8;
buf[1] = (len & 0xFF) as u8;
// todo: tcp_stream.write_all(&buf[..len + 2]).await
Ok(tcp_stream.write_all(&buf[..len + 2]).await?)
// todo: do this? self.tcp_stream.flush()
}
impl ProxyClient {
pub async fn start_async(&self) -> Result<usize> {
let tcp_stream = self.tcp_connect()?;
let udp_socket = self.udp_connect()?;
TcpUdpPipe::new(tokio::net::TcpStream::from_std(tcp_stream).expect("how could this tokio tcp fail?"), UdpSocket::from_std(udp_socket).expect("how could this tokio udp fail?"))
.shuffle_after_first_udp().await
}
pub fn start(&self) -> Result<usize> {
let mut rt = Runtime::new()?;
rt.block_on(async {
self.start_async().await
})
}
pub async fn start_tls_async(&self, hostname: Option<&str>, pinnedpubkey: Option<&str>) -> Result<usize> {
let tcp_stream = self.tcp_connect()?;
let tcp_stream = tokio::net::TcpStream::from_std(tcp_stream).expect("how could this tokio tcp fail?");
use tokio_rustls::{ TlsConnector, rustls::ClientConfig };
let mut config = ClientConfig::new();
config.dangerous().set_certificate_verifier(match pinnedpubkey {
Some(pinnedpubkey) => Arc::new(PinnedpubkeyCertVerifier { pinnedpubkey: pinnedpubkey.to_owned() }),
None => Arc::new(DummyCertVerifier{}),
});
let hostname = match hostname {
Some(hostname) => match DNSNameRef::try_from_ascii_str(hostname) {
Ok(hostname) => hostname,
Err(_) => {
config.enable_sni = false;
DNSNameRef::try_from_ascii_str(&"dummy.hostname").unwrap() // why does rustls ABSOLUTELY REQUIRE this ????
}
},
None => {
config.enable_sni = false;
DNSNameRef::try_from_ascii_str(&"dummy.hostname").unwrap() // why does rustls ABSOLUTELY REQUIRE this ????
}
};
//println!("hostname: {:?}", hostname);
let connector = TlsConnector::from(Arc::new(config));
let tcp_stream= connector.connect(hostname, tcp_stream).await?;
let udp_socket = self.udp_connect()?;
// we want to wait for first udp packet from client first, to set the target to respond to
TcpUdpPipe::new(tcp_stream, UdpSocket::from_std(udp_socket).expect("how could this tokio udp fail?"))
.shuffle_after_first_udp().await
}
pub fn start_tls(&self, hostname: Option<&str>, pinnedpubkey: Option<&str>) -> Result<usize> {
let mut rt = Runtime::new()?;
rt.block_on(async {
self.start_tls_async(hostname, pinnedpubkey).await
})
}
}
use tokio_rustls::rustls;
use tokio_rustls::webpki;
struct DummyCertVerifier;
impl rustls::ServerCertVerifier for DummyCertVerifier {
fn verify_server_cert(&self,
_roots: &rustls::RootCertStore,
_certs: &[rustls::Certificate],
_hostname: webpki::DNSNameRef<'_>,
_ocsp: &[u8]) -> core::result::Result<rustls::ServerCertVerified, rustls::TLSError> {
// verify nothing, subject to MITM
Ok(rustls::ServerCertVerified::assertion())
}
}
struct PinnedpubkeyCertVerifier {
pinnedpubkey: String,
}
impl rustls::ServerCertVerifier for PinnedpubkeyCertVerifier {
fn verify_server_cert(&self,
_roots: &rustls::RootCertStore,
certs: &[rustls::Certificate],
_hostname: webpki::DNSNameRef<'_>,
_ocsp: &[u8]) -> core::result::Result<rustls::ServerCertVerified, rustls::TLSError> {
if certs.is_empty() {
return Err(rustls::TLSError::NoCertificatesPresented);
}
let cert = webpki::trust_anchor_util::cert_der_as_trust_anchor(&certs[0].0)
.map_err(rustls::TLSError::WebPKIError)?;
//println!("spki.len(): {}", cert.spki.len());
//println!("spki: {:?}", cert.spki);
// todo: what is wrong with webpki? it returns *almost* the right answer but missing these leading bytes:
// guess I'll open an issue... (I assume this is some type of algorithm identifying header or something)
let mut pubkey: Vec<u8> = vec![48, 130, 1, 34];
pubkey.extend(cert.spki);
let pubkey = ring::digest::digest(&ring::digest::SHA256, &pubkey);
let pubkey = base64::encode(pubkey);
let pubkey = ["sha256//", &pubkey].join("");
for key in self.pinnedpubkey.split(";") {
if key == pubkey {
return Ok(rustls::ServerCertVerified::assertion());
}
}
Err(rustls::TLSError::General(format!("pubkey '{}' not found in allowed list '{}'", pubkey, self.pinnedpubkey)))
}
}
impl ProxyServer {
pub async fn start_async(&self) -> Result<()> {
let mut listener = tokio::net::TcpListener::bind(&self.tcp_host).await?;
println!("Listening for connections on {}", &self.tcp_host);
loop {
let (stream, _) = listener.accept().await?;
let client_handler = self.client_handler.clone();
tokio::spawn(async move {
client_handler
.handle_client_async(stream).await
.expect("error handling connection");
});
}
#[allow(unreachable_code)]
{
unsafe { std::hint::unreachable_unchecked(); }
Ok(())
}
}
pub fn start(&self) -> Result<()> {
let mut rt = Runtime::new()?;
rt.block_on(async {
self.start_async().await
})
}
pub async fn start_tls_async(&self, tls_key: &str, tls_cert: &str) -> Result<()> {
use std::fs::File;
use std::io::BufReader;
use std::io;
use tokio_rustls::rustls::internal::pemfile::{ certs, pkcs8_private_keys };
let mut tls_key = pkcs8_private_keys(&mut BufReader::new(File::open(tls_key)?))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))?;
if tls_key.is_empty() {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))?;
}
let tls_key = tls_key.remove(0);
let tls_cert = certs(&mut BufReader::new(File::open(tls_cert)?))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))?;
let mut config = rustls::ServerConfig::new(rustls::NoClientAuth::new());
config.set_single_cert(tls_cert, tls_key)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
let acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(config));
let mut listener = tokio::net::TcpListener::bind(&self.tcp_host).await?;
println!("Listening for TLS connections on {}", &self.tcp_host);
loop {
let (stream, _) = listener.accept().await?;
let client_handler = self.client_handler.clone();
let acceptor = acceptor.clone();
tokio::spawn(async move {
let stream = acceptor.accept(stream).await.expect("failed to wrap with TLS?");
client_handler
.handle_client_async(stream).await
.expect("error handling connection");
});
}
#[allow(unreachable_code)]
{
unsafe { std::hint::unreachable_unchecked(); }
Ok(())
}
}
pub fn start_tls(&self, tls_key: &str, tls_cert: &str) -> Result<()> {
let mut rt = Runtime::new()?;
rt.block_on(async {
self.start_tls_async(tls_key, tls_cert).await
})
}
}
impl ProxyServerClientHandler {
pub async fn handle_client_async<T: AsyncReadExt + AsyncWriteExt + std::marker::Unpin + std::marker::Send + 'static>(&self, tcp_stream: T) -> Result<usize> {
TcpUdpPipe::new(tcp_stream,
UdpSocket::from_std(self.udp_bind()?).expect("how could this tokio udp fail?")
).shuffle().await
}
}

View File

@ -50,6 +50,22 @@ impl Server {
assert_eq!(sent, PONG.len());
let mut buf = [0u8; 2048];
match udp_socket.recv_from(&mut buf) {
Ok((len, src_addr)) => {
println!("udp got len: {} from src_addr: {}", len, src_addr);
assert_eq!(len, PONG.len());
assert_eq!(&buf[..len], &PONG[..]);
// now reply back to src_addr to make sure other direction works
let sent = udp_socket.send_to(&PONG, &src_addr)?;
assert_eq!(sent, PONG.len());
}
Err(e) => {
panic!("recv function failed first round: {:?}", e);
}
}
// if we are here, we successfully sent the reply, did we get it back?
match udp_socket.recv(&mut buf) {
Ok(len) => {
println!("udp got len: {}", len);
@ -57,11 +73,11 @@ impl Server {
assert_eq!(&buf[..len], &PONG[..]);
}
Err(e) => {
panic!("recv function failed: {:?}", e);
panic!("recv function failed second round: {:?}", e);
}
}
println!("success! received back exactly what we sent!");
println!("success! received back exactly what we sent both ways!");
Ok(0)
}
@ -70,41 +86,128 @@ impl Server {
fn main() {
let raw_args = env::args().collect();
let args = Args::new(&raw_args);
let mut first_arg = args.get_str(1, "127.0.0.1:51821");
if first_arg.contains("-h") {
println!(
"usage: {} [-h] [-s run a self test through proxy/proxyd] [-is run a self test through proxy/proxyd without spawning other processes] [udp_host, 127.0.0.1:51821] [udp_target, 127.0.0.1:51821] [socket_timeout, 10]",
args.get_str(0, "udp-test")
);
if args.flag("-V") || args.flag("--version") {
print!("udp-test {} ", env!("CARGO_PKG_VERSION"));
#[cfg(not(any(feature = "tls", feature = "openssl_vendored")))]
println!("TLS support: None");
#[cfg(feature = "openssl_vendored")]
println!("TLS support: Static/Vendored OpenSSL");
#[cfg(feature = "tls")]
println!("TLS support: System OpenSSL");
return;
} else if first_arg.contains("-s") {
}
let default_udp_host_target = "127.0.0.1:51820";
let default_socket_timeout = 10;
let tls_key = args.get_option(&["-tk", "--tls-key"]);
let tls_cert = args.get_option(&["-tc", "--tls-cert"]);
let pinnedpubkey = args.get_option(&["--pinnedpubkey"]);
let tls = if tls_key.is_some() && tls_cert.is_some() {
true
} else if tls_key.is_none() && tls_cert.is_none() {
false
} else {
println!("Error: if one of --tls-key or --tls-cert is specified both must be!");
exit(1);
};
let mut first_arg = args.get_str(&["-uh", "--udp-host"], default_udp_host_target);
if args.flag("-h") || args.flag("--help") {
println!(r#"usage: udp-test [options...]
-h, --help print this usage text
-V, --version Show version number and TLS support then quit
-s, --self-test run a self test through proxy
-is, --internal-self-test run a self test through proxy without
spawning other processes
-uh, --udp-host <ip:port> UDP host to listen on, default: {}
-ut, --udp-target <ip:port> UDP target to send packets to, default: {}
-st, --socket-timeout <seconds> Socket timeout (time to wait for data)
before terminating, default: {}
TLS option for self tests only, otherwise self tests are plaintext only:
-tk, --tls-key <ip:port> TLS key to listen with,
requires --tls-cert also
-tc, --tls-cert <ip:port> TLS cert to listen with,
requires --tls-key also
--pinnedpubkey <sha256_hashes> Public key to verify peer against,
format is any number of base64 encoded
sha256 hashes preceded by "sha256//"
and separated by ";". Identical to curl's
--pinnedpubkey and CURLOPT_PINNEDPUBLICKEY
Note: with both --tls-key and --tls-cert,
only for -is (not -s) - means stdin,
also the same file can work for both if you combine them into
one pem file
Environment variable support:
For every long command line option (starting with --), if you replace the
leading -- with WGP_, and replace all remaining - with _, and uppercase
the whole thing, if you don't specify that command line option we will
read that environment variable for the argument. boolean arguments are
true if anything but unset, empty, 0, or false.
Examples:
--tcp-target ARG is WGP_TCP_TARGET=ARG
--socket-timeout 5 is WGP_SOCKET_TIMEOUT=5
--tls is WGP_TLS=1 or WGP_TLS=true
WGP_TLS=0 or WGP_TLS=false would be like not sending --tls
"#, default_udp_host_target, default_udp_host_target, default_socket_timeout);
return;
} else if args.flag("-s") || args.flag("--self-test") {
// here is the hard work, we need to spawn proxyd and proxy from the same dir as udp-test...
let host = "127.0.0.1:51822";
let tcp_host = "127.0.0.1:5555";
let sleep = Duration::from_secs(5);
let udp_test = args.get_str(0, "udp-test");
let proxyd = udp_test.clone().replace("udp-test", "wireguard-proxyd");
let proxy = udp_test.clone().replace("udp-test", "wireguard-proxy");
let udp_test = std::env::current_exe().expect("cannot get path to current executable");
let proxy = udp_test.clone().with_file_name("wireguard-proxy")
.with_extension(udp_test.extension().unwrap_or_else(|| "".as_ref()));
println!("executing: {} '{}' '{}'", proxyd, tcp_host, host);
let mut proxyd = Command::new(proxyd)
.arg(tcp_host)
.arg(host)
.spawn()
.expect("wireguard-proxyd failed to launch");
println!("waiting: {:?} for wireguard-proxyd to come up.....", sleep);
let udp_test = udp_test.to_str().expect("non-utf8 executable path?");
let proxy = proxy.to_str().expect("non-utf8 executable path?");
let mut proxyd_args = vec!["-th", tcp_host, "-ut", host];
let tls_key = tls_key.as_ref().map(String::as_str);
let tls_cert = tls_cert.as_ref().map(String::as_str);
if tls {
let tls_key = tls_key.unwrap();
let tls_cert = tls_cert.unwrap();
proxyd_args.extend(["-tk", &tls_key, "-tc", &tls_cert].iter().cloned());
}
println!("executing: {} {}", proxy, proxyd_args.join(" "));
let mut proxyd = Command::new(proxy.clone())
.args(&proxyd_args)
.spawn()
.expect("wireguard-proxy server failed to launch");
println!("waiting: {:?} for wireguard-proxy server to come up.....", sleep);
thread::sleep(sleep);
println!("executing: {}", proxy);
let mut proxy_args = vec!["-tt", tcp_host];
let pinnedpubkey = pinnedpubkey.as_ref().map(String::as_str);
if tls {
proxy_args.push("--tls");
if pinnedpubkey.is_some() {
proxy_args.push("--pinnedpubkey");
proxy_args.push(&pinnedpubkey.unwrap());
}
}
println!("executing: {} {}", proxy, proxy_args.join(" "));
let mut proxy = Command::new(proxy)
.spawn()
.expect("wireguard-proxy failed to launch");
println!("waiting: {:?} for wireguard-proxy to come up.....", sleep);
.args(proxy_args)
.spawn()
.expect("wireguard-proxy TLS client failed to launch");
println!("waiting: {:?} for wireguard-proxy client to come up.....", sleep);
thread::sleep(sleep);
println!("executing: {} '{}'", udp_test, host);
println!("executing: {} -uh '{}'", udp_test, host);
let mut udp_test = Command::new(udp_test)
.arg("-uh")
.arg(host)
.spawn()
.expect("udp-test failed to launch");
@ -123,7 +226,7 @@ fn main() {
.code()
.expect("could not get udp-test exit code"),
);
} else if first_arg.contains("-is") {
} else if args.flag("-is") || args.flag("--internal-self-test") {
let host = "127.0.0.1:51822";
let tcp_host = "127.0.0.1:5555";
let sleep = Duration::from_secs(5);
@ -142,38 +245,58 @@ fn main() {
proxy_server.client_handler.udp_target, proxy_server.client_handler.socket_timeout,
);
println!("executing: wireguard-proxyd '{}' '{}'", tcp_host, host);
thread::spawn(move || proxy_server.start().expect("error running proxy_server"));
println!("waiting: {:?} for wireguard-proxyd to come up.....", sleep);
if tls {
let tls_key = tls_key.unwrap().to_owned();
let tls_cert = tls_cert.unwrap().to_owned();
println!("executing: wireguard-proxy -th '{}' -ut '{}' -tk '{}' -tc '{}'", tcp_host, host, tls_key, tls_cert);
thread::spawn(move || proxy_server.start_tls(&tls_key, &tls_cert).expect("error running TLS proxy_server"));
} else {
println!("executing: wireguard-proxy -th '{}' -ut '{}'", tcp_host, host);
thread::spawn(move || proxy_server.start().expect("error running proxy_server"));
}
println!("waiting: {:?} for wireguard-proxy server to come up.....", sleep);
thread::sleep(sleep);
let proxy_client = ProxyClient::new(
"127.0.0.1:51821".to_owned(),
"127.0.0.1:51820".to_owned(),
tcp_host.to_owned().to_owned(),
tcp_host.to_owned(),
15,
);
println!(
"udp_host: {}, udp_target: {}, tcp_target: {}, socket_timeout: {:?}",
"udp_host: {}, tcp_target: {}, socket_timeout: {:?}",
proxy_client.udp_host,
proxy_client.udp_target,
proxy_client.tcp_target,
proxy_client.socket_timeout,
);
println!("executing: wireguard-proxy");
thread::spawn(move || proxy_client.start().expect("error running proxy_client"));
println!("waiting: {:?} for wireguard-proxy to come up.....", sleep);
if tls {
let hostname = tcp_host.split(":").next();
let pinnedpubkey = pinnedpubkey.as_ref().map(String::as_str);
match pinnedpubkey {
Some(pinnedpubkey) =>
println!("executing: wireguard-proxy -tt {} --tls --pinnedpubkey {}", tcp_host, pinnedpubkey),
None =>
println!("executing: wireguard-proxy -tt {} --tls", tcp_host),
}
// this is a little funky, is this the only way to do it?
let pinnedpubkey = pinnedpubkey.map(&str::to_owned);
// can use pinnedpubkey.as_deref() below when it's stabilized
thread::spawn(move || proxy_client.start_tls(hostname, pinnedpubkey.as_ref().map(String::as_str)).expect("error running proxy_client"));
} else {
println!("executing: wireguard-proxy -tt {}", tcp_host);
thread::spawn(move || proxy_client.start().expect("error running proxy_client"));
}
println!("waiting: {:?} for wireguard-proxy client to come up.....", sleep);
thread::sleep(sleep);
first_arg = host;
first_arg = host.to_owned();
}
let server = Server::new(
first_arg.to_owned(),
args.get_str(2, "127.0.0.1:51821").to_owned(),
args.get(3, 10),
args.get_str(&["-ut", "--udp-target"], default_udp_host_target).to_owned(),
args.get(&["-st", "--socket-timeout"], default_socket_timeout),
);
println!(

View File

@ -1,31 +1,177 @@
use std::env;
use wireguard_proxy::{Args, ProxyClient};
use wireguard_proxy::{Args, ProxyClient, ProxyServer};
fn main() {
let raw_args = env::args().collect();
let args = Args::new(&raw_args);
if args.get_str(1, "").contains("-h") {
println!(
"usage: {} [-h] [udp_host, 127.0.0.1:51821] [udp_target, 127.0.0.1:51820] [tcp_target, 127.0.0.1:5555] [socket_timeout, 0]",
args.get_str(0, "wireguard-proxy")
);
if args.flag("-V") || args.flag("--version") {
print!("wireguard-proxy {} ", env!("CARGO_PKG_VERSION"));
#[cfg(not(any(feature = "tls", feature = "openssl_vendored", feature = "async")))]
println!("TLS support: None");
#[cfg(feature = "async")]
println!("TLS support: tokio-rustls");
#[cfg(all(feature = "openssl_vendored", not(feature = "async")))]
println!("TLS support: Static/Vendored OpenSSL");
#[cfg(all(feature = "tls", not(feature = "openssl_vendored"), not(feature = "async")))]
println!("TLS support: System OpenSSL");
return;
}
let default_udp_host_target = "127.0.0.1:51820";
let default_socket_timeout = 0;
let tcp_target = args.get_option(&["-tt", "--tcp-target"]);
let tcp_host = args.get_option(&["-th", "--tcp-host"]);
if args.flag("-h") || args.flag("--help") ||
// one of them must be set
(tcp_target.is_none() && tcp_host.is_none()) ||
// but both cannot be set
(tcp_target.is_some() && tcp_host.is_some())
{
println!(r#"usage: wireguard-proxy [options...]
Client Mode (requires --tcp-target):
-tt, --tcp-target <ip:port> TCP target to send packets to, where
wireguard-proxy server is running
-uh, --udp-host <ip:port> UDP host to listen on, point wireguard
client here, default: {}
--tls use TLS when connecting to tcp-target
WARNING: authenticates/verifies nothing
without --pinnedpubkey below!!
--pinnedpubkey <sha256_hashes> Public key to verify peer against,
format is any number of base64 encoded
sha256 hashes preceded by "sha256//"
and separated by ";". Identical to curl's
--pinnedpubkey and CURLOPT_PINNEDPUBLICKEY
--tls-hostname send this in SNI instead of host
from --tcp-target, useful for avoiding
DNS lookup on connect
Server Mode (requires --tcp-host):
-th, --tcp-host <ip:port> TCP host to listen on
-ut, --udp-target <ip:port> UDP target to send packets to, where
wireguard server is running,
default: {}
-ur, --udp-bind-host-range <ip:low-high> UDP host and port range to bind to,
one port per TCP connection, to
listen on for UDP packets to send
back over the TCP connection,
default: 127.0.0.1:30000-40000
-tk, --tls-key <ip:port> TLS key to listen with,
requires --tls-cert also
-tc, --tls-cert <ip:port> TLS cert to listen with,
requires --tls-key also
Note: with both --tls-key and --tls-cert,
- means stdin,
also the same file can work for both if you combine them into
one pem file
Common Options:
-h, --help print this usage text
-V, --version Show version number and TLS support then quit
-st, --socket-timeout <seconds> Socket timeout (time to wait for data)
before terminating, default: {}
Environment variable support:
For every long command line option (starting with --), if you replace the
leading -- with WGP_, and replace all remaining - with _, and uppercase
the whole thing, if you don't specify that command line option we will
read that environment variable for the argument. boolean arguments are
true if anything but unset, empty, 0, or false.
Examples:
--tcp-target ARG is WGP_TCP_TARGET=ARG
--socket-timeout 5 is WGP_SOCKET_TIMEOUT=5
--tls is WGP_TLS=1 or WGP_TLS=true
WGP_TLS=0 or WGP_TLS=false would be like not sending --tls
"#, default_udp_host_target, default_udp_host_target, default_socket_timeout);
return;
}
let socket_timeout = args.get(&["-st", "--socket-timeout"], default_socket_timeout);
if tcp_target.is_some() {
client(&tcp_target.unwrap(), socket_timeout, args);
} else {
server(&tcp_host.unwrap(), socket_timeout, args);
}
}
fn client(tcp_target: &str, socket_timeout: u64, args: Args) {
let proxy_client = ProxyClient::new(
args.get_str(1, "127.0.0.1:51821").to_owned(),
args.get_str(2, "127.0.0.1:51820").to_owned(),
args.get_str(3, "127.0.0.1:5555").to_owned(),
args.get(4, 0),
args.get_str(&["-uh", "--udp-host"], "127.0.0.1:51820").to_owned(),
tcp_target.to_owned(),
socket_timeout,
);
let tls = args.flag("--tls");
println!(
"udp_host: {}, udp_target: {}, tcp_target: {}, socket_timeout: {:?}",
"udp_host: {}, tcp_target: {}, socket_timeout: {:?}, tls: {}",
proxy_client.udp_host,
proxy_client.udp_target,
proxy_client.tcp_target,
proxy_client.socket_timeout,
tls,
);
proxy_client.start().expect("error running proxy_client");
if tls {
let hostname = args.get_option(&["--tls-hostname"]).or_else(|| tcp_target.split(":").next().map(&str::to_owned));
let pinnedpubkey = args.get_option(&["--pinnedpubkey"]);
proxy_client.start_tls(hostname.as_ref().map(String::as_str), pinnedpubkey.as_ref().map(String::as_str)).expect("error running tls proxy_client");
} else {
proxy_client.start().expect("error running proxy_client");
}
}
fn server(tcp_host: &str, socket_timeout: u64, args: Args) {
let udp_bind_host_range_str = args.get_str(&["-ur", "--udp-bind-host-range"], "127.0.0.1:30000-40000");
let mut udp_bind_host_range = udp_bind_host_range_str.split(":");
let udp_host = udp_bind_host_range
.next()
.expect("udp_bind_host_range host invalid");
let mut udp_ports = udp_bind_host_range
.next()
.expect("udp_bind_host_range port range invalid")
.split("-");
let udp_low_port = udp_ports
.next()
.expect("udp_bind_host_range low port invalid")
.trim()
.parse::<u16>()
.expect("udp_bind_host_range low port invalid");
let udp_high_port = udp_ports
.next()
.expect("udp_bind_host_range low port invalid")
.trim()
.parse::<u16>()
.expect("udp_bind_host_range low port invalid");
let proxy_server = ProxyServer::new(
tcp_host.to_owned(),
args.get_str(&["-ut", "--udp-target"], "127.0.0.1:51820").to_owned(),
udp_host.to_string(),
udp_low_port,
udp_high_port,
socket_timeout,
);
let tls_key = args.get_option(&["-tk", "--tls-key"]);
let tls_cert = args.get_option(&["-tc", "--tls-cert"]);
println!(
"udp_target: {}, udp_bind_host_range: {}, socket_timeout: {:?}, tls_key: {:?}, tls_cert: {:?}",
proxy_server.client_handler.udp_target,
udp_bind_host_range_str,
proxy_server.client_handler.socket_timeout,
tls_key,
tls_cert,
);
if tls_key.is_some() && tls_cert.is_some() {
proxy_server.start_tls(&tls_key.unwrap(), &tls_cert.unwrap()).expect("error running TLS proxy_server");
} else if tls_key.is_none() && tls_cert.is_none() {
proxy_server.start().expect("error running proxy_server");
} else {
println!("Error: if one of --tls-key or --tls-cert is specified both must be!");
}
}

View File

@ -1,54 +0,0 @@
use std::env;
use wireguard_proxy::{Args, ProxyServer};
fn main() {
let raw_args = env::args().collect();
let args = Args::new(&raw_args);
if args.get_str(1, "").contains("-h") {
println!(
"usage: {} [-h] [tcp_host, 127.0.0.1:5555] [udp_target, 127.0.0.1:51820] [udp_bind_host_range, 127.0.0.1:30000-40000] [socket_timeout, 0]",
args.get_str(0, "wireguard-proxyd")
);
return;
}
let udp_bind_host_range_str = args.get_str(3, "127.0.0.1:30000-40000");
let mut udp_bind_host_range = udp_bind_host_range_str.split(":");
let udp_host = udp_bind_host_range
.next()
.expect("udp_bind_host_range host invalid");
let mut udp_ports = udp_bind_host_range
.next()
.expect("udp_bind_host_range port range invalid")
.split("-");
let udp_low_port = udp_ports
.next()
.expect("udp_bind_host_range low port invalid")
.trim()
.parse::<u16>()
.expect("udp_bind_host_range low port invalid");
let udp_high_port = udp_ports
.next()
.expect("udp_bind_host_range low port invalid")
.trim()
.parse::<u16>()
.expect("udp_bind_host_range low port invalid");
let proxy_server = ProxyServer::new(
args.get_str(1, "127.0.0.1:5555").to_owned(),
args.get_str(2, "127.0.0.1:51820").to_owned(),
udp_host.to_string(),
udp_low_port,
udp_high_port,
args.get(4, 0),
);
println!(
"udp_target: {}, udp_bind_host_range: {}, socket_timeout: {:?}",
proxy_server.client_handler.udp_target,
udp_bind_host_range_str,
proxy_server.client_handler.socket_timeout,
);
proxy_server.start().expect("error running proxy_server");
}

44
src/error.rs Normal file
View File

@ -0,0 +1,44 @@
use core::result;
#[cfg(not(any(feature = "async")))]
pub type IoResult<T> = result::Result<T, std::io::Error>;
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub struct Error(String);
impl Error {
pub fn new(msg: &str) -> Error {
Error(msg.to_owned())
}
pub fn new_owned(msg: String) -> Error {
Error(msg)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
&self.0
}
}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Error::new(&format!("{}", value))
}
}
/*
impl From<std::option::NoneError> for Error {
fn from(value: std::option::NoneError) -> Self {
Error::new(value.description())
}
}
*/

View File

@ -1,10 +1,37 @@
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream, UdpSocket};
use std::net::{TcpStream, UdpSocket};
use std::str::FromStr;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
mod error;
use error::Result;
fn arg_to_env(arg: &str) -> Option<String> {
if !arg.starts_with("--") {
return None;
}
let env = "WGP_".to_owned();
let mut env = env + &arg.trim_matches('-').replace("-", "_");
env.make_ascii_uppercase();
Some(env)
}
fn env_for_arg(arg: &str) -> Option<String> {
arg_to_env(arg).and_then(|key| std::env::var(key).ok())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arg_to_env() {
assert_eq!(arg_to_env("--tcp-host"), Some("WGP_TCP_HOST".to_owned()));
assert_eq!(arg_to_env("--tls"), Some("WGP_TLS".to_owned()));
assert_eq!(arg_to_env("-h"), None);
assert_eq!(arg_to_env("-th"), None);
}
}
pub struct Args<'a> {
args: &'a Vec<String>,
}
@ -13,14 +40,45 @@ impl<'a> Args<'a> {
pub fn new(args: &'a Vec<String>) -> Args {
Args { args }
}
pub fn get_str(&self, index: usize, def: &'a str) -> &'a str {
match self.args.get(index) {
Some(ret) => ret,
None => def,
pub fn flag(&self, flag: &'a str) -> bool {
if self.args.contains(&flag.to_owned()) {
return true;
}
// because env we want slightly special handling of empty/0/false
match env_for_arg(flag) {
Some(env) => &env != "" && &env != "0" && &env != "false",
None => false,
}
}
pub fn get<T: FromStr>(&self, index: usize, def: T) -> T {
match self.args.get(index) {
pub fn get_option(&self, flags: &[&'a str]) -> Option<String> {
for flag in flags.iter() {
let mut found = false;
for arg in self.args.iter() {
if found {
return Some(arg.to_owned());
}
if arg == flag {
found = true;
}
}
}
// no matching arguments are found, so check env variables as a fallback
for flag in flags.iter() {
let env = env_for_arg(flag);
if env.is_some() {
return env;
}
}
return None;
}
pub fn get_str(&self, flags: &[&'a str], def: &'a str) -> String {
match self.get_option(flags) {
Some(ret) => ret,
None => def.to_owned(),
}
}
pub fn get<T: FromStr>(&self, flags: &[&'a str], def: T) -> T {
match self.get_option(flags) {
Some(ret) => match ret.parse::<T>() {
Ok(ret) => ret,
Err(_) => def, // or panic
@ -30,66 +88,45 @@ impl<'a> Args<'a> {
}
}
pub struct TcpUdpPipe {
buf: [u8; 2050], // 2048 + 2 for len
tcp_stream: TcpStream,
udp_socket: UdpSocket,
}
impl TcpUdpPipe {
pub fn new(tcp_stream: TcpStream, udp_socket: UdpSocket) -> TcpUdpPipe {
TcpUdpPipe {
tcp_stream,
udp_socket,
buf: [0u8; 2050],
}
}
pub fn try_clone(&self) -> std::io::Result<TcpUdpPipe> {
Ok(TcpUdpPipe::new(
self.tcp_stream.try_clone()?,
self.udp_socket.try_clone()?,
))
}
pub fn udp_to_tcp(&mut self) -> std::io::Result<usize> {
let len = self.udp_socket.recv(&mut self.buf[2..])?;
println!("udp got len: {}", len);
self.buf[0] = ((len >> 8) & 0xFF) as u8;
self.buf[1] = (len & 0xFF) as u8;
//let test_len = ((self.buf[0] as usize) << 8) + self.buf[1] as usize;
//println!("tcp sending test_len: {}", test_len);
self.tcp_stream.write(&self.buf[..len + 2])
}
pub fn tcp_to_udp(&mut self) -> std::io::Result<usize> {
self.tcp_stream.read_exact(&mut self.buf[..2])?;
let len = ((self.buf[0] as usize) << 8) + self.buf[1] as usize;
println!("tcp expecting len: {}", len);
self.tcp_stream.read_exact(&mut self.buf[..len])?;
println!("tcp got len: {}", len);
self.udp_socket.send(&self.buf[..len])
//let sent = udp_socket.send_to(&buf[..len], &self.udp_target)?;
//assert_eq!(sent, len);
}
}
pub struct ProxyClient {
pub udp_host: String,
pub udp_target: String,
pub tcp_target: String,
pub socket_timeout: Option<Duration>,
}
pub struct ProxyServer {
pub tcp_host: String,
pub client_handler: Arc<ProxyServerClientHandler>,
}
pub struct ProxyServerClientHandler {
pub udp_target: String,
pub udp_host: String,
pub udp_low_port: u16,
pub udp_high_port: u16,
pub socket_timeout: Option<Duration>,
}
#[cfg(any(feature = "async"))]
#[path = ""]
mod net {
mod asyncmod;
}
#[cfg(not(any(feature = "async")))]
#[path = ""]
mod net {
mod syncmod;
}
impl ProxyClient {
pub fn new(udp_host: String, udp_target: String, tcp_target: String, secs: u64) -> ProxyClient {
pub fn new(udp_host: String, tcp_target: String, secs: u64) -> ProxyClient {
ProxyClient {
udp_host,
udp_target,
tcp_target,
socket_timeout: match secs {
0 => None,
@ -98,34 +135,19 @@ impl ProxyClient {
}
}
pub fn start(&self) -> std::io::Result<usize> {
fn tcp_connect(&self) -> Result<TcpStream> {
let tcp_stream = TcpStream::connect(&self.tcp_target)?;
tcp_stream.set_read_timeout(self.socket_timeout)?;
Ok(tcp_stream)
}
fn udp_connect(&self) -> Result<UdpSocket> {
let udp_socket = UdpSocket::bind(&self.udp_host)?;
udp_socket.set_read_timeout(self.socket_timeout)?;
//udp_socket.connect(&self.udp_target)?; // this isn't strictly needed... just filters who we can receive from
let mut udp_pipe = TcpUdpPipe::new(tcp_stream, udp_socket);
let mut udp_pipe_clone = udp_pipe.try_clone()?;
thread::spawn(move || loop {
udp_pipe_clone
.udp_to_tcp()
.expect("cannot write to tcp_clone");
});
loop {
udp_pipe.tcp_to_udp()?;
}
Ok(udp_socket)
}
}
pub struct ProxyServer {
pub tcp_host: String,
pub client_handler: Arc<ProxyServerClientHandler>,
}
impl ProxyServer {
pub fn new(
tcp_host: String,
@ -150,42 +172,10 @@ impl ProxyServer {
client_handler,
}
}
pub fn start(&self) -> std::io::Result<()> {
let listener = TcpListener::bind(&self.tcp_host)?;
println!("Listening for connections on {}", &self.tcp_host);
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let client_handler = self.client_handler.clone();
thread::spawn(move || {
client_handler
.handle_client(stream)
.expect("error handling connection")
});
}
Err(e) => {
println!("Unable to connect: {}", e);
}
}
}
Ok(())
}
}
pub struct ProxyServerClientHandler {
pub udp_target: String,
pub udp_host: String,
pub udp_low_port: u16,
pub udp_high_port: u16,
pub socket_timeout: Option<Duration>,
}
impl ProxyServerClientHandler {
pub fn handle_client(&self, tcp_stream: TcpStream) -> std::io::Result<usize> {
tcp_stream.set_read_timeout(self.socket_timeout)?;
fn udp_bind(&self) -> Result<UdpSocket> {
let mut port = self.udp_low_port;
let udp_socket = loop {
match UdpSocket::bind((&self.udp_host[..], port)) {
@ -200,17 +190,6 @@ impl ProxyServerClientHandler {
};
udp_socket.set_read_timeout(self.socket_timeout)?;
udp_socket.connect(&self.udp_target)?;
let mut udp_pipe = TcpUdpPipe::new(tcp_stream, udp_socket);
let mut udp_pipe_clone = udp_pipe.try_clone()?;
thread::spawn(move || loop {
udp_pipe_clone
.udp_to_tcp()
.expect("cannot write to tcp_clone");
});
loop {
udp_pipe.tcp_to_udp()?;
}
Ok(udp_socket)
}
}

49
src/notls.rs Normal file
View File

@ -0,0 +1,49 @@
use std::net::TcpStream;
use super::super::TryClone;
use std::io::{Read, Write};
use crate::error::*;
fn err() -> Error {
Error::new("Error: compiled without TLS support")
}
pub struct TlsStream;
impl TlsStream {
pub fn client(_hostname: Option<&str>, _pinnedpubkey: Option<&str>, _tcp_stream: TcpStream) -> Result<TlsStream> {
Err(err())
}
}
impl TryClone<TlsStream> for TlsStream {
fn try_clone(&self) -> Result<TlsStream> {
Err(err())
}
}
impl Read for TlsStream {
fn read(&mut self, _buf: &mut [u8]) -> IoResult<usize> {
unimplemented!()
}
}
impl Write for TlsStream {
fn write(&mut self, _buf: &[u8]) -> IoResult<usize> {
unimplemented!()
}
fn flush(&mut self) -> IoResult<()> {
unimplemented!()
}
}
pub struct TlsListener;
impl TlsListener {
pub fn new(_tls_key: &str, _tls_cert: &str) -> Result<TlsListener> {
Err(err())
}
pub fn wrap(&self, _tcp_stream: TcpStream) -> Result<TlsStream> {
Err(err())
}
}

154
src/openssl.rs Normal file
View File

@ -0,0 +1,154 @@
use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode, SslAcceptor, SslFiletype, HandshakeError};
use std::sync::Arc;
use std::cell::UnsafeCell;
use std::net::TcpStream;
use std::io::{Read, Write};
use super::super::TryClone;
use crate::error::*;
impl TryClone<TlsStream> for TlsStream {
fn try_clone(&self) -> Result<TlsStream> {
Ok(self.clone())
}
}
pub struct TlsStream {
sess: Arc<UnsafeCell<SslStream<TcpStream>>>,
}
impl TlsStream {
fn new(stream: SslStream<TcpStream>) -> TlsStream {
TlsStream {
sess: Arc::new(UnsafeCell::new(stream))
}
}
pub fn client(hostname: Option<&str>, pinnedpubkey: Option<&str>, tcp_stream: TcpStream) -> Result<TlsStream> {
let mut connector = SslConnector::builder(SslMethod::tls())?.build().configure()?;
connector.set_use_server_name_indication(hostname.is_some());
connector.set_verify_hostname(false);
connector.set_verify(SslVerifyMode::NONE);
if pinnedpubkey.is_some() {
let pinnedpubkey = pinnedpubkey.unwrap().to_owned();
connector.set_verify_callback(SslVerifyMode::PEER, move|_preverify_ok, x509_store_ctx| {
//println!("preverify_ok: {}", preverify_ok);
let cert = x509_store_ctx.current_cert().expect("could not get TLS cert");
let pubkey = cert.public_key().expect("could not get public key from TLS cert");
let pubkey = pubkey.public_key_to_der().expect("could not get TLS public key bytes");
//println!("spki.len(): {}", pubkey.len());
//println!("spki: {:?}", pubkey);
let mut sha256 = openssl::sha::Sha256::new();
sha256.update(&pubkey);
let pubkey = sha256.finish();
let pubkey = ["sha256//", &openssl::base64::encode_block(&pubkey)].join("");
println!("pubkey from cert: {}", pubkey);
for key in pinnedpubkey.split(";") {
if key == pubkey {
println!("SUCCESS: pubkey match found!",);
return true;
}
}
println!("ERROR: pubkey match not found!");
false
});
}
let tcp_stream = connector.connect(hostname.unwrap_or(""), tcp_stream)?;
Ok(TlsStream::new(tcp_stream))
}
}
unsafe impl Sync for TlsStream {}
unsafe impl Send for TlsStream {}
impl Clone for TlsStream {
fn clone(&self) -> Self {
TlsStream {
sess: self.sess.clone(),
}
}
}
impl TlsStream {
pub fn borrow_mut(&self) -> &mut SslStream<TcpStream> {
unsafe {
&mut *self.sess.get()
}
}
}
impl Read for TlsStream {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
self.borrow_mut().read(buf)
}
}
impl Write for TlsStream {
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
self.borrow_mut().write(buf)
}
fn flush(&mut self) -> IoResult<()> {
self.borrow_mut().flush()
}
}
pub struct TlsListener {
acceptor: SslAcceptor,
}
impl TlsListener {
pub fn new(tls_key: &str, tls_cert: &str) -> Result<TlsListener> {
let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
if tls_key == "-" || tls_cert == "-" {
let mut key_and_or_cert = Vec::new();
println!("fully reading stdin...");
std::io::stdin().read_to_end(&mut key_and_or_cert)?;
println!("finished reading stdin");
if tls_key == "-" {
let tls_key = openssl::pkey::PKey::private_key_from_pem(&key_and_or_cert)?;
acceptor.set_private_key(&tls_key)?;
} else {
acceptor.set_private_key_file(tls_key, SslFiletype::PEM)?;
}
if tls_cert == "-" {
// todo: read whole chain here or???
let tls_cert = openssl::x509::X509::from_pem(&key_and_or_cert)?;
acceptor.set_certificate(&tls_cert)?;
} else {
acceptor.set_certificate_chain_file(tls_cert)?;
}
} else {
// set from files
acceptor.set_private_key_file(tls_key, SslFiletype::PEM)?;
acceptor.set_certificate_chain_file(tls_cert)?;
}
acceptor.check_private_key()?;
let acceptor = acceptor.build();
Ok(TlsListener {
acceptor
})
}
pub fn wrap(&self, tcp_stream: TcpStream) -> Result<TlsStream> {
Ok(TlsStream::new(self.acceptor.accept(tcp_stream)?))
}
}
impl From<openssl::error::ErrorStack> for Error {
fn from(value: openssl::error::ErrorStack) -> Self {
Error::new_owned(format!("{}", value))
}
}
impl From<HandshakeError<std::net::TcpStream>> for Error {
fn from(value: HandshakeError<std::net::TcpStream>) -> Self {
Error::new(&format!("{}", value))
}
}

208
src/syncmod.rs Normal file
View File

@ -0,0 +1,208 @@
use std::thread;
use crate::error::Result;
use crate::*;
use std::net::TcpListener;
use std::io::{Write, Read};
#[cfg(any(feature = "tls", feature = "openssl_vendored"))]
#[path = ""]
mod tls {
pub mod openssl;
pub use super::tls::openssl::{TlsStream, TlsListener};
}
#[cfg(not(any(feature = "tls", feature = "openssl_vendored")))]
#[path = ""]
mod tls {
pub mod notls;
pub use super::tls::notls::{TlsStream, TlsListener};
}
use tls::{TlsStream, TlsListener};
pub struct TcpUdpPipe<T: Write + Read + TryClone<T> + Send + 'static> {
buf: [u8; 2050], // 2048 + 2 for len
tcp_stream: T,
udp_socket: UdpSocket,
}
impl<T: Write + Read + TryClone<T> + Send + 'static> TcpUdpPipe<T> {
pub fn new(tcp_stream: T, udp_socket: UdpSocket) -> TcpUdpPipe<T> {
TcpUdpPipe {
tcp_stream,
udp_socket,
buf: [0u8; 2050],
}
}
pub fn try_clone(&self) -> Result<TcpUdpPipe<T>> {
Ok(TcpUdpPipe::new(
self.tcp_stream.try_clone()?,
self.udp_socket.try_clone()?,
))
}
pub fn shuffle_after_first_udp(&mut self) -> Result<usize> {
let (len, src_addr) = self.udp_socket.recv_from(&mut self.buf[2..])?;
println!("first packet from {}, connecting to that", src_addr);
self.udp_socket.connect(src_addr)?;
self.send_udp(len)?;
self.shuffle()
}
pub fn udp_to_tcp(&mut self) -> Result<()> {
let len = self.udp_socket.recv(&mut self.buf[2..])?;
self.send_udp(len)
}
fn send_udp(&mut self, len: usize) -> Result<()> {
#[cfg(feature = "verbose")]
println!("udp got len: {}", len);
self.buf[0] = ((len >> 8) & 0xFF) as u8;
self.buf[1] = (len & 0xFF) as u8;
Ok(self.tcp_stream.write_all(&self.buf[..len + 2])?)
// todo: do this? self.tcp_stream.flush()
}
pub fn tcp_to_udp(&mut self) -> Result<usize> {
self.tcp_stream.read_exact(&mut self.buf[..2])?;
let len = ((self.buf[0] as usize) << 8) + self.buf[1] as usize;
#[cfg(feature = "verbose")]
println!("tcp expecting len: {}", len);
self.tcp_stream.read_exact(&mut self.buf[..len])?;
#[cfg(feature = "verbose")]
println!("tcp got len: {}", len);
Ok(self.udp_socket.send(&self.buf[..len])?)
//let sent = udp_socket.send_to(&buf[..len], &self.udp_target)?;
//assert_eq!(sent, len);
}
pub fn shuffle(&mut self) -> Result<usize> {
let mut udp_pipe_clone = self.try_clone()?;
thread::spawn(move || loop {
udp_pipe_clone
.udp_to_tcp()
.expect("cannot write to tcp_clone");
});
loop {
self.tcp_to_udp()?;
}
}
}
pub trait TryClone<T> {
fn try_clone(&self) -> Result<T>;
}
impl TryClone<UdpSocket> for UdpSocket {
fn try_clone(&self) -> Result<UdpSocket> {
Ok(self.try_clone()?)
}
}
impl TryClone<TcpStream> for TcpStream {
fn try_clone(&self) -> Result<TcpStream> {
Ok(self.try_clone()?)
}
}
impl ProxyClient {
pub fn start(&self) -> Result<usize> {
let tcp_stream = self.tcp_connect()?;
let udp_socket = self.udp_connect()?;
// we want to wait for first udp packet from client first, to set the target to respond to
TcpUdpPipe::new(tcp_stream, udp_socket).shuffle_after_first_udp()
}
pub fn start_tls(&self, hostname: Option<&str>, pinnedpubkey: Option<&str>) -> Result<usize> {
let tcp_stream = self.tcp_connect()?;
let tcp_stream = TlsStream::client(hostname, pinnedpubkey, tcp_stream)?;
let udp_socket = self.udp_connect()?;
// we want to wait for first udp packet from client first, to set the target to respond to
TcpUdpPipe::new(tcp_stream, udp_socket).shuffle_after_first_udp()
}
}
impl ProxyServer {
pub fn start(&self) -> Result<()> {
let listener = TcpListener::bind(&self.tcp_host)?;
println!("Listening for connections on {}", &self.tcp_host);
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let client_handler = self.client_handler.clone();
client_handler.set_tcp_options(&stream).expect("cannot set tcp options");
thread::spawn(move || {
client_handler
.handle_client(stream)
.expect("error handling connection")
});
}
Err(e) => {
println!("Unable to connect: {}", e);
}
}
}
Ok(())
}
pub fn start_tls(&self, tls_key: &str, tls_cert: &str) -> Result<()> {
let tls_listener = Arc::new(TlsListener::new(tls_key, tls_cert)?);
let listener = TcpListener::bind(&self.tcp_host)?;
println!("Listening for TLS connections on {}", &self.tcp_host);
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let client_handler = self.client_handler.clone();
client_handler.set_tcp_options(&stream).expect("cannot set tcp options");
let tls_listener = tls_listener.clone();
thread::spawn(move || {
let stream = tls_listener.wrap(stream).expect("cannot wrap with tls");
client_handler
.handle_client_tls(stream)
.expect("error handling connection")
});
}
Err(e) => {
println!("Unable to connect: {}", e);
}
}
}
Ok(())
}
}
impl ProxyServerClientHandler {
pub fn set_tcp_options(&self, tcp_stream: &TcpStream) -> Result<()> {
Ok(tcp_stream.set_read_timeout(self.socket_timeout)?)
}
pub fn handle_client(&self, tcp_stream: TcpStream) -> Result<usize> {
TcpUdpPipe::new(tcp_stream, self.udp_bind()?).shuffle()
}
pub fn handle_client_tls(&self, tcp_stream: TlsStream) -> Result<usize> {
TcpUdpPipe::new(tcp_stream, self.udp_bind()?).shuffle()
}
}

11
systemd/client.conf Normal file
View File

@ -0,0 +1,11 @@
# refer to wireguard-proxy --help for info on what these are
WGP_TCP_TARGET=192.168.1.1:5555
#WGP_TCP_TARGET=example.org:5555
WGP_UDP_HOST=127.0.0.1:51820
#WGP_TLS=true
#WGP_PINNEDPUBKEY=sha256//YhKJKSzoTt2b5FP18fvpHo7fJYqQCjAa3HWY3tvRMwE=;sha256//t62CeU2tQiqkexU74Gxa2eg7fRbEgoChTociMee9wno=
#WGP_TLS_HOSTNAME=example.org
#WGP_SOCKET_TIMEOUT=0

25
systemd/server.conf Normal file
View File

@ -0,0 +1,25 @@
# refer to wireguard-proxy --help for info on what these are
# this binds ipv4 and ipv6 if dual stacked
WGP_TCP_HOST=[::]:5555
# this binds all ipv4 addresses
#WGP_TCP_HOST=0.0.0.0:5555
WGP_UDP_TARGET=127.0.0.1:51820
# on the server you'll want to set this to slightly more than PersistentKeepalive= in your client's wireguard config
# this allows the udp sockets to be closed in a reasonable amount of time after the TCP connections are terminated
# if PersistentKeepalive=25 as is recommended just set this to 60
WGP_SOCKET_TIMEOUT=60
WGP_UDP_BIND_HOST_RANGE=127.0.0.1:30000-40000
# if you don't want proper cert generate with:
# openssl req -new -x509 -sha256 -days 3650 -nodes -subj "/C=US/CN=example.org" -newkey rsa:2048 -out cert.pem -keyout key.pem
# if systemd template has SupplementaryGroups=systemd-network set permissions on key properly:
# chown root.systemd-network key.pem && chmod 640 key.pem
# optionally (but recommended) extract pinnedpubkey hash from the above generated cert like so:
# openssl x509 -in cert.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
#WGP_TLS_KEY=/etc/wireguard-proxy/key.pem
#WGP_TLS_CERT=/etc/wireguard-proxy/cert.pem

View File

@ -0,0 +1,59 @@
[Unit]
Description=wireguard-proxy for %I
After=network-online.target nss-lookup.target
Wants=network-online.target nss-lookup.target
StartLimitIntervalSec=0
Documentation=https://code.moparisthebest.com/moparisthebest/wireguard-proxy
Documentation=https://github.com/moparisthebest/wireguard-proxy
[Service]
EnvironmentFile=/etc/wireguard-proxy/%i.conf
ExecStart=/usr/bin/wireguard-proxy
Restart=always
RestartSec=1s
# anything under here isn't strictly needed, but probably good
# to lock this down with the minimal permissions necessary
# which are not many at all
User=wireguard-proxy
DynamicUser=yes
ConfigurationDirectory=wireguard-proxy
ConfigurationDirectoryMode=0750
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ProtectHostname=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=net
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictRealtime=true
RestrictSUIDSGID=true
RemoveIPC=true
SystemCallArchitectures=native
# these are just needed to bind to low ports
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
# this is any existing group the key/cert can be owned by
# so that our random user can read them
# not needed at all without TLS, can comment out
# see https://github.com/systemd/systemd/issues/9535
SupplementaryGroups=systemd-network
# this would be ideal because you wouldn't need file permissions
# on the key so wireguard-proxy could read it, only systemd
# but only the first of these works which doesn't make this
# feasible for a template...
#StandardInput=file:/full/hardcoded/path/to/key_and_cert.pem
#StandardInput=${WGP_STDIN}
#StandardInput=file:${WGP_STDIN}
#StandardInput=file:/etc/wireguard-proxy/%i.pem
#StandardOutput=journal
[Install]
WantedBy=multi-user.target

71
test.sh
View File

@ -1,22 +1,81 @@
#!/bin/sh
set -x
# cert created with:
# cd ci && echo -e '\n\n\n\n\n\n\n' | openssl req -new -x509 -days 3650 -nodes -out cert.pem -keyout cert.key
export PATH="$(pwd)/target/release:$PATH"
run_tests() {
client_arg="$1"
shift
# first make sure udp-test succeeds running against itself
cargo run --release --bin udp-test || exit 1
udp-test || exit 1
# now run udp-test without spawning other processes
udp-test -is "$@" || exit 1
# now run proxyd pointing to udp-test
cargo run --release --bin wireguard-proxyd -- 127.0.0.1:5555 127.0.0.1:51822 &
wireguard-proxy -th 127.0.0.1:5555 -ut 127.0.0.1:51822 "$@" &
proxyd_pid=$!
# wait for ports to be set up, this is fragile...
sleep 1
sleep 5
# proxy pointing to proxyd
cargo run --release --bin wireguard-proxy &
wireguard-proxy -tt 127.0.0.1:5555 "$client_arg" &
proxy_pid=$!
# wait for ports to be set up, this is fragile...
sleep 1
# and udp-test pointing to proxy, which then hops to proxyd, and finally back to udp-test
cargo run --release --bin udp-test -- 127.0.0.1:51822
udp-test -uh 127.0.0.1:51822
udp_exit=$?
kill $proxyd_pid $proxy_pid
exit $udp_exit
[ $udp_exit -ne 0 ] && exit $udp_exit
# now run udp-test essentially just like the script above, but all in rust
udp-test -s "$@" || exit 1
}
# first run without TLS
cargo clean
cargo build --release --no-default-features || exit 1
run_tests || exit 1
# first run with non-vendored tls
cargo clean
cargo build --release --no-default-features --features tls || exit 1
# first plaintext tests
run_tests || exit 1
# then TLS tests
run_tests --tls --tls-key ci/cert.key --tls-cert ci/cert.pem || exit 1
# second run with vendored tls
cargo clean
cargo build --release --no-default-features --features openssl_vendored || exit 1
# first plaintext tests
run_tests || exit 1
# then TLS tests
run_tests --tls --tls-key ci/cert.key --tls-cert ci/cert.pem || exit 1
# third run with async+rustls
cargo clean
cargo build --release --no-default-features --features async || exit 1
# first plaintext tests
run_tests || exit 1
# then TLS tests
run_tests --tls --tls-key ci/cert.key --tls-cert ci/cert.pem || exit 1
# now pubkey tests
# these should fail
udp-test -s --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4de= && exit 1
udp-test -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4de= && exit 1
# these should pass
udp-test -s --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4dE= || exit 1
udp-test -is --tls-key ci/cert.key --tls-cert ci/cert.pem --pinnedpubkey sha256//BEyQeSjwwUBLXXNuCILHRWyV1gLmY31CdMHNA4VH4dE= || exit 1
exit 0