diff --git a/.github/workflows/trunk.yml b/.github/workflows/trunk.yml index 32385e7..c35b084 100644 --- a/.github/workflows/trunk.yml +++ b/.github/workflows/trunk.yml @@ -1,17 +1,8 @@ -name: Trunk / release build +name: Trunk build on: - pull_request: - paths-ignore: - - "README.md" push: - 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]+' + branch: develop concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -19,246 +10,69 @@ concurrency: permissions: contents: write - packages: write jobs: build: strategy: fail-fast: false - name: Build Windows/Linux-static/MacOS/*BSD-static/Android - runs-on: ubuntu-latest + name: Build Windows/Linux/MacOS/FreeBSD/Android + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: actions/setup-go@v5 + - name: Set up Go + uses: actions/setup-go@v3 with: - go-version: '1.22' + go-version: "1.21" - - name: Check if commit needs a release upload + - name: Build static executables run: | - 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 - 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=arm64 ./build -o yggstack-windows-arm64.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 - echo "::endgroup::" - # - echo "::group::yggstack-darwin-arm64" + 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-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=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 #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 "${{ env.RELEASENAME }}" ${{ env.PRERELEASE }} yggstack-* || gh release upload "${{ env.RELEASENAME }}" yggstack-* --clobber + gh release create trunk --prerelease yggstack-* || gh release upload trunk yggstack-* 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 }} diff --git a/Dockerfile.static b/Dockerfile.static deleted file mode 100644 index 14c4aa5..0000000 --- a/Dockerfile.static +++ /dev/null @@ -1,9 +0,0 @@ -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" ] diff --git a/README.md b/README.md index 368b662..92d0a15 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Yggstack - Yggdrasil as SOCKS proxy / port forwarder -[![Build status](https://github.com/yggdrasil-network/yggstack/actions/workflows/trunk.yml/badge.svg)](https://github.com/yggdrasil-network/yggstack/actions/workflows/trunk.yml) +[![Build status](https://github.com/yggdrasil-network/yggstack/actions/workflows/ci.yml/badge.svg)](https://github.com/yggdrasil-network/yggstack/actions/workflows/ci.yml) ## Introduction @@ -30,18 +30,12 @@ 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.22 or later) +1. Install [Go](https://golang.org) (requires Go 1.17 or later) 2. Clone this repository 2. Run `./build` @@ -72,48 +66,17 @@ 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 (like `ssh -D`): +To run SOCKS proxy server listening on local port 1080 using generated configuration: ``` ./yggstack -useconffile /path/to/yggdrasil.conf -socks 127.0.0.1:1080 ``` -To run SOCKS proxy server listening on UNIX socket file `/tmp/yggstack.sock`: +To expose network services (like a Web server) listening on local port 8080 to Yggdrasil +network address at port 80: ``` -./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::8080 -./yggstack -useconffile /path/to/yggdrasil.conf -local-tcp [::1]:8080::8080 -``` - -UDP: - -``` -./yggstack -useconffile /path/to/yggdrasil.conf -local-udp 127.0.0.1:5353::53 -./yggstack -useconffile /path/to/yggdrasil.conf -local-udp [::1]:5353::53 +./yggstack -useconffile /path/to/yggdrasil.conf -exposetcp 80:127.0.0.1:8080 ``` To run as a standalone node without SOCKS server or TCP port forwarding: @@ -132,37 +95,14 @@ 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 -`.pk.ygg` format without the need for external DNS nameservers. +`.pk.ygg` format. 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: diff --git a/build b/build index f14d867..4a63f46 100755 --- a/build +++ b/build @@ -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:ps" option +while getopts "utc:l:dro:p" option do case "$option" in @@ -21,7 +21,6 @@ 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 diff --git a/cmd/yggstack/main.go b/cmd/yggstack/main.go index 64ca65f..3b7ea9d 100644 --- a/cmd/yggstack/main.go +++ b/cmd/yggstack/main.go @@ -5,16 +5,13 @@ import ( "crypto/ed25519" "encoding/hex" "encoding/json" - "errors" "flag" "fmt" "net" "os" "os/signal" "regexp" - "runtime" "strings" - "sync" "syscall" "github.com/gologme/log" @@ -31,29 +28,17 @@ 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 - socks5Tcp net.Listener - socks5Unix net.Listener -} - -type UDPSession struct { - conn interface{} - remoteAddr net.Addr + core *core.Core + multicast *multicast.Multicast + admin *admin.AdminSocket } // The main function is responsible for configuring and starting Yggdrasil. func main() { - var localtcp types.TCPLocalMappings - var localudp types.UDPLocalMappings - var remotetcp types.TCPRemoteMappings - var remoteudp types.UDPRemoteMappings + var expose types.TCPMappings 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") @@ -67,12 +52,9 @@ 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; or UNIX socket file path, i.e. /tmp/yggstack.sock") + socks := flag.String("socks", "", "address to listen on for SOCKS, i.e. :1080") nameserver := flag.String("nameserver", "", "the Yggdrasil IPv6 address to use as a DNS server for SOCKS") - 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.Var(&expose, "exposetcp", "TCP 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. @@ -113,8 +95,6 @@ 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. @@ -134,7 +114,6 @@ func main() { _ = f.Close() case *genconf: - // Force AdminListen to none in yggstack cfg.AdminListen = "none" var bs []byte if *confjson { @@ -289,164 +268,29 @@ func main() { // Create SOCKS server { - if socks != nil && *socks != "" { + if socks != nil && nameserver != nil && *socks != "" { + resolver := types.NewNameResolver(s, *nameserver) socksOptions := []socks5.Option{ socks5.WithDial(s.DialContext), - } - 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)) + socks5.WithResolver(resolver), } if logger.GetLevel("debug") { socksOptions = append(socksOptions, socks5.WithLogger(logger)) } server := socks5.NewServer(socksOptions...) - 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) - } - }() - } + go server.ListenAndServe("tcp", *socks) // nolint:errcheck } } - // Create local TCP mappings (forwarding connections from local port - // to remote Yggdrasil node) + // Create TCP mappings { - 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 { + for _, mapping := range expose { go func(mapping types.TCPMapping) { listener, err := s.ListenTCP(mapping.Listen) if err != nil { panic(err) } - logger.Infof("Mapping Yggdrasil TCP port %d to %s", mapping.Listen.Port, mapping.Mapped) + logger.Infof("Mapping Yggdrasil port %d to %s", mapping.Listen.Port, mapping.Mapped) for { c, err := listener.Accept() if err != nil { @@ -464,110 +308,15 @@ 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) diff --git a/go.mod b/go.mod index 886bba9..256a94d 100644 --- a/go.mod +++ b/go.mod @@ -1,38 +1,34 @@ module github.com/yggdrasil-network/yggstack -go 1.22.0 - -toolchain go1.22.5 +go 1.21.4 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.5 - github.com/yggdrasil-network/yggdrasil-go v0.5.12 - gvisor.dev/gvisor v0.0.0-20240810013311-326fe0f2a77f + 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 ) require ( - github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 // indirect + github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd // indirect github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d // 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 + 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 ) diff --git a/go.sum b/go.sum index 98926b3..9587354 100644 --- a/go.sum +++ b/go.sum @@ -1,73 +1,179 @@ -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/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/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.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/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/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.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/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/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.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/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/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/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/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/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/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/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/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/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= +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= 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-20240810013311-326fe0f2a77f h1:llng6MGz63x02Xs1Ft71riYTfau4zS8OAZKv5jgPX6M= -gvisor.dev/gvisor v0.0.0-20240810013311-326fe0f2a77f/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU= +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= diff --git a/src/netstack/netstack.go b/src/netstack/netstack.go index 9b723d9..42fb056 100644 --- a/src/netstack/netstack.go +++ b/src/netstack/netstack.go @@ -86,12 +86,12 @@ func (s *YggdrasilNetstack) DialContext(ctx context.Context, network, address st } } -func (s *YggdrasilNetstack) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) { +func (s *YggdrasilNetstack) DialTCP(addr *net.TCPAddr) (net.Conn, error) { fa, pn, _ := convertToFullAddr(addr.IP, addr.Port) return gonet.DialTCP(s.stack, fa, pn) } -func (s *YggdrasilNetstack) DialUDP(addr *net.UDPAddr) (*gonet.UDPConn, error) { +func (s *YggdrasilNetstack) DialUDP(addr *net.UDPAddr) (net.PacketConn, 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) (*gonet.UDPConn, error) { +func (s *YggdrasilNetstack) ListenUDP(addr *net.UDPAddr) (net.PacketConn, error) { fa, pn, _ := convertToFullAddr(addr.IP, addr.Port) return gonet.DialUDP(s.stack, &fa, nil, pn) } diff --git a/src/netstack/yggdrasil.go b/src/netstack/yggdrasil.go index efb8811..3a582aa 100644 --- a/src/netstack/yggdrasil.go +++ b/src/netstack/yggdrasil.go @@ -12,7 +12,6 @@ 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 { @@ -21,17 +20,15 @@ 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), - rstPackets: make(chan *stack.PacketBuffer, 100), + ipv6rwc: rwc, + readBuf: make([]byte, mtu), + writeBuf: make([]byte, mtu), } if err := s.stack.CreateNIC(1, nic); err != nil { return err @@ -51,15 +48,6 @@ 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{} @@ -97,60 +85,29 @@ 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() { - 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.writePacket(pkt) + vv := pkt.ToView() + n, err := vv.Read(e.writeBuf) if err != nil { log.Println(err) - return i - 1, err + return i - 1, &tcpip.ErrAborted{} + } + _, err = e.ipv6rwc.Write(e.writeBuf[:n]) + if err != nil { + log.Println(err) + return i - 1, &tcpip.ErrAborted{} } } @@ -172,9 +129,8 @@ func (e *YggdrasilNIC) ParseHeader(*stack.PacketBuffer) bool { return true } -func (e *YggdrasilNIC) Close() { +func (e *YggdrasilNIC) Close() error { e.stack.stack.RemoveNIC(1) e.dispatcher = nil + return nil } - -func (e *YggdrasilNIC) SetOnCloseAction(func()) {} diff --git a/src/types/mapping.go b/src/types/mapping.go index 015755f..0d2dd3d 100644 --- a/src/types/mapping.go +++ b/src/types/mapping.go @@ -7,381 +7,62 @@ 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 : - - 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 - // :: - - 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 - // ::: - - 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 TCPLocalMappings []TCPMapping +type TCPMappings []TCPMapping -func (m *TCPLocalMappings) String() string { +func (m *TCPMappings) String() string { return "" } -func (m *TCPLocalMappings) Set(value string) error { - first_address, first_port, second_address, second_port, err := - parseMappingString(value) - +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]) if err != nil { - return err + return fmt.Errorf("listen port is invalid: %w", 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") + if listenport == 0 { + return fmt.Errorf("listen port must not be zero") } - - // Create mapping - mapping := TCPMapping{ Listen: &net.TCPAddr{ - Port: first_port, + Port: listenport, }, Mapped: &net.TCPAddr{ IP: net.IPv6loopback, - Port: second_port, + Port: listenport, }, } - - 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) + tokens = tokens[1:] + if len(tokens) > 0 { + mappedaddr := net.ParseIP(tokens[0]) if mappedaddr == nil { - return fmt.Errorf("invalid mapped address %q", second_address) + return fmt.Errorf("invalid mapped address %q", tokens[0]) } - // 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 } diff --git a/src/types/mapping_test.go b/src/types/mapping_test.go index accc273..b96c0e6 100644 --- a/src/types/mapping_test.go +++ b/src/types/mapping_test.go @@ -3,108 +3,26 @@ package types import "testing" func TestEndpointMappings(t *testing.T) { - var tcpMappings TCPMappings - if err := tcpMappings.Set("1234"); err != nil { + var mappings TCPMappings + if err := mappings.Set("1234"); err != nil { t.Fatal(err) } - if err := tcpMappings.Set("1234:192.168.1.1"); err != nil { + if err := mappings.Set("1234:192.168.1.1"); err != nil { t.Fatal(err) } - if err := tcpMappings.Set("1234:192.168.1.1:4321"); err != nil { + if err := mappings.Set("1234:192.168.1.1:4321"); err != nil { t.Fatal(err) } - if err := tcpMappings.Set("192.168.1.2:1234:192.168.1.1:4321"); err != nil { + if err := mappings.Set("1234:[2000::1]:4321"); err != nil { t.Fatal(err) } - 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 { + if err := mappings.Set("a"); err == nil { t.Fatal("'a' should be an invalid exposed port") } - if err := tcpMappings.Set("1234:localhost"); err == nil { + if err := mappings.Set("1234:localhost"); err == nil { t.Fatal("mapped address must be an IP literal") } - 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 { + if err := mappings.Set("1234:localhost:a"); err == nil { t.Fatal("'a' should be an invalid mapped port") } } diff --git a/src/types/udpproxy.go b/src/types/udpproxy.go deleted file mode 100644 index a291b60..0000000 --- a/src/types/udpproxy.go +++ /dev/null @@ -1,22 +0,0 @@ -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 -}