Compare commits

..

No commits in common. "develop" and "trunk" have entirely different histories.

12 changed files with 308 additions and 1180 deletions

View file

@ -1,17 +1,8 @@
name: Trunk / release build name: Trunk build
on: on:
pull_request:
paths-ignore:
- "README.md"
push: push:
branches: branch: develop
- 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 }}
@ -19,246 +10,69 @@ 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-static/MacOS/*BSD-static/Android name: Build Windows/Linux/MacOS/FreeBSD/Android
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-go@v5 - name: Set up Go
uses: actions/setup-go@v3
with: with:
go-version: '1.22' go-version: "1.21"
- name: Check if commit needs a release upload - name: Build static executables
run: | run: |
if [ "${{ github.ref_type }}" = "tag" ]; then
echo "RELEASENAME=${{ github.ref_name }}" >> "$GITHUB_ENV"
elif [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/develop" ]; then
echo "PRERELEASE=--prerelease" >> "$GITHUB_ENV"
echo "RELEASENAME=trunk" >> "$GITHUB_ENV"
else
echo "Not a tag or push to develop branch, skipping upload"
echo "SKIP_UPLOAD=1" >> "$GITHUB_ENV"
fi
- name: Build yggstack executables
run: |
set -e
echo "::group::yggstack-windows-amd64.exe"
GOOS=windows GOARCH=amd64 ./build -o yggstack-windows-amd64.exe
echo "::endgroup::"
#
echo "::group::yggstack-windows-arm64.exe"
GOOS=windows GOARCH=arm64 ./build -o yggstack-windows-arm64.exe
echo "::endgroup::"
#
echo "::group::yggstack-windows-armv7.exe"
GOOS=windows GOARCH=arm GOARM=7 ./build -o yggstack-windows-armv7.exe GOOS=windows GOARCH=arm GOARM=7 ./build -o yggstack-windows-armv7.exe
echo "::endgroup::" GOOS=windows GOARCH=arm64 ./build -o yggstack-windows-arm64.exe
#
echo "::group::yggstack-windows-i386.exe"
GOOS=windows GOARCH=386 ./build -o yggstack-windows-i386.exe GOOS=windows GOARCH=386 ./build -o yggstack-windows-i386.exe
GOOS=windows GOARCH=386 ./build -o yggstack-windows-386.exe GOOS=windows GOARCH=amd64 ./build -o yggstack-windows-amd64.exe
echo "::endgroup::" GOOS=linux GOARCH=386 ./build -o yggstack-linux-i386
# GOOS=linux GOARCH=amd64 ./build -o yggstack-linux-amd64
# NOTE: Go toolchain does produce dynamically linked binaries GOOS=linux GOARCH=arm GOARM=6 ./build -o yggstack-linux-armv6
# for native-architecture Linux/*BSD. Since we are building on amd64, GOOS=linux GOARCH=arm GOARM=7 ./build -o yggstack-linux-armv7
# request this buid to be fully static like the othe builds GOOS=linux GOARCH=arm64 ./build -o yggstack-linux-arm64
echo "::group::yggstack-linux-amd64-static" GOOS=linux GOARCH=mips GOMIPS=softfloat ./build -o yggstack-linux-mips-sf
GOOS=linux GOARCH=amd64 ./build -s -o yggstack-linux-amd64-static GOOS=linux GOARCH=mipsle GOMIPS=softfloat ./build -o yggstack-linux-mipsle-sf
! ldd yggstack-linux-amd64-static GOOS=linux GOARCH=mips64 ./build -o yggstack-linux-mips64
echo "::endgroup::" GOOS=linux GOARCH=mips64le ./build -o yggstack-linux-mips64le
# GOOS=linux GOARCH=ppc64 ./build -o yggstack-linux-ppc64
echo "::group::yggstack-linux-arm64-static" GOOS=linux GOARCH=ppc64le ./build -o yggstack-linux-ppc64le
GOOS=linux GOARCH=arm64 ./build -o yggstack-linux-arm64-static GOOS=linux GOARCH=riscv64 ./build -o yggstack-linux-riscv64
! ldd yggstack-linux-arm64-static GOOS=linux GOARCH=s390x ./build -o yggstack-linux-s390x
echo "::endgroup::"
#
echo "::group::yggstack-linux-armv6-static"
GOOS=linux GOARCH=arm GOARM=6 ./build -o yggstack-linux-armv6-static
! ldd yggstack-linux-armv6-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-armv7-static"
GOOS=linux GOARCH=arm GOARM=7 ./build -o yggstack-linux-armv7-static
! ldd yggstack-linux-armv7-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-i386-static"
GOOS=linux GOARCH=386 ./build -o yggstack-linux-i386-static
GOOS=linux GOARCH=386 ./build -o yggstack-linux-386-static
! ldd yggstack-linux-i386-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-mips-sf-static"
GOOS=linux GOARCH=mips GOMIPS=softfloat ./build -o yggstack-linux-mips-sf-static
! ldd yggstack-linux-mips-sf-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-mipsle-sf-static"
GOOS=linux GOARCH=mipsle GOMIPS=softfloat ./build -o yggstack-linux-mipsle-sf-static
! ldd yggstack-linux-mipsle-sf-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-mips64-static"
GOOS=linux GOARCH=mips64 ./build -o yggstack-linux-mips64-static
! ldd yggstack-linux-mips64-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-mips64le-static"
GOOS=linux GOARCH=mips64le ./build -o yggstack-linux-mips64le-static
! ldd yggstack-linux-mips64le-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-ppc64-static"
GOOS=linux GOARCH=ppc64 ./build -o yggstack-linux-ppc64-static
! ldd yggstack-linux-ppc64-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-ppc64le-static"
GOOS=linux GOARCH=ppc64le ./build -o yggstack-linux-ppc64le-static
! ldd yggstack-linux-ppc64le-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-riscv64-static"
GOOS=linux GOARCH=riscv64 ./build -o yggstack-linux-riscv64-static
! ldd yggstack-linux-riscv64-static
echo "::endgroup::"
#
echo "::group::yggstack-linux-s390x-static"
GOOS=linux GOARCH=s390x ./build -o yggstack-linux-s390x-static
! ldd yggstack-linux-s390x-static
echo "::endgroup::"
#
echo "::group::yggstack-darwin-amd64"
GOOS=darwin GOARCH=amd64 ./build -o yggstack-darwin-amd64
echo "::endgroup::"
#
echo "::group::yggstack-darwin-arm64"
GOOS=darwin GOARCH=arm64 ./build -o yggstack-darwin-arm64 GOOS=darwin GOARCH=arm64 ./build -o yggstack-darwin-arm64
echo "::endgroup::" GOOS=darwin GOARCH=amd64 ./build -o yggstack-darwin-amd64
# GOOS=freebsd GOARCH=arm64 ./build -o yggstack-freebsd-arm64
echo "::group::yggstack-freebsd-amd64-static" GOOS=freebsd GOARCH=amd64 ./build -o yggstack-freebsd-amd64
GOOS=freebsd GOARCH=amd64 ./build -o yggstack-freebsd-amd64-static GOOS=freebsd GOARCH=arm GOARM=6 ./build -o yggstack-freebsd-armv6
echo "::endgroup::" GOOS=freebsd GOARCH=arm GOARM=7 ./build -o yggstack-freebsd-armv7
# GOOS=freebsd GOARCH=386 ./build -o yggstack-freebsd-i386
echo "::group::yggstack-freebsd-arm64-static" GOOS=openbsd GOARCH=arm64 ./build -o yggstack-openbsd-arm64
GOOS=freebsd GOARCH=arm64 ./build -o yggstack-freebsd-arm64-static GOOS=openbsd GOARCH=amd64 ./build -o yggstack-openbsd-amd64
echo "::endgroup::" GOOS=openbsd GOARCH=arm GOARM=6 ./build -o yggstack-openbsd-armv6
# GOOS=openbsd GOARCH=arm GOARM=7 ./build -o yggstack-openbsd-armv7
echo "::group::yggstack-freebsd-armv6-static" GOOS=openbsd GOARCH=386 ./build -o yggstack-openbsd-i386
GOOS=freebsd GOARCH=arm GOARM=6 ./build -o yggstack-freebsd-armv6-static GOOS=netbsd GOARCH=arm64 ./build -o yggstack-netbsd-arm64
echo "::endgroup::" GOOS=netbsd GOARCH=amd64 ./build -o yggstack-netbsd-amd64
# GOOS=netbsd GOARCH=arm GOARM=6 ./build -o yggstack-netbsd-armv6
echo "::group::yggstack-freebsd-armv7-static" GOOS=netbsd GOARCH=arm GOARM=7 ./build -o yggstack-netbsd-armv7
GOOS=freebsd GOARCH=arm GOARM=7 ./build -o yggstack-freebsd-armv7-static GOOS=netbsd GOARCH=386 ./build -o yggstack-netbsd-i386
echo "::endgroup::" CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang" GOOS=android GOARCH=arm64 ./build -o yggstack-android-arm64
# CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android21-clang" GOOS=android GOARCH=amd64 ./build -o yggstack-android-amd64
echo "::group::yggstack-freebsd-i386-static" 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
GOOS=freebsd GOARCH=386 ./build -o yggstack-freebsd-i386-static CC="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android21-clang" GOOS=android GOARCH=386 ./build -o yggstack-android-i386
GOOS=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 "${{ env.RELEASENAME }}" ${{ env.PRERELEASE }} yggstack-* || gh release upload "${{ env.RELEASENAME }}" yggstack-* --clobber gh release create trunk --prerelease yggstack-* || gh release upload trunk yggstack-*
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 }}

View file

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

View file

@ -1,6 +1,6 @@
# Yggstack - Yggdrasil as SOCKS proxy / port forwarder # Yggstack - Yggdrasil as SOCKS proxy / port forwarder
[![Build status](https://github.com/yggdrasil-network/yggstack/actions/workflows/trunk.yml/badge.svg)](https://github.com/yggdrasil-network/yggstack/actions/workflows/trunk.yml) [![Build status](https://github.com/yggdrasil-network/yggstack/actions/workflows/ci.yml/badge.svg)](https://github.com/yggdrasil-network/yggstack/actions/workflows/ci.yml)
## Introduction ## Introduction
@ -30,18 +30,12 @@ Please see our [Installation](https://yggdrasil-network.github.io/installation.h
page for more information. You may also find other platform-specific wrappers, scripts 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.22 or later) 1. Install [Go](https://golang.org) (requires Go 1.17 or later)
2. Clone this repository 2. Clone this repository
2. Run `./build` 2. Run `./build`
@ -72,48 +66,17 @@ 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 To run SOCKS proxy server listening on local port 1080 using generated configuration:
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 run SOCKS proxy server listening on UNIX socket file `/tmp/yggstack.sock`: To expose network services (like a Web server) listening on local port 8080 to Yggdrasil
network address at port 80:
``` ```
./yggstack -useconffile /path/to/yggdrasil.conf -socks /tmp/yggstack.sock ./yggstack -useconffile /path/to/yggdrasil.conf -exposetcp 80:127.0.0.1:8080
```
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:
@ -132,33 +95,10 @@ 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 without the need for external DNS nameservers. `<publickey>.pk.ygg` format.
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

3
build
View file

@ -9,7 +9,7 @@ PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
ARGS="-v" ARGS="-v"
while getopts "utc:l:dro:ps" option while getopts "utc:l:dro:p" option
do do
case "$option" case "$option"
in in
@ -21,7 +21,6 @@ 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

View file

@ -5,16 +5,13 @@ 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"
@ -31,29 +28,17 @@ 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 localtcp types.TCPLocalMappings var expose types.TCPMappings
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")
@ -67,12 +52,9 @@ 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; or UNIX socket file path, i.e. /tmp/yggstack.sock") socks := flag.String("socks", "", "address to listen on for SOCKS, i.e. :1080")
nameserver := flag.String("nameserver", "", "the Yggdrasil IPv6 address to use as a DNS server for SOCKS") nameserver := flag.String("nameserver", "", "the Yggdrasil IPv6 address to use as a DNS server for SOCKS")
flag.Var(&localtcp, "local-tcp", "TCP ports to forward to the remote Yggdradil node, e.g. 22:[a:b:c:d]:22, 127.0.0.1:22:[a:b:c:d]:22") flag.Var(&expose, "exposetcp", "TCP ports to expose to the network, e.g. 22, 2022:22, 22:192.168.1.1:2022")
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.
@ -113,8 +95,6 @@ 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.
@ -134,7 +114,6 @@ 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 {
@ -289,164 +268,29 @@ func main() {
// Create SOCKS server // Create SOCKS server
{ {
if socks != nil && *socks != "" { if socks != nil && nameserver != 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...)
if strings.Contains(*socks, ":") { go server.ListenAndServe("tcp", *socks) // nolint:errcheck
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 local TCP mappings (forwarding connections from local port // Create TCP mappings
// to remote Yggdrasil node)
{ {
for _, mapping := range localtcp { for _, mapping := range expose {
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 TCP port %d to %s", mapping.Listen.Port, mapping.Mapped) logger.Infof("Mapping Yggdrasil 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 {
@ -464,110 +308,15 @@ func main() {
} }
} }
// Create remote UDP mappings (forwarding connections from Yggdrasil
// node to local port)
{
for _, mapping := range remoteudp {
go func(mapping types.UDPMapping) {
mtu := n.core.MTU()
udpListenConn, err := s.ListenUDP(mapping.Listen)
if err != nil {
panic(err)
}
logger.Infof("Mapping Yggdrasil UDP port %d to %s", mapping.Listen.Port, mapping.Mapped)
remoteUdpConnections := new(sync.Map)
udpBuffer := make([]byte, mtu)
for {
bytesRead, remoteUdpAddr, err := udpListenConn.ReadFrom(udpBuffer)
if err != nil {
logger.Debugf("udp readFrom error: %v", err)
}
if bytesRead == 0 {
continue
}
remoteUdpAddrStr := remoteUdpAddr.String()
var udpSession *UDPSession = nil
connVal, ok := remoteUdpConnections.Load(remoteUdpAddrStr)
if !ok {
logger.Debugf("Creating new session for %s", remoteUdpAddr.String())
udpFwdConn, err := net.DialUDP("udp", nil, mapping.Mapped)
if err != nil {
logger.Errorf("Failed to connect to %s: %s", mapping.Mapped, err)
continue
}
udpSession = &UDPSession{
conn: udpFwdConn,
remoteAddr: remoteUdpAddr,
}
remoteUdpConnections.Store(remoteUdpAddrStr, udpSession)
go types.ReverseProxyUDP(mtu, udpListenConn, remoteUdpAddr, udpFwdConn)
} else {
udpSession, ok = connVal.(*UDPSession)
if !ok {
continue
}
}
udpFwdConnPtr := udpSession.conn.(*net.UDPConn)
udpFwdConn := *udpFwdConnPtr
_, err = udpFwdConn.Write(udpBuffer[:bytesRead])
if err != nil {
logger.Debugf("Cannot write from yggdrasil to udp listener: %q", err)
udpFwdConn.Close()
remoteUdpConnections.Delete(remoteUdpAddrStr)
continue
}
}
}(mapping)
}
}
// Block until we are told to shut down. // 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
View file

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

218
go.sum
View file

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

View file

@ -86,12 +86,12 @@ func (s *YggdrasilNetstack) DialContext(ctx context.Context, network, address st
} }
} }
func (s *YggdrasilNetstack) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) { func (s *YggdrasilNetstack) DialTCP(addr *net.TCPAddr) (net.Conn, error) {
fa, pn, _ := convertToFullAddr(addr.IP, addr.Port) 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) (*gonet.UDPConn, error) { func (s *YggdrasilNetstack) DialUDP(addr *net.UDPAddr) (net.PacketConn, 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) (*gonet.UDPConn, error) { func (s *YggdrasilNetstack) ListenUDP(addr *net.UDPAddr) (net.PacketConn, 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)
} }

View file

@ -12,7 +12,6 @@ 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 {
@ -21,7 +20,6 @@ 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 {
@ -31,7 +29,6 @@ func (s *YggdrasilNetstack) NewYggdrasilNIC(ygg *core.Core) tcpip.Error {
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
@ -51,15 +48,6 @@ 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{}
@ -97,60 +85,29 @@ func (e *YggdrasilNIC) IsAttached() bool { return e.dispatcher != nil }
func (e *YggdrasilNIC) MTU() uint32 { return uint32(e.ipv6rwc.MTU()) } func (e *YggdrasilNIC) 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() {
if pkt.Data().Size() == 0 { vv := pkt.ToView()
if pkt.Network().TransportProtocol() == tcp.ProtocolNumber { n, err := vv.Read(e.writeBuf)
tcpHeader := header.TCP(pkt.TransportHeader().Slice())
if (tcpHeader.Flags() & header.TCPFlagRst) == header.TCPFlagRst {
e.rstPackets <- pkt
continue
}
}
}
err = e.writePacket(pkt)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return i - 1, err return i - 1, &tcpip.ErrAborted{}
}
_, err = e.ipv6rwc.Write(e.writeBuf[:n])
if err != nil {
log.Println(err)
return i - 1, &tcpip.ErrAborted{}
} }
} }
@ -172,9 +129,8 @@ func (e *YggdrasilNIC) ParseHeader(*stack.PacketBuffer) bool {
return true return true
} }
func (e *YggdrasilNIC) Close() { func (e *YggdrasilNIC) Close() error {
e.stack.stack.RemoveNIC(1) e.stack.stack.RemoveNIC(1)
e.dispatcher = nil e.dispatcher = nil
return nil
} }
func (e *YggdrasilNIC) SetOnCloseAction(func()) {}

View file

@ -7,381 +7,62 @@ 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 TCPLocalMappings []TCPMapping type TCPMappings []TCPMapping
func (m *TCPLocalMappings) String() string { func (m *TCPMappings) String() string {
return "" return ""
} }
func (m *TCPLocalMappings) Set(value string) error { func (m *TCPMappings) Set(value string) error {
first_address, first_port, second_address, second_port, err := tokens := strings.Split(value, ":")
parseMappingString(value) if len(tokens) > 2 {
tokens = strings.SplitN(value, ":", 2)
host, port, err := net.SplitHostPort(tokens[1])
if err != nil { if err != nil {
return err return fmt.Errorf("failed to split host and port: %w", err)
} }
tokens = append(tokens[:1], host, port)
// First address can be ipv4/ipv6 }
// Second address can be only Yggdrasil ipv6 listenport, err := strconv.Atoi(tokens[0])
if err != nil {
if !strings.Contains(second_address, ":") { return fmt.Errorf("listen port is invalid: %w", err)
return fmt.Errorf("Yggdrasil listening address can be only IPv6") }
if listenport == 0 {
return fmt.Errorf("listen port must not be zero")
} }
// Create mapping
mapping := TCPMapping{ mapping := TCPMapping{
Listen: &net.TCPAddr{ Listen: &net.TCPAddr{
Port: first_port, Port: listenport,
}, },
Mapped: &net.TCPAddr{ Mapped: &net.TCPAddr{
IP: net.IPv6loopback, IP: net.IPv6loopback,
Port: second_port, Port: listenport,
}, },
} }
tokens = tokens[1:]
if first_address != "" { if len(tokens) > 0 {
listenaddr := net.ParseIP(first_address) mappedaddr := net.ParseIP(tokens[0])
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", second_address) return fmt.Errorf("invalid mapped address %q", tokens[0])
} }
// TODO: Filter Yggdrasil IPs here
mapping.Mapped.IP = mappedaddr mapping.Mapped.IP = mappedaddr
tokens = tokens[1:]
} }
if len(tokens) > 0 {
*m = append(*m, mapping) mappedport, err := strconv.Atoi(tokens[0])
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 { if err != nil {
return err return fmt.Errorf("mapped port is invalid: %w", err)
} }
if mappedport == 0 {
// First address must be empty return fmt.Errorf("mapped port must not be zero")
// Second address can be ipv4/ipv6
if first_address != "" {
return fmt.Errorf("Yggdrasil listening must be empty")
} }
mapping.Mapped.Port = mappedport
// 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
} }

View file

@ -3,108 +3,26 @@ package types
import "testing" import "testing"
func TestEndpointMappings(t *testing.T) { func TestEndpointMappings(t *testing.T) {
var tcpMappings TCPMappings var mappings TCPMappings
if err := tcpMappings.Set("1234"); err != nil { if err := mappings.Set("1234"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := tcpMappings.Set("1234:192.168.1.1"); err != nil { if err := mappings.Set("1234:192.168.1.1"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := tcpMappings.Set("1234:192.168.1.1:4321"); err != nil { if err := mappings.Set("1234:192.168.1.1:4321"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := tcpMappings.Set("192.168.1.2:1234:192.168.1.1:4321"); err != nil { if err := mappings.Set("1234:[2000::1]:4321"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := tcpMappings.Set("1234:[2000::1]:4321"); err != nil { if err := mappings.Set("a"); 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 := tcpMappings.Set("1234:localhost"); err == nil { if err := mappings.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 := tcpMappings.Set("127.0.0.1:1234:localhost"); err == nil { if err := mappings.Set("1234:localhost:a"); 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")
} }
} }

View file

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