Compare commits

...

39 commits

Author SHA1 Message Date
Vasyl Gello
8ad1962f64 Implement error checking on listeners
Some checks failed
Trunk / release build / Build Windows/Linux-static/MacOS/*BSD-static/Android (push) Has been cancelled
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2025-02-08 13:26:54 +00:00
Neil Alexander
83bc9a2eec
Update to Yggdrasil 0.5.12 2024-12-18 22:39:27 +00:00
Neil Alexander
696625c14f
Update to Yggdrasil 0.5.10 2024-11-24 14:33:28 +00:00
Vasyl Gello
763e4db047 Bump yggdrasil to 0.5.9
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-10-19 19:39:53 +00:00
Vasyl Gello
c65f79d01a Bump yggdrasil-go and gvisor
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-13 09:27:06 +00:00
Vasyl Gello
58b3efd2da Trigger CI builds on pull requests
... but upload only tags or pyshes to develop branch

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-06 13:12:14 +00:00
Vasyl Gello
bfc106637a Fix UDP port forward not working with Yggdrasil client
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-06 13:12:14 +00:00
Vasyl Gello
08f51fded2 Canonicalize DNS example in readme
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-06 06:44:44 +03:00
Vasyl Gello
7a72fe25ab Edit README.md
* Go needed version is 1.22 now thx to gvisor
  * Add external DNS nameserver usage example

[skip CI] skip CI

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-06 06:38:45 +03:00
Vasyl Gello
873b19b7c6 Fix release upload
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-05 22:47:56 +03:00
Vasyl Gello
98b1a1c79d Add caching Go setup step
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-05 22:19:11 +03:00
Vasyl Gello
b132d6af16 Make CI run on branch or tag push
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-05 22:15:59 +03:00
Vasyl Gello
4844607750 Bump dependencies
* ygdrasil-go 0.5.7

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-05 22:13:02 +03:00
Vasyl Gello
9395ddbfa2 Expand trunk builds for tags
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-08-05 21:57:38 +03:00
Vasyl Gello
5a87e43f9a Try building only static executables for Linux/*BSD
It appears Go toolchain silently uses netgo+osusergo+static
if cross-compiling but provides dynamicaly-linked executable
for native arch. Keeping track of all Linux and *BSD sysroots
is impractical so lets ship static executables where possible.
They may bot honor system DNS resolver but at least they work
everywhere.

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-26 11:54:55 +03:00
Vasyl Gello
fef083ae4a Declare *bsd builds are static and drop CGO_ENABLED for ststic build
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-26 11:35:36 +03:00
Vasyl Gello
95a41a3e8f Use golang:1.21-buster for glibc stuff
From Matrix Yggdrasil developer room:

The only problem is on Ubuntu 20.04:
yggstack-linux-amd64: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./yggstack-linux-amd64)
yggstack-linux-amd64: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./yggstack-linux-amd64)

We do not need latest Ubuntu as we dont use external linking in static binaries.

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-26 07:14:29 +03:00
Vasyl Gello
3be03c793f Try publishing static binaries as distroless containers
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-25 11:22:28 +03:00
Vasyl Gello
b534d1205e Try building static linux binaries without external linking 2024-07-25 07:12:59 +03:00
Vasyl Gello
1eb9a94d2c Try to use netgo,osusergo,static for static builds
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-25 07:02:14 +03:00
Vasyl Gello
9da884697a
Fix gvisor changes
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-24 18:37:35 +03:00
Vasyl Gello
f0eecd21d4
Bump yggdrasil-go and gvisor
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-24 18:16:56 +03:00
Vasyl Gello
8a6c67475c
Switch back to glibc for static builds
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-24 17:55:45 +03:00
Vasyl Gello
2c09a1d7b2
Fix .pk.ygg resolver after refactoring
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-23 22:44:14 +03:00
Vasyl Gello
5008a4ea2d Do not leak sessions
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-22 07:23:16 +03:00
Vasyl Gello
457b1d99c5 Build Linux static executables with musl-cross
(except ppc64, ppc64le)

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-19 14:49:16 +03:00
Vasyl Gello
f529064aa0 Refactor UDP port forwarding and document usage 2024-07-19 12:07:56 +03:00
Vasyl Gello
0783b429fd [WIP] Introduce TCP/UDP local/remote port forwarding
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-19 05:50:53 +03:00
Vasyl Gello
30d51ba566 Try to fix #4
... by catching TCP RST packets in WritePackets and sending them
during the next WritePackets call where no RST packet is being sent

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-19 05:50:52 +03:00
Vasyl Gello
b160b3f66d
Add UDP port exposure
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-18 18:01:11 +03:00
Vasyl Gello
582fe511fa Fix unix domain socket creation/cleanup logic
* If there is another instance still listening on the same
    Unix domain socket, bail out

  * If there is a leftover from crashed yggstack etc,
    clean the socket file and proceed

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-07-16 07:32:07 +03:00
Artem Andreenko
dcc0284e3b Implemented UNIX socket support for SOCKS5 server in yggstack command and updated README.md with usage instructions. 2024-07-15 10:19:15 +00:00
Neil Alexander
174bb7026d
Update to Yggdrasil v0.5.6 2024-06-01 16:15:03 +01:00
Vasyl Gello
edbaa72445 Do notvtry to open admin socket in autoconf too
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-03-24 19:44:52 +02:00
Vasyl Gello
7b5300a476 Try to fix gh upload 2024-01-28 14:10:18 +02:00
Neil Alexander
5520ef03bf
Update to Yggdrasil v0.5.5 2024-01-28 11:11:02 +00:00
Vasyl Gello
f465d71770 [skip CI] Add information about downloads
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-01-14 06:55:07 +02:00
Vasyl Gello
8f21eaa31a [skip ci] Overwrite trunk build artifacts in release
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-01-07 17:49:30 +02:00
Vasyl Gello
3fa4809cd1 [Skip CI] Fix badge
Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-01-07 17:37:54 +02:00
12 changed files with 1180 additions and 308 deletions

View file

@ -1,8 +1,17 @@
name: Trunk build
name: Trunk / release build
on:
pull_request:
paths-ignore:
- "README.md"
push:
branch: develop
branches:
- develop
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+-[0-9]+'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -10,69 +19,246 @@ concurrency:
permissions:
contents: write
packages: write
jobs:
build:
strategy:
fail-fast: false
name: Build Windows/Linux/MacOS/FreeBSD/Android
runs-on: ubuntu-20.04
name: Build Windows/Linux-static/MacOS/*BSD-static/Android
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v3
- uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: '1.22'
- name: Build static executables
- name: Check if commit needs a release upload
run: |
GOOS=windows GOARCH=arm GOARM=7 ./build -o yggstack-windows-armv7.exe
GOOS=windows GOARCH=arm64 ./build -o yggstack-windows-arm64.exe
GOOS=windows GOARCH=386 ./build -o yggstack-windows-i386.exe
if [ "${{ github.ref_type }}" = "tag" ]; then
echo "RELEASENAME=${{ github.ref_name }}" >> "$GITHUB_ENV"
elif [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/develop" ]; then
echo "PRERELEASE=--prerelease" >> "$GITHUB_ENV"
echo "RELEASENAME=trunk" >> "$GITHUB_ENV"
else
echo "Not a tag or push to develop branch, skipping upload"
echo "SKIP_UPLOAD=1" >> "$GITHUB_ENV"
fi
- name: Build yggstack executables
run: |
set -e
echo "::group::yggstack-windows-amd64.exe"
GOOS=windows GOARCH=amd64 ./build -o yggstack-windows-amd64.exe
GOOS=linux GOARCH=386 ./build -o yggstack-linux-i386
GOOS=linux GOARCH=amd64 ./build -o yggstack-linux-amd64
GOOS=linux GOARCH=arm GOARM=6 ./build -o yggstack-linux-armv6
GOOS=linux GOARCH=arm GOARM=7 ./build -o yggstack-linux-armv7
GOOS=linux GOARCH=arm64 ./build -o yggstack-linux-arm64
GOOS=linux GOARCH=mips GOMIPS=softfloat ./build -o yggstack-linux-mips-sf
GOOS=linux GOARCH=mipsle GOMIPS=softfloat ./build -o yggstack-linux-mipsle-sf
GOOS=linux GOARCH=mips64 ./build -o yggstack-linux-mips64
GOOS=linux GOARCH=mips64le ./build -o yggstack-linux-mips64le
GOOS=linux GOARCH=ppc64 ./build -o yggstack-linux-ppc64
GOOS=linux GOARCH=ppc64le ./build -o yggstack-linux-ppc64le
GOOS=linux GOARCH=riscv64 ./build -o yggstack-linux-riscv64
GOOS=linux GOARCH=s390x ./build -o yggstack-linux-s390x
GOOS=darwin GOARCH=arm64 ./build -o yggstack-darwin-arm64
echo "::endgroup::"
#
echo "::group::yggstack-windows-arm64.exe"
GOOS=windows GOARCH=arm64 ./build -o yggstack-windows-arm64.exe
echo "::endgroup::"
#
echo "::group::yggstack-windows-armv7.exe"
GOOS=windows GOARCH=arm GOARM=7 ./build -o yggstack-windows-armv7.exe
echo "::endgroup::"
#
echo "::group::yggstack-windows-i386.exe"
GOOS=windows GOARCH=386 ./build -o yggstack-windows-i386.exe
GOOS=windows GOARCH=386 ./build -o yggstack-windows-386.exe
echo "::endgroup::"
#
# NOTE: Go toolchain does produce dynamically linked binaries
# for native-architecture Linux/*BSD. Since we are building on amd64,
# request this buid to be fully static like the othe builds
echo "::group::yggstack-linux-amd64-static"
GOOS=linux GOARCH=amd64 ./build -s -o yggstack-linux-amd64-static
! ldd yggstack-linux-amd64-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-arm64-static"
GOOS=linux GOARCH=arm64 ./build -o yggstack-linux-arm64-static
! ldd yggstack-linux-arm64-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-armv6-static"
GOOS=linux GOARCH=arm GOARM=6 ./build -o yggstack-linux-armv6-static
! ldd yggstack-linux-armv6-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-armv7-static"
GOOS=linux GOARCH=arm GOARM=7 ./build -o yggstack-linux-armv7-static
! ldd yggstack-linux-armv7-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-i386-static"
GOOS=linux GOARCH=386 ./build -o yggstack-linux-i386-static
GOOS=linux GOARCH=386 ./build -o yggstack-linux-386-static
! ldd yggstack-linux-i386-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-mips-sf-static"
GOOS=linux GOARCH=mips GOMIPS=softfloat ./build -o yggstack-linux-mips-sf-static
! ldd yggstack-linux-mips-sf-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-mipsle-sf-static"
GOOS=linux GOARCH=mipsle GOMIPS=softfloat ./build -o yggstack-linux-mipsle-sf-static
! ldd yggstack-linux-mipsle-sf-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-mips64-static"
GOOS=linux GOARCH=mips64 ./build -o yggstack-linux-mips64-static
! ldd yggstack-linux-mips64-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-mips64le-static"
GOOS=linux GOARCH=mips64le ./build -o yggstack-linux-mips64le-static
! ldd yggstack-linux-mips64le-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-ppc64-static"
GOOS=linux GOARCH=ppc64 ./build -o yggstack-linux-ppc64-static
! ldd yggstack-linux-ppc64-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-ppc64le-static"
GOOS=linux GOARCH=ppc64le ./build -o yggstack-linux-ppc64le-static
! ldd yggstack-linux-ppc64le-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-riscv64-static"
GOOS=linux GOARCH=riscv64 ./build -o yggstack-linux-riscv64-static
! ldd yggstack-linux-riscv64-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-s390x-static"
GOOS=linux GOARCH=s390x ./build -o yggstack-linux-s390x-static
! ldd yggstack-linux-s390x-static
echo "::endgroup::"
#
echo "::group::yggstack-darwin-amd64"
GOOS=darwin GOARCH=amd64 ./build -o yggstack-darwin-amd64
GOOS=freebsd GOARCH=arm64 ./build -o yggstack-freebsd-arm64
GOOS=freebsd GOARCH=amd64 ./build -o yggstack-freebsd-amd64
GOOS=freebsd GOARCH=arm GOARM=6 ./build -o yggstack-freebsd-armv6
GOOS=freebsd GOARCH=arm GOARM=7 ./build -o yggstack-freebsd-armv7
GOOS=freebsd GOARCH=386 ./build -o yggstack-freebsd-i386
GOOS=openbsd GOARCH=arm64 ./build -o yggstack-openbsd-arm64
GOOS=openbsd GOARCH=amd64 ./build -o yggstack-openbsd-amd64
GOOS=openbsd GOARCH=arm GOARM=6 ./build -o yggstack-openbsd-armv6
GOOS=openbsd GOARCH=arm GOARM=7 ./build -o yggstack-openbsd-armv7
GOOS=openbsd GOARCH=386 ./build -o yggstack-openbsd-i386
GOOS=netbsd GOARCH=arm64 ./build -o yggstack-netbsd-arm64
GOOS=netbsd GOARCH=amd64 ./build -o yggstack-netbsd-amd64
GOOS=netbsd GOARCH=arm GOARM=6 ./build -o yggstack-netbsd-armv6
GOOS=netbsd GOARCH=arm GOARM=7 ./build -o yggstack-netbsd-armv7
GOOS=netbsd GOARCH=386 ./build -o yggstack-netbsd-i386
CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang" GOOS=android GOARCH=arm64 ./build -o yggstack-android-arm64
CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android21-clang" GOOS=android GOARCH=amd64 ./build -o yggstack-android-amd64
CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang" GOOS=android GOARCH=arm GOARM=7 ./build -o yggstack-android-armv7
CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android21-clang" GOOS=android GOARCH=386 ./build -o yggstack-android-i386
echo "::endgroup::"
#
echo "::group::yggstack-darwin-arm64"
GOOS=darwin GOARCH=arm64 ./build -o yggstack-darwin-arm64
echo "::endgroup::"
#
echo "::group::yggstack-freebsd-amd64-static"
GOOS=freebsd GOARCH=amd64 ./build -o yggstack-freebsd-amd64-static
echo "::endgroup::"
#
echo "::group::yggstack-freebsd-arm64-static"
GOOS=freebsd GOARCH=arm64 ./build -o yggstack-freebsd-arm64-static
echo "::endgroup::"
#
echo "::group::yggstack-freebsd-armv6-static"
GOOS=freebsd GOARCH=arm GOARM=6 ./build -o yggstack-freebsd-armv6-static
echo "::endgroup::"
#
echo "::group::yggstack-freebsd-armv7-static"
GOOS=freebsd GOARCH=arm GOARM=7 ./build -o yggstack-freebsd-armv7-static
echo "::endgroup::"
#
echo "::group::yggstack-freebsd-i386-static"
GOOS=freebsd GOARCH=386 ./build -o yggstack-freebsd-i386-static
GOOS=freebsd GOARCH=386 ./build -o yggstack-freebsd-386-static
echo "::endgroup::"
#
echo "::group::yggstack-openbsd-amd64-static"
GOOS=openbsd GOARCH=amd64 ./build -o yggstack-openbsd-amd64-static
echo "::endgroup::"
#
echo "::group::yggstack-openbsd-arm64-static"
GOOS=openbsd GOARCH=arm64 ./build -o yggstack-openbsd-arm64-static
echo "::endgroup::"
#
echo "::group::yggstack-openbsd-armv6-static"
GOOS=openbsd GOARCH=arm GOARM=6 ./build -o yggstack-openbsd-armv6-static
echo "::endgroup::"
#
echo "::group::yggstack-openbsd-armv7-static"
GOOS=openbsd GOARCH=arm GOARM=7 ./build -o yggstack-openbsd-armv7-static
echo "::endgroup::"
#
echo "::group::yggstack-openbsd-i386-static"
GOOS=openbsd GOARCH=386 ./build -o yggstack-openbsd-i386-static
GOOS=openbsd GOARCH=386 ./build -o yggstack-openbsd-386-static
echo "::endgroup::"
#
echo "::group::yggstack-netbsd-amd64-static"
GOOS=netbsd GOARCH=amd64 ./build -o yggstack-netbsd-amd64-static
echo "::endgroup::"
#
echo "::group::yggstack-netbsd-arm64-static"
GOOS=netbsd GOARCH=arm64 ./build -o yggstack-netbsd-arm64-static
echo "::endgroup::"
#
echo "::group::yggstack-netbsd-armv6-static"
GOOS=netbsd GOARCH=arm GOARM=6 ./build -o yggstack-netbsd-armv6-static
echo "::endgroup::"
#
echo "::group::yggstack-netbsd-armv7-static"
GOOS=netbsd GOARCH=arm GOARM=7 ./build -o yggstack-netbsd-armv7-static
echo "::endgroup::"
#
echo "::group::yggstack-netbsd-i386-static"
GOOS=netbsd GOARCH=386 ./build -o yggstack-netbsd-i386-static
GOOS=netbsd GOARCH=386 ./build -o yggstack-netbsd-386-static
echo "::endgroup::"
#
echo "::group::yggstack-android-amd64"
CGO_ENABLED=1 CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android21-clang" GOOS=android GOARCH=amd64 ./build -o yggstack-android-amd64
echo "::endgroup::"
#
echo "::group::yggstack-android-arm64"
CGO_ENABLED=1 CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang" GOOS=android GOARCH=arm64 ./build -o yggstack-android-arm64
echo "::endgroup::"
#
echo "::group::yggstack-android-armv7"
CGO_ENABLED=1 CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang" GOOS=android GOARCH=arm GOARM=7 ./build -o yggstack-android-armv7
echo "::endgroup::"
#
echo "::group::yggstack-android-i386"
CGO_ENABLED=1 CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android21-clang" GOOS=android GOARCH=386 ./build -o yggstack-android-i386
CGO_ENABLED=1 CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android21-clang" GOOS=android GOARCH=386 ./build -o yggstack-android-386
echo "::endgroup::"
#
#echo "::group::yggstack-ios-arm64"
#GOOS=ios GOARCH=arm64 CC=$(go env GOROOT)/misc/ios/clangwrap.sh ./build -o yggstack-ios-arm64
#echo "::endgroup::"
#
#echo "::group::yggstack-ios-amd64"
#GOOS=ios GOARCH=amd64 CC=$(go env GOROOT)/misc/ios/clangwrap.sh ./build -o yggstack-ios-amd64
#echo "::endgroup::"
- name: Publish release
if: ${{ env.SKIP_UPLOAD == '' }}
run: |
gh release create trunk --prerelease yggstack-* || gh release upload trunk yggstack-*
gh release create "${{ env.RELEASENAME }}" ${{ env.PRERELEASE }} yggstack-* || gh release upload "${{ env.RELEASENAME }}" yggstack-* --clobber
env:
GH_TOKEN: ${{ github.token }}
- name: Set up Docker Buildx
if: ${{ env.SKIP_UPLOAD != '1' }}
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: ${{ env.SKIP_UPLOAD == '' }}
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push yggstack container image
if: ${{ env.SKIP_UPLOAD == '' }}
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.static
platforms: linux/386, linux/amd64, linux/arm/v6, linux/arm/v7, linux/arm64, linux/ppc64le, linux/riscv64, linux/s390x
push: true
tags: ghcr.io/yggdrasil-network/yggstack:${{ env.RELEASENAME }}

9
Dockerfile.static Normal file
View file

@ -0,0 +1,9 @@
FROM --platform=linux/amd64 gcr.io/distroless/static
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
COPY --chown=0:0 --chmod=0755 yggstack-${TARGETOS}-${TARGETARCH}${TARGETVARIANT}-static /bin/yggstack
ENTRYPOINT [ "/bin/yggstack" ]

View file

@ -1,6 +1,6 @@
# Yggstack - Yggdrasil as SOCKS proxy / port forwarder
[![Build status](https://github.com/yggdrasil-network/yggstack/actions/workflows/ci.yml/badge.svg)](https://github.com/yggdrasil-network/yggstack/actions/workflows/ci.yml)
[![Build status](https://github.com/yggdrasil-network/yggstack/actions/workflows/trunk.yml/badge.svg)](https://github.com/yggdrasil-network/yggstack/actions/workflows/trunk.yml)
## Introduction
@ -30,12 +30,18 @@ Please see our [Installation](https://yggdrasil-network.github.io/installation.h
page for more information. You may also find other platform-specific wrappers, scripts
or tools in the `contrib` folder.
## Downloading
Bleeding-edge binaries can be downloaded via [trunk release](https://github.com/yggdrasil-network/yggstack/releases/tag/trunk)
Tagged releases provide packages similar to Yggdrasil.
## Building
If you want to build from source, as opposed to installing one of the pre-built
packages:
1. Install [Go](https://golang.org) (requires Go 1.17 or later)
1. Install [Go](https://golang.org) (requires Go 1.22 or later)
2. Clone this repository
2. Run `./build`
@ -66,17 +72,48 @@ other configuration such as listen addresses or multicast addresses, etc.
### Run Yggstack
To run SOCKS proxy server listening on local port 1080 using generated configuration:
To run SOCKS proxy server listening on local port 1080 using generated
configuration (like `ssh -D`):
```
./yggstack -useconffile /path/to/yggdrasil.conf -socks 127.0.0.1:1080
```
To expose network services (like a Web server) listening on local port 8080 to Yggdrasil
network address at port 80:
To run SOCKS proxy server listening on UNIX socket file `/tmp/yggstack.sock`:
```
./yggstack -useconffile /path/to/yggdrasil.conf -exposetcp 80:127.0.0.1:8080
./yggstack -useconffile /path/to/yggdrasil.conf -socks /tmp/yggstack.sock
```
To expose network services (like a Web server) listening on local port 8080
to Yggdrasil network address at port 80 (like `ssh -R`):
TCP:
```
./yggstack -useconffile /path/to/yggdrasil.conf -remote-tcp 80:127.0.0.1:8080
```
UDP:
```
./yggstack -useconffile /path/to/yggdrasil.conf -remote-udp 53:127.0.0.1:53
```
To forward remote port on some other Yggdrasil node to local machine (like `ssh -L`):
TCP:
```
./yggstack -useconffile /path/to/yggdrasil.conf -local-tcp 127.0.0.1:8080:<remote-yggdrasil-ipv6>:8080
./yggstack -useconffile /path/to/yggdrasil.conf -local-tcp [::1]:8080:<remote-yggdrasil-ipv6>:8080
```
UDP:
```
./yggstack -useconffile /path/to/yggdrasil.conf -local-udp 127.0.0.1:5353:<remote-yggdrasil-ipv6>:53
./yggstack -useconffile /path/to/yggdrasil.conf -local-udp [::1]:5353:<remote-yggdrasil-ipv6>:53
```
To run as a standalone node without SOCKS server or TCP port forwarding:
@ -95,14 +132,37 @@ Unlike mainline Yggdrasil, Yggstack does NOT require privileged access.
You can even run several Yggstack instances with different configurations
on the same OS and user!
### External DNS nameservers
If a client tool like `curl` fails to resolve `.ygg` domain, and yggstack prints
the following warning on start-up:
```
2024/08/06 03:27:20 DNS nameserver is not set!
2024/08/06 03:27:20 SOCKS server will not be able to resolve hostnames other than .pk.ygg !
```
start yggstack pointing to a [DNS server](https://yggdrasil-network.github.io/services.html#dns),
for example:
```
yggstack -useconffile /path/to/yggdrasil.conf -nameserver '[324:71e:281a:9ed3::53]:53' -socks 127.0.0.1:1080
```
and test if resolver works:
```
curl -x socks5h://127.0.0.1:1080 http://web.mc.ygg
```
### pk.ygg DNS resolver
One unique feature of Yggstack is built-in DNS resolver functionality using
`<publickey>.pk.ygg` format.
`<publickey>.pk.ygg` format without the need for external DNS nameservers.
For example, HowToYgg website (whose public key is `d40d4a7153cf288ea28f1865f6cfe95143a478b5c8c9e7cb002a0633d10a53eb`)
can be accessed by any Web browser supporting SOCKS servers
via `http://d40d4a7153cf288ea28f1865f6cfe95143a478b5c8c9e7cb002a0633d10a53eb.pk.ygg`
via `http://d40d4a7153cf288ea28f1865f6cfe95143a478b5c8c9e7cb002a0633d10a53eb.pk.ygg`
You can even use cURL with Yggstack:

3
build
View file

@ -9,7 +9,7 @@ PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
ARGS="-v"
while getopts "utc:l:dro:p" option
while getopts "utc:l:dro:ps" option
do
case "$option"
in
@ -21,6 +21,7 @@ do
r) ARGS="$ARGS -race";;
o) ARGS="$ARGS -o $OPTARG";;
p) ARGS="$ARGS -buildmode=pie";;
s) ARGS="$ARGS -tags netgo,osusersgo,static" LDFLAGS="$LDFLAGS -extldflags '-static'" CGO_ENABLED=0;;
esac
done

View file

@ -5,13 +5,16 @@ import (
"crypto/ed25519"
"encoding/hex"
"encoding/json"
"errors"
"flag"
"fmt"
"net"
"os"
"os/signal"
"regexp"
"runtime"
"strings"
"sync"
"syscall"
"github.com/gologme/log"
@ -28,17 +31,29 @@ import (
"github.com/yggdrasil-network/yggstack/src/netstack"
"github.com/yggdrasil-network/yggstack/src/types"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
)
type node struct {
core *core.Core
multicast *multicast.Multicast
admin *admin.AdminSocket
core *core.Core
multicast *multicast.Multicast
admin *admin.AdminSocket
socks5Tcp net.Listener
socks5Unix net.Listener
}
type UDPSession struct {
conn interface{}
remoteAddr net.Addr
}
// The main function is responsible for configuring and starting Yggdrasil.
func main() {
var expose types.TCPMappings
var localtcp types.TCPLocalMappings
var localudp types.UDPLocalMappings
var remotetcp types.TCPRemoteMappings
var remoteudp types.UDPRemoteMappings
genconf := flag.Bool("genconf", false, "print a new config to stdout")
useconf := flag.Bool("useconf", false, "read HJSON/JSON config from stdin")
useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path")
@ -52,9 +67,12 @@ func main() {
getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet")
getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key")
loglevel := flag.String("loglevel", "info", "loglevel to enable")
socks := flag.String("socks", "", "address to listen on for SOCKS, i.e. :1080")
socks := flag.String("socks", "", "address to listen on for SOCKS, i.e. :1080; or UNIX socket file path, i.e. /tmp/yggstack.sock")
nameserver := flag.String("nameserver", "", "the Yggdrasil IPv6 address to use as a DNS server for SOCKS")
flag.Var(&expose, "exposetcp", "TCP ports to expose to the network, e.g. 22, 2022:22, 22:192.168.1.1:2022")
flag.Var(&localtcp, "local-tcp", "TCP ports to forward to the remote Yggdradil node, e.g. 22:[a:b:c:d]:22, 127.0.0.1:22:[a:b:c:d]:22")
flag.Var(&localudp, "local-udp", "UDP ports to forward to the remote Yggdrasil node, e.g. 22:[a:b:c:d]:2022, 127.0.0.1:[a:b:c:d]:22")
flag.Var(&remotetcp, "remote-tcp", "TCP ports to expose to the network, e.g. 22, 2022:22, 22:192.168.1.1:2022")
flag.Var(&remoteudp, "remote-udp", "UDP ports to expose to the network, e.g. 22, 2022:22, 22:192.168.1.1:2022")
flag.Parse()
// Catch interrupts from the operating system to exit gracefully.
@ -95,6 +113,8 @@ func main() {
return
case *autoconf:
// Force AdminListen to none in yggstack
cfg.AdminListen = "none"
// Use an autoconf-generated config, this will give us random keys and
// port numbers, and will use an automatically selected TUN interface.
@ -114,6 +134,7 @@ func main() {
_ = f.Close()
case *genconf:
// Force AdminListen to none in yggstack
cfg.AdminListen = "none"
var bs []byte
if *confjson {
@ -268,29 +289,164 @@ func main() {
// Create SOCKS server
{
if socks != nil && nameserver != nil && *socks != "" {
resolver := types.NewNameResolver(s, *nameserver)
if socks != nil && *socks != "" {
socksOptions := []socks5.Option{
socks5.WithDial(s.DialContext),
socks5.WithResolver(resolver),
}
if nameserver != nil {
if *nameserver == "" {
logger.Infof("DNS nameserver is not set!")
logger.Infof("SOCKS server will not be able to resolve hostnames other than .pk.ygg !")
}
resolver := types.NewNameResolver(s, *nameserver)
socksOptions = append(socksOptions, socks5.WithResolver(resolver))
}
if logger.GetLevel("debug") {
socksOptions = append(socksOptions, socks5.WithLogger(logger))
}
server := socks5.NewServer(socksOptions...)
go server.ListenAndServe("tcp", *socks) // nolint:errcheck
if strings.Contains(*socks, ":") {
logger.Infof("Starting SOCKS server on %s", *socks)
n.socks5Tcp, err = net.Listen("tcp", *socks)
if err != nil {
panic(err)
}
go func() {
err := server.Serve(n.socks5Tcp)
if err != nil {
panic(err)
}
}()
} else {
logger.Infof("Starting SOCKS server with socket file %s", *socks)
n.socks5Unix, err = net.Listen("unix", *socks)
if err != nil {
// If address in use, try connecting to
// the socket to see if other yggstack
// instance is listening on it
if isErrorAddressAlreadyInUse(err) {
_, err = net.Dial("unix", *socks)
if err != nil {
// Unlink dead socket if not connected
err = os.RemoveAll(*socks)
if err != nil {
panic(err)
}
} else {
panic(fmt.Errorf("Another yggstack instance is listening on socket '%s'", *socks))
}
} else {
panic(err)
}
}
go func() {
err := server.Serve(n.socks5Unix)
if err != nil {
panic(err)
}
}()
}
}
}
// Create TCP mappings
// Create local TCP mappings (forwarding connections from local port
// to remote Yggdrasil node)
{
for _, mapping := range expose {
for _, mapping := range localtcp {
go func(mapping types.TCPMapping) {
listener, err := net.ListenTCP("tcp", mapping.Listen)
if err != nil {
panic(err)
}
logger.Infof("Mapping local TCP port %d to Yggdrasil %s", mapping.Listen.Port, mapping.Mapped)
for {
c, err := listener.Accept()
if err != nil {
panic(err)
}
r, err := s.DialTCP(mapping.Mapped)
if err != nil {
logger.Errorf("Failed to connect to %s: %s", mapping.Mapped, err)
_ = c.Close()
continue
}
go types.ProxyTCP(n.core.MTU(), c, r)
}
}(mapping)
}
}
// Create local UDP mappings (forwarding connections from local port
// to remote Yggdrasil node)
{
for _, mapping := range localudp {
go func(mapping types.UDPMapping) {
mtu := n.core.MTU()
udpListenConn, err := net.ListenUDP("udp", mapping.Listen)
if err != nil {
panic(err)
}
logger.Infof("Mapping local UDP port %d to Yggdrasil %s", mapping.Listen.Port, mapping.Mapped)
localUdpConnections := new(sync.Map)
udpBuffer := make([]byte, mtu)
for {
bytesRead, remoteUdpAddr, err := udpListenConn.ReadFrom(udpBuffer)
if err != nil {
if bytesRead == 0 {
continue
}
}
remoteUdpAddrStr := remoteUdpAddr.String()
connVal, ok := localUdpConnections.Load(remoteUdpAddrStr)
if !ok {
logger.Debugf("Creating new session for %s", remoteUdpAddr.String())
udpFwdConn, err := s.DialUDP(mapping.Mapped)
if err != nil {
logger.Errorf("Failed to connect to %s: %s", mapping.Mapped, err)
continue
}
udpSession := &UDPSession{
conn: udpFwdConn,
remoteAddr: remoteUdpAddr,
}
localUdpConnections.Store(remoteUdpAddrStr, udpSession)
go types.ReverseProxyUDP(mtu, udpListenConn, remoteUdpAddr, udpFwdConn)
}
udpSession, ok := connVal.(*UDPSession)
if !ok {
continue
}
udpFwdConnPtr := udpSession.conn.(*gonet.UDPConn)
udpFwdConn := *udpFwdConnPtr
_, err = udpFwdConn.Write(udpBuffer[:bytesRead])
if err != nil {
logger.Debugf("Cannot write from yggdrasil to udp listener: %q", err)
udpFwdConn.Close()
localUdpConnections.Delete(remoteUdpAddrStr)
continue
}
}
}(mapping)
}
}
// Create remote TCP mappings (forwarding connections from Yggdrasil
// node to local port)
{
for _, mapping := range remotetcp {
go func(mapping types.TCPMapping) {
listener, err := s.ListenTCP(mapping.Listen)
if err != nil {
panic(err)
}
logger.Infof("Mapping Yggdrasil port %d to %s", mapping.Listen.Port, mapping.Mapped)
logger.Infof("Mapping Yggdrasil TCP port %d to %s", mapping.Listen.Port, mapping.Mapped)
for {
c, err := listener.Accept()
if err != nil {
@ -308,15 +464,110 @@ func main() {
}
}
// Create remote UDP mappings (forwarding connections from Yggdrasil
// node to local port)
{
for _, mapping := range remoteudp {
go func(mapping types.UDPMapping) {
mtu := n.core.MTU()
udpListenConn, err := s.ListenUDP(mapping.Listen)
if err != nil {
panic(err)
}
logger.Infof("Mapping Yggdrasil UDP port %d to %s", mapping.Listen.Port, mapping.Mapped)
remoteUdpConnections := new(sync.Map)
udpBuffer := make([]byte, mtu)
for {
bytesRead, remoteUdpAddr, err := udpListenConn.ReadFrom(udpBuffer)
if err != nil {
logger.Debugf("udp readFrom error: %v", err)
}
if bytesRead == 0 {
continue
}
remoteUdpAddrStr := remoteUdpAddr.String()
var udpSession *UDPSession = nil
connVal, ok := remoteUdpConnections.Load(remoteUdpAddrStr)
if !ok {
logger.Debugf("Creating new session for %s", remoteUdpAddr.String())
udpFwdConn, err := net.DialUDP("udp", nil, mapping.Mapped)
if err != nil {
logger.Errorf("Failed to connect to %s: %s", mapping.Mapped, err)
continue
}
udpSession = &UDPSession{
conn: udpFwdConn,
remoteAddr: remoteUdpAddr,
}
remoteUdpConnections.Store(remoteUdpAddrStr, udpSession)
go types.ReverseProxyUDP(mtu, udpListenConn, remoteUdpAddr, udpFwdConn)
} else {
udpSession, ok = connVal.(*UDPSession)
if !ok {
continue
}
}
udpFwdConnPtr := udpSession.conn.(*net.UDPConn)
udpFwdConn := *udpFwdConnPtr
_, err = udpFwdConn.Write(udpBuffer[:bytesRead])
if err != nil {
logger.Debugf("Cannot write from yggdrasil to udp listener: %q", err)
udpFwdConn.Close()
remoteUdpConnections.Delete(remoteUdpAddrStr)
continue
}
}
}(mapping)
}
}
// Block until we are told to shut down.
<-ctx.Done()
// Shut down the node.
_ = n.admin.Stop()
_ = n.multicast.Stop()
if n.socks5Unix != nil {
_ = n.socks5Unix.Close()
_ = os.RemoveAll(*socks)
logger.Infof("Stopped SOCKS5 UNIX socket listener")
}
if n.socks5Tcp != nil {
_ = n.socks5Tcp.Close()
logger.Infof("Stopped SOCKS5 TCP listener")
}
n.core.Stop()
}
// Helper to detect if socket address is in use
// https://stackoverflow.com/a/52152912
func isErrorAddressAlreadyInUse(err error) bool {
var eOsSyscall *os.SyscallError
if !errors.As(err, &eOsSyscall) {
return false
}
var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr)
if !errors.As(eOsSyscall, &errErrno) {
return false
}
if errors.Is(errErrno, syscall.EADDRINUSE) {
return true
}
const WSAEADDRINUSE = 10048
if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE {
return true
}
return false
}
// Helper to set logging level
func setLogLevel(loglevel string, logger *log.Logger) {
levels := [...]string{"error", "warn", "info", "debug", "trace"}
loglevel = strings.ToLower(loglevel)

48
go.mod
View file

@ -1,34 +1,38 @@
module github.com/yggdrasil-network/yggstack
go 1.21.4
go 1.22.0
toolchain go1.22.5
require (
github.com/gologme/log v1.3.0
github.com/hashicorp/go-syslog v1.0.0
github.com/hjson/hjson-go/v4 v4.4.0
github.com/things-go/go-socks5 v0.0.4
github.com/yggdrasil-network/yggdrasil-go v0.5.4
gvisor.dev/gvisor v0.0.0-20240103195848-a9a6a6819b00
github.com/things-go/go-socks5 v0.0.5
github.com/yggdrasil-network/yggdrasil-go v0.5.12
gvisor.dev/gvisor v0.0.0-20240810013311-326fe0f2a77f
)
require (
github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd // indirect
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 // indirect
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/bits-and-blooms/bloom/v3 v3.6.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/quic-go/quic-go v0.40.1 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.16.1 // indirect
github.com/bits-and-blooms/bitset v1.14.3 // indirect
github.com/bits-and-blooms/bloom/v3 v3.7.0 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
github.com/quic-go/quic-go v0.48.2 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.26.0 // indirect
)

218
go.sum
View file

@ -1,179 +1,73 @@
github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8 h1:qyXiPZClVoe6QsbsiDP23g0Ze/MNXiHIAL/nVNfauH0=
github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd h1:458tnmZ4zM2gbLtefdYbaxyAJevDNEWu6tLKEqbK4wg=
github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 h1:d8N0z+udAnbU5PdjpLSNPTWlqeU/nnYsQ42B6+879aw=
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0=
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
github.com/bits-and-blooms/bitset v1.3.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.11.0 h1:RMyy2mBBShArUAhfVRZJ2xyBO58KCBCtZFShw3umo6k=
github.com/bits-and-blooms/bitset v1.11.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bloom/v3 v3.3.1 h1:K2+A19bXT8gJR5mU7y+1yW6hsKfNCjcP2uNfLFKncjQ=
github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90=
github.com/bits-and-blooms/bloom/v3 v3.6.0 h1:dTU0OVLJSoOhz9m68FTXMFfA39nR8U/nTCs1zb26mOI=
github.com/bits-and-blooms/bloom/v3 v3.6.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/bits-and-blooms/bitset v1.14.3 h1:Gd2c8lSNf9pKXom5JtD7AaKO8o7fGQ2LtFj1436qilA=
github.com/bits-and-blooms/bitset v1.14.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls=
github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo=
github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8=
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs=
github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hjson/hjson-go/v4 v4.3.0 h1:dyrzJdqqFGhHt+FSrs5n9s6b0fPM8oSJdWo+oS3YnJw=
github.com/hjson/hjson-go/v4 v4.3.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
github.com/hjson/hjson-go/v4 v4.3.1 h1:wfmDwHGxjzmYKXRFL0Qr9nonY/Xxe5y7IalwjlY7ekA=
github.com/hjson/hjson-go/v4 v4.3.1/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
github.com/hjson/hjson-go/v4 v4.4.0 h1:D/NPvqOCH6/eisTb5/ztuIS8GUvmpHaLOcNk1Bjr298=
github.com/hjson/hjson-go/v4 v4.4.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU=
github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.39.3 h1:o3YB6t2SR+HU/pgwF29kJ6g4jJIJEwEZ8CKia1h1TKg=
github.com/quic-go/quic-go v0.39.3/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw=
github.com/quic-go/quic-go v0.40.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q=
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/things-go/go-socks5 v0.0.4 h1:jMQjIc+qhD4z9cITOMnBiwo9dDmpGuXmBlkRFrl/qD0=
github.com/things-go/go-socks5 v0.0.4/go.mod h1:sh4K6WHrmHZpjxLTCHyYtXYH8OUuD+yZun41NomR1IQ=
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/things-go/go-socks5 v0.0.5 h1:qvKaGcBkfDrUL33SchHN93srAmYGzb4CxSM2DPYufe8=
github.com/things-go/go-socks5 v0.0.5/go.mod h1:mtzInf8v5xmsBpHZVbIw2YQYhc4K0jRwzfsH64Uh0IQ=
github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=
github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/yggdrasil-network/yggdrasil-go v0.5.3 h1:tfAajYmaiS1L9XevdEC1VUpScpgFfCW7/oEEWxdv5aM=
github.com/yggdrasil-network/yggdrasil-go v0.5.3/go.mod h1:qRzHB8bKEIpd1pQVYoZ+SedJJTBG0X9sbyctWxQDDGA=
github.com/yggdrasil-network/yggdrasil-go v0.5.4 h1:A7ZFmxkkbZhtqJgQXBVDw5sHsi25aUawLlJCCHnNsAs=
github.com/yggdrasil-network/yggdrasil-go v0.5.4/go.mod h1:TLmU4X0nfzCY9t5xABtFQ6GLoOtCae8xVatC9JwjD5I=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/yggdrasil-network/yggdrasil-go v0.5.12 h1:SaQ8d59JP+uFy+nOWXTx1ETM5r2uCfe1Gt/d+IodHJw=
github.com/yggdrasil-network/yggdrasil-go v0.5.12/go.mod h1:u4DU6dpTfWmVs8r0WjW1T3UpGyeUh9vRrS8zngvncwM=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20231125084359-4b4191b8cad1 h1:J9340hJZ+P01JeVPK8GatP/6Bk7j2dyLUiKct5k7IBA=
gvisor.dev/gvisor v0.0.0-20231125084359-4b4191b8cad1/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
gvisor.dev/gvisor v0.0.0-20240103195848-a9a6a6819b00 h1:CaYh1ABhmDNrDvY7Xs8y4/TqK67I4r3Jb1du+J4PG60=
gvisor.dev/gvisor v0.0.0-20240103195848-a9a6a6819b00/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
gvisor.dev/gvisor v0.0.0-20240104232245-1e61310ce61e h1:KNBb7yeP1HRByrH54W1Bw2LCrKualEWp8oQtCJarK00=
gvisor.dev/gvisor v0.0.0-20240104232245-1e61310ce61e/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
gvisor.dev/gvisor v0.0.0-20240810013311-326fe0f2a77f h1:llng6MGz63x02Xs1Ft71riYTfau4zS8OAZKv5jgPX6M=
gvisor.dev/gvisor v0.0.0-20240810013311-326fe0f2a77f/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU=

View file

@ -86,12 +86,12 @@ func (s *YggdrasilNetstack) DialContext(ctx context.Context, network, address st
}
}
func (s *YggdrasilNetstack) DialTCP(addr *net.TCPAddr) (net.Conn, error) {
func (s *YggdrasilNetstack) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) {
fa, pn, _ := convertToFullAddr(addr.IP, addr.Port)
return gonet.DialTCP(s.stack, fa, pn)
}
func (s *YggdrasilNetstack) DialUDP(addr *net.UDPAddr) (net.PacketConn, error) {
func (s *YggdrasilNetstack) DialUDP(addr *net.UDPAddr) (*gonet.UDPConn, error) {
fa, pn, _ := convertToFullAddr(addr.IP, addr.Port)
return gonet.DialUDP(s.stack, nil, &fa, pn)
}
@ -101,7 +101,7 @@ func (s *YggdrasilNetstack) ListenTCP(addr *net.TCPAddr) (net.Listener, error) {
return gonet.ListenTCP(s.stack, fa, pn)
}
func (s *YggdrasilNetstack) ListenUDP(addr *net.UDPAddr) (net.PacketConn, error) {
func (s *YggdrasilNetstack) ListenUDP(addr *net.UDPAddr) (*gonet.UDPConn, error) {
fa, pn, _ := convertToFullAddr(addr.IP, addr.Port)
return gonet.DialUDP(s.stack, &fa, nil, pn)
}

View file

@ -12,6 +12,7 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
)
type YggdrasilNIC struct {
@ -20,15 +21,17 @@ type YggdrasilNIC struct {
dispatcher stack.NetworkDispatcher
readBuf []byte
writeBuf []byte
rstPackets chan *stack.PacketBuffer
}
func (s *YggdrasilNetstack) NewYggdrasilNIC(ygg *core.Core) tcpip.Error {
rwc := ipv6rwc.NewReadWriteCloser(ygg)
mtu := rwc.MTU()
nic := &YggdrasilNIC{
ipv6rwc: rwc,
readBuf: make([]byte, mtu),
writeBuf: make([]byte, mtu),
ipv6rwc: rwc,
readBuf: make([]byte, mtu),
writeBuf: make([]byte, mtu),
rstPackets: make(chan *stack.PacketBuffer, 100),
}
if err := s.stack.CreateNIC(1, nic); err != nil {
return err
@ -48,6 +51,15 @@ func (s *YggdrasilNetstack) NewYggdrasilNIC(ygg *core.Core) tcpip.Error {
nic.dispatcher.DeliverNetworkPacket(ipv6.ProtocolNumber, pkb)
}
}()
go func() {
for {
pkt := <- nic.rstPackets
if pkt == nil {
continue
}
_ = nic.writePacket(pkt)
}
}()
_, snet, err := net.ParseCIDR("0200::/7")
if err != nil {
return &tcpip.ErrBadAddress{}
@ -85,29 +97,60 @@ func (e *YggdrasilNIC) IsAttached() bool { return e.dispatcher != nil }
func (e *YggdrasilNIC) MTU() uint32 { return uint32(e.ipv6rwc.MTU()) }
func (e *YggdrasilNIC) SetMTU(uint32) {}
func (*YggdrasilNIC) Capabilities() stack.LinkEndpointCapabilities { return stack.CapabilityNone }
func (*YggdrasilNIC) MaxHeaderLength() uint16 { return 40 }
func (*YggdrasilNIC) LinkAddress() tcpip.LinkAddress { return "" }
func (*YggdrasilNIC) SetLinkAddress(tcpip.LinkAddress) {}
func (*YggdrasilNIC) Wait() {}
func (e *YggdrasilNIC) writePacket(
pkt *stack.PacketBuffer,
) tcpip.Error {
// We need to recover from panic() here because
// parser in ToView() gets confused on some packets
// without payload and panics
defer func() {
r := recover()
if r != nil {
}
}()
vv := pkt.ToView()
n, err := vv.Read(e.writeBuf)
if err != nil {
return &tcpip.ErrAborted{}
}
_, err = e.ipv6rwc.Write(e.writeBuf[:n])
if err != nil {
return &tcpip.ErrAborted{}
}
return nil
}
func (e *YggdrasilNIC) WritePackets(
list stack.PacketBufferList,
) (int, tcpip.Error) {
var i int = 0
var err tcpip.Error = nil
for i, pkt := range list.AsSlice() {
vv := pkt.ToView()
n, err := vv.Read(e.writeBuf)
if err != nil {
log.Println(err)
return i - 1, &tcpip.ErrAborted{}
if pkt.Data().Size() == 0 {
if pkt.Network().TransportProtocol() == tcp.ProtocolNumber {
tcpHeader := header.TCP(pkt.TransportHeader().Slice())
if (tcpHeader.Flags() & header.TCPFlagRst) == header.TCPFlagRst {
e.rstPackets <- pkt
continue
}
}
}
_, err = e.ipv6rwc.Write(e.writeBuf[:n])
err = e.writePacket(pkt)
if err != nil {
log.Println(err)
return i - 1, &tcpip.ErrAborted{}
return i - 1, err
}
}
@ -129,8 +172,9 @@ func (e *YggdrasilNIC) ParseHeader(*stack.PacketBuffer) bool {
return true
}
func (e *YggdrasilNIC) Close() error {
func (e *YggdrasilNIC) Close() {
e.stack.stack.RemoveNIC(1)
e.dispatcher = nil
return nil
}
func (e *YggdrasilNIC) SetOnCloseAction(func()) {}

View file

@ -7,62 +7,381 @@ import (
"strings"
)
func parseMappingString(value string) (first_address string, first_port int, second_address string, second_port int, err error) {
var first_port_string string = ""
var second_port_string string = ""
tokens := strings.Split(value, ":")
tokens_len := len(tokens)
// If token count is 1, then it is first and second port the same
if tokens_len == 1 {
first_port, err = strconv.Atoi(tokens[0])
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
second_port = first_port
}
// If token count is 2, then it is <first-port>:<second-port>
if tokens_len == 2 {
first_port, err = strconv.Atoi(tokens[0])
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
second_port, err = strconv.Atoi(tokens[1])
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
}
// If token count is 3, parse it as
// <first-port>:<second-address>:<second-port>
if tokens_len == 3 {
first_port, err = strconv.Atoi(tokens[0])
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
second_address, second_port_string, err = net.SplitHostPort(
tokens[1] + ":" + tokens[2])
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
second_port, err = strconv.Atoi(second_port_string)
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
}
// If token count is 4, parse it as
// <first-address>:<first-port>:<second-address>:<second-port>
if tokens_len == 4 {
first_address, first_port_string, err = net.SplitHostPort(
tokens[0] + ":" + tokens[1])
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
second_address, second_port_string, err = net.SplitHostPort(
tokens[0] + ":" + tokens[1])
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
first_port, err = strconv.Atoi(first_port_string)
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
second_port, err = strconv.Atoi(second_port_string)
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
}
if tokens_len > 4 {
// Last token needs to be the second_port
second_port, err = strconv.Atoi(tokens[tokens_len-1])
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
// Cut seen tokens
tokens = tokens[:tokens_len-1]
tokens_len = len(tokens)
if strings.HasSuffix(tokens[tokens_len-1], "]") {
// Reverse-walk over tokens to find the end of
// numeric ipv6 address
for i := tokens_len - 1; i >= 0; i-- {
if strings.HasPrefix(tokens[i], "[") {
// Store second address
second_address = strings.Join(tokens[i:], ":")
second_address, _ = strings.CutPrefix(second_address, "[")
second_address, _ = strings.CutSuffix(second_address, "]")
// Cut seen tokens
tokens = tokens[:i]
// break from loop
break
}
}
} else {
// next is second address in non-numerical-ipv6 form
second_address = tokens[tokens_len-1]
tokens = tokens[:tokens_len-1]
}
tokens_len = len(tokens)
if tokens_len < 1 {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
// Last token needs to be the first_port
first_port, err = strconv.Atoi(tokens[tokens_len-1])
if err != nil {
return "", 0, "", 0, fmt.Errorf("Malformed mapping spec '%s'", value)
}
// Cut seen tokens
tokens = tokens[:tokens_len-1]
tokens_len = len(tokens)
if tokens_len > 0 {
if strings.HasSuffix(tokens[tokens_len-1], "]") {
// Reverse-walk over tokens to find the end of
// numeric ipv6 address
for i := tokens_len - 1; i >= 0; i-- {
if strings.HasPrefix(tokens[i], "[") {
// Store first address
first_address = strings.Join(tokens[i:], ":")
first_address, _ = strings.CutPrefix(first_address, "[")
first_address, _ = strings.CutSuffix(first_address, "]")
// break from loop
break
}
}
} else {
// next is first address in non-numerical-ipv6 form
first_address = tokens[tokens_len-1]
}
}
}
if first_port == 0 || second_port == 0 {
return "", 0, "", 0, fmt.Errorf("Ports must not be zero")
}
return first_address, first_port, second_address, second_port, nil
}
type TCPMapping struct {
Listen *net.TCPAddr
Mapped *net.TCPAddr
}
type TCPMappings []TCPMapping
type TCPLocalMappings []TCPMapping
func (m *TCPMappings) String() string {
func (m *TCPLocalMappings) String() string {
return ""
}
func (m *TCPMappings) Set(value string) error {
tokens := strings.Split(value, ":")
if len(tokens) > 2 {
tokens = strings.SplitN(value, ":", 2)
host, port, err := net.SplitHostPort(tokens[1])
if err != nil {
return fmt.Errorf("failed to split host and port: %w", err)
}
tokens = append(tokens[:1], host, port)
}
listenport, err := strconv.Atoi(tokens[0])
func (m *TCPLocalMappings) Set(value string) error {
first_address, first_port, second_address, second_port, err :=
parseMappingString(value)
if err != nil {
return fmt.Errorf("listen port is invalid: %w", err)
return err
}
if listenport == 0 {
return fmt.Errorf("listen port must not be zero")
// First address can be ipv4/ipv6
// Second address can be only Yggdrasil ipv6
if !strings.Contains(second_address, ":") {
return fmt.Errorf("Yggdrasil listening address can be only IPv6")
}
// Create mapping
mapping := TCPMapping{
Listen: &net.TCPAddr{
Port: listenport,
Port: first_port,
},
Mapped: &net.TCPAddr{
IP: net.IPv6loopback,
Port: listenport,
Port: second_port,
},
}
tokens = tokens[1:]
if len(tokens) > 0 {
mappedaddr := net.ParseIP(tokens[0])
if first_address != "" {
listenaddr := net.ParseIP(first_address)
if listenaddr == nil {
return fmt.Errorf("invalid listen address %q", first_address)
}
mapping.Listen.IP = listenaddr
}
if second_address != "" {
mappedaddr := net.ParseIP(second_address)
if mappedaddr == nil {
return fmt.Errorf("invalid mapped address %q", tokens[0])
return fmt.Errorf("invalid mapped address %q", second_address)
}
// TODO: Filter Yggdrasil IPs here
mapping.Mapped.IP = mappedaddr
tokens = tokens[1:]
}
if len(tokens) > 0 {
mappedport, err := strconv.Atoi(tokens[0])
if err != nil {
return fmt.Errorf("mapped port is invalid: %w", err)
}
if mappedport == 0 {
return fmt.Errorf("mapped port must not be zero")
}
mapping.Mapped.Port = mappedport
}
*m = append(*m, mapping)
return nil
}
type TCPRemoteMappings []TCPMapping
func (m *TCPRemoteMappings) String() string {
return ""
}
func (m *TCPRemoteMappings) Set(value string) error {
first_address, first_port, second_address, second_port, err :=
parseMappingString(value)
if err != nil {
return err
}
// First address must be empty
// Second address can be ipv4/ipv6
if first_address != "" {
return fmt.Errorf("Yggdrasil listening must be empty")
}
// Create mapping
mapping := TCPMapping{
Listen: &net.TCPAddr{
Port: first_port,
},
Mapped: &net.TCPAddr{
IP: net.IPv6loopback,
Port: second_port,
},
}
if first_address != "" {
listenaddr := net.ParseIP(first_address)
if listenaddr == nil {
return fmt.Errorf("invalid listen address %q", first_address)
}
mapping.Listen.IP = listenaddr
}
if second_address != "" {
mappedaddr := net.ParseIP(second_address)
if mappedaddr == nil {
return fmt.Errorf("invalid mapped address %q", second_address)
}
mapping.Mapped.IP = mappedaddr
}
*m = append(*m, mapping)
return nil
}
type UDPMapping struct {
Listen *net.UDPAddr
Mapped *net.UDPAddr
}
type UDPLocalMappings []UDPMapping
func (m *UDPLocalMappings) String() string {
return ""
}
func (m *UDPLocalMappings) Set(value string) error {
first_address, first_port, second_address, second_port, err :=
parseMappingString(value)
if err != nil {
return err
}
// First address can be ipv4/ipv6
// Second address can be only Yggdrasil ipv6
if !strings.Contains(second_address, ":") {
return fmt.Errorf("Yggdrasil listening address can be only IPv6")
}
// Create mapping
mapping := UDPMapping{
Listen: &net.UDPAddr{
Port: first_port,
},
Mapped: &net.UDPAddr{
IP: net.IPv6loopback,
Port: second_port,
},
}
if first_address != "" {
listenaddr := net.ParseIP(first_address)
if listenaddr == nil {
return fmt.Errorf("invalid listen address %q", first_address)
}
mapping.Listen.IP = listenaddr
}
if second_address != "" {
mappedaddr := net.ParseIP(second_address)
if mappedaddr == nil {
return fmt.Errorf("invalid mapped address %q", second_address)
}
// TODO: Filter Yggdrasil IPs here
mapping.Mapped.IP = mappedaddr
}
*m = append(*m, mapping)
return nil
}
type UDPRemoteMappings []UDPMapping
func (m *UDPRemoteMappings) String() string {
return ""
}
func (m *UDPRemoteMappings) Set(value string) error {
first_address, first_port, second_address, second_port, err :=
parseMappingString(value)
if err != nil {
return err
}
// First address must be empty
// Second address can be ipv4/ipv6
if first_address != "" {
return fmt.Errorf("Yggdrasil listening must be empty")
}
// Create mapping
mapping := UDPMapping{
Listen: &net.UDPAddr{
Port: first_port,
},
Mapped: &net.UDPAddr{
IP: net.IPv6loopback,
Port: second_port,
},
}
if first_address != "" {
listenaddr := net.ParseIP(first_address)
if listenaddr == nil {
return fmt.Errorf("invalid listen address %q", first_address)
}
mapping.Listen.IP = listenaddr
}
if second_address != "" {
mappedaddr := net.ParseIP(second_address)
if mappedaddr == nil {
return fmt.Errorf("invalid mapped address %q", second_address)
}
mapping.Mapped.IP = mappedaddr
}
*m = append(*m, mapping)
return nil
}

View file

@ -3,26 +3,108 @@ package types
import "testing"
func TestEndpointMappings(t *testing.T) {
var mappings TCPMappings
if err := mappings.Set("1234"); err != nil {
var tcpMappings TCPMappings
if err := tcpMappings.Set("1234"); err != nil {
t.Fatal(err)
}
if err := mappings.Set("1234:192.168.1.1"); err != nil {
if err := tcpMappings.Set("1234:192.168.1.1"); err != nil {
t.Fatal(err)
}
if err := mappings.Set("1234:192.168.1.1:4321"); err != nil {
if err := tcpMappings.Set("1234:192.168.1.1:4321"); err != nil {
t.Fatal(err)
}
if err := mappings.Set("1234:[2000::1]:4321"); err != nil {
if err := tcpMappings.Set("192.168.1.2:1234:192.168.1.1:4321"); err != nil {
t.Fatal(err)
}
if err := mappings.Set("a"); err == nil {
if err := tcpMappings.Set("1234:[2000::1]:4321"); err != nil {
t.Fatal(err)
}
if err := tcpMappings.Set("[2001:1]:1234:[2000::1]:4321"); err != nil {
t.Fatal(err)
}
if err := tcpMappings.Set("a"); err == nil {
t.Fatal("'a' should be an invalid exposed port")
}
if err := mappings.Set("1234:localhost"); err == nil {
if err := tcpMappings.Set("1234:localhost"); err == nil {
t.Fatal("mapped address must be an IP literal")
}
if err := mappings.Set("1234:localhost:a"); err == nil {
if err := tcpMappings.Set("127.0.0.1:1234:localhost"); err == nil {
t.Fatal("mapped address must be an IP literal")
}
if err := tcpMappings.Set("[2000:1]:1234:localhost"); err == nil {
t.Fatal("mapped address must be an IP literal")
}
if err := tcpMappings.Set("localhost:1234:127.0.0.1"); err == nil {
t.Fatal("listen address must be an IP literal")
}
if err := tcpMappings.Set("localhost:1234:127.0.0.1"); err == nil {
t.Fatal("listen address must be an IP literal")
}
if err := tcpMappings.Set("localhost:1234:[2000:1]"); err == nil {
t.Fatal("listen address must be an IP literal")
}
if err := tcpMappings.Set("localhost:1234:[2000:1]"); err == nil {
t.Fatal("listen address must be an IP literal")
}
if err := tcpMappings.Set("1234:localhost:a"); err == nil {
t.Fatal("'a' should be an invalid mapped port")
}
if err := tcpMappings.Set("127.0.0.1:1234:127.0.0.1:a"); err == nil {
t.Fatal("'a' should be an invalid mapped port")
}
if err := tcpMappings.Set("[2000::1]:1234:[2000::1]:a"); err == nil {
t.Fatal("'a' should be an invalid mapped port")
}
var udpMappings UDPMappings
if err := udpMappings.Set("1234"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("1234:192.168.1.1"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("1234:192.168.1.1:4321"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("192.168.1.2:1234:192.168.1.1:4321"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("1234:[2000::1]:4321"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("[2001:1]:1234:[2000::1]:4321"); err != nil {
t.Fatal(err)
}
if err := udpMappings.Set("a"); err == nil {
t.Fatal("'a' should be an invalid exposed port")
}
if err := udpMappings.Set("1234:localhost"); err == nil {
t.Fatal("mapped address must be an IP literal")
}
if err := udpMappings.Set("127.0.0.1:1234:localhost"); err == nil {
t.Fatal("mapped address must be an IP literal")
}
if err := udpMappings.Set("[2000:1]:1234:localhost"); err == nil {
t.Fatal("mapped address must be an IP literal")
}
if err := udpMappings.Set("localhost:1234:127.0.0.1"); err == nil {
t.Fatal("listen address must be an IP literal")
}
if err := udpMappings.Set("localhost:1234:127.0.0.1"); err == nil {
t.Fatal("listen address must be an IP literal")
}
if err := udpMappings.Set("localhost:1234:[2000:1]"); err == nil {
t.Fatal("listen address must be an IP literal")
}
if err := udpMappings.Set("localhost:1234:[2000:1]"); err == nil {
t.Fatal("listen address must be an IP literal")
}
if err := udpMappings.Set("1234:localhost:a"); err == nil {
t.Fatal("'a' should be an invalid mapped port")
}
if err := udpMappings.Set("127.0.0.1:1234:127.0.0.1:a"); err == nil {
t.Fatal("'a' should be an invalid mapped port")
}
if err := udpMappings.Set("[2000::1]:1234:[2000::1]:a"); err == nil {
t.Fatal("'a' should be an invalid mapped port")
}
}

22
src/types/udpproxy.go Normal file
View file

@ -0,0 +1,22 @@
package types
import (
"net"
)
func ReverseProxyUDP(mtu uint64, dst net.PacketConn, dstAddr net.Addr, src net.Conn) error {
buf := make([]byte, mtu)
for {
n, err := src.Read(buf[:])
if err != nil {
return err
}
if n > 0 {
n, err = dst.WriteTo(buf[:n], dstAddr)
if err != nil {
return err
}
}
}
return nil
}