mirror of
				https://github.com/yggdrasil-network/yggstack.git
				synced 2025-11-04 08:25:06 +03:00 
			
		
		
		
	Compare commits
	
		
			39 commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						8ad1962f64 | ||
| 
							 | 
						83bc9a2eec | ||
| 
							 | 
						696625c14f | ||
| 
							 | 
						763e4db047 | ||
| 
							 | 
						c65f79d01a | ||
| 
							 | 
						58b3efd2da | ||
| 
							 | 
						bfc106637a | ||
| 
							 | 
						08f51fded2 | ||
| 
							 | 
						7a72fe25ab | ||
| 
							 | 
						873b19b7c6 | ||
| 
							 | 
						98b1a1c79d | ||
| 
							 | 
						b132d6af16 | ||
| 
							 | 
						4844607750 | ||
| 
							 | 
						9395ddbfa2 | ||
| 
							 | 
						5a87e43f9a | ||
| 
							 | 
						fef083ae4a | ||
| 
							 | 
						95a41a3e8f | ||
| 
							 | 
						3be03c793f | ||
| 
							 | 
						b534d1205e | ||
| 
							 | 
						1eb9a94d2c | ||
| 
							 | 
						9da884697a | ||
| 
							 | 
						f0eecd21d4 | ||
| 
							 | 
						8a6c67475c | ||
| 
							 | 
						2c09a1d7b2 | ||
| 
							 | 
						5008a4ea2d | ||
| 
							 | 
						457b1d99c5 | ||
| 
							 | 
						f529064aa0 | ||
| 
							 | 
						0783b429fd | ||
| 
							 | 
						30d51ba566 | ||
| 
							 | 
						b160b3f66d | ||
| 
							 | 
						582fe511fa | ||
| 
							 | 
						dcc0284e3b | ||
| 
							 | 
						174bb7026d | ||
| 
							 | 
						edbaa72445 | ||
| 
							 | 
						7b5300a476 | ||
| 
							 | 
						5520ef03bf | ||
| 
							 | 
						f465d71770 | ||
| 
							 | 
						8f21eaa31a | ||
| 
							 | 
						3fa4809cd1 | 
					 12 changed files with 1180 additions and 308 deletions
				
			
		
							
								
								
									
										278
									
								
								.github/workflows/trunk.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										278
									
								
								.github/workflows/trunk.yml
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,8 +1,17 @@
 | 
				
			||||||
name: Trunk build
 | 
					name: Trunk / release build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
on:
 | 
					on:
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					    paths-ignore:
 | 
				
			||||||
 | 
					      - "README.md"
 | 
				
			||||||
  push:
 | 
					  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:
 | 
					concurrency:
 | 
				
			||||||
  group: ${{ github.workflow }}-${{ github.ref }}
 | 
					  group: ${{ github.workflow }}-${{ github.ref }}
 | 
				
			||||||
| 
						 | 
					@ -10,69 +19,246 @@ concurrency:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
permissions:
 | 
					permissions:
 | 
				
			||||||
  contents: write
 | 
					  contents: write
 | 
				
			||||||
 | 
					  packages: write
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  build:
 | 
					  build:
 | 
				
			||||||
    strategy:
 | 
					    strategy:
 | 
				
			||||||
      fail-fast: false
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    name: Build Windows/Linux/MacOS/FreeBSD/Android
 | 
					    name: Build Windows/Linux-static/MacOS/*BSD-static/Android
 | 
				
			||||||
    runs-on: ubuntu-20.04
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v3
 | 
					      - uses: actions/checkout@v4
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          fetch-depth: 0
 | 
					          fetch-depth: 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Set up Go
 | 
					      - uses: actions/setup-go@v5
 | 
				
			||||||
        uses: actions/setup-go@v3
 | 
					 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          go-version: "1.21"
 | 
					          go-version: '1.22'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Build static executables
 | 
					      - name: Check if commit needs a release upload
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          GOOS=windows GOARCH=arm GOARM=7 ./build -o yggstack-windows-armv7.exe
 | 
					          if [ "${{ github.ref_type }}" = "tag" ]; then
 | 
				
			||||||
          GOOS=windows GOARCH=arm64 ./build -o yggstack-windows-arm64.exe
 | 
					            echo "RELEASENAME=${{ github.ref_name }}" >> "$GITHUB_ENV"
 | 
				
			||||||
          GOOS=windows GOARCH=386 ./build -o yggstack-windows-i386.exe
 | 
					          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=windows GOARCH=amd64 ./build -o yggstack-windows-amd64.exe
 | 
				
			||||||
          GOOS=linux GOARCH=386 ./build -o yggstack-linux-i386
 | 
					          echo "::endgroup::"
 | 
				
			||||||
          GOOS=linux GOARCH=amd64 ./build -o yggstack-linux-amd64
 | 
					          #
 | 
				
			||||||
          GOOS=linux GOARCH=arm GOARM=6 ./build -o yggstack-linux-armv6
 | 
					          echo "::group::yggstack-windows-arm64.exe"
 | 
				
			||||||
          GOOS=linux GOARCH=arm GOARM=7 ./build -o yggstack-linux-armv7
 | 
					          GOOS=windows GOARCH=arm64 ./build -o yggstack-windows-arm64.exe
 | 
				
			||||||
          GOOS=linux GOARCH=arm64 ./build -o yggstack-linux-arm64
 | 
					          echo "::endgroup::"
 | 
				
			||||||
          GOOS=linux GOARCH=mips GOMIPS=softfloat ./build -o yggstack-linux-mips-sf
 | 
					          #
 | 
				
			||||||
          GOOS=linux GOARCH=mipsle GOMIPS=softfloat ./build -o yggstack-linux-mipsle-sf
 | 
					          echo "::group::yggstack-windows-armv7.exe"
 | 
				
			||||||
          GOOS=linux GOARCH=mips64 ./build -o yggstack-linux-mips64
 | 
					          GOOS=windows GOARCH=arm GOARM=7 ./build -o yggstack-windows-armv7.exe
 | 
				
			||||||
          GOOS=linux GOARCH=mips64le ./build -o yggstack-linux-mips64le
 | 
					          echo "::endgroup::"
 | 
				
			||||||
          GOOS=linux GOARCH=ppc64 ./build -o yggstack-linux-ppc64
 | 
					          #
 | 
				
			||||||
          GOOS=linux GOARCH=ppc64le ./build -o yggstack-linux-ppc64le
 | 
					          echo "::group::yggstack-windows-i386.exe"
 | 
				
			||||||
          GOOS=linux GOARCH=riscv64 ./build -o yggstack-linux-riscv64
 | 
					          GOOS=windows GOARCH=386 ./build -o yggstack-windows-i386.exe
 | 
				
			||||||
          GOOS=linux GOARCH=s390x ./build -o yggstack-linux-s390x
 | 
					          GOOS=windows GOARCH=386 ./build -o yggstack-windows-386.exe
 | 
				
			||||||
          GOOS=darwin GOARCH=arm64 ./build -o yggstack-darwin-arm64
 | 
					          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=darwin GOARCH=amd64 ./build -o yggstack-darwin-amd64
 | 
				
			||||||
          GOOS=freebsd GOARCH=arm64 ./build -o yggstack-freebsd-arm64
 | 
					          echo "::endgroup::"
 | 
				
			||||||
          GOOS=freebsd GOARCH=amd64 ./build -o yggstack-freebsd-amd64
 | 
					          #
 | 
				
			||||||
          GOOS=freebsd GOARCH=arm GOARM=6 ./build -o yggstack-freebsd-armv6
 | 
					          echo "::group::yggstack-darwin-arm64"
 | 
				
			||||||
          GOOS=freebsd GOARCH=arm GOARM=7 ./build -o yggstack-freebsd-armv7
 | 
					          GOOS=darwin GOARCH=arm64 ./build -o yggstack-darwin-arm64
 | 
				
			||||||
          GOOS=freebsd GOARCH=386 ./build -o yggstack-freebsd-i386
 | 
					          echo "::endgroup::"
 | 
				
			||||||
          GOOS=openbsd GOARCH=arm64 ./build -o yggstack-openbsd-arm64
 | 
					          #
 | 
				
			||||||
          GOOS=openbsd GOARCH=amd64 ./build -o yggstack-openbsd-amd64
 | 
					          echo "::group::yggstack-freebsd-amd64-static"
 | 
				
			||||||
          GOOS=openbsd GOARCH=arm GOARM=6 ./build -o yggstack-openbsd-armv6
 | 
					          GOOS=freebsd GOARCH=amd64 ./build -o yggstack-freebsd-amd64-static
 | 
				
			||||||
          GOOS=openbsd GOARCH=arm GOARM=7 ./build -o yggstack-openbsd-armv7
 | 
					          echo "::endgroup::"
 | 
				
			||||||
          GOOS=openbsd GOARCH=386 ./build -o yggstack-openbsd-i386
 | 
					          #
 | 
				
			||||||
          GOOS=netbsd GOARCH=arm64 ./build -o yggstack-netbsd-arm64
 | 
					          echo "::group::yggstack-freebsd-arm64-static"
 | 
				
			||||||
          GOOS=netbsd GOARCH=amd64 ./build -o yggstack-netbsd-amd64
 | 
					          GOOS=freebsd GOARCH=arm64 ./build -o yggstack-freebsd-arm64-static
 | 
				
			||||||
          GOOS=netbsd GOARCH=arm GOARM=6 ./build -o yggstack-netbsd-armv6
 | 
					          echo "::endgroup::"
 | 
				
			||||||
          GOOS=netbsd GOARCH=arm GOARM=7 ./build -o yggstack-netbsd-armv7
 | 
					          #
 | 
				
			||||||
          GOOS=netbsd GOARCH=386 ./build -o yggstack-netbsd-i386
 | 
					          echo "::group::yggstack-freebsd-armv6-static"
 | 
				
			||||||
          CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang" GOOS=android GOARCH=arm64 ./build -o yggstack-android-arm64
 | 
					          GOOS=freebsd GOARCH=arm GOARM=6 ./build -o yggstack-freebsd-armv6-static
 | 
				
			||||||
          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::"
 | 
				
			||||||
          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 "::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
 | 
					          #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
 | 
					          #GOOS=ios GOARCH=amd64 CC=$(go env GOROOT)/misc/ios/clangwrap.sh ./build -o yggstack-ios-amd64
 | 
				
			||||||
 | 
					          #echo "::endgroup::"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Publish release
 | 
					      - name: Publish release
 | 
				
			||||||
 | 
					        if: ${{ env.SKIP_UPLOAD == '' }}
 | 
				
			||||||
        run: |
 | 
					        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:
 | 
					        env:
 | 
				
			||||||
          GH_TOKEN: ${{ github.token }}
 | 
					          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
									
								
							
							
						
						
									
										9
									
								
								Dockerfile.static
									
										
									
									
									
										Normal 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" ]
 | 
				
			||||||
							
								
								
									
										76
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										76
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
# Yggstack - Yggdrasil as SOCKS proxy / port forwarder
 | 
					# Yggstack - Yggdrasil as SOCKS proxy / port forwarder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://github.com/yggdrasil-network/yggstack/actions/workflows/ci.yml)
 | 
					[](https://github.com/yggdrasil-network/yggstack/actions/workflows/trunk.yml)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Introduction
 | 
					## 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
 | 
					page for more information. You may also find other platform-specific wrappers, scripts
 | 
				
			||||||
or tools in the `contrib` folder.
 | 
					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
 | 
					## Building
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you want to build from source, as opposed to installing one of the pre-built
 | 
					If you want to build from source, as opposed to installing one of the pre-built
 | 
				
			||||||
packages:
 | 
					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. Clone this repository
 | 
				
			||||||
2. Run `./build`
 | 
					2. Run `./build`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,17 +72,48 @@ other configuration such as listen addresses or multicast addresses, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Run Yggstack
 | 
					### 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
 | 
					./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
 | 
					To run SOCKS proxy server listening on UNIX socket file `/tmp/yggstack.sock`:
 | 
				
			||||||
network address at port 80:
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
./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:
 | 
					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
 | 
					You can even run several Yggstack instances with different configurations
 | 
				
			||||||
on the same OS and user!
 | 
					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
 | 
					### pk.ygg DNS resolver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
One unique feature of Yggstack is built-in DNS resolver functionality using
 | 
					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`)
 | 
					For example, HowToYgg website (whose public key is `d40d4a7153cf288ea28f1865f6cfe95143a478b5c8c9e7cb002a0633d10a53eb`)
 | 
				
			||||||
can be accessed by any Web browser supporting SOCKS servers
 | 
					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:
 | 
					You can even use cURL with Yggstack:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								build
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								build
									
										
									
									
									
								
							| 
						 | 
					@ -9,7 +9,7 @@ PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
 | 
				
			||||||
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
 | 
					LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
 | 
				
			||||||
ARGS="-v"
 | 
					ARGS="-v"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
while getopts "utc:l:dro:p" option
 | 
					while getopts "utc:l:dro:ps" option
 | 
				
			||||||
do
 | 
					do
 | 
				
			||||||
  case "$option"
 | 
					  case "$option"
 | 
				
			||||||
  in
 | 
					  in
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ do
 | 
				
			||||||
  r) ARGS="$ARGS -race";;
 | 
					  r) ARGS="$ARGS -race";;
 | 
				
			||||||
  o) ARGS="$ARGS -o $OPTARG";;
 | 
					  o) ARGS="$ARGS -o $OPTARG";;
 | 
				
			||||||
  p) ARGS="$ARGS -buildmode=pie";;
 | 
					  p) ARGS="$ARGS -buildmode=pie";;
 | 
				
			||||||
 | 
					  s) ARGS="$ARGS -tags netgo,osusersgo,static" LDFLAGS="$LDFLAGS -extldflags '-static'" CGO_ENABLED=0;;
 | 
				
			||||||
  esac
 | 
					  esac
 | 
				
			||||||
done
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,13 +5,16 @@ import (
 | 
				
			||||||
	"crypto/ed25519"
 | 
						"crypto/ed25519"
 | 
				
			||||||
	"encoding/hex"
 | 
						"encoding/hex"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/signal"
 | 
						"os/signal"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gologme/log"
 | 
						"github.com/gologme/log"
 | 
				
			||||||
| 
						 | 
					@ -28,17 +31,29 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/yggdrasil-network/yggstack/src/netstack"
 | 
						"github.com/yggdrasil-network/yggstack/src/netstack"
 | 
				
			||||||
	"github.com/yggdrasil-network/yggstack/src/types"
 | 
						"github.com/yggdrasil-network/yggstack/src/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type node struct {
 | 
					type node struct {
 | 
				
			||||||
	core      *core.Core
 | 
						core           *core.Core
 | 
				
			||||||
	multicast *multicast.Multicast
 | 
						multicast      *multicast.Multicast
 | 
				
			||||||
	admin     *admin.AdminSocket
 | 
						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.
 | 
					// The main function is responsible for configuring and starting Yggdrasil.
 | 
				
			||||||
func main() {
 | 
					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")
 | 
						genconf := flag.Bool("genconf", false, "print a new config to stdout")
 | 
				
			||||||
	useconf := flag.Bool("useconf", false, "read HJSON/JSON config from stdin")
 | 
						useconf := flag.Bool("useconf", false, "read HJSON/JSON config from stdin")
 | 
				
			||||||
	useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path")
 | 
						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")
 | 
						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")
 | 
						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")
 | 
						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")
 | 
						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()
 | 
						flag.Parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Catch interrupts from the operating system to exit gracefully.
 | 
						// Catch interrupts from the operating system to exit gracefully.
 | 
				
			||||||
| 
						 | 
					@ -95,6 +113,8 @@ func main() {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case *autoconf:
 | 
						case *autoconf:
 | 
				
			||||||
 | 
							// Force AdminListen to none in yggstack
 | 
				
			||||||
 | 
							cfg.AdminListen = "none"
 | 
				
			||||||
		// Use an autoconf-generated config, this will give us random keys and
 | 
							// Use an autoconf-generated config, this will give us random keys and
 | 
				
			||||||
		// port numbers, and will use an automatically selected TUN interface.
 | 
							// port numbers, and will use an automatically selected TUN interface.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -114,6 +134,7 @@ func main() {
 | 
				
			||||||
		_ = f.Close()
 | 
							_ = f.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case *genconf:
 | 
						case *genconf:
 | 
				
			||||||
 | 
							// Force AdminListen to none in yggstack
 | 
				
			||||||
		cfg.AdminListen = "none"
 | 
							cfg.AdminListen = "none"
 | 
				
			||||||
		var bs []byte
 | 
							var bs []byte
 | 
				
			||||||
		if *confjson {
 | 
							if *confjson {
 | 
				
			||||||
| 
						 | 
					@ -268,29 +289,164 @@ func main() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create SOCKS server
 | 
						// Create SOCKS server
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if socks != nil && nameserver != nil && *socks != "" {
 | 
							if socks != nil && *socks != "" {
 | 
				
			||||||
			resolver := types.NewNameResolver(s, *nameserver)
 | 
					 | 
				
			||||||
			socksOptions := []socks5.Option{
 | 
								socksOptions := []socks5.Option{
 | 
				
			||||||
				socks5.WithDial(s.DialContext),
 | 
									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") {
 | 
								if logger.GetLevel("debug") {
 | 
				
			||||||
				socksOptions = append(socksOptions, socks5.WithLogger(logger))
 | 
									socksOptions = append(socksOptions, socks5.WithLogger(logger))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			server := socks5.NewServer(socksOptions...)
 | 
								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) {
 | 
								go func(mapping types.TCPMapping) {
 | 
				
			||||||
				listener, err := s.ListenTCP(mapping.Listen)
 | 
									listener, err := s.ListenTCP(mapping.Listen)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					panic(err)
 | 
										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 {
 | 
									for {
 | 
				
			||||||
					c, err := listener.Accept()
 | 
										c, err := listener.Accept()
 | 
				
			||||||
					if err != nil {
 | 
										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.
 | 
						// Block until we are told to shut down.
 | 
				
			||||||
	<-ctx.Done()
 | 
						<-ctx.Done()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Shut down the node.
 | 
						// Shut down the node.
 | 
				
			||||||
	_ = n.admin.Stop()
 | 
						_ = n.admin.Stop()
 | 
				
			||||||
	_ = n.multicast.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()
 | 
						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) {
 | 
					func setLogLevel(loglevel string, logger *log.Logger) {
 | 
				
			||||||
	levels := [...]string{"error", "warn", "info", "debug", "trace"}
 | 
						levels := [...]string{"error", "warn", "info", "debug", "trace"}
 | 
				
			||||||
	loglevel = strings.ToLower(loglevel)
 | 
						loglevel = strings.ToLower(loglevel)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										48
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										48
									
								
								go.mod
									
										
									
									
									
								
							| 
						 | 
					@ -1,34 +1,38 @@
 | 
				
			||||||
module github.com/yggdrasil-network/yggstack
 | 
					module github.com/yggdrasil-network/yggstack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
go 1.21.4
 | 
					go 1.22.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					toolchain go1.22.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/gologme/log v1.3.0
 | 
						github.com/gologme/log v1.3.0
 | 
				
			||||||
	github.com/hashicorp/go-syslog v1.0.0
 | 
						github.com/hashicorp/go-syslog v1.0.0
 | 
				
			||||||
	github.com/hjson/hjson-go/v4 v4.4.0
 | 
						github.com/hjson/hjson-go/v4 v4.4.0
 | 
				
			||||||
	github.com/things-go/go-socks5 v0.0.4
 | 
						github.com/things-go/go-socks5 v0.0.5
 | 
				
			||||||
	github.com/yggdrasil-network/yggdrasil-go v0.5.4
 | 
						github.com/yggdrasil-network/yggdrasil-go v0.5.12
 | 
				
			||||||
	gvisor.dev/gvisor v0.0.0-20240103195848-a9a6a6819b00
 | 
						gvisor.dev/gvisor v0.0.0-20240810013311-326fe0f2a77f
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					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/Arceliar/phony v0.0.0-20220903101357-530938a4b13d // indirect
 | 
				
			||||||
	github.com/bits-and-blooms/bitset v1.13.0 // indirect
 | 
						github.com/bits-and-blooms/bitset v1.14.3 // indirect
 | 
				
			||||||
	github.com/bits-and-blooms/bloom/v3 v3.6.0 // indirect
 | 
						github.com/bits-and-blooms/bloom/v3 v3.7.0 // indirect
 | 
				
			||||||
	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
 | 
						github.com/coder/websocket v1.8.12 // indirect
 | 
				
			||||||
	github.com/google/btree v1.1.2 // indirect
 | 
						github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
 | 
				
			||||||
	github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect
 | 
						github.com/google/btree v1.1.3 // indirect
 | 
				
			||||||
	github.com/onsi/ginkgo/v2 v2.13.2 // indirect
 | 
						github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect
 | 
				
			||||||
	github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
 | 
						github.com/onsi/ginkgo/v2 v2.20.2 // indirect
 | 
				
			||||||
	github.com/quic-go/quic-go v0.40.1 // indirect
 | 
						github.com/quic-go/quic-go v0.48.2 // indirect
 | 
				
			||||||
	go.uber.org/mock v0.4.0 // indirect
 | 
						github.com/wlynxg/anet v0.0.5 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.17.0 // indirect
 | 
						go.uber.org/mock v0.5.0 // indirect
 | 
				
			||||||
	golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
 | 
						golang.org/x/crypto v0.31.0 // indirect
 | 
				
			||||||
	golang.org/x/mod v0.14.0 // indirect
 | 
						golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
 | 
				
			||||||
	golang.org/x/net v0.19.0 // indirect
 | 
						golang.org/x/mod v0.21.0 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.16.0 // indirect
 | 
						golang.org/x/net v0.32.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.14.0 // indirect
 | 
						golang.org/x/sync v0.10.0 // indirect
 | 
				
			||||||
	golang.org/x/time v0.5.0 // indirect
 | 
						golang.org/x/sys v0.28.0 // indirect
 | 
				
			||||||
	golang.org/x/tools v0.16.1 // 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
									
										
									
									
									
								
							
							
						
						
									
										218
									
								
								go.sum
									
										
									
									
									
								
							| 
						 | 
					@ -1,179 +1,73 @@
 | 
				
			||||||
github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8 h1:qyXiPZClVoe6QsbsiDP23g0Ze/MNXiHIAL/nVNfauH0=
 | 
					github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 h1:d8N0z+udAnbU5PdjpLSNPTWlqeU/nnYsQ42B6+879aw=
 | 
				
			||||||
github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
 | 
					github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0=
 | 
				
			||||||
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 h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
 | 
				
			||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
 | 
					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.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.14.3 h1:Gd2c8lSNf9pKXom5JtD7AaKO8o7fGQ2LtFj1436qilA=
 | 
				
			||||||
github.com/bits-and-blooms/bitset v1.11.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
 | 
					github.com/bits-and-blooms/bitset v1.14.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
 | 
				
			||||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
 | 
					github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls=
 | 
				
			||||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
 | 
					github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg=
 | 
				
			||||||
github.com/bits-and-blooms/bloom/v3 v3.3.1 h1:K2+A19bXT8gJR5mU7y+1yW6hsKfNCjcP2uNfLFKncjQ=
 | 
					github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
 | 
				
			||||||
github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90=
 | 
					github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
 | 
				
			||||||
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					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.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 | 
				
			||||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 | 
					github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 | 
				
			||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
 | 
					github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 | 
				
			||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
 | 
					github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 | 
				
			||||||
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 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo=
 | 
				
			||||||
github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4=
 | 
					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.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
 | 
				
			||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
 | 
					github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
 | 
				
			||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 | 
					github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 | 
				
			||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
					github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
				
			||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
 | 
					github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs=
 | 
				
			||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
					github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
 | 
				
			||||||
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 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
 | 
				
			||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
 | 
					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 h1:D/NPvqOCH6/eisTb5/ztuIS8GUvmpHaLOcNk1Bjr298=
 | 
				
			||||||
github.com/hjson/hjson-go/v4 v4.4.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
 | 
					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.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
 | 
				
			||||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
 | 
					github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
 | 
				
			||||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
 | 
					github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
 | 
				
			||||||
github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU=
 | 
					github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
 | 
				
			||||||
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					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/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
 | 
				
			||||||
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
 | 
					github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
 | 
				
			||||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
 | 
					github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 | 
				
			||||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
 | 
					github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
				
			||||||
github.com/quic-go/quic-go v0.39.3 h1:o3YB6t2SR+HU/pgwF29kJ6g4jJIJEwEZ8CKia1h1TKg=
 | 
					github.com/things-go/go-socks5 v0.0.5 h1:qvKaGcBkfDrUL33SchHN93srAmYGzb4CxSM2DPYufe8=
 | 
				
			||||||
github.com/quic-go/quic-go v0.39.3/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
 | 
					github.com/things-go/go-socks5 v0.0.5/go.mod h1:mtzInf8v5xmsBpHZVbIw2YQYhc4K0jRwzfsH64Uh0IQ=
 | 
				
			||||||
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 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=
 | 
				
			||||||
github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
 | 
					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/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
 | 
				
			||||||
github.com/yggdrasil-network/yggdrasil-go v0.5.3/go.mod h1:qRzHB8bKEIpd1pQVYoZ+SedJJTBG0X9sbyctWxQDDGA=
 | 
					github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
 | 
				
			||||||
github.com/yggdrasil-network/yggdrasil-go v0.5.4 h1:A7ZFmxkkbZhtqJgQXBVDw5sHsi25aUawLlJCCHnNsAs=
 | 
					github.com/yggdrasil-network/yggdrasil-go v0.5.12 h1:SaQ8d59JP+uFy+nOWXTx1ETM5r2uCfe1Gt/d+IodHJw=
 | 
				
			||||||
github.com/yggdrasil-network/yggdrasil-go v0.5.4/go.mod h1:TLmU4X0nfzCY9t5xABtFQ6GLoOtCae8xVatC9JwjD5I=
 | 
					github.com/yggdrasil-network/yggdrasil-go v0.5.12/go.mod h1:u4DU6dpTfWmVs8r0WjW1T3UpGyeUh9vRrS8zngvncwM=
 | 
				
			||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 | 
					go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
 | 
				
			||||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
 | 
					go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
 | 
				
			||||||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
 | 
					golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
 | 
				
			||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
 | 
					golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
 | 
				
			||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
 | 
					golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
					golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
					golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
 | 
				
			||||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 | 
					golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
 | 
				
			||||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
 | 
					golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
 | 
				
			||||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
 | 
					golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
 | 
				
			||||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
 | 
					golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
 | 
				
			||||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
 | 
					golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 | 
				
			||||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
 | 
					golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
 | 
				
			||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
 | 
					golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
 | 
					golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
 | 
					golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
 | 
					golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
 | 
					golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
 | 
					golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
 | 
					golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
 | 
				
			||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 | 
					google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
 | 
				
			||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 | 
					google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 | 
				
			||||||
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					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-20240810013311-326fe0f2a77f h1:llng6MGz63x02Xs1Ft71riYTfau4zS8OAZKv5jgPX6M=
 | 
				
			||||||
gvisor.dev/gvisor v0.0.0-20231125084359-4b4191b8cad1/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
 | 
					gvisor.dev/gvisor v0.0.0-20240810013311-326fe0f2a77f/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU=
 | 
				
			||||||
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=
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
						fa, pn, _ := convertToFullAddr(addr.IP, addr.Port)
 | 
				
			||||||
	return gonet.DialTCP(s.stack, fa, pn)
 | 
						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)
 | 
						fa, pn, _ := convertToFullAddr(addr.IP, addr.Port)
 | 
				
			||||||
	return gonet.DialUDP(s.stack, nil, &fa, pn)
 | 
						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)
 | 
						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)
 | 
						fa, pn, _ := convertToFullAddr(addr.IP, addr.Port)
 | 
				
			||||||
	return gonet.DialUDP(s.stack, &fa, nil, pn)
 | 
						return gonet.DialUDP(s.stack, &fa, nil, pn)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ import (
 | 
				
			||||||
	"gvisor.dev/gvisor/pkg/tcpip/header"
 | 
						"gvisor.dev/gvisor/pkg/tcpip/header"
 | 
				
			||||||
	"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
 | 
						"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
 | 
				
			||||||
	"gvisor.dev/gvisor/pkg/tcpip/stack"
 | 
						"gvisor.dev/gvisor/pkg/tcpip/stack"
 | 
				
			||||||
 | 
						"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type YggdrasilNIC struct {
 | 
					type YggdrasilNIC struct {
 | 
				
			||||||
| 
						 | 
					@ -20,15 +21,17 @@ type YggdrasilNIC struct {
 | 
				
			||||||
	dispatcher stack.NetworkDispatcher
 | 
						dispatcher stack.NetworkDispatcher
 | 
				
			||||||
	readBuf    []byte
 | 
						readBuf    []byte
 | 
				
			||||||
	writeBuf   []byte
 | 
						writeBuf   []byte
 | 
				
			||||||
 | 
						rstPackets chan *stack.PacketBuffer
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *YggdrasilNetstack) NewYggdrasilNIC(ygg *core.Core) tcpip.Error {
 | 
					func (s *YggdrasilNetstack) NewYggdrasilNIC(ygg *core.Core) tcpip.Error {
 | 
				
			||||||
	rwc := ipv6rwc.NewReadWriteCloser(ygg)
 | 
						rwc := ipv6rwc.NewReadWriteCloser(ygg)
 | 
				
			||||||
	mtu := rwc.MTU()
 | 
						mtu := rwc.MTU()
 | 
				
			||||||
	nic := &YggdrasilNIC{
 | 
						nic := &YggdrasilNIC{
 | 
				
			||||||
		ipv6rwc:  rwc,
 | 
							ipv6rwc:    rwc,
 | 
				
			||||||
		readBuf:  make([]byte, mtu),
 | 
							readBuf:    make([]byte, mtu),
 | 
				
			||||||
		writeBuf: make([]byte, mtu),
 | 
							writeBuf:   make([]byte, mtu),
 | 
				
			||||||
 | 
							rstPackets: make(chan *stack.PacketBuffer, 100),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := s.stack.CreateNIC(1, nic); err != nil {
 | 
						if err := s.stack.CreateNIC(1, nic); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
| 
						 | 
					@ -48,6 +51,15 @@ func (s *YggdrasilNetstack) NewYggdrasilNIC(ygg *core.Core) tcpip.Error {
 | 
				
			||||||
			nic.dispatcher.DeliverNetworkPacket(ipv6.ProtocolNumber, pkb)
 | 
								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")
 | 
						_, snet, err := net.ParseCIDR("0200::/7")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return &tcpip.ErrBadAddress{}
 | 
							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) MTU() uint32 { return uint32(e.ipv6rwc.MTU()) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *YggdrasilNIC) SetMTU(uint32) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (*YggdrasilNIC) Capabilities() stack.LinkEndpointCapabilities { return stack.CapabilityNone }
 | 
					func (*YggdrasilNIC) Capabilities() stack.LinkEndpointCapabilities { return stack.CapabilityNone }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (*YggdrasilNIC) MaxHeaderLength() uint16 { return 40 }
 | 
					func (*YggdrasilNIC) MaxHeaderLength() uint16 { return 40 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (*YggdrasilNIC) LinkAddress() tcpip.LinkAddress { return "" }
 | 
					func (*YggdrasilNIC) LinkAddress() tcpip.LinkAddress { return "" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*YggdrasilNIC) SetLinkAddress(tcpip.LinkAddress) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (*YggdrasilNIC) Wait() {}
 | 
					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(
 | 
					func (e *YggdrasilNIC) WritePackets(
 | 
				
			||||||
	list stack.PacketBufferList,
 | 
						list stack.PacketBufferList,
 | 
				
			||||||
) (int, tcpip.Error) {
 | 
					) (int, tcpip.Error) {
 | 
				
			||||||
	var i int = 0
 | 
						var i int = 0
 | 
				
			||||||
 | 
						var err tcpip.Error = nil
 | 
				
			||||||
	for i, pkt := range list.AsSlice() {
 | 
						for i, pkt := range list.AsSlice() {
 | 
				
			||||||
		vv := pkt.ToView()
 | 
							if pkt.Data().Size() == 0 {
 | 
				
			||||||
		n, err := vv.Read(e.writeBuf)
 | 
								if pkt.Network().TransportProtocol() == tcp.ProtocolNumber {
 | 
				
			||||||
		if err != nil {
 | 
									tcpHeader := header.TCP(pkt.TransportHeader().Slice())
 | 
				
			||||||
			log.Println(err)
 | 
									if (tcpHeader.Flags() & header.TCPFlagRst) == header.TCPFlagRst {
 | 
				
			||||||
			return i - 1, &tcpip.ErrAborted{}
 | 
										e.rstPackets <- pkt
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_, err = e.ipv6rwc.Write(e.writeBuf[:n])
 | 
							err = e.writePacket(pkt)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Println(err)
 | 
								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
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (e *YggdrasilNIC) Close() error {
 | 
					func (e *YggdrasilNIC) Close() {
 | 
				
			||||||
	e.stack.stack.RemoveNIC(1)
 | 
						e.stack.stack.RemoveNIC(1)
 | 
				
			||||||
	e.dispatcher = nil
 | 
						e.dispatcher = nil
 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *YggdrasilNIC) SetOnCloseAction(func()) {}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,62 +7,381 @@ import (
 | 
				
			||||||
	"strings"
 | 
						"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 {
 | 
					type TCPMapping struct {
 | 
				
			||||||
	Listen *net.TCPAddr
 | 
						Listen *net.TCPAddr
 | 
				
			||||||
	Mapped *net.TCPAddr
 | 
						Mapped *net.TCPAddr
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TCPMappings []TCPMapping
 | 
					type TCPLocalMappings []TCPMapping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *TCPMappings) String() string {
 | 
					func (m *TCPLocalMappings) String() string {
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *TCPMappings) Set(value string) error {
 | 
					func (m *TCPLocalMappings) Set(value string) error {
 | 
				
			||||||
	tokens := strings.Split(value, ":")
 | 
						first_address, first_port, second_address, second_port, err :=
 | 
				
			||||||
	if len(tokens) > 2 {
 | 
							parseMappingString(value)
 | 
				
			||||||
		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 {
 | 
						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{
 | 
						mapping := TCPMapping{
 | 
				
			||||||
		Listen: &net.TCPAddr{
 | 
							Listen: &net.TCPAddr{
 | 
				
			||||||
			Port: listenport,
 | 
								Port: first_port,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Mapped: &net.TCPAddr{
 | 
							Mapped: &net.TCPAddr{
 | 
				
			||||||
			IP:   net.IPv6loopback,
 | 
								IP:   net.IPv6loopback,
 | 
				
			||||||
			Port: listenport,
 | 
								Port: second_port,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tokens = tokens[1:]
 | 
					
 | 
				
			||||||
	if len(tokens) > 0 {
 | 
						if first_address != "" {
 | 
				
			||||||
		mappedaddr := net.ParseIP(tokens[0])
 | 
							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 {
 | 
							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
 | 
							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)
 | 
						*m = append(*m, mapping)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,26 +3,108 @@ package types
 | 
				
			||||||
import "testing"
 | 
					import "testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestEndpointMappings(t *testing.T) {
 | 
					func TestEndpointMappings(t *testing.T) {
 | 
				
			||||||
	var mappings TCPMappings
 | 
						var tcpMappings TCPMappings
 | 
				
			||||||
	if err := mappings.Set("1234"); err != nil {
 | 
						if err := tcpMappings.Set("1234"); err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							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)
 | 
							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)
 | 
							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)
 | 
							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")
 | 
							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")
 | 
							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")
 | 
							t.Fatal("'a' should be an invalid mapped port")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								src/types/udpproxy.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/types/udpproxy.go
									
										
									
									
									
										Normal 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue