mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-04-28 14:15:06 +03:00
Merge branch 'develop' into master
This commit is contained in:
commit
573574c8b6
78 changed files with 3245 additions and 2206 deletions
|
@ -1,230 +0,0 @@
|
||||||
# Golang CircleCI 2.0 configuration file
|
|
||||||
#
|
|
||||||
# Check https://circleci.com/docs/2.0/language-go/ for more details
|
|
||||||
version: 2.1
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.16
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Run golangci-lint
|
|
||||||
command: |
|
|
||||||
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.31.0
|
|
||||||
golangci-lint run
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Run Go tests
|
|
||||||
command: |
|
|
||||||
go test ./...
|
|
||||||
|
|
||||||
build-linux:
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.16
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Create artifact upload directory and set variables
|
|
||||||
command: |
|
|
||||||
mkdir /tmp/upload
|
|
||||||
echo 'export CINAME=$(sh contrib/semver/name.sh)' >> $BASH_ENV
|
|
||||||
echo 'export CIVERSION=$(sh contrib/semver/version.sh --bare)' >> $BASH_ENV
|
|
||||||
echo 'export CIVERSIONRPM=$(sh contrib/semver/version.sh --bare | tr "-" ".")' >> $BASH_ENV
|
|
||||||
echo 'export CIBRANCH=$(echo $CIRCLE_BRANCH | tr -d "/")' >> $BASH_ENV
|
|
||||||
case "$CINAME" in \
|
|
||||||
"yggdrasil") (echo 'export CICONFLICTS=yggdrasil-develop' >> $BASH_ENV) ;; \
|
|
||||||
"yggdrasil-develop") (echo 'export CICONFLICTS=yggdrasil' >> $BASH_ENV) ;; \
|
|
||||||
*) (echo 'export CICONFLICTS="yggdrasil yggdrasil-develop"' >> $BASH_ENV) ;; \
|
|
||||||
esac
|
|
||||||
git config --global user.email "$(git log --format='%ae' HEAD -1)";
|
|
||||||
git config --global user.name "$(git log --format='%an' HEAD -1)";
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Install RPM utilities
|
|
||||||
command: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y rpm file
|
|
||||||
mkdir -p ~/rpmbuild/BUILD ~/rpmbuild/RPMS ~/rpmbuild/SOURCES ~/rpmbuild/SPECS ~/rpmbuild/SRPMS
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Test debug builds
|
|
||||||
command: |
|
|
||||||
./build -d
|
|
||||||
test -f yggdrasil && test -f yggdrasilctl
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Build for Linux (including Debian packages)
|
|
||||||
command: |
|
|
||||||
rm -f {yggdrasil,yggdrasilctl}
|
|
||||||
PKGARCH=amd64 sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-amd64;
|
|
||||||
PKGARCH=i386 sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-i386;
|
|
||||||
PKGARCH=mipsel sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-mipsel && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-mipsel;
|
|
||||||
PKGARCH=mips sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-mips && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-mips;
|
|
||||||
PKGARCH=armhf sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-armhf && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-armhf;
|
|
||||||
PKGARCH=armel sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-armel && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-armel;
|
|
||||||
PKGARCH=arm64 sh contrib/deb/generate.sh && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-linux-arm64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-linux-arm64;
|
|
||||||
mv *.deb /tmp/upload/
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Build for Linux (RPM packages)
|
|
||||||
command: |
|
|
||||||
git clone https://github.com/yggdrasil-network/yggdrasil-package-rpm ~/rpmbuild/SPECS
|
|
||||||
cd ../ && tar -czvf ~/rpmbuild/SOURCES/v$CIVERSIONRPM --transform "s/project/yggdrasil-go-$CIBRANCH-$CIVERSIONRPM/" project
|
|
||||||
sed -i "s/yggdrasil-go/yggdrasil-go-$CIBRANCH/" ~/rpmbuild/SPECS/yggdrasil.spec
|
|
||||||
sed -i "s/^PKGNAME=yggdrasil/PKGNAME=yggdrasil-$CIBRANCH/" ~/rpmbuild/SPECS/yggdrasil.spec
|
|
||||||
sed -i "s/^Name\:.*/Name\: $CINAME/" ~/rpmbuild/SPECS/yggdrasil.spec
|
|
||||||
sed -i "s/^Version\:.*/Version\: $CIVERSIONRPM/" ~/rpmbuild/SPECS/yggdrasil.spec
|
|
||||||
sed -i "s/^Conflicts\:.*/Conflicts\: $CICONFLICTS/" ~/rpmbuild/SPECS/yggdrasil.spec
|
|
||||||
cat ~/rpmbuild/SPECS/yggdrasil.spec
|
|
||||||
GOARCH=amd64 rpmbuild -v --nodeps --target=x86_64 -ba ~/rpmbuild/SPECS/yggdrasil.spec
|
|
||||||
#GOARCH=386 rpmbuild -v --nodeps --target=i386 -bb ~/rpmbuild/SPECS/yggdrasil.spec
|
|
||||||
find ~/rpmbuild/RPMS/ -name '*.rpm' -exec mv {} /tmp/upload \;
|
|
||||||
find ~/rpmbuild/SRPMS/ -name '*.rpm' -exec mv {} /tmp/upload \;
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Build for EdgeRouter and VyOS
|
|
||||||
command: |
|
|
||||||
rm -f {yggdrasil,yggdrasilctl}
|
|
||||||
git clone https://github.com/neilalexander/vyatta-yggdrasil /tmp/vyatta-yggdrasil;
|
|
||||||
cd /tmp/vyatta-yggdrasil;
|
|
||||||
BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-edgerouter-x $CIRCLE_BRANCH;
|
|
||||||
BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-edgerouter-lite $CIRCLE_BRANCH;
|
|
||||||
BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-vyos-i386 $CIRCLE_BRANCH
|
|
||||||
BUILDDIR_YGG=$CIRCLE_WORKING_DIRECTORY ./build-vyos-amd64 $CIRCLE_BRANCH
|
|
||||||
mv *.deb /tmp/upload;
|
|
||||||
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: /tmp
|
|
||||||
paths:
|
|
||||||
- upload
|
|
||||||
|
|
||||||
build-macos:
|
|
||||||
macos:
|
|
||||||
xcode: "10.0.0"
|
|
||||||
|
|
||||||
working_directory: ~/go/src/github.com/yggdrasil-network/yggdrasil-go
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Create artifact upload directory and set variables
|
|
||||||
command: |
|
|
||||||
mkdir /tmp/upload
|
|
||||||
echo 'export CINAME=$(sh contrib/semver/name.sh)' >> $BASH_ENV
|
|
||||||
echo 'export CIVERSION=$(sh contrib/semver/version.sh --bare)' >> $BASH_ENV
|
|
||||||
echo 'export PATH=$PATH:/usr/local/go/bin:~/go/bin' >> $BASH_ENV
|
|
||||||
git config --global user.email "$(git log --format='%ae' HEAD -1)";
|
|
||||||
git config --global user.name "$(git log --format='%an' HEAD -1)";
|
|
||||||
echo -e "Host *\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Install Go 1.16
|
|
||||||
command: |
|
|
||||||
cd /tmp
|
|
||||||
curl -LO https://dl.google.com/go/go1.16.darwin-amd64.pkg
|
|
||||||
sudo installer -pkg /tmp/go1.16.darwin-amd64.pkg -target /
|
|
||||||
|
|
||||||
#- run:
|
|
||||||
# name: Install Gomobile
|
|
||||||
# command: |
|
|
||||||
# GO111MODULE=off go get golang.org/x/mobile/cmd/gomobile
|
|
||||||
# gomobile init
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Build for macOS
|
|
||||||
command: |
|
|
||||||
GO111MODULE=on GOOS=darwin GOARCH=amd64 ./build
|
|
||||||
cp yggdrasil /tmp/upload/$CINAME-$CIVERSION-darwin-amd64
|
|
||||||
cp yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-darwin-amd64;
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Build for macOS (.pkg format)
|
|
||||||
command: |
|
|
||||||
PKGARCH=amd64 sh contrib/macos/create-pkg.sh
|
|
||||||
mv *.pkg /tmp/upload/
|
|
||||||
|
|
||||||
#- run:
|
|
||||||
# name: Build framework for iOS (.framework format)
|
|
||||||
# command: |
|
|
||||||
# sudo GO111MODULE=off go get -v github.com/yggdrasil-network/yggdrasil-go/cmd/...
|
|
||||||
# sudo GO111MODULE=off go get -v github.com/yggdrasil-network/yggdrasil-go/src/...
|
|
||||||
# GO111MODULE=off ./build -i
|
|
||||||
# mv *.framework /tmp/upload
|
|
||||||
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: /tmp
|
|
||||||
paths:
|
|
||||||
- upload
|
|
||||||
|
|
||||||
build-other:
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.16
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Create artifact upload directory and set variables
|
|
||||||
command: |
|
|
||||||
mkdir /tmp/upload
|
|
||||||
echo 'export CINAME=$(sh contrib/semver/name.sh)' >> $BASH_ENV
|
|
||||||
echo 'export CIVERSION=$(sh contrib/semver/version.sh --bare)' >> $BASH_ENV
|
|
||||||
git config --global user.email "$(git log --format='%ae' HEAD -1)";
|
|
||||||
git config --global user.name "$(git log --format='%an' HEAD -1)";
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Build for OpenBSD
|
|
||||||
command: |
|
|
||||||
rm -f {yggdrasil,yggdrasilctl}
|
|
||||||
GOOS=openbsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-openbsd-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-openbsd-amd64;
|
|
||||||
GOOS=openbsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-openbsd-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-openbsd-i386;
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Build for FreeBSD
|
|
||||||
command: |
|
|
||||||
rm -f {yggdrasil,yggdrasilctl}
|
|
||||||
GOOS=freebsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-freebsd-amd64 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-freebsd-amd64;
|
|
||||||
GOOS=freebsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/$CINAME-$CIVERSION-freebsd-i386 && mv yggdrasilctl /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-freebsd-i386;
|
|
||||||
|
|
||||||
- run:
|
|
||||||
name: Build for Windows
|
|
||||||
command: |
|
|
||||||
rm -f {yggdrasil,yggdrasilctl}
|
|
||||||
GOOS=windows GOARCH=amd64 ./build && mv yggdrasil.exe /tmp/upload/$CINAME-$CIVERSION-windows-amd64.exe && mv yggdrasilctl.exe /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-windows-amd64.exe;
|
|
||||||
GOOS=windows GOARCH=386 ./build && mv yggdrasil.exe /tmp/upload/$CINAME-$CIVERSION-windows-i386.exe && mv yggdrasilctl.exe /tmp/upload/$CINAME-$CIVERSION-yggdrasilctl-windows-i386.exe;
|
|
||||||
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: /tmp
|
|
||||||
paths:
|
|
||||||
- upload
|
|
||||||
|
|
||||||
upload:
|
|
||||||
machine: true
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: /tmp
|
|
||||||
|
|
||||||
- store_artifacts:
|
|
||||||
path: /tmp/upload
|
|
||||||
destination: /
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
version: 2.1
|
|
||||||
build:
|
|
||||||
jobs:
|
|
||||||
- lint
|
|
||||||
- build-linux
|
|
||||||
- build-macos
|
|
||||||
- build-other
|
|
||||||
- upload:
|
|
||||||
requires:
|
|
||||||
- build-linux
|
|
||||||
- build-macos
|
|
||||||
- build-other
|
|
131
.github/workflows/ci.yml
vendored
Normal file
131
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
name: Yggdrasil
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
release:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
with:
|
||||||
|
args: --issues-exit-code=1
|
||||||
|
|
||||||
|
codeql:
|
||||||
|
name: Analyse
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: go
|
||||||
|
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|
||||||
|
build-linux:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
goversion: ["1.17", "1.18", "1.19"]
|
||||||
|
|
||||||
|
name: Build & Test (Linux, Go ${{ matrix.goversion }})
|
||||||
|
needs: [lint]
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.goversion }}
|
||||||
|
|
||||||
|
- name: Build Yggdrasil
|
||||||
|
run: go build -v ./...
|
||||||
|
|
||||||
|
- name: Unit tests
|
||||||
|
run: go test -v ./...
|
||||||
|
|
||||||
|
build-windows:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
goversion: ["1.17", "1.18", "1.19"]
|
||||||
|
|
||||||
|
name: Build & Test (Windows, Go ${{ matrix.goversion }})
|
||||||
|
needs: [lint]
|
||||||
|
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.goversion }}
|
||||||
|
|
||||||
|
- name: Build Yggdrasil
|
||||||
|
run: go build -v ./...
|
||||||
|
|
||||||
|
- name: Unit tests
|
||||||
|
run: go test -v ./...
|
||||||
|
|
||||||
|
build-macos:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
goversion: ["1.17", "1.18", "1.19"]
|
||||||
|
|
||||||
|
name: Build & Test (macOS, Go ${{ matrix.goversion }})
|
||||||
|
needs: [lint]
|
||||||
|
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.goversion }}
|
||||||
|
|
||||||
|
- name: Build Yggdrasil
|
||||||
|
run: go build -v ./...
|
||||||
|
|
||||||
|
- name: Unit tests
|
||||||
|
run: go test -v ./...
|
||||||
|
|
||||||
|
tests-ok:
|
||||||
|
name: All tests passed
|
||||||
|
needs: [lint, codeql, build-linux, build-windows, build-macos]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
steps:
|
||||||
|
- name: Check all tests passed
|
||||||
|
uses: re-actors/alls-green@release/v1
|
||||||
|
with:
|
||||||
|
jobs: ${{ toJSON(needs) }}
|
137
.github/workflows/pkg.yml
vendored
Normal file
137
.github/workflows/pkg.yml
vendored
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
name: Packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-packages-debian:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pkgarch: ["amd64", "i386", "mips", "mipsel", "armhf", "armel", "arm64"]
|
||||||
|
|
||||||
|
name: Package (Debian, ${{ matrix.pkgarch }})
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
env:
|
||||||
|
PKGARCH: ${{ matrix.pkgarch }}
|
||||||
|
run: sh contrib/deb/generate.sh
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Debian package (${{ matrix.pkgarch }})
|
||||||
|
path: "*.deb"
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
build-packages-macos:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pkgarch: ["amd64", "arm64"]
|
||||||
|
|
||||||
|
name: Package (macOS, ${{ matrix.pkgarch }})
|
||||||
|
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
env:
|
||||||
|
PKGARCH: ${{ matrix.pkgarch }}
|
||||||
|
run: sh contrib/macos/create-pkg.sh
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: macOS package (${{ matrix.pkgarch }})
|
||||||
|
path: "*.pkg"
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
build-packages-windows:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pkgarch: ["x64", "x86", "arm", "arm64"]
|
||||||
|
|
||||||
|
name: Package (Windows, ${{ matrix.pkgarch }})
|
||||||
|
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }}
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Windows package (${{ matrix.pkgarch }})
|
||||||
|
path: "*.msi"
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
build-packages-router:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pkgarch: ["edgerouter-x", "edgerouter-lite", "vyos-amd64", "vyos-i386"]
|
||||||
|
|
||||||
|
name: Package (Router, ${{ matrix.pkgarch }})
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
path: yggdrasil
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: neilalexander/vyatta-yggdrasil
|
||||||
|
path: vyatta-yggdrasil
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
env:
|
||||||
|
BUILDDIR_YGG: /home/runner/work/yggdrasil-go/yggdrasil-go/yggdrasil
|
||||||
|
run: cd /home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil && ./build-${{ matrix.pkgarch }}
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Router package (${{ matrix.pkgarch }})
|
||||||
|
path: "/home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil/*.deb"
|
||||||
|
if-no-files-found: error
|
225
CHANGELOG.md
225
CHANGELOG.md
|
@ -1,4 +1,5 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
|
@ -25,63 +26,116 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- in case of vulnerabilities.
|
- in case of vulnerabilities.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## [0.4.0] - 2021-07-04
|
## [0.4.4] - 2022-07-07
|
||||||
### Added
|
|
||||||
- New routing scheme, which is backwards incompatible with previous versions of Yggdrasil
|
### Fixed
|
||||||
- The wire protocol version number, exchanged as part of the peer setup handshake, has been increased to 0.4
|
|
||||||
- Nodes running this new version **will not** be able to peer with earlier versions of Yggdrasil
|
- ICMPv6 "Packet Too Big" payload size has been increased, which should fix Path MTU Discovery (PMTUD) when two nodes have different `IfMTU` values configured
|
||||||
- Please note that **the network may be temporarily unstable** while infrastructure is being upgraded to the new release
|
- A crash has been fixed when handling debug packet responses
|
||||||
- TLS connections now use public key pinning
|
- `yggdrasilctl getSelf` should now report coordinates correctly again
|
||||||
- If no public key was already pinned, then the public key received as part of the TLS handshake is pinned to the connection
|
|
||||||
- The public key received as part of the handshake is checked against the pinned keys, and if no match is found, the connection is rejected
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Go 1.17 is now required to build Yggdrasil
|
||||||
|
|
||||||
|
## [0.4.3] - 2022-02-06
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `bytes_sent`, `bytes_recvd` and `uptime` have been added to `getPeers`
|
||||||
|
- Clearer logging when connections are rejected due to incompatible peer versions
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Latency-based parent selection tiebreak is now reliable on platforms even with low timer resolution
|
||||||
|
- Tree distance calculation offsets have been corrected
|
||||||
|
|
||||||
|
## [0.4.2] - 2021-11-03
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Reverted a dependency update which resulted in problems building with Go 1.16 and running on Windows
|
||||||
|
|
||||||
|
## [0.4.1] - 2021-11-03
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- TLS peerings now support Server Name Indication (SNI)
|
||||||
|
- The SNI is sent automatically if the peering URI contains a DNS name
|
||||||
|
- A custom SNI can be specified by adding the `?sni=domain.com` parameter to the peering URI
|
||||||
|
- A new `ipv6rwc` API package now implements the IPv6-specific logic separate from the `tun` package
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- A crash when calculating the partial public key for very high IPv6 addresses has been fixed
|
||||||
|
- A crash due to a concurrent map write has been fixed
|
||||||
|
- A crash due to missing TUN configuration has been fixed
|
||||||
|
- A race condition in the keystore code has been fixed
|
||||||
|
|
||||||
|
## [0.4.0] - 2021-07-04
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- New routing scheme, which is backwards incompatible with previous versions of Yggdrasil
|
||||||
|
- The wire protocol version number, exchanged as part of the peer setup handshake, has been increased to 0.4
|
||||||
|
- Nodes running this new version **will not** be able to peer with earlier versions of Yggdrasil
|
||||||
|
- Please note that **the network may be temporarily unstable** while infrastructure is being upgraded to the new release
|
||||||
|
- TLS connections now use public key pinning
|
||||||
|
- If no public key was already pinned, then the public key received as part of the TLS handshake is pinned to the connection
|
||||||
|
- The public key received as part of the handshake is checked against the pinned keys, and if no match is found, the connection is rejected
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
- IP addresses are now derived from ed25519 public (signing) keys
|
- IP addresses are now derived from ed25519 public (signing) keys
|
||||||
- Previously, addresses were derived from a hash of X25519 (Diffie-Hellman) keys
|
- Previously, addresses were derived from a hash of X25519 (Diffie-Hellman) keys
|
||||||
- Importantly, this means that **all internal IPv6 addresses will change with this release** — this will affect anyone running public services or relying on Yggdrasil for remote access
|
- Importantly, this means that **all internal IPv6 addresses will change with this release** — this will affect anyone running public services or relying on Yggdrasil for remote access
|
||||||
- It is now recommended to peer over TLS
|
- It is now recommended to peer over TLS
|
||||||
- Link-local peers from multicast peer discovery will now connect over TLS, with the key from the multicast beacon pinned to the connection
|
- Link-local peers from multicast peer discovery will now connect over TLS, with the key from the multicast beacon pinned to the connection
|
||||||
- `socks://` peers now expect the destination endpoint to be a `tls://` listener, instead of a `tcp://` listener
|
- `socks://` peers now expect the destination endpoint to be a `tls://` listener, instead of a `tcp://` listener
|
||||||
- Multicast peer discovery is now more configurable
|
- Multicast peer discovery is now more configurable
|
||||||
- There are separate configuration options to control if beacons are sent, what port to listen on for incoming connections (if sending beacons), and whether or not to listen for beacons from other nodes (and open connections when receiving a beacon)
|
- There are separate configuration options to control if beacons are sent, what port to listen on for incoming connections (if sending beacons), and whether or not to listen for beacons from other nodes (and open connections when receiving a beacon)
|
||||||
- Each configuration entry in the list specifies a regular expression to match against interface names
|
- Each configuration entry in the list specifies a regular expression to match against interface names
|
||||||
- If an interface matches multiple regex in the list, it will use the settings for the first entry in the list that it matches with
|
- If an interface matches multiple regex in the list, it will use the settings for the first entry in the list that it matches with
|
||||||
- The session and routing code has been entirely redesigned and rewritten
|
- The session and routing code has been entirely redesigned and rewritten
|
||||||
- This is still an early work-in-progress, so the code hasn't been as well tested or optimized as the old code base — please bear with us for these next few releases as we work through any bugs or issues
|
- This is still an early work-in-progress, so the code hasn't been as well tested or optimized as the old code base — please bear with us for these next few releases as we work through any bugs or issues
|
||||||
- Generally speaking, we expect to see reduced bandwidth use and improved reliability with the new design, especially in cases where nodes move around or change peerings frequently
|
- Generally speaking, we expect to see reduced bandwidth use and improved reliability with the new design, especially in cases where nodes move around or change peerings frequently
|
||||||
- Cryptographic sessions no longer use a single shared (ephemeral) secret for the entire life of the session. Keys are now rotated regularly for ongoing sessions (currently rotated at least once per round trip exchange of traffic, subject to change in future releases)
|
- Cryptographic sessions no longer use a single shared (ephemeral) secret for the entire life of the session. Keys are now rotated regularly for ongoing sessions (currently rotated at least once per round trip exchange of traffic, subject to change in future releases)
|
||||||
- Source routing has been added. Under normal circumstances, this is what is used to forward session traffic (e.g. the user's IPv6 traffic)
|
- Source routing has been added. Under normal circumstances, this is what is used to forward session traffic (e.g. the user's IPv6 traffic)
|
||||||
- DHT-based routing has been added. This is used when the sender does not know a source route to the destination. Forwarding through the DHT is less efficient, but the only information that it requires the sender to know is the destination node's (static) key. This is primarily used during the key exchange at session setup, or as a temporary fallback when a source route fails due to changes in the network
|
- DHT-based routing has been added. This is used when the sender does not know a source route to the destination. Forwarding through the DHT is less efficient, but the only information that it requires the sender to know is the destination node's (static) key. This is primarily used during the key exchange at session setup, or as a temporary fallback when a source route fails due to changes in the network
|
||||||
- The new DHT design is no longer RPC-based, does not support crawling and does not inherently allow nodes to look up the owner of an arbitrary key. Responding to lookups is now implemented at the application level and a response is only sent if the destination key matches the node's `/128` IP or `/64` prefix
|
- The new DHT design is no longer RPC-based, does not support crawling and does not inherently allow nodes to look up the owner of an arbitrary key. Responding to lookups is now implemented at the application level and a response is only sent if the destination key matches the node's `/128` IP or `/64` prefix
|
||||||
- The greedy routing scheme, used to forward all traffic in previous releases, is now only used for protocol traffic (i.e. DHT setup and source route discovery)
|
- The greedy routing scheme, used to forward all traffic in previous releases, is now only used for protocol traffic (i.e. DHT setup and source route discovery)
|
||||||
- The routing logic now lives in a [standalone library](https://github.com/Arceliar/ironwood). You are encouraged **not** to use it, as it's still considered pre-alpha, but it's available for those who want to experiment with the new routing algorithm in other contexts
|
- The routing logic now lives in a [standalone library](https://github.com/Arceliar/ironwood). You are encouraged **not** to use it, as it's still considered pre-alpha, but it's available for those who want to experiment with the new routing algorithm in other contexts
|
||||||
- Session MTUs may be slightly lower now, in order to accommodate large packet headers if required
|
- Session MTUs may be slightly lower now, in order to accommodate large packet headers if required
|
||||||
- Many of the admin functions available over `yggdrasilctl` have been changed or removed as part of rewrites to the code
|
- Many of the admin functions available over `yggdrasilctl` have been changed or removed as part of rewrites to the code
|
||||||
- Several remote `debug` functions have been added temporarily, to allow for crawling and census gathering during the transition to the new version, but we intend to remove this at some point in the (possibly distant) future
|
- Several remote `debug` functions have been added temporarily, to allow for crawling and census gathering during the transition to the new version, but we intend to remove this at some point in the (possibly distant) future
|
||||||
- The list of available functions will likely be expanded in future releases
|
- The list of available functions will likely be expanded in future releases
|
||||||
- The configuration file format has been updated in response to the changed/removed features
|
- The configuration file format has been updated in response to the changed/removed features
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Tunnel routing (a.k.a. crypto-key routing or "CKR") has been removed
|
- Tunnel routing (a.k.a. crypto-key routing or "CKR") has been removed
|
||||||
- It was far too easy to accidentally break routing altogether by capturing the route to peers with the TUN adapter
|
- It was far too easy to accidentally break routing altogether by capturing the route to peers with the TUN adapter
|
||||||
- We recommend tunnelling an existing standard over Yggdrasil instead (e.g. `ip6gre`, `ip6gretap` or other similar encapsulations, using Yggdrasil IPv6 addresses as the tunnel endpoints)
|
- We recommend tunnelling an existing standard over Yggdrasil instead (e.g. `ip6gre`, `ip6gretap` or other similar encapsulations, using Yggdrasil IPv6 addresses as the tunnel endpoints)
|
||||||
- All `TunnelRouting` configuration options will no longer take effect
|
- All `TunnelRouting` configuration options will no longer take effect
|
||||||
- Session firewall has been removed
|
- Session firewall has been removed
|
||||||
- This was never a true firewall — it didn't behave like a stateful IP firewall, often allowed return traffic unexpectedly and was simply a way to prevent a node from being flooded with unwanted sessions, so the name could be misleading and usually lead to a false sense of security
|
- This was never a true firewall — it didn't behave like a stateful IP firewall, often allowed return traffic unexpectedly and was simply a way to prevent a node from being flooded with unwanted sessions, so the name could be misleading and usually lead to a false sense of security
|
||||||
- Due to design changes, the new code needs to address the possible memory exhaustion attacks in other ways and a single configurable list no longer makes sense
|
- Due to design changes, the new code needs to address the possible memory exhaustion attacks in other ways and a single configurable list no longer makes sense
|
||||||
- Users who want a firewall or other packet filter mechansim should configure something supported by their OS instead (e.g. `ip6tables`)
|
- Users who want a firewall or other packet filter mechansim should configure something supported by their OS instead (e.g. `ip6tables`)
|
||||||
- All `SessionFirewall` configuration options will no longer take effect
|
- All `SessionFirewall` configuration options will no longer take effect
|
||||||
- `SIGHUP` handling to reload the configuration at runtime has been removed
|
- `SIGHUP` handling to reload the configuration at runtime has been removed
|
||||||
- It was not obvious which parts of the configuration could be reloaded at runtime, and which required the application to be killed and restarted to take effect
|
- It was not obvious which parts of the configuration could be reloaded at runtime, and which required the application to be killed and restarted to take effect
|
||||||
- Reloading the config without restarting was also a delicate and bug-prone process, and was distracting from more important developments
|
- Reloading the config without restarting was also a delicate and bug-prone process, and was distracting from more important developments
|
||||||
- `SIGHUP` will be handled normally (i.e. by exiting)
|
- `SIGHUP` will be handled normally (i.e. by exiting)
|
||||||
- `cmd/yggrasilsim` has been removed, and is unlikely to return to this repository
|
- `cmd/yggrasilsim` has been removed, and is unlikely to return to this repository
|
||||||
|
|
||||||
## [0.3.16] - 2021-03-18
|
## [0.3.16] - 2021-03-18
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- New simulation code under `cmd/yggdrasilsim` (work-in-progress)
|
- New simulation code under `cmd/yggdrasilsim` (work-in-progress)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Multi-threading in the switch
|
- Multi-threading in the switch
|
||||||
- Swich lookups happen independently for each (incoming) peer connection, instead of being funneled to a single dedicated switch worker
|
- Swich lookups happen independently for each (incoming) peer connection, instead of being funneled to a single dedicated switch worker
|
||||||
- Packets are queued for each (outgoing) peer connection, instead of being handled by a single dedicated switch worker
|
- Packets are queued for each (outgoing) peer connection, instead of being handled by a single dedicated switch worker
|
||||||
|
@ -97,13 +151,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Upgrade build to Go 1.16
|
- Upgrade build to Go 1.16
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed a bug where the connection listener could exit prematurely due to resoruce exhaustion (if e.g. too many connections were opened)
|
- Fixed a bug where the connection listener could exit prematurely due to resoruce exhaustion (if e.g. too many connections were opened)
|
||||||
- Fixed DefaultIfName for OpenBSD (`/dev/tun0` -> `tun0`)
|
- Fixed DefaultIfName for OpenBSD (`/dev/tun0` -> `tun0`)
|
||||||
- Fixed an issue where a peer could sometimes never be added to the switch
|
- Fixed an issue where a peer could sometimes never be added to the switch
|
||||||
- Fixed a goroutine leak that could occur if a peer with an open connection continued to spam additional connection attempts
|
- Fixed a goroutine leak that could occur if a peer with an open connection continued to spam additional connection attempts
|
||||||
|
|
||||||
## [0.3.15] - 2020-09-27
|
## [0.3.15] - 2020-09-27
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Support for pinning remote public keys in peering strings has been added, e.g.
|
- Support for pinning remote public keys in peering strings has been added, e.g.
|
||||||
- By signing public key: `tcp://host:port?ed25519=key`
|
- By signing public key: `tcp://host:port?ed25519=key`
|
||||||
- By encryption public key: `tcp://host:port?curve25519=key`
|
- By encryption public key: `tcp://host:port?curve25519=key`
|
||||||
|
@ -113,25 +170,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Added support for SOCKS proxy authentication, e.g. `socks://user@password:host/...`
|
- Added support for SOCKS proxy authentication, e.g. `socks://user@password:host/...`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Some bugs in the multicast code that could cause unnecessary CPU usage have been fixed
|
- Some bugs in the multicast code that could cause unnecessary CPU usage have been fixed
|
||||||
- A possible multicast deadlock on macOS when enumerating interfaces has been fixed
|
- A possible multicast deadlock on macOS when enumerating interfaces has been fixed
|
||||||
- A deadlock in the connection code has been fixed
|
- A deadlock in the connection code has been fixed
|
||||||
- Updated HJSON dependency that caused some build problems
|
- Updated HJSON dependency that caused some build problems
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- `DisconnectPeer` and `RemovePeer` have been separated and implemented properly now
|
- `DisconnectPeer` and `RemovePeer` have been separated and implemented properly now
|
||||||
- Less nodes are stored in the DHT now, reducing ambient network traffic and possible instability
|
- Less nodes are stored in the DHT now, reducing ambient network traffic and possible instability
|
||||||
- Default config file for FreeBSD is now at `/usr/local/etc/yggdrasil.conf` instead of `/etc/yggdrasil.conf`
|
- Default config file for FreeBSD is now at `/usr/local/etc/yggdrasil.conf` instead of `/etc/yggdrasil.conf`
|
||||||
|
|
||||||
## [0.3.14] - 2020-03-28
|
## [0.3.14] - 2020-03-28
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixes a memory leak that may occur if packets are incorrectly never removed from a switch queue
|
- Fixes a memory leak that may occur if packets are incorrectly never removed from a switch queue
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Make DHT searches a bit more reliable by tracking the 16 most recently visited nodes
|
- Make DHT searches a bit more reliable by tracking the 16 most recently visited nodes
|
||||||
|
|
||||||
## [0.3.13] - 2020-02-21
|
## [0.3.13] - 2020-02-21
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Support for the Wireguard TUN driver, which now replaces Water and provides far better support and performance on Windows
|
- Support for the Wireguard TUN driver, which now replaces Water and provides far better support and performance on Windows
|
||||||
- Windows `.msi` installer files are now supported (bundling the Wireguard TUN driver)
|
- Windows `.msi` installer files are now supported (bundling the Wireguard TUN driver)
|
||||||
- NodeInfo code is now actorised, should be more reliable
|
- NodeInfo code is now actorised, should be more reliable
|
||||||
|
@ -139,6 +203,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- The Yggdrasil API now supports dialing a remote node using the public key instead of the Node ID
|
- The Yggdrasil API now supports dialing a remote node using the public key instead of the Node ID
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The `-loglevel` command line parameter is now cumulative and automatically includes all levels below the one specified
|
- The `-loglevel` command line parameter is now cumulative and automatically includes all levels below the one specified
|
||||||
- DHT search code has been significantly simplified and processes rumoured nodes in parallel, speeding up search time
|
- DHT search code has been significantly simplified and processes rumoured nodes in parallel, speeding up search time
|
||||||
- DHT search results are now sorted
|
- DHT search results are now sorted
|
||||||
|
@ -146,26 +211,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- The Yggdrasil API now returns public keys instead of node IDs when querying for local and remote addresses
|
- The Yggdrasil API now returns public keys instead of node IDs when querying for local and remote addresses
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- The multicast code no longer panics when shutting down the node
|
- The multicast code no longer panics when shutting down the node
|
||||||
- A potential OOB error when calculating IPv4 flow labels (when tunnel routing is enabled) has been fixed
|
- A potential OOB error when calculating IPv4 flow labels (when tunnel routing is enabled) has been fixed
|
||||||
- A bug resulting in incorrect idle notifications in the switch should now be fixed
|
- A bug resulting in incorrect idle notifications in the switch should now be fixed
|
||||||
- MTUs are now using a common datatype throughout the codebase
|
- MTUs are now using a common datatype throughout the codebase
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- TAP mode has been removed entirely, since it is no longer supported with the Wireguard TUN package. Please note that if you are using TAP mode, you may need to revise your config!
|
- TAP mode has been removed entirely, since it is no longer supported with the Wireguard TUN package. Please note that if you are using TAP mode, you may need to revise your config!
|
||||||
- NetBSD support has been removed until the Wireguard TUN package supports NetBSD
|
- NetBSD support has been removed until the Wireguard TUN package supports NetBSD
|
||||||
|
|
||||||
## [0.3.12] - 2019-11-24
|
## [0.3.12] - 2019-11-24
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- New API functions `SetMaximumSessionMTU` and `GetMaximumSessionMTU`
|
- New API functions `SetMaximumSessionMTU` and `GetMaximumSessionMTU`
|
||||||
- New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file, for use with `-useconffile` or `-useconf`
|
- New command line parameters `-address` and `-subnet` for getting the address/subnet from the config file, for use with `-useconffile` or `-useconf`
|
||||||
- A warning is now produced in the Yggdrasil output at startup when the MTU in the config is invalid or has been adjusted for some reason
|
- A warning is now produced in the Yggdrasil output at startup when the MTU in the config is invalid or has been adjusted for some reason
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- On Linux, outgoing `InterfacePeers` connections now use `SO_BINDTODEVICE` to prefer an outgoing interface
|
- On Linux, outgoing `InterfacePeers` connections now use `SO_BINDTODEVICE` to prefer an outgoing interface
|
||||||
- The `genkeys` utility is now in `cmd` rather than `misc`
|
- The `genkeys` utility is now in `cmd` rather than `misc`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- A data race condition has been fixed when updating session coordinates
|
- A data race condition has been fixed when updating session coordinates
|
||||||
- A crash when shutting down when no multicast interfaces are configured has been fixed
|
- A crash when shutting down when no multicast interfaces are configured has been fixed
|
||||||
- A deadlock when calling `AddPeer` multiple times has been fixed
|
- A deadlock when calling `AddPeer` multiple times has been fixed
|
||||||
|
@ -174,10 +245,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- The MTU calculation now correctly accounts for ethernet headers when running in TAP mode
|
- The MTU calculation now correctly accounts for ethernet headers when running in TAP mode
|
||||||
|
|
||||||
## [0.3.11] - 2019-10-25
|
## [0.3.11] - 2019-10-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Support for TLS listeners and peers has been added, allowing the use of `tls://host:port` in `Peers`, `InterfacePeers` and `Listen` configuration settings - this allows hiding Yggdrasil peerings inside regular TLS connections
|
- Support for TLS listeners and peers has been added, allowing the use of `tls://host:port` in `Peers`, `InterfacePeers` and `Listen` configuration settings - this allows hiding Yggdrasil peerings inside regular TLS connections
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Go 1.13 or later is now required for building Yggdrasil
|
- Go 1.13 or later is now required for building Yggdrasil
|
||||||
- Some exported API functions have been updated to work with standard Go interfaces:
|
- Some exported API functions have been updated to work with standard Go interfaces:
|
||||||
- `net.Conn` instead of `yggdrasil.Conn`
|
- `net.Conn` instead of `yggdrasil.Conn`
|
||||||
|
@ -187,27 +261,35 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Multicast module reloading behaviour has been improved
|
- Multicast module reloading behaviour has been improved
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- An incorrectly held mutex in the crypto-key routing code has been fixed
|
- An incorrectly held mutex in the crypto-key routing code has been fixed
|
||||||
- Multicast module no longer opens a listener socket if no multicast interfaces are configured
|
- Multicast module no longer opens a listener socket if no multicast interfaces are configured
|
||||||
|
|
||||||
## [0.3.10] - 2019-10-10
|
## [0.3.10] - 2019-10-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- The core library now includes several unit tests for peering and `yggdrasil.Conn` connections
|
- The core library now includes several unit tests for peering and `yggdrasil.Conn` connections
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- On recent Linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load
|
- On recent Linux kernels, Yggdrasil will now set the `tcp_congestion_control` algorithm used for its own TCP sockets to [BBR](https://github.com/google/bbr), which reduces latency under load
|
||||||
- The systemd service configuration in `contrib` (and, by extension, some of our packages) now attempts to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do
|
- The systemd service configuration in `contrib` (and, by extension, some of our packages) now attempts to load the `tun` module, in case TUN/TAP support is available but not loaded, and it restricts Yggdrasil to the `CAP_NET_ADMIN` capability for managing the TUN/TAP adapter, rather than letting it do whatever the (typically `root`) user can do
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- The `yggdrasil.Conn.RemoteAddr()` function no longer blocks, fixing a deadlock when CKR is used while under heavy load
|
- The `yggdrasil.Conn.RemoteAddr()` function no longer blocks, fixing a deadlock when CKR is used while under heavy load
|
||||||
|
|
||||||
## [0.3.9] - 2019-09-27
|
## [0.3.9] - 2019-09-27
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Yggdrasil will now complain more verbosely when a peer URI is incorrectly formatted
|
- Yggdrasil will now complain more verbosely when a peer URI is incorrectly formatted
|
||||||
- Soft-shutdown methods have been added, allowing a node to shut down gracefully when terminated
|
- Soft-shutdown methods have been added, allowing a node to shut down gracefully when terminated
|
||||||
- New multicast interval logic which sends multicast beacons more often when Yggdrasil is first started to increase the chance of finding nearby nodes quickly after startup
|
- New multicast interval logic which sends multicast beacons more often when Yggdrasil is first started to increase the chance of finding nearby nodes quickly after startup
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The switch now buffers packets more eagerly in an attempt to give the best link a chance to send, which appears to reduce packet reordering when crossing aggregate sets of peerings
|
- The switch now buffers packets more eagerly in an attempt to give the best link a chance to send, which appears to reduce packet reordering when crossing aggregate sets of peerings
|
||||||
- Substantial amounts of the codebase have been refactored to use the actor model, which should substantially reduce the chance of deadlocks
|
- Substantial amounts of the codebase have been refactored to use the actor model, which should substantially reduce the chance of deadlocks
|
||||||
- Nonce tracking in sessions has been modified so that memory usage is reduced whilst still only allowing duplicate packets within a small window
|
- Nonce tracking in sessions has been modified so that memory usage is reduced whilst still only allowing duplicate packets within a small window
|
||||||
|
@ -216,6 +298,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- The maximum queue size is now managed exclusively by the switch rather than by the core
|
- The maximum queue size is now managed exclusively by the switch rather than by the core
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- The broken `hjson-go` dependency which affected builds of the previous version has now been resolved in the module manifest
|
- The broken `hjson-go` dependency which affected builds of the previous version has now been resolved in the module manifest
|
||||||
- Some minor memory leaks in the switch have been fixed, which improves memory usage on mobile builds
|
- Some minor memory leaks in the switch have been fixed, which improves memory usage on mobile builds
|
||||||
- A memory leak in the add-peer loop has been fixed
|
- A memory leak in the add-peer loop has been fixed
|
||||||
|
@ -227,10 +310,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- A panic which could occur when the TUN/TAP interface reads an undersized/corrupted packet has been fixed
|
- A panic which could occur when the TUN/TAP interface reads an undersized/corrupted packet has been fixed
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- A number of legacy debug functions have now been removed and a number of exported API functions are now better documented
|
- A number of legacy debug functions have now been removed and a number of exported API functions are now better documented
|
||||||
|
|
||||||
## [0.3.8] - 2019-08-21
|
## [0.3.8] - 2019-08-21
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Yggdrasil can now send multiple packets from the switch at once, which results in improved throughput with smaller packets or lower MTUs
|
- Yggdrasil can now send multiple packets from the switch at once, which results in improved throughput with smaller packets or lower MTUs
|
||||||
- Performance has been slightly improved by not allocating cancellations where not necessary
|
- Performance has been slightly improved by not allocating cancellations where not necessary
|
||||||
- Crypto-key routing options have been renamed for clarity
|
- Crypto-key routing options have been renamed for clarity
|
||||||
|
@ -243,20 +329,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- New nonce tracking should help to reduce the number of packets dropped as a result of multiple/aggregate paths or congestion control in the switch
|
- New nonce tracking should help to reduce the number of packets dropped as a result of multiple/aggregate paths or congestion control in the switch
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- A deadlock was fixed in the session code which could result in Yggdrasil failing to pass traffic after some time
|
- A deadlock was fixed in the session code which could result in Yggdrasil failing to pass traffic after some time
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
- Address verification was not strict enough, which could result in a malicious session sending traffic with unexpected or spoofed source or destination addresses which Yggdrasil could fail to reject
|
- Address verification was not strict enough, which could result in a malicious session sending traffic with unexpected or spoofed source or destination addresses which Yggdrasil could fail to reject
|
||||||
- Versions `0.3.6` and `0.3.7` are vulnerable - users of these versions should upgrade as soon as possible
|
- Versions `0.3.6` and `0.3.7` are vulnerable - users of these versions should upgrade as soon as possible
|
||||||
- Versions `0.3.5` and earlier are not affected
|
- Versions `0.3.5` and earlier are not affected
|
||||||
|
|
||||||
## [0.3.7] - 2019-08-14
|
## [0.3.7] - 2019-08-14
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The switch should now forward packets along a single path more consistently in cases where congestion is low and multiple equal-length paths exist, which should improve stability and result in fewer out-of-order packets
|
- The switch should now forward packets along a single path more consistently in cases where congestion is low and multiple equal-length paths exist, which should improve stability and result in fewer out-of-order packets
|
||||||
- Sessions should now be more tolerant of out-of-order packets, by replacing a bitmask with a variable sized heap+map structure to track recently received nonces, which should reduce the number of packets dropped due to reordering when multiple paths are used or multiple independent flows are transmitted through the same session
|
- Sessions should now be more tolerant of out-of-order packets, by replacing a bitmask with a variable sized heap+map structure to track recently received nonces, which should reduce the number of packets dropped due to reordering when multiple paths are used or multiple independent flows are transmitted through the same session
|
||||||
- The admin socket can no longer return a dotfile representation of the known parts of the network, this could be rebuilt by clients using information from `getSwitchPeers`,`getDHT` and `getSessions`
|
- The admin socket can no longer return a dotfile representation of the known parts of the network, this could be rebuilt by clients using information from `getSwitchPeers`,`getDHT` and `getSessions`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- A number of significant performance regressions introduced in version 0.3.6 have been fixed, resulting in better performance
|
- A number of significant performance regressions introduced in version 0.3.6 have been fixed, resulting in better performance
|
||||||
- Flow labels are now used to prioritise traffic flows again correctly
|
- Flow labels are now used to prioritise traffic flows again correctly
|
||||||
- In low-traffic scenarios where there are multiple peerings between a pair of nodes, Yggdrasil now prefers the most active peering instead of the least active, helping to reduce packet reordering
|
- In low-traffic scenarios where there are multiple peerings between a pair of nodes, Yggdrasil now prefers the most active peering instead of the least active, helping to reduce packet reordering
|
||||||
|
@ -270,13 +361,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- A number of minor allocation and pointer fixes
|
- A number of minor allocation and pointer fixes
|
||||||
|
|
||||||
## [0.3.6] - 2019-08-03
|
## [0.3.6] - 2019-08-03
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Yggdrasil now has a public API with interfaces such as `yggdrasil.ConnDialer`, `yggdrasil.ConnListener` and `yggdrasil.Conn` for using Yggdrasil as a transport directly within applications
|
- Yggdrasil now has a public API with interfaces such as `yggdrasil.ConnDialer`, `yggdrasil.ConnListener` and `yggdrasil.Conn` for using Yggdrasil as a transport directly within applications
|
||||||
- Session gatekeeper functions, part of the API, which can be used to control whether to allow or reject incoming or outgoing sessions dynamically (compared to the previous fixed whitelist/blacklist approach)
|
- Session gatekeeper functions, part of the API, which can be used to control whether to allow or reject incoming or outgoing sessions dynamically (compared to the previous fixed whitelist/blacklist approach)
|
||||||
- Support for logging to files or syslog (where supported)
|
- Support for logging to files or syslog (where supported)
|
||||||
- Platform defaults now include the ability to set sane defaults for multicast interfaces
|
- Platform defaults now include the ability to set sane defaults for multicast interfaces
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Following a massive refactoring exercise, Yggdrasil's codebase has now been broken out into modules
|
- Following a massive refactoring exercise, Yggdrasil's codebase has now been broken out into modules
|
||||||
- Core node functionality in the `yggdrasil` package with a public API
|
- Core node functionality in the `yggdrasil` package with a public API
|
||||||
- This allows Yggdrasil to be integrated directly into other applications and used as a transport
|
- This allows Yggdrasil to be integrated directly into other applications and used as a transport
|
||||||
|
@ -289,6 +383,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Upstream dependency references have been updated, which includes a number of fixes in the Water library
|
- Upstream dependency references have been updated, which includes a number of fixes in the Water library
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Multicast discovery is no longer disabled if the nominated interfaces aren't available on the system yet, e.g. during boot
|
- Multicast discovery is no longer disabled if the nominated interfaces aren't available on the system yet, e.g. during boot
|
||||||
- Multicast interfaces are now re-evaluated more frequently so that Yggdrasil doesn't need to be restarted to use interfaces that have become available since startup
|
- Multicast interfaces are now re-evaluated more frequently so that Yggdrasil doesn't need to be restarted to use interfaces that have become available since startup
|
||||||
- Admin socket error cases are now handled better
|
- Admin socket error cases are now handled better
|
||||||
|
@ -303,12 +398,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Lots of small bug tweaks and clean-ups throughout the codebase
|
- Lots of small bug tweaks and clean-ups throughout the codebase
|
||||||
|
|
||||||
## [0.3.5] - 2019-03-13
|
## [0.3.5] - 2019-03-13
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- The `AllowedEncryptionPublicKeys` option has now been fixed to handle incoming connections properly and no longer blocks outgoing connections (this was broken in v0.3.4)
|
- The `AllowedEncryptionPublicKeys` option has now been fixed to handle incoming connections properly and no longer blocks outgoing connections (this was broken in v0.3.4)
|
||||||
- Multicast TCP listeners will now be stopped correctly when the link-local address on the interface changes or disappears altogether
|
- Multicast TCP listeners will now be stopped correctly when the link-local address on the interface changes or disappears altogether
|
||||||
|
|
||||||
## [0.3.4] - 2019-03-12
|
## [0.3.4] - 2019-03-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Support for multiple listeners (although currently only TCP listeners are supported)
|
- Support for multiple listeners (although currently only TCP listeners are supported)
|
||||||
- New multicast behaviour where each multicast interface is given its own link-local listener and does not depend on the `Listen` configuration
|
- New multicast behaviour where each multicast interface is given its own link-local listener and does not depend on the `Listen` configuration
|
||||||
- Blocking detection in the switch to avoid parenting a blocked peer
|
- Blocking detection in the switch to avoid parenting a blocked peer
|
||||||
|
@ -319,6 +418,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Added `LinkLocalTCPPort` option for controlling the port number that link-local TCP listeners will listen on by default when setting up `MulticastInterfaces` (a node restart is currently required for changes to `LinkLocalTCPPort` to take effect - it cannot be updated by reloading config during runtime)
|
- Added `LinkLocalTCPPort` option for controlling the port number that link-local TCP listeners will listen on by default when setting up `MulticastInterfaces` (a node restart is currently required for changes to `LinkLocalTCPPort` to take effect - it cannot be updated by reloading config during runtime)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The `Listen` configuration statement is now an array instead of a string
|
- The `Listen` configuration statement is now an array instead of a string
|
||||||
- The `Listen` configuration statement should now conform to the same formatting as peers with the protocol prefix, e.g. `tcp://[::]:0`
|
- The `Listen` configuration statement should now conform to the same formatting as peers with the protocol prefix, e.g. `tcp://[::]:0`
|
||||||
- Session workers are now non-blocking
|
- Session workers are now non-blocking
|
||||||
|
@ -327,6 +427,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Peer forwarding is now prioritised instead of randomised
|
- Peer forwarding is now prioritised instead of randomised
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Admin socket `getTunTap` call now returns properly instead of claiming no interface is enabled in all cases
|
- Admin socket `getTunTap` call now returns properly instead of claiming no interface is enabled in all cases
|
||||||
- Handling of `getRoutes` etc in `yggdrasilctl` is now working
|
- Handling of `getRoutes` etc in `yggdrasilctl` is now working
|
||||||
- Local interface names are no longer leaked in multicast packets
|
- Local interface names are no longer leaked in multicast packets
|
||||||
|
@ -334,7 +435,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Yggdrasil now correctly responds to multicast interfaces going up and down during runtime
|
- Yggdrasil now correctly responds to multicast interfaces going up and down during runtime
|
||||||
|
|
||||||
## [0.3.3] - 2019-02-18
|
## [0.3.3] - 2019-02-18
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Dynamic reconfiguration, which allows reloading the configuration file to make changes during runtime by sending a `SIGHUP` signal (note: this only works with `-useconffile` and not `-useconf` and currently reconfiguring TUN/TAP is not supported)
|
- Dynamic reconfiguration, which allows reloading the configuration file to make changes during runtime by sending a `SIGHUP` signal (note: this only works with `-useconffile` and not `-useconf` and currently reconfiguring TUN/TAP is not supported)
|
||||||
- Support for building Yggdrasil as an iOS or Android framework if the appropriate tools (e.g. `gomobile`/`gobind` + SDKs) are available
|
- Support for building Yggdrasil as an iOS or Android framework if the appropriate tools (e.g. `gomobile`/`gobind` + SDKs) are available
|
||||||
- Connection contexts used for TCP connections which allow more exotic socket options to be set, e.g.
|
- Connection contexts used for TCP connections which allow more exotic socket options to be set, e.g.
|
||||||
|
@ -343,26 +446,33 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Flexible logging support, which allows for logging at different levels of verbosity
|
- Flexible logging support, which allows for logging at different levels of verbosity
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Switch changes to improve parent selection
|
- Switch changes to improve parent selection
|
||||||
- Node configuration is now stored centrally, rather than having fragments/copies distributed at startup time
|
- Node configuration is now stored centrally, rather than having fragments/copies distributed at startup time
|
||||||
- Significant refactoring in various areas, including for link types (TCP, AWDL etc), generic streams and adapters
|
- Significant refactoring in various areas, including for link types (TCP, AWDL etc), generic streams and adapters
|
||||||
- macOS builds through CircleCI are now 64-bit only
|
- macOS builds through CircleCI are now 64-bit only
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Simplified `systemd` service now in `contrib`
|
- Simplified `systemd` service now in `contrib`
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- `ReadTimeout` option is now deprecated
|
- `ReadTimeout` option is now deprecated
|
||||||
|
|
||||||
## [0.3.2] - 2018-12-26
|
## [0.3.2] - 2018-12-26
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- The admin socket is now multithreaded, greatly improving performance of the crawler and allowing concurrent lookups to take place
|
- The admin socket is now multithreaded, greatly improving performance of the crawler and allowing concurrent lookups to take place
|
||||||
- The ability to hide NodeInfo defaults through either setting the `NodeInfoPrivacy` option or through setting individual `NodeInfo` attributes to `null`
|
- The ability to hide NodeInfo defaults through either setting the `NodeInfoPrivacy` option or through setting individual `NodeInfo` attributes to `null`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The `armhf` build now targets ARMv6 instead of ARMv7, adding support for Raspberry Pi Zero and other older models, amongst others
|
- The `armhf` build now targets ARMv6 instead of ARMv7, adding support for Raspberry Pi Zero and other older models, amongst others
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- DHT entries are now populated using a copy in memory to fix various potential DHT bugs
|
- DHT entries are now populated using a copy in memory to fix various potential DHT bugs
|
||||||
- DHT traffic should now throttle back exponentially to reduce idle traffic
|
- DHT traffic should now throttle back exponentially to reduce idle traffic
|
||||||
- Adjust how nodes are inserted into the DHT which should help to reduce some incorrect DHT traffic
|
- Adjust how nodes are inserted into the DHT which should help to reduce some incorrect DHT traffic
|
||||||
|
@ -370,7 +480,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- In TUN mode, ICMPv6 packets are now ignored whereas they were incorrectly processed before
|
- In TUN mode, ICMPv6 packets are now ignored whereas they were incorrectly processed before
|
||||||
|
|
||||||
## [0.3.1] - 2018-12-17
|
## [0.3.1] - 2018-12-17
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Build name and version is now imprinted onto the binaries if available/specified during build
|
- Build name and version is now imprinted onto the binaries if available/specified during build
|
||||||
- Ability to disable admin socket with `AdminListen: none`
|
- Ability to disable admin socket with `AdminListen: none`
|
||||||
- `AF_UNIX` domain sockets for the admin socket
|
- `AF_UNIX` domain sockets for the admin socket
|
||||||
|
@ -380,18 +492,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Adds flags `-c`, `-l` and `-t` to `build` script for specifying `GCFLAGS`, `LDFLAGS` or whether to keep symbol/DWARF tables
|
- Adds flags `-c`, `-l` and `-t` to `build` script for specifying `GCFLAGS`, `LDFLAGS` or whether to keep symbol/DWARF tables
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Default `AdminListen` in newly generated config is now `unix:///var/run/yggdrasil.sock`
|
- Default `AdminListen` in newly generated config is now `unix:///var/run/yggdrasil.sock`
|
||||||
- Formatting of `getRoutes` in the admin socket has been improved
|
- Formatting of `getRoutes` in the admin socket has been improved
|
||||||
- Debian package now adds `yggdrasil` group to assist with `AF_UNIX` admin socket permissions
|
- Debian package now adds `yggdrasil` group to assist with `AF_UNIX` admin socket permissions
|
||||||
- Crypto, address and other utility code refactored into separate Go packages
|
- Crypto, address and other utility code refactored into separate Go packages
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Switch peer convergence is now much faster again (previously it was taking up to a minute once the peering was established)
|
- Switch peer convergence is now much faster again (previously it was taking up to a minute once the peering was established)
|
||||||
- `yggdrasilctl` is now less prone to crashing when parameters are specified incorrectly
|
- `yggdrasilctl` is now less prone to crashing when parameters are specified incorrectly
|
||||||
- Panic fixed when `Peers` or `InterfacePeers` was commented out
|
- Panic fixed when `Peers` or `InterfacePeers` was commented out
|
||||||
|
|
||||||
## [0.3.0] - 2018-12-12
|
## [0.3.0] - 2018-12-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Crypto-key routing support for tunnelling both IPv4 and IPv6 over Yggdrasil
|
- Crypto-key routing support for tunnelling both IPv4 and IPv6 over Yggdrasil
|
||||||
- Add advanced `SwitchOptions` in configuration file for tuning the switch
|
- Add advanced `SwitchOptions` in configuration file for tuning the switch
|
||||||
- Add `dhtPing` to the admin socket to aid in crawling the network
|
- Add `dhtPing` to the admin socket to aid in crawling the network
|
||||||
|
@ -404,6 +520,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- `yggdrasilctl` now returns more useful logging in the event of a fatal error
|
- `yggdrasilctl` now returns more useful logging in the event of a fatal error
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Switched to Chord DHT (instead of Kademlia, although still compatible at the protocol level)
|
- Switched to Chord DHT (instead of Kademlia, although still compatible at the protocol level)
|
||||||
- The `AdminListen` option and `yggdrasilctl` now default to `unix:///var/run/yggdrasil.sock` on BSDs, macOS and Linux
|
- The `AdminListen` option and `yggdrasilctl` now default to `unix:///var/run/yggdrasil.sock` on BSDs, macOS and Linux
|
||||||
- Cleaned up some of the parameter naming in the admin socket
|
- Cleaned up some of the parameter naming in the admin socket
|
||||||
|
@ -413,12 +530,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- `yggdrasilctl` now has more useful help text (with `-help` or when no arguments passed)
|
- `yggdrasilctl` now has more useful help text (with `-help` or when no arguments passed)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Memory leaks in the DHT fixed
|
- Memory leaks in the DHT fixed
|
||||||
- Crash fixed where the ICMPv6 NDP goroutine would incorrectly start in TUN mode
|
- Crash fixed where the ICMPv6 NDP goroutine would incorrectly start in TUN mode
|
||||||
- Removing peers from the switch table if they stop sending switch messages but keep the TCP connection alive
|
- Removing peers from the switch table if they stop sending switch messages but keep the TCP connection alive
|
||||||
|
|
||||||
## [0.2.7] - 2018-10-13
|
## [0.2.7] - 2018-10-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Session firewall, which makes it possible to control who can open sessions with your node
|
- Session firewall, which makes it possible to control who can open sessions with your node
|
||||||
- Add `getSwitchQueues` to admin socket
|
- Add `getSwitchQueues` to admin socket
|
||||||
- Add `InterfacePeers` for configuring static peerings via specific network interfaces
|
- Add `InterfacePeers` for configuring static peerings via specific network interfaces
|
||||||
|
@ -426,81 +546,106 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- FreeBSD service script in `contrib`
|
- FreeBSD service script in `contrib`
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
- CircleCI builds are now built with Go 1.11 instead of Go 1.9
|
- CircleCI builds are now built with Go 1.11 instead of Go 1.9
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- Race condition in the switch table, reported by trn
|
- Race condition in the switch table, reported by trn
|
||||||
- Debug builds are now tested by CircleCI as well as platform release builds
|
- Debug builds are now tested by CircleCI as well as platform release builds
|
||||||
- Port number fixed on admin graph from unknown nodes
|
- Port number fixed on admin graph from unknown nodes
|
||||||
|
|
||||||
## [0.2.6] - 2018-07-31
|
## [0.2.6] - 2018-07-31
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Configurable TCP timeouts to assist in peering over Tor/I2P
|
- Configurable TCP timeouts to assist in peering over Tor/I2P
|
||||||
- Prefer IPv6 flow label when extending coordinates to sort backpressure queues
|
- Prefer IPv6 flow label when extending coordinates to sort backpressure queues
|
||||||
- `arm64` builds through CircleCI
|
- `arm64` builds through CircleCI
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Sort dot graph links by integer value
|
- Sort dot graph links by integer value
|
||||||
|
|
||||||
## [0.2.5] - 2018-07-19
|
## [0.2.5] - 2018-07-19
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Make `yggdrasilctl` less case sensitive
|
- Make `yggdrasilctl` less case sensitive
|
||||||
- More verbose TCP disconnect messages
|
- More verbose TCP disconnect messages
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed debug builds
|
- Fixed debug builds
|
||||||
- Cap maximum MTU on Linux in TAP mode
|
- Cap maximum MTU on Linux in TAP mode
|
||||||
- Process successfully-read TCP traffic before checking for / handling errors (fixes EOF behavior)
|
- Process successfully-read TCP traffic before checking for / handling errors (fixes EOF behavior)
|
||||||
|
|
||||||
## [0.2.4] - 2018-07-08
|
## [0.2.4] - 2018-07-08
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Support for UNIX domain sockets for the admin socket using `unix:///path/to/file.sock`
|
- Support for UNIX domain sockets for the admin socket using `unix:///path/to/file.sock`
|
||||||
- Centralised platform-specific defaults
|
- Centralised platform-specific defaults
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Backpressure tuning, including reducing resource consumption
|
- Backpressure tuning, including reducing resource consumption
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- macOS local ping bug, which previously prevented you from pinging your own `utun` adapter's IPv6 address
|
- macOS local ping bug, which previously prevented you from pinging your own `utun` adapter's IPv6 address
|
||||||
|
|
||||||
## [0.2.3] - 2018-06-29
|
## [0.2.3] - 2018-06-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Begin keeping changelog (incomplete and possibly inaccurate information before this point).
|
- Begin keeping changelog (incomplete and possibly inaccurate information before this point).
|
||||||
- Build RPMs in CircleCI using alien. This provides package support for Fedora, Red Hat Enterprise Linux, CentOS and other RPM-based distributions.
|
- Build RPMs in CircleCI using alien. This provides package support for Fedora, Red Hat Enterprise Linux, CentOS and other RPM-based distributions.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Local backpressure improvements.
|
- Local backpressure improvements.
|
||||||
- Change `box_pub_key` to `key` in admin API for simplicity.
|
- Change `box_pub_key` to `key` in admin API for simplicity.
|
||||||
- Session cleanup.
|
- Session cleanup.
|
||||||
|
|
||||||
## [0.2.2] - 2018-06-21
|
## [0.2.2] - 2018-06-21
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Add `yggdrasilconf` utility for testing with the `vyatta-yggdrasil` package.
|
- Add `yggdrasilconf` utility for testing with the `vyatta-yggdrasil` package.
|
||||||
- Add a randomized retry delay after TCP disconnects, to prevent synchronization livelocks.
|
- Add a randomized retry delay after TCP disconnects, to prevent synchronization livelocks.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Update build script to strip by default, which significantly reduces the size of the binary.
|
- Update build script to strip by default, which significantly reduces the size of the binary.
|
||||||
- Add debug `-d` and UPX `-u` flags to the `build` script.
|
- Add debug `-d` and UPX `-u` flags to the `build` script.
|
||||||
- Start pprof in debug builds based on an environment variable (e.g. `PPROFLISTEN=localhost:6060`), instead of a flag.
|
- Start pprof in debug builds based on an environment variable (e.g. `PPROFLISTEN=localhost:6060`), instead of a flag.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fix typo in big-endian BOM so that both little-endian and big-endian UTF-16 files are detected correctly.
|
- Fix typo in big-endian BOM so that both little-endian and big-endian UTF-16 files are detected correctly.
|
||||||
|
|
||||||
## [0.2.1] - 2018-06-15
|
## [0.2.1] - 2018-06-15
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The address range was moved from `fd00::/8` to `200::/7`. This range was chosen as it is marked as deprecated. The change prevents overlap with other ULA privately assigned ranges.
|
- The address range was moved from `fd00::/8` to `200::/7`. This range was chosen as it is marked as deprecated. The change prevents overlap with other ULA privately assigned ranges.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- UTF-16 detection conversion for configuration files, which can particularly be a problem on Windows 10 if a configuration file is generated from within PowerShell.
|
- UTF-16 detection conversion for configuration files, which can particularly be a problem on Windows 10 if a configuration file is generated from within PowerShell.
|
||||||
- Fixes to the Debian package control file.
|
- Fixes to the Debian package control file.
|
||||||
- Fixes to the launchd service for macOS.
|
- Fixes to the launchd service for macOS.
|
||||||
- Fixes to the DHT and switch.
|
- Fixes to the DHT and switch.
|
||||||
|
|
||||||
## [0.2.0] - 2018-06-13
|
## [0.2.0] - 2018-06-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Exchange version information during connection setup, to prevent connections with incompatible versions.
|
- Exchange version information during connection setup, to prevent connections with incompatible versions.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Wire format changes (backwards incompatible).
|
- Wire format changes (backwards incompatible).
|
||||||
- Less maintenance traffic per peer.
|
- Less maintenance traffic per peer.
|
||||||
- Exponential back-off for DHT maintenance traffic (less maintenance traffic for known good peers).
|
- Exponential back-off for DHT maintenance traffic (less maintenance traffic for known good peers).
|
||||||
|
@ -508,18 +653,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Use local queue sizes for a sort of local-only backpressure routing, instead of the removed bandwidth estimates, when deciding where to send a packet.
|
- Use local queue sizes for a sort of local-only backpressure routing, instead of the removed bandwidth estimates, when deciding where to send a packet.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- UDP peering, this may be added again if/when a better implementation appears.
|
- UDP peering, this may be added again if/when a better implementation appears.
|
||||||
- Per peer bandwidth estimation, as this has been replaced with an early local backpressure implementation.
|
- Per peer bandwidth estimation, as this has been replaced with an early local backpressure implementation.
|
||||||
|
|
||||||
## [0.1.0] - 2018-02-01
|
## [0.1.0] - 2018-02-01
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Adopt semantic versioning.
|
- Adopt semantic versioning.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Wire format changes (backwards incompatible).
|
- Wire format changes (backwards incompatible).
|
||||||
- Many other undocumented changes leading up to this release and before the next one.
|
- Many other undocumented changes leading up to this release and before the next one.
|
||||||
|
|
||||||
## [0.0.1] - 2017-12-28
|
## [0.0.1] - 2017-12-28
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- First commit.
|
- First commit.
|
||||||
- Initial public release.
|
- Initial public release.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# Yggdrasil
|
# Yggdrasil
|
||||||
|
|
||||||
[](https://github.com/yggdrasil-network/yggdrasil-go/actions/workflows/ci.yml)
|
||||||
)](https://circleci.com/gh/yggdrasil-network/yggdrasil-go)
|
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
|
@ -25,7 +24,7 @@ or tools in the `contrib` folder.
|
||||||
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.16 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`
|
||||||
|
|
||||||
|
@ -57,6 +56,7 @@ other configuration such as listen addresses or multicast addresses, etc.
|
||||||
### Run Yggdrasil
|
### Run Yggdrasil
|
||||||
|
|
||||||
To run with the generated static configuration:
|
To run with the generated static configuration:
|
||||||
|
|
||||||
```
|
```
|
||||||
./yggdrasil -useconffile /path/to/yggdrasil.conf
|
./yggdrasil -useconffile /path/to/yggdrasil.conf
|
||||||
```
|
```
|
||||||
|
|
20
appveyor.yml
20
appveyor.yml
|
@ -1,20 +0,0 @@
|
||||||
version: '{build}'
|
|
||||||
pull_requests:
|
|
||||||
do_not_increment_build_number: true
|
|
||||||
os: Visual Studio 2019
|
|
||||||
shallow_clone: false
|
|
||||||
|
|
||||||
environment:
|
|
||||||
MSYS2_PATH_TYPE: inherit
|
|
||||||
CHERE_INVOKING: enabled_from_arguments
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- cmd: >-
|
|
||||||
cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x64"
|
|
||||||
- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x86"
|
|
||||||
|
|
||||||
test: off
|
|
||||||
|
|
||||||
artifacts:
|
|
||||||
- path: '*.msi'
|
|
32
build
32
build
|
@ -9,13 +9,11 @@ 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 "uaitc:l:dro:p" option
|
while getopts "utc:l:dro:p" option
|
||||||
do
|
do
|
||||||
case "$option"
|
case "$option"
|
||||||
in
|
in
|
||||||
u) UPX=true;;
|
u) UPX=true;;
|
||||||
i) IOS=true;;
|
|
||||||
a) ANDROID=true;;
|
|
||||||
t) TABLES=true;;
|
t) TABLES=true;;
|
||||||
c) GCFLAGS="$GCFLAGS $OPTARG";;
|
c) GCFLAGS="$GCFLAGS $OPTARG";;
|
||||||
l) LDFLAGS="$LDFLAGS $OPTARG";;
|
l) LDFLAGS="$LDFLAGS $OPTARG";;
|
||||||
|
@ -30,25 +28,11 @@ if [ -z $TABLES ] && [ -z $DEBUG ]; then
|
||||||
LDFLAGS="$LDFLAGS -s -w"
|
LDFLAGS="$LDFLAGS -s -w"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $IOS ]; then
|
for CMD in yggdrasil yggdrasilctl ; do
|
||||||
echo "Building framework for iOS"
|
echo "Building: $CMD"
|
||||||
go get golang.org/x/mobile/bind
|
go build $ARGS -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" ./cmd/$CMD
|
||||||
gomobile bind -target ios -tags mobile -o Yggdrasil.framework -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" \
|
|
||||||
github.com/yggdrasil-network/yggdrasil-extras/src/mobile \
|
|
||||||
github.com/yggdrasil-network/yggdrasil-go/src/config
|
|
||||||
elif [ $ANDROID ]; then
|
|
||||||
echo "Building aar for Android"
|
|
||||||
go get golang.org/x/mobile/bind
|
|
||||||
gomobile bind -target android -tags mobile -o yggdrasil.aar -ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" \
|
|
||||||
github.com/yggdrasil-network/yggdrasil-extras/src/mobile \
|
|
||||||
github.com/yggdrasil-network/yggdrasil-go/src/config
|
|
||||||
else
|
|
||||||
for CMD in yggdrasil yggdrasilctl ; do
|
|
||||||
echo "Building: $CMD"
|
|
||||||
go build $ARGS -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" ./cmd/$CMD
|
|
||||||
|
|
||||||
if [ $UPX ]; then
|
if [ $UPX ]; then
|
||||||
upx --brute $CMD
|
upx --brute $CMD
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
|
||||||
|
|
|
@ -8,10 +8,11 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ import (
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||||
|
@ -35,8 +37,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
core core.Core
|
core *core.Core
|
||||||
config *config.NodeConfig
|
|
||||||
tuntap *tuntap.TunAdapter
|
tuntap *tuntap.TunAdapter
|
||||||
multicast *multicast.Multicast
|
multicast *multicast.Multicast
|
||||||
admin *admin.AdminSocket
|
admin *admin.AdminSocket
|
||||||
|
@ -50,10 +51,10 @@ func readConfig(log *log.Logger, useconf bool, useconffile string, normaliseconf
|
||||||
var err error
|
var err error
|
||||||
if useconffile != "" {
|
if useconffile != "" {
|
||||||
// Read the file from the filesystem
|
// Read the file from the filesystem
|
||||||
conf, err = ioutil.ReadFile(useconffile)
|
conf, err = os.ReadFile(useconffile)
|
||||||
} else {
|
} else {
|
||||||
// Read the file from stdin.
|
// Read the file from stdin.
|
||||||
conf, err = ioutil.ReadAll(os.Stdin)
|
conf, err = io.ReadAll(os.Stdin)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -80,48 +81,6 @@ func readConfig(log *log.Logger, useconf bool, useconffile string, normaliseconf
|
||||||
if err := hjson.Unmarshal(conf, &dat); err != nil {
|
if err := hjson.Unmarshal(conf, &dat); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
// Check if we have old field names
|
|
||||||
if _, ok := dat["TunnelRouting"]; ok {
|
|
||||||
log.Warnln("WARNING: Tunnel routing is no longer supported")
|
|
||||||
}
|
|
||||||
if old, ok := dat["SigningPrivateKey"]; ok {
|
|
||||||
log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option has been renamed to \"PrivateKey\"")
|
|
||||||
if _, ok := dat["PrivateKey"]; !ok {
|
|
||||||
if privstr, err := hex.DecodeString(old.(string)); err == nil {
|
|
||||||
priv := ed25519.PrivateKey(privstr)
|
|
||||||
pub := priv.Public().(ed25519.PublicKey)
|
|
||||||
dat["PrivateKey"] = hex.EncodeToString(priv[:])
|
|
||||||
dat["PublicKey"] = hex.EncodeToString(pub[:])
|
|
||||||
} else {
|
|
||||||
log.Warnln("WARNING: The \"SigningPrivateKey\" configuration option contains an invalid value and will be ignored")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if oldmc, ok := dat["MulticastInterfaces"]; ok {
|
|
||||||
if oldmcvals, ok := oldmc.([]interface{}); ok {
|
|
||||||
var newmc []config.MulticastInterfaceConfig
|
|
||||||
for _, oldmcval := range oldmcvals {
|
|
||||||
if str, ok := oldmcval.(string); ok {
|
|
||||||
newmc = append(newmc, config.MulticastInterfaceConfig{
|
|
||||||
Regex: str,
|
|
||||||
Beacon: true,
|
|
||||||
Listen: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if newmc != nil {
|
|
||||||
if oldport, ok := dat["LinkLocalTCPPort"]; ok {
|
|
||||||
// numbers parse to float64 by default
|
|
||||||
if port, ok := oldport.(float64); ok {
|
|
||||||
for idx := range newmc {
|
|
||||||
newmc[idx].Port = uint16(port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dat["MulticastInterfaces"] = newmc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Sanitise the config
|
// Sanitise the config
|
||||||
confJson, err := json.Marshal(dat)
|
confJson, err := json.Marshal(dat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -184,14 +143,14 @@ func setLogLevel(loglevel string, logger *log.Logger) {
|
||||||
type yggArgs struct {
|
type yggArgs struct {
|
||||||
genconf bool
|
genconf bool
|
||||||
useconf bool
|
useconf bool
|
||||||
useconffile string
|
|
||||||
normaliseconf bool
|
normaliseconf bool
|
||||||
confjson bool
|
confjson bool
|
||||||
autoconf bool
|
autoconf bool
|
||||||
ver bool
|
ver bool
|
||||||
logto string
|
|
||||||
getaddr bool
|
getaddr bool
|
||||||
getsnet bool
|
getsnet bool
|
||||||
|
useconffile string
|
||||||
|
logto string
|
||||||
loglevel string
|
loglevel string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,44 +280,86 @@ func run(args yggArgs, ctx context.Context, done chan struct{}) {
|
||||||
fmt.Println(ipnet.String())
|
fmt.Println(ipnet.String())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the Yggdrasil node itself. The node{} type includes a Core, so we
|
n := &node{}
|
||||||
// don't need to create this manually.
|
|
||||||
n := node{config: cfg}
|
// Setup the Yggdrasil node itself.
|
||||||
// Now start Yggdrasil - this starts the DHT, router, switch and other core
|
{
|
||||||
// components needed for Yggdrasil to operate
|
sk, err := hex.DecodeString(cfg.PrivateKey)
|
||||||
if err = n.core.Start(cfg, logger); err != nil {
|
if err != nil {
|
||||||
logger.Errorln("An error occurred during startup")
|
panic(err)
|
||||||
panic(err)
|
}
|
||||||
|
options := []core.SetupOption{}
|
||||||
|
for _, addr := range cfg.Listen {
|
||||||
|
options = append(options, core.ListenAddress(addr))
|
||||||
|
}
|
||||||
|
for _, peer := range cfg.Peers {
|
||||||
|
options = append(options, core.Peer{URI: peer})
|
||||||
|
}
|
||||||
|
for intf, peers := range cfg.InterfacePeers {
|
||||||
|
for _, peer := range peers {
|
||||||
|
options = append(options, core.Peer{URI: peer, SourceInterface: intf})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, allowed := range cfg.AllowedPublicKeys {
|
||||||
|
k, err := hex.DecodeString(allowed)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
options = append(options, core.AllowedPublicKey(k[:]))
|
||||||
|
}
|
||||||
|
if n.core, err = core.New(sk[:], logger, options...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Register the session firewall gatekeeper function
|
|
||||||
// Allocate our modules
|
// Setup the admin socket.
|
||||||
n.admin = &admin.AdminSocket{}
|
{
|
||||||
n.multicast = &multicast.Multicast{}
|
options := []admin.SetupOption{
|
||||||
n.tuntap = &tuntap.TunAdapter{}
|
admin.ListenAddress(cfg.AdminListen),
|
||||||
// Start the admin socket
|
}
|
||||||
if err := n.admin.Init(&n.core, cfg, logger, nil); err != nil {
|
if n.admin, err = admin.New(n.core, logger, options...); err != nil {
|
||||||
logger.Errorln("An error occurred initialising admin socket:", err)
|
panic(err)
|
||||||
} else if err := n.admin.Start(); err != nil {
|
}
|
||||||
logger.Errorln("An error occurred starting admin socket:", err)
|
if n.admin != nil {
|
||||||
|
n.admin.SetupAdminHandlers()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
n.admin.SetupAdminHandlers(n.admin)
|
|
||||||
// Start the multicast interface
|
// Setup the multicast module.
|
||||||
if err := n.multicast.Init(&n.core, cfg, logger, nil); err != nil {
|
{
|
||||||
logger.Errorln("An error occurred initialising multicast:", err)
|
options := []multicast.SetupOption{}
|
||||||
} else if err := n.multicast.Start(); err != nil {
|
for _, intf := range cfg.MulticastInterfaces {
|
||||||
logger.Errorln("An error occurred starting multicast:", err)
|
options = append(options, multicast.MulticastInterface{
|
||||||
|
Regex: regexp.MustCompile(intf.Regex),
|
||||||
|
Beacon: intf.Beacon,
|
||||||
|
Listen: intf.Listen,
|
||||||
|
Port: intf.Port,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if n.multicast, err = multicast.New(n.core, logger, options...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if n.admin != nil && n.multicast != nil {
|
||||||
|
n.multicast.SetupAdminHandlers(n.admin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
n.multicast.SetupAdminHandlers(n.admin)
|
|
||||||
// Start the TUN/TAP interface
|
// Setup the TUN module.
|
||||||
if err := n.tuntap.Init(&n.core, cfg, logger, nil); err != nil {
|
{
|
||||||
logger.Errorln("An error occurred initialising TUN/TAP:", err)
|
options := []tuntap.SetupOption{
|
||||||
} else if err := n.tuntap.Start(); err != nil {
|
tuntap.InterfaceName(cfg.IfName),
|
||||||
logger.Errorln("An error occurred starting TUN/TAP:", err)
|
tuntap.InterfaceMTU(cfg.IfMTU),
|
||||||
|
}
|
||||||
|
if n.tuntap, err = tuntap.New(ipv6rwc.NewReadWriteCloser(n.core), logger, options...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if n.admin != nil && n.tuntap != nil {
|
||||||
|
n.tuntap.SetupAdminHandlers(n.admin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
n.tuntap.SetupAdminHandlers(n.admin)
|
|
||||||
// Make some nice output that tells us what our IPv6 address and subnet are.
|
// Make some nice output that tells us what our IPv6 address and subnet are.
|
||||||
// This is just logged to stdout for the user.
|
// This is just logged to stdout for the user.
|
||||||
address := n.core.Address()
|
address := n.core.Address()
|
||||||
|
|
91
cmd/yggdrasilctl/cmd_line_env.go
Normal file
91
cmd/yggdrasilctl/cmd_line_env.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/hjson/hjson-go"
|
||||||
|
"golang.org/x/text/encoding/unicode"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CmdLineEnv struct {
|
||||||
|
args []string
|
||||||
|
endpoint, server string
|
||||||
|
injson, ver bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCmdLineEnv() CmdLineEnv {
|
||||||
|
var cmdLineEnv CmdLineEnv
|
||||||
|
cmdLineEnv.endpoint = defaults.GetDefaults().DefaultAdminListen
|
||||||
|
return cmdLineEnv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() {
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] command [key=value] [key=value] ...\n\n", os.Args[0])
|
||||||
|
fmt.Println("Options:")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Please note that options must always specified BEFORE the command\non the command line or they will be ignored.")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Commands:\n - Use \"list\" for a list of available commands")
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Examples:")
|
||||||
|
fmt.Println(" - ", os.Args[0], "list")
|
||||||
|
fmt.Println(" - ", os.Args[0], "getPeers")
|
||||||
|
fmt.Println(" - ", os.Args[0], "-v getSelf")
|
||||||
|
fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
|
||||||
|
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT")
|
||||||
|
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT")
|
||||||
|
}
|
||||||
|
|
||||||
|
server := flag.String("endpoint", cmdLineEnv.endpoint, "Admin socket endpoint")
|
||||||
|
injson := flag.Bool("json", false, "Output in JSON format (as opposed to pretty-print)")
|
||||||
|
ver := flag.Bool("version", false, "Prints the version of this build")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
cmdLineEnv.args = flag.Args()
|
||||||
|
cmdLineEnv.server = *server
|
||||||
|
cmdLineEnv.injson = *injson
|
||||||
|
cmdLineEnv.ver = *ver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmdLineEnv *CmdLineEnv) setEndpoint(logger *log.Logger) {
|
||||||
|
if cmdLineEnv.server == cmdLineEnv.endpoint {
|
||||||
|
if config, err := os.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil {
|
||||||
|
if bytes.Equal(config[0:2], []byte{0xFF, 0xFE}) ||
|
||||||
|
bytes.Equal(config[0:2], []byte{0xFE, 0xFF}) {
|
||||||
|
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
|
||||||
|
decoder := utf.NewDecoder()
|
||||||
|
config, err = decoder.Bytes(config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var dat map[string]interface{}
|
||||||
|
if err := hjson.Unmarshal(config, &dat); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if ep, ok := dat["AdminListen"].(string); ok && (ep != "none" && ep != "") {
|
||||||
|
cmdLineEnv.endpoint = ep
|
||||||
|
logger.Println("Found platform default config file", defaults.GetDefaults().DefaultConfigFile)
|
||||||
|
logger.Println("Using endpoint", cmdLineEnv.endpoint, "from AdminListen")
|
||||||
|
} else {
|
||||||
|
logger.Println("Configuration file doesn't contain appropriate AdminListen option")
|
||||||
|
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Println("Can't open config file from default location", defaults.GetDefaults().DefaultConfigFile)
|
||||||
|
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmdLineEnv.endpoint = cmdLineEnv.server
|
||||||
|
logger.Println("Using endpoint", cmdLineEnv.endpoint, "from command line")
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,24 +6,21 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/text/encoding/unicode"
|
"github.com/olekukonko/tablewriter"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
||||||
"github.com/hjson/hjson-go"
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/tuntap"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
type admin_info map[string]interface{}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// makes sure we can use defer and still return an error code to the OS
|
// makes sure we can use defer and still return an error code to the OS
|
||||||
os.Exit(run())
|
os.Exit(run())
|
||||||
|
@ -32,6 +29,7 @@ func main() {
|
||||||
func run() int {
|
func run() int {
|
||||||
logbuffer := &bytes.Buffer{}
|
logbuffer := &bytes.Buffer{}
|
||||||
logger := log.New(logbuffer, "", log.Flags())
|
logger := log.New(logbuffer, "", log.Flags())
|
||||||
|
|
||||||
defer func() int {
|
defer func() int {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
logger.Println("Fatal error:", r)
|
logger.Println("Fatal error:", r)
|
||||||
|
@ -41,83 +39,30 @@ func run() int {
|
||||||
return 0
|
return 0
|
||||||
}()
|
}()
|
||||||
|
|
||||||
endpoint := defaults.GetDefaults().DefaultAdminListen
|
cmdLineEnv := newCmdLineEnv()
|
||||||
|
cmdLineEnv.parseFlagsAndArgs()
|
||||||
|
|
||||||
flag.Usage = func() {
|
if cmdLineEnv.ver {
|
||||||
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] command [key=value] [key=value] ...\n\n", os.Args[0])
|
|
||||||
fmt.Println("Options:")
|
|
||||||
flag.PrintDefaults()
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println("Please note that options must always specified BEFORE the command\non the command line or they will be ignored.")
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println("Commands:\n - Use \"list\" for a list of available commands")
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println("Examples:")
|
|
||||||
fmt.Println(" - ", os.Args[0], "list")
|
|
||||||
fmt.Println(" - ", os.Args[0], "getPeers")
|
|
||||||
fmt.Println(" - ", os.Args[0], "-v getSelf")
|
|
||||||
fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
|
|
||||||
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT")
|
|
||||||
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT")
|
|
||||||
}
|
|
||||||
server := flag.String("endpoint", endpoint, "Admin socket endpoint")
|
|
||||||
injson := flag.Bool("json", false, "Output in JSON format (as opposed to pretty-print)")
|
|
||||||
verbose := flag.Bool("v", false, "Verbose output (includes public keys)")
|
|
||||||
ver := flag.Bool("version", false, "Prints the version of this build")
|
|
||||||
flag.Parse()
|
|
||||||
args := flag.Args()
|
|
||||||
|
|
||||||
if *ver {
|
|
||||||
fmt.Println("Build name:", version.BuildName())
|
fmt.Println("Build name:", version.BuildName())
|
||||||
fmt.Println("Build version:", version.BuildVersion())
|
fmt.Println("Build version:", version.BuildVersion())
|
||||||
fmt.Println("To get the version number of the running Yggdrasil node, run", os.Args[0], "getSelf")
|
fmt.Println("To get the version number of the running Yggdrasil node, run", os.Args[0], "getSelf")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(cmdLineEnv.args) == 0 {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if *server == endpoint {
|
cmdLineEnv.setEndpoint(logger)
|
||||||
if config, err := ioutil.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil {
|
|
||||||
if bytes.Equal(config[0:2], []byte{0xFF, 0xFE}) ||
|
|
||||||
bytes.Equal(config[0:2], []byte{0xFE, 0xFF}) {
|
|
||||||
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
|
|
||||||
decoder := utf.NewDecoder()
|
|
||||||
config, err = decoder.Bytes(config)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var dat map[string]interface{}
|
|
||||||
if err := hjson.Unmarshal(config, &dat); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if ep, ok := dat["AdminListen"].(string); ok && (ep != "none" && ep != "") {
|
|
||||||
endpoint = ep
|
|
||||||
logger.Println("Found platform default config file", defaults.GetDefaults().DefaultConfigFile)
|
|
||||||
logger.Println("Using endpoint", endpoint, "from AdminListen")
|
|
||||||
} else {
|
|
||||||
logger.Println("Configuration file doesn't contain appropriate AdminListen option")
|
|
||||||
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.Println("Can't open config file from default location", defaults.GetDefaults().DefaultConfigFile)
|
|
||||||
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
endpoint = *server
|
|
||||||
logger.Println("Using endpoint", endpoint, "from command line")
|
|
||||||
}
|
|
||||||
|
|
||||||
var conn net.Conn
|
var conn net.Conn
|
||||||
u, err := url.Parse(endpoint)
|
u, err := url.Parse(cmdLineEnv.endpoint)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
switch strings.ToLower(u.Scheme) {
|
switch strings.ToLower(u.Scheme) {
|
||||||
case "unix":
|
case "unix":
|
||||||
logger.Println("Connecting to UNIX socket", endpoint[7:])
|
logger.Println("Connecting to UNIX socket", cmdLineEnv.endpoint[7:])
|
||||||
conn, err = net.Dial("unix", endpoint[7:])
|
conn, err = net.Dial("unix", cmdLineEnv.endpoint[7:])
|
||||||
case "tcp":
|
case "tcp":
|
||||||
logger.Println("Connecting to TCP socket", u.Host)
|
logger.Println("Connecting to TCP socket", u.Host)
|
||||||
conn, err = net.Dial("tcp", u.Host)
|
conn, err = net.Dial("tcp", u.Host)
|
||||||
|
@ -127,49 +72,36 @@ func run() int {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.Println("Connecting to TCP socket", u.Host)
|
logger.Println("Connecting to TCP socket", u.Host)
|
||||||
conn, err = net.Dial("tcp", endpoint)
|
conn, err = net.Dial("tcp", cmdLineEnv.endpoint)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Println("Connected")
|
logger.Println("Connected")
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
decoder := json.NewDecoder(conn)
|
decoder := json.NewDecoder(conn)
|
||||||
encoder := json.NewEncoder(conn)
|
encoder := json.NewEncoder(conn)
|
||||||
send := make(admin_info)
|
send := &admin.AdminSocketRequest{}
|
||||||
recv := make(admin_info)
|
recv := &admin.AdminSocketResponse{}
|
||||||
|
|
||||||
for c, a := range args {
|
for c, a := range cmdLineEnv.args {
|
||||||
if c == 0 {
|
if c == 0 {
|
||||||
if strings.HasPrefix(a, "-") {
|
if strings.HasPrefix(a, "-") {
|
||||||
logger.Printf("Ignoring flag %s as it should be specified before other parameters\n", a)
|
logger.Printf("Ignoring flag %s as it should be specified before other parameters\n", a)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
logger.Printf("Sending request: %v\n", a)
|
logger.Printf("Sending request: %v\n", a)
|
||||||
send["request"] = a
|
send.Name = a
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tokens := strings.Split(a, "=")
|
tokens := strings.SplitN(a, "=", 1)
|
||||||
if len(tokens) == 1 {
|
switch {
|
||||||
send[tokens[0]] = true
|
case len(tokens) == 1:
|
||||||
} else if len(tokens) > 2 {
|
panic("incomplete argument supplied")
|
||||||
send[tokens[0]] = strings.Join(tokens[1:], "=")
|
default:
|
||||||
} else if len(tokens) == 2 {
|
send.Arguments[tokens[0]] = tokens[1]
|
||||||
if i, err := strconv.Atoi(tokens[1]); err == nil {
|
|
||||||
logger.Printf("Sending parameter %s: %d\n", tokens[0], i)
|
|
||||||
send[tokens[0]] = i
|
|
||||||
} else {
|
|
||||||
switch strings.ToLower(tokens[1]) {
|
|
||||||
case "true":
|
|
||||||
send[tokens[0]] = true
|
|
||||||
case "false":
|
|
||||||
send[tokens[0]] = false
|
|
||||||
default:
|
|
||||||
send[tokens[0]] = tokens[1]
|
|
||||||
}
|
|
||||||
logger.Printf("Sending parameter %s: %v\n", tokens[0], send[tokens[0]])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,276 +109,167 @@ func run() int {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
logger.Printf("Request sent")
|
logger.Printf("Request sent")
|
||||||
if err := decoder.Decode(&recv); err == nil {
|
if err := decoder.Decode(&recv); err != nil {
|
||||||
logger.Printf("Response received")
|
panic(err)
|
||||||
if recv["status"] == "error" {
|
|
||||||
if err, ok := recv["error"]; ok {
|
|
||||||
fmt.Println("Admin socket returned an error:", err)
|
|
||||||
} else {
|
|
||||||
fmt.Println("Admin socket returned an error but didn't specify any error text")
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if _, ok := recv["request"]; !ok {
|
|
||||||
fmt.Println("Missing request in response (malformed response?)")
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if _, ok := recv["response"]; !ok {
|
|
||||||
fmt.Println("Missing response body (malformed response?)")
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
req := recv["request"].(map[string]interface{})
|
|
||||||
res := recv["response"].(map[string]interface{})
|
|
||||||
|
|
||||||
if *injson {
|
|
||||||
if json, err := json.MarshalIndent(res, "", " "); err == nil {
|
|
||||||
fmt.Println(string(json))
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
switch strings.ToLower(req["request"].(string)) {
|
|
||||||
case "dot":
|
|
||||||
fmt.Println(res["dot"])
|
|
||||||
case "list", "getpeers", "getswitchpeers", "getdht", "getsessions", "dhtping":
|
|
||||||
maxWidths := make(map[string]int)
|
|
||||||
var keyOrder []string
|
|
||||||
keysOrdered := false
|
|
||||||
|
|
||||||
for _, tlv := range res {
|
|
||||||
for slk, slv := range tlv.(map[string]interface{}) {
|
|
||||||
if !keysOrdered {
|
|
||||||
for k := range slv.(map[string]interface{}) {
|
|
||||||
if !*verbose {
|
|
||||||
if k == "box_pub_key" || k == "box_sig_key" || k == "nodeinfo" || k == "was_mtu_fixed" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyOrder = append(keyOrder, fmt.Sprint(k))
|
|
||||||
}
|
|
||||||
sort.Strings(keyOrder)
|
|
||||||
keysOrdered = true
|
|
||||||
}
|
|
||||||
for k, v := range slv.(map[string]interface{}) {
|
|
||||||
if len(fmt.Sprint(slk)) > maxWidths["key"] {
|
|
||||||
maxWidths["key"] = len(fmt.Sprint(slk))
|
|
||||||
}
|
|
||||||
if len(fmt.Sprint(v)) > maxWidths[k] {
|
|
||||||
maxWidths[k] = len(fmt.Sprint(v))
|
|
||||||
if maxWidths[k] < len(k) {
|
|
||||||
maxWidths[k] = len(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(keyOrder) > 0 {
|
|
||||||
fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", "")
|
|
||||||
for _, v := range keyOrder {
|
|
||||||
fmt.Printf("%-"+fmt.Sprint(maxWidths[v])+"s ", v)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
|
|
||||||
for slk, slv := range tlv.(map[string]interface{}) {
|
|
||||||
fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", slk)
|
|
||||||
for _, k := range keyOrder {
|
|
||||||
preformatted := slv.(map[string]interface{})[k]
|
|
||||||
var formatted string
|
|
||||||
switch k {
|
|
||||||
case "bytes_sent", "bytes_recvd":
|
|
||||||
formatted = fmt.Sprintf("%d", uint(preformatted.(float64)))
|
|
||||||
case "uptime", "last_seen":
|
|
||||||
seconds := uint(preformatted.(float64)) % 60
|
|
||||||
minutes := uint(preformatted.(float64)/60) % 60
|
|
||||||
hours := uint(preformatted.(float64) / 60 / 60)
|
|
||||||
formatted = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
|
|
||||||
default:
|
|
||||||
formatted = fmt.Sprint(preformatted)
|
|
||||||
}
|
|
||||||
fmt.Printf("%-"+fmt.Sprint(maxWidths[k])+"s ", formatted)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "gettuntap", "settuntap":
|
|
||||||
for k, v := range res {
|
|
||||||
fmt.Println("Interface name:", k)
|
|
||||||
if mtu, ok := v.(map[string]interface{})["mtu"].(float64); ok {
|
|
||||||
fmt.Println("Interface MTU:", mtu)
|
|
||||||
}
|
|
||||||
if tap_mode, ok := v.(map[string]interface{})["tap_mode"].(bool); ok {
|
|
||||||
fmt.Println("TAP mode:", tap_mode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "getself":
|
|
||||||
for k, v := range res["self"].(map[string]interface{}) {
|
|
||||||
if buildname, ok := v.(map[string]interface{})["build_name"].(string); ok && buildname != "unknown" {
|
|
||||||
fmt.Println("Build name:", buildname)
|
|
||||||
}
|
|
||||||
if buildversion, ok := v.(map[string]interface{})["build_version"].(string); ok && buildversion != "unknown" {
|
|
||||||
fmt.Println("Build version:", buildversion)
|
|
||||||
}
|
|
||||||
fmt.Println("IPv6 address:", k)
|
|
||||||
if subnet, ok := v.(map[string]interface{})["subnet"].(string); ok {
|
|
||||||
fmt.Println("IPv6 subnet:", subnet)
|
|
||||||
}
|
|
||||||
if boxSigKey, ok := v.(map[string]interface{})["key"].(string); ok {
|
|
||||||
fmt.Println("Public key:", boxSigKey)
|
|
||||||
}
|
|
||||||
if coords, ok := v.(map[string]interface{})["coords"].(string); ok {
|
|
||||||
fmt.Println("Coords:", coords)
|
|
||||||
}
|
|
||||||
if *verbose {
|
|
||||||
if nodeID, ok := v.(map[string]interface{})["node_id"].(string); ok {
|
|
||||||
fmt.Println("Node ID:", nodeID)
|
|
||||||
}
|
|
||||||
if boxPubKey, ok := v.(map[string]interface{})["box_pub_key"].(string); ok {
|
|
||||||
fmt.Println("Public encryption key:", boxPubKey)
|
|
||||||
}
|
|
||||||
if boxSigKey, ok := v.(map[string]interface{})["box_sig_key"].(string); ok {
|
|
||||||
fmt.Println("Public signing key:", boxSigKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "getswitchqueues":
|
|
||||||
maximumqueuesize := float64(4194304)
|
|
||||||
portqueues := make(map[float64]float64)
|
|
||||||
portqueuesize := make(map[float64]float64)
|
|
||||||
portqueuepackets := make(map[float64]float64)
|
|
||||||
v := res["switchqueues"].(map[string]interface{})
|
|
||||||
if queuecount, ok := v["queues_count"].(float64); ok {
|
|
||||||
fmt.Printf("Active queue count: %d queues\n", uint(queuecount))
|
|
||||||
}
|
|
||||||
if queuesize, ok := v["queues_size"].(float64); ok {
|
|
||||||
fmt.Printf("Active queue size: %d bytes\n", uint(queuesize))
|
|
||||||
}
|
|
||||||
if highestqueuecount, ok := v["highest_queues_count"].(float64); ok {
|
|
||||||
fmt.Printf("Highest queue count: %d queues\n", uint(highestqueuecount))
|
|
||||||
}
|
|
||||||
if highestqueuesize, ok := v["highest_queues_size"].(float64); ok {
|
|
||||||
fmt.Printf("Highest queue size: %d bytes\n", uint(highestqueuesize))
|
|
||||||
}
|
|
||||||
if m, ok := v["maximum_queues_size"].(float64); ok {
|
|
||||||
maximumqueuesize = m
|
|
||||||
fmt.Printf("Maximum queue size: %d bytes\n", uint(maximumqueuesize))
|
|
||||||
}
|
|
||||||
if queues, ok := v["queues"].([]interface{}); ok {
|
|
||||||
if len(queues) != 0 {
|
|
||||||
fmt.Println("Active queues:")
|
|
||||||
for _, v := range queues {
|
|
||||||
queueport := v.(map[string]interface{})["queue_port"].(float64)
|
|
||||||
queuesize := v.(map[string]interface{})["queue_size"].(float64)
|
|
||||||
queuepackets := v.(map[string]interface{})["queue_packets"].(float64)
|
|
||||||
queueid := v.(map[string]interface{})["queue_id"].(string)
|
|
||||||
portqueues[queueport]++
|
|
||||||
portqueuesize[queueport] += queuesize
|
|
||||||
portqueuepackets[queueport] += queuepackets
|
|
||||||
queuesizepercent := (100 / maximumqueuesize) * queuesize
|
|
||||||
fmt.Printf("- Switch port %d, Stream ID: %v, size: %d bytes (%d%% full), %d packets\n",
|
|
||||||
uint(queueport), []byte(queueid), uint(queuesize),
|
|
||||||
uint(queuesizepercent), uint(queuepackets))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(portqueuesize) > 0 && len(portqueuepackets) > 0 {
|
|
||||||
fmt.Println("Aggregated statistics by switchport:")
|
|
||||||
for k, v := range portqueuesize {
|
|
||||||
queuesizepercent := (100 / (portqueues[k] * maximumqueuesize)) * v
|
|
||||||
fmt.Printf("- Switch port %d, size: %d bytes (%d%% full), %d packets\n",
|
|
||||||
uint(k), uint(v), uint(queuesizepercent), uint(portqueuepackets[k]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "addpeer", "removepeer", "addallowedencryptionpublickey", "removeallowedencryptionpublickey", "addsourcesubnet", "addroute", "removesourcesubnet", "removeroute":
|
|
||||||
if _, ok := res["added"]; ok {
|
|
||||||
for _, v := range res["added"].([]interface{}) {
|
|
||||||
fmt.Println("Added:", fmt.Sprint(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, ok := res["not_added"]; ok {
|
|
||||||
for _, v := range res["not_added"].([]interface{}) {
|
|
||||||
fmt.Println("Not added:", fmt.Sprint(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, ok := res["removed"]; ok {
|
|
||||||
for _, v := range res["removed"].([]interface{}) {
|
|
||||||
fmt.Println("Removed:", fmt.Sprint(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, ok := res["not_removed"]; ok {
|
|
||||||
for _, v := range res["not_removed"].([]interface{}) {
|
|
||||||
fmt.Println("Not removed:", fmt.Sprint(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "getallowedencryptionpublickeys":
|
|
||||||
if _, ok := res["allowed_box_pubs"]; !ok {
|
|
||||||
fmt.Println("All connections are allowed")
|
|
||||||
} else if res["allowed_box_pubs"] == nil {
|
|
||||||
fmt.Println("All connections are allowed")
|
|
||||||
} else {
|
|
||||||
fmt.Println("Connections are allowed only from the following public box keys:")
|
|
||||||
for _, v := range res["allowed_box_pubs"].([]interface{}) {
|
|
||||||
fmt.Println("-", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "getmulticastinterfaces":
|
|
||||||
if _, ok := res["multicast_interfaces"]; !ok {
|
|
||||||
fmt.Println("No multicast interfaces found")
|
|
||||||
} else if res["multicast_interfaces"] == nil {
|
|
||||||
fmt.Println("No multicast interfaces found")
|
|
||||||
} else {
|
|
||||||
fmt.Println("Multicast peer discovery is active on:")
|
|
||||||
for _, v := range res["multicast_interfaces"].([]interface{}) {
|
|
||||||
fmt.Println("-", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "getsourcesubnets":
|
|
||||||
if _, ok := res["source_subnets"]; !ok {
|
|
||||||
fmt.Println("No source subnets found")
|
|
||||||
} else if res["source_subnets"] == nil {
|
|
||||||
fmt.Println("No source subnets found")
|
|
||||||
} else {
|
|
||||||
fmt.Println("Source subnets:")
|
|
||||||
for _, v := range res["source_subnets"].([]interface{}) {
|
|
||||||
fmt.Println("-", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "getroutes":
|
|
||||||
if routes, ok := res["routes"].(map[string]interface{}); !ok {
|
|
||||||
fmt.Println("No routes found")
|
|
||||||
} else {
|
|
||||||
if res["routes"] == nil || len(routes) == 0 {
|
|
||||||
fmt.Println("No routes found")
|
|
||||||
} else {
|
|
||||||
fmt.Println("Routes:")
|
|
||||||
for k, v := range routes {
|
|
||||||
if pv, ok := v.(string); ok {
|
|
||||||
fmt.Println("-", k, " via ", pv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "settunnelrouting":
|
|
||||||
fallthrough
|
|
||||||
case "gettunnelrouting":
|
|
||||||
if enabled, ok := res["enabled"].(bool); !ok {
|
|
||||||
fmt.Println("Tunnel routing is disabled")
|
|
||||||
} else if !enabled {
|
|
||||||
fmt.Println("Tunnel routing is disabled")
|
|
||||||
} else {
|
|
||||||
fmt.Println("Tunnel routing is enabled")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil {
|
|
||||||
fmt.Println(string(json))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.Println("Error receiving response:", err)
|
|
||||||
}
|
}
|
||||||
|
if recv.Status == "error" {
|
||||||
if v, ok := recv["status"]; ok && v != "success" {
|
if err := recv.Error; err != "" {
|
||||||
|
fmt.Println("Admin socket returned an error:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Admin socket returned an error but didn't specify any error text")
|
||||||
|
}
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
if cmdLineEnv.injson {
|
||||||
|
if json, err := json.MarshalIndent(recv.Response, "", " "); err == nil {
|
||||||
|
fmt.Println(string(json))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
table := tablewriter.NewWriter(os.Stdout)
|
||||||
|
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||||
|
table.SetAutoFormatHeaders(false)
|
||||||
|
table.SetCenterSeparator("")
|
||||||
|
table.SetColumnSeparator("")
|
||||||
|
table.SetRowSeparator("")
|
||||||
|
table.SetHeaderLine(false)
|
||||||
|
table.SetBorder(false)
|
||||||
|
table.SetTablePadding("\t") // pad with tabs
|
||||||
|
table.SetNoWhiteSpace(true)
|
||||||
|
table.SetAutoWrapText(false)
|
||||||
|
|
||||||
|
switch strings.ToLower(recv.Request.Name) {
|
||||||
|
case "list":
|
||||||
|
var resp admin.ListResponse
|
||||||
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
table.SetHeader([]string{"Command", "Arguments", "Description"})
|
||||||
|
for _, entry := range resp.List {
|
||||||
|
for i := range entry.Fields {
|
||||||
|
entry.Fields[i] = entry.Fields[i] + "=..."
|
||||||
|
}
|
||||||
|
table.Append([]string{entry.Command, strings.Join(entry.Fields, ", "), entry.Description})
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
case "getself":
|
||||||
|
var resp admin.GetSelfResponse
|
||||||
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
table.Append([]string{"Build name:", resp.BuildName})
|
||||||
|
table.Append([]string{"Build version:", resp.BuildVersion})
|
||||||
|
table.Append([]string{"IPv6 address:", resp.IPAddress})
|
||||||
|
table.Append([]string{"IPv6 subnet:", resp.Subnet})
|
||||||
|
table.Append([]string{"Coordinates:", fmt.Sprintf("%v", resp.Coords)})
|
||||||
|
table.Append([]string{"Public key:", resp.PublicKey})
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
case "getpeers":
|
||||||
|
var resp admin.GetPeersResponse
|
||||||
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
table.SetHeader([]string{"Port", "Public Key", "IP Address", "Peering URI", "Uptime", "RX", "TX"})
|
||||||
|
for _, peer := range resp.Peers {
|
||||||
|
table.Append([]string{
|
||||||
|
fmt.Sprintf("%d", peer.Port),
|
||||||
|
peer.PublicKey,
|
||||||
|
peer.IPAddress,
|
||||||
|
peer.Remote,
|
||||||
|
(time.Duration(peer.Uptime) * time.Second).String(),
|
||||||
|
peer.RXBytes.String(),
|
||||||
|
peer.TXBytes.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
case "getdht":
|
||||||
|
var resp admin.GetDHTResponse
|
||||||
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
table.SetHeader([]string{"Public Key", "IP Address", "Port", "Rest"})
|
||||||
|
for _, dht := range resp.DHT {
|
||||||
|
table.Append([]string{
|
||||||
|
dht.PublicKey,
|
||||||
|
dht.IPAddress,
|
||||||
|
fmt.Sprintf("%d", dht.Port),
|
||||||
|
fmt.Sprintf("%d", dht.Rest),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
case "getpaths":
|
||||||
|
var resp admin.GetPathsResponse
|
||||||
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
table.SetHeader([]string{"Public Key", "IP Address", "Path"})
|
||||||
|
for _, p := range resp.Paths {
|
||||||
|
table.Append([]string{
|
||||||
|
p.PublicKey,
|
||||||
|
p.IPAddress,
|
||||||
|
fmt.Sprintf("%v", p.Path),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
case "getsessions":
|
||||||
|
var resp admin.GetSessionsResponse
|
||||||
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
table.SetHeader([]string{"Public Key", "IP Address", "Uptime", "RX", "TX"})
|
||||||
|
for _, p := range resp.Sessions {
|
||||||
|
table.Append([]string{
|
||||||
|
p.PublicKey,
|
||||||
|
p.IPAddress,
|
||||||
|
(time.Duration(p.Uptime) * time.Second).String(),
|
||||||
|
p.RXBytes.String(),
|
||||||
|
p.TXBytes.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
case "getnodeinfo":
|
||||||
|
var resp core.GetNodeInfoResponse
|
||||||
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range resp {
|
||||||
|
fmt.Println(string(v))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case "getmulticastinterfaces":
|
||||||
|
var resp multicast.GetMulticastInterfacesResponse
|
||||||
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
table.SetHeader([]string{"Interface"})
|
||||||
|
for _, p := range resp.Interfaces {
|
||||||
|
table.Append([]string{p})
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
case "gettun":
|
||||||
|
var resp tuntap.GetTUNResponse
|
||||||
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
table.Append([]string{"TUN enabled:", fmt.Sprintf("%#v", resp.Enabled)})
|
||||||
|
if resp.Enabled {
|
||||||
|
table.Append([]string{"Interface name:", resp.Name})
|
||||||
|
table.Append([]string{"Interface MTU:", fmt.Sprintf("%d", resp.MTU)})
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Println(string(recv.Response))
|
||||||
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@ command -v mkbom >/dev/null 2>&1 || (
|
||||||
sudo make install || (echo "Failed to build mkbom"; exit 1)
|
sudo make install || (echo "Failed to build mkbom"; exit 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Build Yggdrasil
|
||||||
|
echo "running GO111MODULE=on GOOS=darwin GOARCH=${PKGARCH-amd64} ./build"
|
||||||
|
GO111MODULE=on GOOS=darwin GOARCH=${PKGARCH-amd64} ./build
|
||||||
|
|
||||||
# Check if we can find the files we need - they should
|
# Check if we can find the files we need - they should
|
||||||
# exist if you are running this script from the root of
|
# exist if you are running this script from the root of
|
||||||
# the yggdrasil-go repo and you have ran ./build
|
# the yggdrasil-go repo and you have ran ./build
|
||||||
|
@ -81,6 +85,7 @@ PKGNAME=$(sh contrib/semver/name.sh)
|
||||||
PKGVERSION=$(sh contrib/semver/version.sh --bare)
|
PKGVERSION=$(sh contrib/semver/version.sh --bare)
|
||||||
PKGARCH=${PKGARCH-amd64}
|
PKGARCH=${PKGARCH-amd64}
|
||||||
PAYLOADSIZE=$(( $(wc -c pkgbuild/flat/base.pkg/Payload | awk '{ print $1 }') / 1024 ))
|
PAYLOADSIZE=$(( $(wc -c pkgbuild/flat/base.pkg/Payload | awk '{ print $1 }') / 1024 ))
|
||||||
|
[ "$PKGARCH" = "amd64" ] && PKGHOSTARCH="x86_64" || PKGHOSTARCH=${PKGARCH}
|
||||||
|
|
||||||
# Create the PackageInfo file
|
# Create the PackageInfo file
|
||||||
cat > pkgbuild/flat/base.pkg/PackageInfo << EOF
|
cat > pkgbuild/flat/base.pkg/PackageInfo << EOF
|
||||||
|
@ -100,7 +105,7 @@ cat > pkgbuild/flat/Distribution << EOF
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<installer-script minSpecVersion="1.000000" authoringTool="com.apple.PackageMaker" authoringToolVersion="3.0.3" authoringToolBuild="174">
|
<installer-script minSpecVersion="1.000000" authoringTool="com.apple.PackageMaker" authoringToolVersion="3.0.3" authoringToolBuild="174">
|
||||||
<title>Yggdrasil (${PKGNAME}-${PKGVERSION})</title>
|
<title>Yggdrasil (${PKGNAME}-${PKGVERSION})</title>
|
||||||
<options customize="never" allow-external-scripts="no"/>
|
<options customize="never" allow-external-scripts="no" hostArchitectures="${PKGHOSTARCH}" />
|
||||||
<domains enable_anywhere="true"/>
|
<domains enable_anywhere="true"/>
|
||||||
<installation-check script="pm_install_check();"/>
|
<installation-check script="pm_install_check();"/>
|
||||||
<script>
|
<script>
|
||||||
|
|
52
contrib/mobile/build
Executable file
52
contrib/mobile/build
Executable file
|
@ -0,0 +1,52 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -ef
|
||||||
|
|
||||||
|
[ ! -d contrib/mobile ] && (echo "Must run ./contrib/mobile/build [-i] [-a] from the repository top level folder"; exit 1)
|
||||||
|
|
||||||
|
PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version}
|
||||||
|
PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
|
||||||
|
PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
|
||||||
|
|
||||||
|
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
|
||||||
|
ARGS="-v"
|
||||||
|
|
||||||
|
while getopts "aitc:l:d" option
|
||||||
|
do
|
||||||
|
case "$option"
|
||||||
|
in
|
||||||
|
i) IOS=true;;
|
||||||
|
a) ANDROID=true;;
|
||||||
|
t) TABLES=true;;
|
||||||
|
c) GCFLAGS="$GCFLAGS $OPTARG";;
|
||||||
|
l) LDFLAGS="$LDFLAGS $OPTARG";;
|
||||||
|
d) ARGS="$ARGS -tags debug" DEBUG=true;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z $TABLES ] && [ -z $DEBUG ]; then
|
||||||
|
LDFLAGS="$LDFLAGS -s -w"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! $IOS ] && [ ! $ANDROID ]; then
|
||||||
|
echo "Must specify -a (Android), -i (iOS) or both"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $IOS ]; then
|
||||||
|
echo "Building framework for iOS"
|
||||||
|
go get golang.org/x/mobile/bind
|
||||||
|
gomobile bind \
|
||||||
|
-target ios -tags mobile -o Yggdrasil.xcframework \
|
||||||
|
-ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" \
|
||||||
|
./contrib/mobile ./src/config;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $ANDROID ]; then
|
||||||
|
echo "Building aar for Android"
|
||||||
|
go get golang.org/x/mobile/bind
|
||||||
|
gomobile bind \
|
||||||
|
-target android -tags mobile -o yggdrasil.aar \
|
||||||
|
-ldflags="$LDFLAGS $STRIP" -gcflags="$GCFLAGS" \
|
||||||
|
./contrib/mobile ./src/config;
|
||||||
|
fi
|
210
contrib/mobile/mobile.go
Normal file
210
contrib/mobile/mobile.go
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
package mobile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/gologme/log"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
||||||
|
|
||||||
|
_ "golang.org/x/mobile/bind"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Yggdrasil mobile package is meant to "plug the gap" for mobile support, as
|
||||||
|
// Gomobile will not create headers for Swift/Obj-C etc if they have complex
|
||||||
|
// (non-native) types. Therefore for iOS we will expose some nice simple
|
||||||
|
// functions. Note that in the case of iOS we handle reading/writing to/from TUN
|
||||||
|
// in Swift therefore we use the "dummy" TUN interface instead.
|
||||||
|
type Yggdrasil struct {
|
||||||
|
core *core.Core
|
||||||
|
iprwc *ipv6rwc.ReadWriteCloser
|
||||||
|
config *config.NodeConfig
|
||||||
|
multicast *multicast.Multicast
|
||||||
|
log MobileLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartAutoconfigure starts a node with a randomly generated config
|
||||||
|
func (m *Yggdrasil) StartAutoconfigure() error {
|
||||||
|
return m.StartJSON([]byte("{}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartJSON starts a node with the given JSON config. You can get JSON config
|
||||||
|
// (rather than HJSON) by using the GenerateConfigJSON() function
|
||||||
|
func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
||||||
|
logger := log.New(m.log, "", 0)
|
||||||
|
logger.EnableLevel("error")
|
||||||
|
logger.EnableLevel("warn")
|
||||||
|
logger.EnableLevel("info")
|
||||||
|
m.config = defaults.GenerateConfig()
|
||||||
|
if err := json.Unmarshal(configjson, &m.config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Setup the Yggdrasil node itself.
|
||||||
|
{
|
||||||
|
sk, err := hex.DecodeString(m.config.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
options := []core.SetupOption{}
|
||||||
|
for _, peer := range m.config.Peers {
|
||||||
|
options = append(options, core.Peer{URI: peer})
|
||||||
|
}
|
||||||
|
for intf, peers := range m.config.InterfacePeers {
|
||||||
|
for _, peer := range peers {
|
||||||
|
options = append(options, core.Peer{URI: peer, SourceInterface: intf})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, allowed := range m.config.AllowedPublicKeys {
|
||||||
|
k, err := hex.DecodeString(allowed)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
options = append(options, core.AllowedPublicKey(k[:]))
|
||||||
|
}
|
||||||
|
m.core, err = core.New(sk[:], logger, options...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the multicast module.
|
||||||
|
if len(m.config.MulticastInterfaces) > 0 {
|
||||||
|
var err error
|
||||||
|
options := []multicast.SetupOption{}
|
||||||
|
for _, intf := range m.config.MulticastInterfaces {
|
||||||
|
options = append(options, multicast.MulticastInterface{
|
||||||
|
Regex: regexp.MustCompile(intf.Regex),
|
||||||
|
Beacon: intf.Beacon,
|
||||||
|
Listen: intf.Listen,
|
||||||
|
Port: intf.Port,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
m.multicast, err = multicast.New(m.core, logger, options...)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln("An error occurred starting multicast:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mtu := m.config.IfMTU
|
||||||
|
m.iprwc = ipv6rwc.NewReadWriteCloser(m.core)
|
||||||
|
if m.iprwc.MaxMTU() < mtu {
|
||||||
|
mtu = m.iprwc.MaxMTU()
|
||||||
|
}
|
||||||
|
m.iprwc.SetMTU(mtu)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send sends a packet to Yggdrasil. It should be a fully formed
|
||||||
|
// IPv6 packet
|
||||||
|
func (m *Yggdrasil) Send(p []byte) error {
|
||||||
|
if m.iprwc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, _ = m.iprwc.Write(p)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recv waits for and reads a packet coming from Yggdrasil. It
|
||||||
|
// will be a fully formed IPv6 packet
|
||||||
|
func (m *Yggdrasil) Recv() ([]byte, error) {
|
||||||
|
if m.iprwc == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var buf [65535]byte
|
||||||
|
n, _ := m.iprwc.Read(buf[:])
|
||||||
|
return buf[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the mobile Yggdrasil instance
|
||||||
|
func (m *Yggdrasil) Stop() error {
|
||||||
|
logger := log.New(m.log, "", 0)
|
||||||
|
logger.EnableLevel("info")
|
||||||
|
logger.Infof("Stop the mobile Yggdrasil instance %s", "")
|
||||||
|
if err := m.multicast.Stop(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.core.Stop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateConfigJSON generates mobile-friendly configuration in JSON format
|
||||||
|
func GenerateConfigJSON() []byte {
|
||||||
|
nc := defaults.GenerateConfig()
|
||||||
|
nc.IfName = "none"
|
||||||
|
if json, err := json.Marshal(nc); err == nil {
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddressString gets the node's IPv6 address
|
||||||
|
func (m *Yggdrasil) GetAddressString() string {
|
||||||
|
ip := m.core.Address()
|
||||||
|
return ip.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSubnetString gets the node's IPv6 subnet in CIDR notation
|
||||||
|
func (m *Yggdrasil) GetSubnetString() string {
|
||||||
|
subnet := m.core.Subnet()
|
||||||
|
return subnet.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicKeyString gets the node's public key in hex form
|
||||||
|
func (m *Yggdrasil) GetPublicKeyString() string {
|
||||||
|
return hex.EncodeToString(m.core.GetSelf().Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCoordsString gets the node's coordinates
|
||||||
|
func (m *Yggdrasil) GetCoordsString() string {
|
||||||
|
return fmt.Sprintf("%v", m.core.GetSelf().Coords)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Yggdrasil) GetPeersJSON() (result string) {
|
||||||
|
peers := []struct {
|
||||||
|
core.PeerInfo
|
||||||
|
IP string
|
||||||
|
}{}
|
||||||
|
for _, v := range m.core.GetPeers() {
|
||||||
|
a := address.AddrForKey(v.Key)
|
||||||
|
ip := net.IP(a[:]).String()
|
||||||
|
peers = append(peers, struct {
|
||||||
|
core.PeerInfo
|
||||||
|
IP string
|
||||||
|
}{
|
||||||
|
PeerInfo: v,
|
||||||
|
IP: ip,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if res, err := json.Marshal(peers); err == nil {
|
||||||
|
return string(res)
|
||||||
|
} else {
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Yggdrasil) GetDHTJSON() (result string) {
|
||||||
|
if res, err := json.Marshal(m.core.GetDHT()); err == nil {
|
||||||
|
return string(res)
|
||||||
|
} else {
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMTU returns the configured node MTU. This must be called AFTER Start.
|
||||||
|
func (m *Yggdrasil) GetMTU() int {
|
||||||
|
return int(m.core.MTU())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVersion() string {
|
||||||
|
return version.BuildVersion()
|
||||||
|
}
|
13
contrib/mobile/mobile_android.go
Normal file
13
contrib/mobile/mobile_android.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
//go:build android
|
||||||
|
// +build android
|
||||||
|
|
||||||
|
package mobile
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
type MobileLogger struct{}
|
||||||
|
|
||||||
|
func (nsl MobileLogger) Write(p []byte) (n int, err error) {
|
||||||
|
log.Println(string(p))
|
||||||
|
return len(p), nil
|
||||||
|
}
|
28
contrib/mobile/mobile_ios.go
Normal file
28
contrib/mobile/mobile_ios.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//go:build ios
|
||||||
|
// +build ios
|
||||||
|
|
||||||
|
package mobile
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -x objective-c
|
||||||
|
#cgo LDFLAGS: -framework Foundation
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
void Log(const char *text) {
|
||||||
|
NSString *nss = [NSString stringWithUTF8String:text];
|
||||||
|
NSLog(@"%@", nss);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MobileLogger struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nsl MobileLogger) Write(p []byte) (n int, err error) {
|
||||||
|
p = append(p, 0)
|
||||||
|
cstr := (*C.char)(unsafe.Pointer(&p[0]))
|
||||||
|
C.Log(cstr)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
14
contrib/mobile/mobile_other.go
Normal file
14
contrib/mobile/mobile_other.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
//go:build !android && !ios
|
||||||
|
// +build !android,!ios
|
||||||
|
|
||||||
|
package mobile
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type MobileLogger struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nsl MobileLogger) Write(p []byte) (n int, err error) {
|
||||||
|
fmt.Print(string(p))
|
||||||
|
return len(p), nil
|
||||||
|
}
|
16
contrib/mobile/mobile_test.go
Normal file
16
contrib/mobile/mobile_test.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package mobile
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestStartYggdrasil(t *testing.T) {
|
||||||
|
ygg := &Yggdrasil{}
|
||||||
|
if err := ygg.StartAutoconfigure(); err != nil {
|
||||||
|
t.Fatalf("Failed to start Yggdrasil: %s", err)
|
||||||
|
}
|
||||||
|
t.Log("Address:", ygg.GetAddressString())
|
||||||
|
t.Log("Subnet:", ygg.GetSubnetString())
|
||||||
|
t.Log("Coords:", ygg.GetCoordsString())
|
||||||
|
if err := ygg.Stop(); err != nil {
|
||||||
|
t.Fatalf("Failed to stop Yggdrasil: %s", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# This script generates an MSI file for Yggdrasil for a given architecture. It
|
# This script generates an MSI file for Yggdrasil for a given architecture. It
|
||||||
# needs to run on Windows within MSYS2 and Go 1.13 or later must be installed on
|
# needs to run on Windows within MSYS2 and Go 1.17 or later must be installed on
|
||||||
# the system and within the PATH. This is ran currently by Appveyor (see
|
# the system and within the PATH. This is ran currently by GitHub Actions (see
|
||||||
# appveyor.yml in the repository root) for both x86 and x64.
|
# the workflows in the repository).
|
||||||
#
|
#
|
||||||
# Author: Neil Alexander <neilalexander@users.noreply.github.com>
|
# Author: Neil Alexander <neilalexander@users.noreply.github.com>
|
||||||
|
|
||||||
|
@ -11,37 +11,21 @@
|
||||||
PKGARCH=$1
|
PKGARCH=$1
|
||||||
if [ "${PKGARCH}" == "" ];
|
if [ "${PKGARCH}" == "" ];
|
||||||
then
|
then
|
||||||
echo "tell me the architecture: x86, x64 or arm"
|
echo "tell me the architecture: x86, x64, arm or arm64"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get the rest of the repository history. This is needed within Appveyor because
|
|
||||||
# otherwise we don't get all of the branch histories and therefore the semver
|
|
||||||
# scripts don't work properly.
|
|
||||||
if [ "${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH}" != "" ];
|
|
||||||
then
|
|
||||||
git fetch --all
|
|
||||||
git checkout ${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH}
|
|
||||||
elif [ "${APPVEYOR_REPO_BRANCH}" != "" ];
|
|
||||||
then
|
|
||||||
git fetch --all
|
|
||||||
git checkout ${APPVEYOR_REPO_BRANCH}
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install prerequisites within MSYS2
|
|
||||||
pacman -S --needed --noconfirm unzip git curl
|
|
||||||
|
|
||||||
# Download the wix tools!
|
# Download the wix tools!
|
||||||
if [ ! -d wixbin ];
|
if [ ! -d wixbin ];
|
||||||
then
|
then
|
||||||
curl -LO https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip
|
curl -LO https://wixtoolset.org/downloads/v3.14.0.3910/wix314-binaries.zip
|
||||||
if [ `md5sum wix311-binaries.zip | cut -f 1 -d " "` != "47a506f8ab6666ee3cc502fb07d0ee2a" ];
|
if [ `md5sum wix314-binaries.zip | cut -f 1 -d " "` != "34f655cf108086838dd5a76d4318063b" ];
|
||||||
then
|
then
|
||||||
echo "wix package didn't match expected checksum"
|
echo "wix package didn't match expected checksum"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
mkdir -p wixbin
|
mkdir -p wixbin
|
||||||
unzip -o wix311-binaries.zip -d wixbin || (
|
unzip -o wix314-binaries.zip -d wixbin || (
|
||||||
echo "failed to unzip WiX"
|
echo "failed to unzip WiX"
|
||||||
exit 1
|
exit 1
|
||||||
)
|
)
|
||||||
|
@ -51,7 +35,7 @@ fi
|
||||||
[ "${PKGARCH}" == "x64" ] && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./build
|
[ "${PKGARCH}" == "x64" ] && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./build
|
||||||
[ "${PKGARCH}" == "x86" ] && GOOS=windows GOARCH=386 CGO_ENABLED=0 ./build
|
[ "${PKGARCH}" == "x86" ] && GOOS=windows GOARCH=386 CGO_ENABLED=0 ./build
|
||||||
[ "${PKGARCH}" == "arm" ] && GOOS=windows GOARCH=arm CGO_ENABLED=0 ./build
|
[ "${PKGARCH}" == "arm" ] && GOOS=windows GOARCH=arm CGO_ENABLED=0 ./build
|
||||||
#[ "${PKGARCH}" == "arm64" ] && GOOS=windows GOARCH=arm64 CGO_ENABLED=0 ./build
|
[ "${PKGARCH}" == "arm64" ] && GOOS=windows GOARCH=arm64 CGO_ENABLED=0 ./build
|
||||||
|
|
||||||
# Create the postinstall script
|
# Create the postinstall script
|
||||||
cat > updateconfig.bat << EOF
|
cat > updateconfig.bat << EOF
|
||||||
|
@ -69,14 +53,14 @@ EOF
|
||||||
PKGNAME=$(sh contrib/semver/name.sh)
|
PKGNAME=$(sh contrib/semver/name.sh)
|
||||||
PKGVERSION=$(sh contrib/msi/msversion.sh --bare)
|
PKGVERSION=$(sh contrib/msi/msversion.sh --bare)
|
||||||
PKGVERSIONMS=$(echo $PKGVERSION | tr - .)
|
PKGVERSIONMS=$(echo $PKGVERSION | tr - .)
|
||||||
[ "${PKGARCH}" == "x64" ] && \
|
([ "${PKGARCH}" == "x64" ] || [ "${PKGARCH}" == "arm64" ]) && \
|
||||||
PKGGUID="77757838-1a23-40a5-a720-c3b43e0260cc" PKGINSTFOLDER="ProgramFiles64Folder" || \
|
PKGGUID="77757838-1a23-40a5-a720-c3b43e0260cc" PKGINSTFOLDER="ProgramFiles64Folder" || \
|
||||||
PKGGUID="54a3294e-a441-4322-aefb-3bb40dd022bb" PKGINSTFOLDER="ProgramFilesFolder"
|
PKGGUID="54a3294e-a441-4322-aefb-3bb40dd022bb" PKGINSTFOLDER="ProgramFilesFolder"
|
||||||
|
|
||||||
# Download the Wintun driver
|
# Download the Wintun driver
|
||||||
if [ ! -d wintun ];
|
if [ ! -d wintun ];
|
||||||
then
|
then
|
||||||
curl -o wintun.zip https://www.wintun.net/builds/wintun-0.11.zip
|
curl -o wintun.zip https://www.wintun.net/builds/wintun-0.14.1.zip
|
||||||
unzip wintun.zip
|
unzip wintun.zip
|
||||||
fi
|
fi
|
||||||
if [ $PKGARCH = "x64" ]; then
|
if [ $PKGARCH = "x64" ]; then
|
||||||
|
@ -85,8 +69,8 @@ elif [ $PKGARCH = "x86" ]; then
|
||||||
PKGWINTUNDLL=wintun/bin/x86/wintun.dll
|
PKGWINTUNDLL=wintun/bin/x86/wintun.dll
|
||||||
elif [ $PKGARCH = "arm" ]; then
|
elif [ $PKGARCH = "arm" ]; then
|
||||||
PKGWINTUNDLL=wintun/bin/arm/wintun.dll
|
PKGWINTUNDLL=wintun/bin/arm/wintun.dll
|
||||||
#elif [ $PKGARCH = "arm64" ]; then
|
elif [ $PKGARCH = "arm64" ]; then
|
||||||
# PKGWINTUNDLL=wintun/bin/arm64/wintun.dll
|
PKGWINTUNDLL=wintun/bin/arm64/wintun.dll
|
||||||
else
|
else
|
||||||
echo "wasn't sure which architecture to get wintun for"
|
echo "wasn't sure which architecture to get wintun for"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Get the current branch name
|
# Get the current branch name
|
||||||
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
|
BRANCH="$GITHUB_REF_NAME"
|
||||||
|
if [ -z "$BRANCH" ]; then
|
||||||
|
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
# Complain if the git history is not available
|
|
||||||
if [ $? != 0 ] || [ -z "$BRANCH" ]; then
|
if [ $? != 0 ] || [ -z "$BRANCH" ]; then
|
||||||
printf "yggdrasil"
|
printf "yggdrasil"
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=yggdrasil
|
Description=yggdrasil
|
||||||
Wants=network.target
|
Wants=network-online.target
|
||||||
Wants=yggdrasil-default-config.service
|
Wants=yggdrasil-default-config.service
|
||||||
After=network.target
|
After=network-online.target
|
||||||
After=yggdrasil-default-config.service
|
After=yggdrasil-default-config.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
@ -10,7 +10,7 @@ Group=yggdrasil
|
||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
ProtectSystem=true
|
ProtectSystem=true
|
||||||
SyslogIdentifier=yggdrasil
|
SyslogIdentifier=yggdrasil
|
||||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
|
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
ExecStartPre=+-/sbin/modprobe tun
|
ExecStartPre=+-/sbin/modprobe tun
|
||||||
ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil.conf
|
ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil.conf
|
||||||
ExecReload=/bin/kill -HUP $MAINPID
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
|
|
38
go.mod
38
go.mod
|
@ -1,26 +1,38 @@
|
||||||
module github.com/yggdrasil-network/yggdrasil-go
|
module github.com/yggdrasil-network/yggdrasil-go
|
||||||
|
|
||||||
go 1.16
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Arceliar/ironwood v0.0.0-20210619124114-6ad55cae5031
|
github.com/Arceliar/ironwood v0.0.0-20220903132624-ee60c16bcfcf
|
||||||
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979
|
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979
|
||||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
|
||||||
github.com/cheggaaa/pb/v3 v3.0.8
|
github.com/cheggaaa/pb/v3 v3.0.8
|
||||||
github.com/fatih/color v1.12.0 // indirect
|
|
||||||
github.com/gologme/log v1.2.0
|
github.com/gologme/log v1.2.0
|
||||||
github.com/hashicorp/go-syslog v1.0.0
|
github.com/hashicorp/go-syslog v1.0.0
|
||||||
github.com/hjson/hjson-go v3.1.0+incompatible
|
github.com/hjson/hjson-go v3.1.0+incompatible
|
||||||
github.com/kardianos/minwinsvc v1.0.0
|
github.com/kardianos/minwinsvc v1.0.0
|
||||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.4.1
|
github.com/mitchellh/mapstructure v1.4.1
|
||||||
github.com/vishvananda/netlink v1.1.0
|
github.com/vishvananda/netlink v1.1.0
|
||||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
|
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
|
||||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
||||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273
|
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b
|
||||||
golang.org/x/text v0.3.7-0.20210503195748-5c7c50ebbd4f
|
golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2
|
golang.zx2c4.com/wireguard/windows v0.4.12
|
||||||
golang.zx2c4.com/wireguard/windows v0.3.14
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||||
|
golang.org/x/tools v0.1.12 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||||
|
github.com/fatih/color v1.12.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
|
||||||
)
|
)
|
||||||
|
|
80
go.sum
80
go.sum
|
@ -1,7 +1,8 @@
|
||||||
github.com/Arceliar/ironwood v0.0.0-20210619124114-6ad55cae5031 h1:DZVDfYhVdu+0wAiRHoY1olyNkKxIot9UjBnbQFzuUlM=
|
github.com/Arceliar/ironwood v0.0.0-20220903132624-ee60c16bcfcf h1:kjPkmDHUTWUma/4tqDl208bOk3jsUEqOJA6TsMZo5Jk=
|
||||||
github.com/Arceliar/ironwood v0.0.0-20210619124114-6ad55cae5031/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk=
|
github.com/Arceliar/ironwood v0.0.0-20220903132624-ee60c16bcfcf/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk=
|
||||||
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ=
|
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ=
|
||||||
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI=
|
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI=
|
||||||
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||||
|
@ -25,11 +26,14 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
||||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
@ -38,42 +42,74 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp
|
||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
||||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
|
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||||
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk=
|
||||||
|
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
||||||
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b h1:k+E048sYJHyVnsr1GDrRZWQ32D2C7lWs9JRc0bel53A=
|
golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 h1:faDu4veV+8pcThn4fewv6TVlNCezafGoC1gM/mxQLbQ=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/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 h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7-0.20210503195748-5c7c50ebbd4f h1:yQJrRE0hDxDFmZLlRaw+3vusO4fwNHgHIjUOMO7bHYI=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.7-0.20210503195748-5c7c50ebbd4f/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0=
|
||||||
|
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20210510202332-9844c74f67ec/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2 h1:wfOOSvHgIzTZ9h5Vb6yUFZNn7uf3bT7PeYsHOO7tYDM=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
golang.zx2c4.com/wireguard/windows v0.3.14 h1:5yIDYyrQyGkLqV+tzY4ilMNeIvQeMXAz0glZz9u179A=
|
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
golang.zx2c4.com/wireguard/windows v0.3.14/go.mod h1:3P4IEAsb+BjlKZmpUXgy74c0iX9AVwwr3WcVJ8nPgME=
|
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20211012062646-82d2aa87aa62/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a h1:tTbyylK9/D3u/wEP26Vx7L700UpY48nhioJWZM1vhZw=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20211017052713-f87e87af0d9a/go.mod h1:id8Oh3eCCmpj9uVGWVjsUAl6UPX5ysMLzu6QxJU2UOU=
|
||||||
|
golang.zx2c4.com/wireguard/windows v0.4.12 h1:CUmbdWKVNzTSsVb4yUAiEwL3KsabdJkEPdDjCHxBlhA=
|
||||||
|
golang.zx2c4.com/wireguard/windows v0.4.12/go.mod h1:PW4y+d9oY83XU9rRwRwrJDwEMuhVjMxu2gfD1cfzS7w=
|
||||||
|
|
|
@ -64,7 +64,7 @@ func AddrForKey(publicKey ed25519.PublicKey) *Address {
|
||||||
buf[idx] = ^buf[idx]
|
buf[idx] = ^buf[idx]
|
||||||
}
|
}
|
||||||
var addr Address
|
var addr Address
|
||||||
var temp []byte
|
var temp = make([]byte, 0, 32)
|
||||||
done := false
|
done := false
|
||||||
ones := byte(0)
|
ones := byte(0)
|
||||||
bits := byte(0)
|
bits := byte(0)
|
||||||
|
@ -108,7 +108,7 @@ func SubnetForKey(publicKey ed25519.PublicKey) *Subnet {
|
||||||
}
|
}
|
||||||
var snet Subnet
|
var snet Subnet
|
||||||
copy(snet[:], addr[:])
|
copy(snet[:], addr[:])
|
||||||
prefix := GetPrefix()
|
prefix := GetPrefix() // nolint:staticcheck
|
||||||
snet[len(prefix)-1] |= 0x01
|
snet[len(prefix)-1] |= 0x01
|
||||||
return &snet
|
return &snet
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ func SubnetForKey(publicKey ed25519.PublicKey) *Subnet {
|
||||||
// This is used for key lookup.
|
// This is used for key lookup.
|
||||||
func (a *Address) GetKey() ed25519.PublicKey {
|
func (a *Address) GetKey() ed25519.PublicKey {
|
||||||
var key [ed25519.PublicKeySize]byte
|
var key [ed25519.PublicKeySize]byte
|
||||||
prefix := GetPrefix()
|
prefix := GetPrefix() // nolint:staticcheck
|
||||||
ones := int(a[len(prefix)])
|
ones := int(a[len(prefix)])
|
||||||
for idx := 0; idx < ones; idx++ {
|
for idx := 0; idx < ones; idx++ {
|
||||||
key[idx/8] |= 0x80 >> byte(idx%8)
|
key[idx/8] |= 0x80 >> byte(idx%8)
|
||||||
|
@ -129,7 +129,11 @@ func (a *Address) GetKey() ed25519.PublicKey {
|
||||||
bits <<= byte(idx % 8)
|
bits <<= byte(idx % 8)
|
||||||
keyIdx := keyOffset + (idx - addrOffset)
|
keyIdx := keyOffset + (idx - addrOffset)
|
||||||
bits >>= byte(keyIdx % 8)
|
bits >>= byte(keyIdx % 8)
|
||||||
key[keyIdx/8] |= bits
|
idx := keyIdx / 8
|
||||||
|
if idx >= len(key) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
key[idx] |= bits
|
||||||
}
|
}
|
||||||
for idx := range key {
|
for idx := range key {
|
||||||
key[idx] = ^key[idx]
|
key[idx] = ^key[idx]
|
||||||
|
|
114
src/address/address_test.go
Normal file
114
src/address/address_test.go
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package address
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddress_Address_IsValid(t *testing.T) {
|
||||||
|
var address Address
|
||||||
|
rand.Read(address[:])
|
||||||
|
|
||||||
|
address[0] = 0
|
||||||
|
|
||||||
|
if address.IsValid() {
|
||||||
|
t.Fatal("invalid address marked as valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
address[0] = 0x03
|
||||||
|
|
||||||
|
if address.IsValid() {
|
||||||
|
t.Fatal("invalid address marked as valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
address[0] = 0x02
|
||||||
|
|
||||||
|
if !address.IsValid() {
|
||||||
|
t.Fatal("valid address marked as invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddress_Subnet_IsValid(t *testing.T) {
|
||||||
|
var subnet Subnet
|
||||||
|
rand.Read(subnet[:])
|
||||||
|
|
||||||
|
subnet[0] = 0
|
||||||
|
|
||||||
|
if subnet.IsValid() {
|
||||||
|
t.Fatal("invalid subnet marked as valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet[0] = 0x02
|
||||||
|
|
||||||
|
if subnet.IsValid() {
|
||||||
|
t.Fatal("invalid subnet marked as valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet[0] = 0x03
|
||||||
|
|
||||||
|
if !subnet.IsValid() {
|
||||||
|
t.Fatal("valid subnet marked as invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddress_AddrForKey(t *testing.T) {
|
||||||
|
publicKey := ed25519.PublicKey{
|
||||||
|
189, 186, 207, 216, 34, 64, 222, 61, 205, 18, 57, 36, 203, 181, 82, 86,
|
||||||
|
251, 141, 171, 8, 170, 152, 227, 5, 82, 138, 184, 79, 65, 158, 110, 251,
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedAddress := Address{
|
||||||
|
2, 0, 132, 138, 96, 79, 187, 126, 67, 132, 101, 219, 141, 182, 104, 149,
|
||||||
|
}
|
||||||
|
|
||||||
|
if *AddrForKey(publicKey) != expectedAddress {
|
||||||
|
t.Fatal("invalid address returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddress_SubnetForKey(t *testing.T) {
|
||||||
|
publicKey := ed25519.PublicKey{
|
||||||
|
189, 186, 207, 216, 34, 64, 222, 61, 205, 18, 57, 36, 203, 181, 82, 86,
|
||||||
|
251, 141, 171, 8, 170, 152, 227, 5, 82, 138, 184, 79, 65, 158, 110, 251,
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedSubnet := Subnet{3, 0, 132, 138, 96, 79, 187, 126}
|
||||||
|
|
||||||
|
if *SubnetForKey(publicKey) != expectedSubnet {
|
||||||
|
t.Fatal("invalid subnet returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddress_Address_GetKey(t *testing.T) {
|
||||||
|
address := Address{
|
||||||
|
2, 0, 132, 138, 96, 79, 187, 126, 67, 132, 101, 219, 141, 182, 104, 149,
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedPublicKey := ed25519.PublicKey{
|
||||||
|
189, 186, 207, 216, 34, 64, 222, 61,
|
||||||
|
205, 18, 57, 36, 203, 181, 127, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(address.GetKey(), expectedPublicKey) {
|
||||||
|
t.Fatal("invalid public key returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddress_Subnet_GetKey(t *testing.T) {
|
||||||
|
subnet := Subnet{3, 0, 132, 138, 96, 79, 187, 126}
|
||||||
|
|
||||||
|
expectedPublicKey := ed25519.PublicKey{
|
||||||
|
189, 186, 207, 216, 34, 64, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(subnet.GetKey(), expectedPublicKey) {
|
||||||
|
t.Fatal("invalid public key returned")
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,55 +7,64 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gologme/log"
|
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Add authentication
|
// TODO: Add authentication
|
||||||
|
|
||||||
type AdminSocket struct {
|
type AdminSocket struct {
|
||||||
core *core.Core
|
core *core.Core
|
||||||
log *log.Logger
|
log util.Logger
|
||||||
listenaddr string
|
listener net.Listener
|
||||||
listener net.Listener
|
handlers map[string]handler
|
||||||
handlers map[string]handler
|
done chan struct{}
|
||||||
done chan struct{}
|
config struct {
|
||||||
|
listenaddr ListenAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdminSocketRequest struct {
|
||||||
|
Name string `json:"request"`
|
||||||
|
Arguments map[string]string `json:"arguments,omitempty"`
|
||||||
|
KeepAlive bool `json:"keepalive,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdminSocketResponse struct {
|
type AdminSocketResponse struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Request struct {
|
Error string `json:"error,omitempty"`
|
||||||
Name string `json:"request"`
|
Request AdminSocketRequest `json:"request"`
|
||||||
KeepAlive bool `json:"keepalive"`
|
Response json.RawMessage `json:"response"`
|
||||||
} `json:"request"`
|
|
||||||
Response interface{} `json:"response"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
args []string // List of human-readable argument names
|
desc string // What does the endpoint do?
|
||||||
handler func(json.RawMessage) (interface{}, error) // First is input map, second is output
|
args []string // List of human-readable argument names
|
||||||
|
handler core.AddHandlerFunc // First is input map, second is output
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListResponse struct {
|
type ListResponse struct {
|
||||||
List map[string]ListEntry `json:"list"`
|
List []ListEntry `json:"list"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListEntry struct {
|
type ListEntry struct {
|
||||||
Fields []string `json:"fields"`
|
Command string `json:"command"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Fields []string `json:"fields,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddHandler is called for each admin function to add the handler and help documentation to the API.
|
// AddHandler is called for each admin function to add the handler and help documentation to the API.
|
||||||
func (a *AdminSocket) AddHandler(name string, args []string, handlerfunc func(json.RawMessage) (interface{}, error)) error {
|
func (a *AdminSocket) AddHandler(name, desc string, args []string, handlerfunc core.AddHandlerFunc) error {
|
||||||
if _, ok := a.handlers[strings.ToLower(name)]; ok {
|
if _, ok := a.handlers[strings.ToLower(name)]; ok {
|
||||||
return errors.New("handler already exists")
|
return errors.New("handler already exists")
|
||||||
}
|
}
|
||||||
a.handlers[strings.ToLower(name)] = handler{
|
a.handlers[strings.ToLower(name)] = handler{
|
||||||
|
desc: desc,
|
||||||
args: args,
|
args: args,
|
||||||
handler: handlerfunc,
|
handler: handlerfunc,
|
||||||
}
|
}
|
||||||
|
@ -63,101 +72,114 @@ func (a *AdminSocket) AddHandler(name string, args []string, handlerfunc func(js
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init runs the initial admin setup.
|
// Init runs the initial admin setup.
|
||||||
func (a *AdminSocket) Init(c *core.Core, nc *config.NodeConfig, log *log.Logger, options interface{}) error {
|
func New(c *core.Core, log util.Logger, opts ...SetupOption) (*AdminSocket, error) {
|
||||||
a.core = c
|
a := &AdminSocket{
|
||||||
a.log = log
|
core: c,
|
||||||
a.handlers = make(map[string]handler)
|
log: log,
|
||||||
nc.RLock()
|
handlers: make(map[string]handler),
|
||||||
a.listenaddr = nc.AdminListen
|
}
|
||||||
nc.RUnlock()
|
for _, opt := range opts {
|
||||||
a.done = make(chan struct{})
|
a._applyOption(opt)
|
||||||
close(a.done) // Start in a done / not-started state
|
}
|
||||||
_ = a.AddHandler("list", []string{}, func(_ json.RawMessage) (interface{}, error) {
|
if a.config.listenaddr == "none" || a.config.listenaddr == "" {
|
||||||
res := &ListResponse{
|
return nil, nil
|
||||||
List: map[string]ListEntry{},
|
}
|
||||||
}
|
_ = a.AddHandler("list", "List available commands", []string{}, func(_ json.RawMessage) (interface{}, error) {
|
||||||
|
res := &ListResponse{}
|
||||||
for name, handler := range a.handlers {
|
for name, handler := range a.handlers {
|
||||||
res.List[name] = ListEntry{
|
res.List = append(res.List, ListEntry{
|
||||||
Fields: handler.args,
|
Command: name,
|
||||||
}
|
Description: handler.desc,
|
||||||
|
Fields: handler.args,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
sort.SliceStable(res.List, func(i, j int) bool {
|
||||||
|
return strings.Compare(res.List[i].Command, res.List[j].Command) < 0
|
||||||
|
})
|
||||||
return res, nil
|
return res, nil
|
||||||
})
|
})
|
||||||
a.core.SetAdmin(a)
|
a.done = make(chan struct{})
|
||||||
return nil
|
go a.listen()
|
||||||
|
return a, a.core.SetAdmin(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
|
func (a *AdminSocket) SetupAdminHandlers() {
|
||||||
_ = a.AddHandler("getSelf", []string{}, func(in json.RawMessage) (interface{}, error) {
|
_ = a.AddHandler(
|
||||||
req := &GetSelfRequest{}
|
"getSelf", "Show details about this node", []string{},
|
||||||
res := &GetSelfResponse{}
|
func(in json.RawMessage) (interface{}, error) {
|
||||||
if err := json.Unmarshal(in, &req); err != nil {
|
req := &GetSelfRequest{}
|
||||||
return nil, err
|
res := &GetSelfResponse{}
|
||||||
}
|
if err := json.Unmarshal(in, &req); err != nil {
|
||||||
if err := a.getSelfHandler(req, res); err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
if err := a.getSelfHandler(req, res); err != nil {
|
||||||
return res, nil
|
return nil, err
|
||||||
})
|
}
|
||||||
_ = a.AddHandler("getPeers", []string{}, func(in json.RawMessage) (interface{}, error) {
|
return res, nil
|
||||||
req := &GetPeersRequest{}
|
},
|
||||||
res := &GetPeersResponse{}
|
)
|
||||||
if err := json.Unmarshal(in, &req); err != nil {
|
_ = a.AddHandler(
|
||||||
return nil, err
|
"getPeers", "Show directly connected peers", []string{},
|
||||||
}
|
func(in json.RawMessage) (interface{}, error) {
|
||||||
if err := a.getPeersHandler(req, res); err != nil {
|
req := &GetPeersRequest{}
|
||||||
return nil, err
|
res := &GetPeersResponse{}
|
||||||
}
|
if err := json.Unmarshal(in, &req); err != nil {
|
||||||
return res, nil
|
return nil, err
|
||||||
})
|
}
|
||||||
_ = a.AddHandler("getDHT", []string{}, func(in json.RawMessage) (interface{}, error) {
|
if err := a.getPeersHandler(req, res); err != nil {
|
||||||
req := &GetDHTRequest{}
|
return nil, err
|
||||||
res := &GetDHTResponse{}
|
}
|
||||||
if err := json.Unmarshal(in, &req); err != nil {
|
return res, nil
|
||||||
return nil, err
|
},
|
||||||
}
|
)
|
||||||
if err := a.getDHTHandler(req, res); err != nil {
|
_ = a.AddHandler(
|
||||||
return nil, err
|
"getDHT", "Show known DHT entries", []string{},
|
||||||
}
|
func(in json.RawMessage) (interface{}, error) {
|
||||||
return res, nil
|
req := &GetDHTRequest{}
|
||||||
})
|
res := &GetDHTResponse{}
|
||||||
_ = a.AddHandler("getPaths", []string{}, func(in json.RawMessage) (interface{}, error) {
|
if err := json.Unmarshal(in, &req); err != nil {
|
||||||
req := &GetPathsRequest{}
|
return nil, err
|
||||||
res := &GetPathsResponse{}
|
}
|
||||||
if err := json.Unmarshal(in, &req); err != nil {
|
if err := a.getDHTHandler(req, res); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := a.getPathsHandler(req, res); err != nil {
|
return res, nil
|
||||||
return nil, err
|
},
|
||||||
}
|
)
|
||||||
return res, nil
|
_ = a.AddHandler(
|
||||||
})
|
"getPaths", "Show established paths through this node", []string{},
|
||||||
_ = a.AddHandler("getSessions", []string{}, func(in json.RawMessage) (interface{}, error) {
|
func(in json.RawMessage) (interface{}, error) {
|
||||||
req := &GetSessionsRequest{}
|
req := &GetPathsRequest{}
|
||||||
res := &GetSessionsResponse{}
|
res := &GetPathsResponse{}
|
||||||
if err := json.Unmarshal(in, &req); err != nil {
|
if err := json.Unmarshal(in, &req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := a.getSessionsHandler(req, res); err != nil {
|
if err := a.getPathsHandler(req, res); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
_ = a.AddHandler(
|
||||||
|
"getSessions", "Show established traffic sessions with remote nodes", []string{},
|
||||||
|
func(in json.RawMessage) (interface{}, error) {
|
||||||
|
req := &GetSessionsRequest{}
|
||||||
|
res := &GetSessionsResponse{}
|
||||||
|
if err := json.Unmarshal(in, &req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := a.getSessionsHandler(req, res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
//_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler)
|
//_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler)
|
||||||
//_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler)
|
//_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler)
|
||||||
//_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler)
|
//_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler)
|
||||||
//_ = a.AddHandler("debug_remoteGetDHT", []string{"key"}, t.proto.getDHTHandler)
|
//_ = a.AddHandler("debug_remoteGetDHT", []string{"key"}, t.proto.getDHTHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start runs the admin API socket to listen for / respond to admin API calls.
|
|
||||||
func (a *AdminSocket) Start() error {
|
|
||||||
if a.listenaddr != "none" && a.listenaddr != "" {
|
|
||||||
a.done = make(chan struct{})
|
|
||||||
go a.listen()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsStarted returns true if the module has been started.
|
// IsStarted returns true if the module has been started.
|
||||||
func (a *AdminSocket) IsStarted() bool {
|
func (a *AdminSocket) IsStarted() bool {
|
||||||
select {
|
select {
|
||||||
|
@ -172,6 +194,9 @@ func (a *AdminSocket) IsStarted() bool {
|
||||||
|
|
||||||
// Stop will stop the admin API and close the socket.
|
// Stop will stop the admin API and close the socket.
|
||||||
func (a *AdminSocket) Stop() error {
|
func (a *AdminSocket) Stop() error {
|
||||||
|
if a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if a.listener != nil {
|
if a.listener != nil {
|
||||||
select {
|
select {
|
||||||
case <-a.done:
|
case <-a.done:
|
||||||
|
@ -185,31 +210,32 @@ func (a *AdminSocket) Stop() error {
|
||||||
|
|
||||||
// listen is run by start and manages API connections.
|
// listen is run by start and manages API connections.
|
||||||
func (a *AdminSocket) listen() {
|
func (a *AdminSocket) listen() {
|
||||||
u, err := url.Parse(a.listenaddr)
|
listenaddr := string(a.config.listenaddr)
|
||||||
|
u, err := url.Parse(listenaddr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
switch strings.ToLower(u.Scheme) {
|
switch strings.ToLower(u.Scheme) {
|
||||||
case "unix":
|
case "unix":
|
||||||
if _, err := os.Stat(a.listenaddr[7:]); err == nil {
|
if _, err := os.Stat(listenaddr[7:]); err == nil {
|
||||||
a.log.Debugln("Admin socket", a.listenaddr[7:], "already exists, trying to clean up")
|
a.log.Debugln("Admin socket", listenaddr[7:], "already exists, trying to clean up")
|
||||||
if _, err := net.DialTimeout("unix", a.listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() {
|
if _, err := net.DialTimeout("unix", listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() {
|
||||||
a.log.Errorln("Admin socket", a.listenaddr[7:], "already exists and is in use by another process")
|
a.log.Errorln("Admin socket", listenaddr[7:], "already exists and is in use by another process")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
} else {
|
} else {
|
||||||
if err := os.Remove(a.listenaddr[7:]); err == nil {
|
if err := os.Remove(listenaddr[7:]); err == nil {
|
||||||
a.log.Debugln(a.listenaddr[7:], "was cleaned up")
|
a.log.Debugln(listenaddr[7:], "was cleaned up")
|
||||||
} else {
|
} else {
|
||||||
a.log.Errorln(a.listenaddr[7:], "already exists and was not cleaned up:", err)
|
a.log.Errorln(listenaddr[7:], "already exists and was not cleaned up:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a.listener, err = net.Listen("unix", a.listenaddr[7:])
|
a.listener, err = net.Listen("unix", listenaddr[7:])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
switch a.listenaddr[7:8] {
|
switch listenaddr[7:8] {
|
||||||
case "@": // maybe abstract namespace
|
case "@": // maybe abstract namespace
|
||||||
default:
|
default:
|
||||||
if err := os.Chmod(a.listenaddr[7:], 0660); err != nil {
|
if err := os.Chmod(listenaddr[7:], 0660); err != nil {
|
||||||
a.log.Warnln("WARNING:", a.listenaddr[:7], "may have unsafe permissions!")
|
a.log.Warnln("WARNING:", listenaddr[:7], "may have unsafe permissions!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,10 +243,10 @@ func (a *AdminSocket) listen() {
|
||||||
a.listener, err = net.Listen("tcp", u.Host)
|
a.listener, err = net.Listen("tcp", u.Host)
|
||||||
default:
|
default:
|
||||||
// err = errors.New(fmt.Sprint("protocol not supported: ", u.Scheme))
|
// err = errors.New(fmt.Sprint("protocol not supported: ", u.Scheme))
|
||||||
a.listener, err = net.Listen("tcp", a.listenaddr)
|
a.listener, err = net.Listen("tcp", listenaddr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
a.listener, err = net.Listen("tcp", a.listenaddr)
|
a.listener, err = net.Listen("tcp", listenaddr)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.log.Errorf("Admin socket failed to listen: %v", err)
|
a.log.Errorf("Admin socket failed to listen: %v", err)
|
||||||
|
@ -272,29 +298,34 @@ func (a *AdminSocket) handleRequest(conn net.Conn) {
|
||||||
for {
|
for {
|
||||||
var err error
|
var err error
|
||||||
var buf json.RawMessage
|
var buf json.RawMessage
|
||||||
_ = decoder.Decode(&buf)
|
|
||||||
var resp AdminSocketResponse
|
var resp AdminSocketResponse
|
||||||
resp.Status = "success"
|
if err := func() error {
|
||||||
if err = json.Unmarshal(buf, &resp.Request); err == nil {
|
if err = decoder.Decode(&buf); err != nil {
|
||||||
if resp.Request.Name == "" {
|
return fmt.Errorf("Failed to find request")
|
||||||
resp.Status = "error"
|
|
||||||
resp.Response = &ErrorResponse{
|
|
||||||
Error: "No request specified",
|
|
||||||
}
|
|
||||||
} else if h, ok := a.handlers[strings.ToLower(resp.Request.Name)]; ok {
|
|
||||||
resp.Response, err = h.handler(buf)
|
|
||||||
if err != nil {
|
|
||||||
resp.Status = "error"
|
|
||||||
resp.Response = &ErrorResponse{
|
|
||||||
Error: err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resp.Status = "error"
|
|
||||||
resp.Response = &ErrorResponse{
|
|
||||||
Error: fmt.Sprintf("Unknown action '%s', try 'list' for help", resp.Request.Name),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if err = json.Unmarshal(buf, &resp.Request); err != nil {
|
||||||
|
return fmt.Errorf("Failed to unmarshal request")
|
||||||
|
}
|
||||||
|
if resp.Request.Name == "" {
|
||||||
|
return fmt.Errorf("No request specified")
|
||||||
|
}
|
||||||
|
reqname := strings.ToLower(resp.Request.Name)
|
||||||
|
handler, ok := a.handlers[reqname]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Unknown action '%s', try 'list' for help", reqname)
|
||||||
|
}
|
||||||
|
res, err := handler.handler(buf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Handler returned error: %w", err)
|
||||||
|
}
|
||||||
|
if resp.Response, err = json.Marshal(res); err != nil {
|
||||||
|
return fmt.Errorf("Failed to marshal response: %w", err)
|
||||||
|
}
|
||||||
|
resp.Status = "success"
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
resp.Status = "error"
|
||||||
|
resp.Error = err.Error()
|
||||||
}
|
}
|
||||||
if err = encoder.Encode(resp); err != nil {
|
if err = encoder.Encode(resp); err != nil {
|
||||||
a.log.Debugln("Encode error:", err)
|
a.log.Debugln("Encode error:", err)
|
||||||
|
@ -306,3 +337,18 @@ func (a *AdminSocket) handleRequest(conn net.Conn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DataUnit uint64
|
||||||
|
|
||||||
|
func (d DataUnit) String() string {
|
||||||
|
switch {
|
||||||
|
case d > 1024*1024*1024*1024:
|
||||||
|
return fmt.Sprintf("%2.ftb", float64(d)/1024/1024/1024/1024)
|
||||||
|
case d > 1024*1024*1024:
|
||||||
|
return fmt.Sprintf("%2.fgb", float64(d)/1024/1024/1024)
|
||||||
|
case d > 1024*1024:
|
||||||
|
return fmt.Sprintf("%2.fmb", float64(d)/1024/1024)
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%2.fkb", float64(d)/1024)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package admin
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
)
|
)
|
||||||
|
@ -10,25 +12,30 @@ import (
|
||||||
type GetDHTRequest struct{}
|
type GetDHTRequest struct{}
|
||||||
|
|
||||||
type GetDHTResponse struct {
|
type GetDHTResponse struct {
|
||||||
DHT map[string]DHTEntry `json:"dht"`
|
DHT []DHTEntry `json:"dht"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DHTEntry struct {
|
type DHTEntry struct {
|
||||||
|
IPAddress string `json:"address"`
|
||||||
PublicKey string `json:"key"`
|
PublicKey string `json:"key"`
|
||||||
Port uint64 `json:"port"`
|
Port uint64 `json:"port"`
|
||||||
Rest uint64 `json:"rest"`
|
Rest uint64 `json:"rest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getDHTHandler(req *GetDHTRequest, res *GetDHTResponse) error {
|
func (a *AdminSocket) getDHTHandler(req *GetDHTRequest, res *GetDHTResponse) error {
|
||||||
res.DHT = map[string]DHTEntry{}
|
dht := a.core.GetDHT()
|
||||||
for _, d := range a.core.GetDHT() {
|
res.DHT = make([]DHTEntry, 0, len(dht))
|
||||||
|
for _, d := range dht {
|
||||||
addr := address.AddrForKey(d.Key)
|
addr := address.AddrForKey(d.Key)
|
||||||
so := net.IP(addr[:]).String()
|
res.DHT = append(res.DHT, DHTEntry{
|
||||||
res.DHT[so] = DHTEntry{
|
IPAddress: net.IP(addr[:]).String(),
|
||||||
PublicKey: hex.EncodeToString(d.Key[:]),
|
PublicKey: hex.EncodeToString(d.Key[:]),
|
||||||
Port: d.Port,
|
Port: d.Port,
|
||||||
Rest: d.Rest,
|
Rest: d.Rest,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
sort.SliceStable(res.DHT, func(i, j int) bool {
|
||||||
|
return strings.Compare(res.DHT[i].PublicKey, res.DHT[j].PublicKey) < 0
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package admin
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
)
|
)
|
||||||
|
@ -11,23 +13,28 @@ type GetPathsRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetPathsResponse struct {
|
type GetPathsResponse struct {
|
||||||
Paths map[string]PathEntry `json:"paths"`
|
Paths []PathEntry `json:"paths"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PathEntry struct {
|
type PathEntry struct {
|
||||||
|
IPAddress string `json:"address"`
|
||||||
PublicKey string `json:"key"`
|
PublicKey string `json:"key"`
|
||||||
Path []uint64 `json:"path"`
|
Path []uint64 `json:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsResponse) error {
|
func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsResponse) error {
|
||||||
res.Paths = map[string]PathEntry{}
|
paths := a.core.GetPaths()
|
||||||
for _, p := range a.core.GetPaths() {
|
res.Paths = make([]PathEntry, 0, len(paths))
|
||||||
|
for _, p := range paths {
|
||||||
addr := address.AddrForKey(p.Key)
|
addr := address.AddrForKey(p.Key)
|
||||||
so := net.IP(addr[:]).String()
|
res.Paths = append(res.Paths, PathEntry{
|
||||||
res.Paths[so] = PathEntry{
|
IPAddress: net.IP(addr[:]).String(),
|
||||||
PublicKey: hex.EncodeToString(p.Key),
|
PublicKey: hex.EncodeToString(p.Key),
|
||||||
Path: p.Path,
|
Path: p.Path,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
sort.SliceStable(res.Paths, func(i, j int) bool {
|
||||||
|
return strings.Compare(res.Paths[i].PublicKey, res.Paths[j].PublicKey) < 0
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package admin
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
)
|
)
|
||||||
|
@ -11,27 +12,38 @@ type GetPeersRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetPeersResponse struct {
|
type GetPeersResponse struct {
|
||||||
Peers map[string]PeerEntry `json:"peers"`
|
Peers []PeerEntry `json:"peers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeerEntry struct {
|
type PeerEntry struct {
|
||||||
|
IPAddress string `json:"address"`
|
||||||
PublicKey string `json:"key"`
|
PublicKey string `json:"key"`
|
||||||
Port uint64 `json:"port"`
|
Port uint64 `json:"port"`
|
||||||
Coords []uint64 `json:"coords"`
|
Coords []uint64 `json:"coords"`
|
||||||
Remote string `json:"remote"`
|
Remote string `json:"remote"`
|
||||||
|
RXBytes DataUnit `json:"bytes_recvd"`
|
||||||
|
TXBytes DataUnit `json:"bytes_sent"`
|
||||||
|
Uptime float64 `json:"uptime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error {
|
func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error {
|
||||||
res.Peers = map[string]PeerEntry{}
|
peers := a.core.GetPeers()
|
||||||
for _, p := range a.core.GetPeers() {
|
res.Peers = make([]PeerEntry, 0, len(peers))
|
||||||
|
for _, p := range peers {
|
||||||
addr := address.AddrForKey(p.Key)
|
addr := address.AddrForKey(p.Key)
|
||||||
so := net.IP(addr[:]).String()
|
res.Peers = append(res.Peers, PeerEntry{
|
||||||
res.Peers[so] = PeerEntry{
|
IPAddress: net.IP(addr[:]).String(),
|
||||||
PublicKey: hex.EncodeToString(p.Key),
|
PublicKey: hex.EncodeToString(p.Key),
|
||||||
Port: p.Port,
|
Port: p.Port,
|
||||||
Coords: p.Coords,
|
Coords: p.Coords,
|
||||||
Remote: p.Remote,
|
Remote: p.Remote,
|
||||||
}
|
RXBytes: DataUnit(p.RXBytes),
|
||||||
|
TXBytes: DataUnit(p.TXBytes),
|
||||||
|
Uptime: p.Uptime.Seconds(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
sort.Slice(res.Peers, func(i, j int) bool {
|
||||||
|
return res.Peers[i].Port < res.Peers[j].Port
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,28 +9,22 @@ import (
|
||||||
type GetSelfRequest struct{}
|
type GetSelfRequest struct{}
|
||||||
|
|
||||||
type GetSelfResponse struct {
|
type GetSelfResponse struct {
|
||||||
Self map[string]SelfEntry `json:"self"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SelfEntry struct {
|
|
||||||
BuildName string `json:"build_name"`
|
BuildName string `json:"build_name"`
|
||||||
BuildVersion string `json:"build_version"`
|
BuildVersion string `json:"build_version"`
|
||||||
PublicKey string `json:"key"`
|
PublicKey string `json:"key"`
|
||||||
|
IPAddress string `json:"address"`
|
||||||
Coords []uint64 `json:"coords"`
|
Coords []uint64 `json:"coords"`
|
||||||
Subnet string `json:"subnet"`
|
Subnet string `json:"subnet"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getSelfHandler(req *GetSelfRequest, res *GetSelfResponse) error {
|
func (a *AdminSocket) getSelfHandler(req *GetSelfRequest, res *GetSelfResponse) error {
|
||||||
res.Self = make(map[string]SelfEntry)
|
|
||||||
self := a.core.GetSelf()
|
self := a.core.GetSelf()
|
||||||
addr := a.core.Address().String()
|
|
||||||
snet := a.core.Subnet()
|
snet := a.core.Subnet()
|
||||||
res.Self[addr] = SelfEntry{
|
res.BuildName = version.BuildName()
|
||||||
BuildName: version.BuildName(),
|
res.BuildVersion = version.BuildVersion()
|
||||||
BuildVersion: version.BuildVersion(),
|
res.PublicKey = hex.EncodeToString(self.Key[:])
|
||||||
PublicKey: hex.EncodeToString(self.Key[:]),
|
res.IPAddress = a.core.Address().String()
|
||||||
Subnet: snet.String(),
|
res.Subnet = snet.String()
|
||||||
Coords: self.Coords,
|
res.Coords = self.Coords
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package admin
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
)
|
)
|
||||||
|
@ -10,21 +12,32 @@ import (
|
||||||
type GetSessionsRequest struct{}
|
type GetSessionsRequest struct{}
|
||||||
|
|
||||||
type GetSessionsResponse struct {
|
type GetSessionsResponse struct {
|
||||||
Sessions map[string]SessionEntry `json:"sessions"`
|
Sessions []SessionEntry `json:"sessions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionEntry struct {
|
type SessionEntry struct {
|
||||||
PublicKey string `json:"key"`
|
IPAddress string `json:"address"`
|
||||||
|
PublicKey string `json:"key"`
|
||||||
|
RXBytes DataUnit `json:"bytes_recvd"`
|
||||||
|
TXBytes DataUnit `json:"bytes_sent"`
|
||||||
|
Uptime float64 `json:"uptime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getSessionsHandler(req *GetSessionsRequest, res *GetSessionsResponse) error {
|
func (a *AdminSocket) getSessionsHandler(req *GetSessionsRequest, res *GetSessionsResponse) error {
|
||||||
res.Sessions = map[string]SessionEntry{}
|
sessions := a.core.GetSessions()
|
||||||
for _, s := range a.core.GetSessions() {
|
res.Sessions = make([]SessionEntry, 0, len(sessions))
|
||||||
|
for _, s := range sessions {
|
||||||
addr := address.AddrForKey(s.Key)
|
addr := address.AddrForKey(s.Key)
|
||||||
so := net.IP(addr[:]).String()
|
res.Sessions = append(res.Sessions, SessionEntry{
|
||||||
res.Sessions[so] = SessionEntry{
|
IPAddress: net.IP(addr[:]).String(),
|
||||||
PublicKey: hex.EncodeToString(s.Key[:]),
|
PublicKey: hex.EncodeToString(s.Key[:]),
|
||||||
}
|
RXBytes: DataUnit(s.RXBytes),
|
||||||
|
TXBytes: DataUnit(s.TXBytes),
|
||||||
|
Uptime: s.Uptime.Seconds(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
sort.SliceStable(res.Sessions, func(i, j int) bool {
|
||||||
|
return strings.Compare(res.Sessions[i].PublicKey, res.Sessions[j].PublicKey) < 0
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
16
src/admin/options.go
Normal file
16
src/admin/options.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package admin
|
||||||
|
|
||||||
|
func (c *AdminSocket) _applyOption(opt SetupOption) {
|
||||||
|
switch v := opt.(type) {
|
||||||
|
case ListenAddress:
|
||||||
|
c.config.listenaddr = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetupOption interface {
|
||||||
|
isSetupOption()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListenAddress string
|
||||||
|
|
||||||
|
func (a ListenAddress) isSetupOption() {}
|
54
src/config/config_test.go
Normal file
54
src/config/config_test.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig_Keys(t *testing.T) {
|
||||||
|
var nodeConfig NodeConfig
|
||||||
|
nodeConfig.NewKeys()
|
||||||
|
|
||||||
|
publicKey1, err := hex.DecodeString(nodeConfig.PublicKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("can not decode generated public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(publicKey1) == 0 {
|
||||||
|
t.Fatal("empty public key generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKey1, err := hex.DecodeString(nodeConfig.PrivateKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("can not decode generated private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(privateKey1) == 0 {
|
||||||
|
t.Fatal("empty private key generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeConfig.NewKeys()
|
||||||
|
|
||||||
|
publicKey2, err := hex.DecodeString(nodeConfig.PublicKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("can not decode generated public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(publicKey2, publicKey1) {
|
||||||
|
t.Fatal("same public key generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKey2, err := hex.DecodeString(nodeConfig.PrivateKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("can not decode generated private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Equal(privateKey2, privateKey1) {
|
||||||
|
t.Fatal("same private key generated")
|
||||||
|
}
|
||||||
|
}
|
174
src/core/api.go
174
src/core/api.go
|
@ -2,70 +2,82 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
//"encoding/hex"
|
//"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
//"errors"
|
//"errors"
|
||||||
//"fmt"
|
//"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
//"sort"
|
//"sort"
|
||||||
//"time"
|
//"time"
|
||||||
|
|
||||||
"github.com/gologme/log"
|
"github.com/Arceliar/phony"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
//"github.com/Arceliar/phony"
|
//"github.com/Arceliar/phony"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Self struct {
|
type SelfInfo struct {
|
||||||
Key ed25519.PublicKey
|
Key ed25519.PublicKey
|
||||||
Root ed25519.PublicKey
|
Root ed25519.PublicKey
|
||||||
Coords []uint64
|
Coords []uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Peer struct {
|
type PeerInfo struct {
|
||||||
Key ed25519.PublicKey
|
Key ed25519.PublicKey
|
||||||
Root ed25519.PublicKey
|
Root ed25519.PublicKey
|
||||||
Coords []uint64
|
Coords []uint64
|
||||||
Port uint64
|
Port uint64
|
||||||
Remote string
|
Remote string
|
||||||
|
RXBytes uint64
|
||||||
|
TXBytes uint64
|
||||||
|
Uptime time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type DHTEntry struct {
|
type DHTEntryInfo struct {
|
||||||
Key ed25519.PublicKey
|
Key ed25519.PublicKey
|
||||||
Port uint64
|
Port uint64
|
||||||
Rest uint64
|
Rest uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type PathEntry struct {
|
type PathEntryInfo struct {
|
||||||
Key ed25519.PublicKey
|
Key ed25519.PublicKey
|
||||||
Path []uint64
|
Path []uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Session struct {
|
type SessionInfo struct {
|
||||||
Key ed25519.PublicKey
|
Key ed25519.PublicKey
|
||||||
|
RXBytes uint64
|
||||||
|
TXBytes uint64
|
||||||
|
Uptime time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) GetSelf() Self {
|
func (c *Core) GetSelf() SelfInfo {
|
||||||
var self Self
|
var self SelfInfo
|
||||||
s := c.pc.PacketConn.Debug.GetSelf()
|
s := c.PacketConn.PacketConn.Debug.GetSelf()
|
||||||
self.Key = s.Key
|
self.Key = s.Key
|
||||||
self.Root = s.Root
|
self.Root = s.Root
|
||||||
self.Coords = s.Coords
|
self.Coords = s.Coords
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) GetPeers() []Peer {
|
func (c *Core) GetPeers() []PeerInfo {
|
||||||
var peers []Peer
|
var peers []PeerInfo
|
||||||
names := make(map[net.Conn]string)
|
names := make(map[net.Conn]string)
|
||||||
c.links.mutex.Lock()
|
phony.Block(&c.links, func() {
|
||||||
for _, info := range c.links.links {
|
for _, info := range c.links._links {
|
||||||
names[info.conn] = info.lname
|
names[info.conn] = info.lname
|
||||||
}
|
}
|
||||||
c.links.mutex.Unlock()
|
})
|
||||||
ps := c.pc.PacketConn.Debug.GetPeers()
|
ps := c.PacketConn.PacketConn.Debug.GetPeers()
|
||||||
for _, p := range ps {
|
for _, p := range ps {
|
||||||
var info Peer
|
var info PeerInfo
|
||||||
info.Key = p.Key
|
info.Key = p.Key
|
||||||
info.Root = p.Root
|
info.Root = p.Root
|
||||||
info.Coords = p.Coords
|
info.Coords = p.Coords
|
||||||
|
@ -74,16 +86,21 @@ func (c *Core) GetPeers() []Peer {
|
||||||
if name := names[p.Conn]; name != "" {
|
if name := names[p.Conn]; name != "" {
|
||||||
info.Remote = name
|
info.Remote = name
|
||||||
}
|
}
|
||||||
|
if linkconn, ok := p.Conn.(*linkConn); ok {
|
||||||
|
info.RXBytes = atomic.LoadUint64(&linkconn.rx)
|
||||||
|
info.TXBytes = atomic.LoadUint64(&linkconn.tx)
|
||||||
|
info.Uptime = time.Since(linkconn.up)
|
||||||
|
}
|
||||||
peers = append(peers, info)
|
peers = append(peers, info)
|
||||||
}
|
}
|
||||||
return peers
|
return peers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) GetDHT() []DHTEntry {
|
func (c *Core) GetDHT() []DHTEntryInfo {
|
||||||
var dhts []DHTEntry
|
var dhts []DHTEntryInfo
|
||||||
ds := c.pc.PacketConn.Debug.GetDHT()
|
ds := c.PacketConn.PacketConn.Debug.GetDHT()
|
||||||
for _, d := range ds {
|
for _, d := range ds {
|
||||||
var info DHTEntry
|
var info DHTEntryInfo
|
||||||
info.Key = d.Key
|
info.Key = d.Key
|
||||||
info.Port = d.Port
|
info.Port = d.Port
|
||||||
info.Rest = d.Rest
|
info.Rest = d.Rest
|
||||||
|
@ -92,11 +109,11 @@ func (c *Core) GetDHT() []DHTEntry {
|
||||||
return dhts
|
return dhts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) GetPaths() []PathEntry {
|
func (c *Core) GetPaths() []PathEntryInfo {
|
||||||
var paths []PathEntry
|
var paths []PathEntryInfo
|
||||||
ps := c.pc.PacketConn.Debug.GetPaths()
|
ps := c.PacketConn.PacketConn.Debug.GetPaths()
|
||||||
for _, p := range ps {
|
for _, p := range ps {
|
||||||
var info PathEntry
|
var info PathEntryInfo
|
||||||
info.Key = p.Key
|
info.Key = p.Key
|
||||||
info.Path = p.Path
|
info.Path = p.Path
|
||||||
paths = append(paths, info)
|
paths = append(paths, info)
|
||||||
|
@ -104,12 +121,15 @@ func (c *Core) GetPaths() []PathEntry {
|
||||||
return paths
|
return paths
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) GetSessions() []Session {
|
func (c *Core) GetSessions() []SessionInfo {
|
||||||
var sessions []Session
|
var sessions []SessionInfo
|
||||||
ss := c.pc.Debug.GetSessions()
|
ss := c.PacketConn.Debug.GetSessions()
|
||||||
for _, s := range ss {
|
for _, s := range ss {
|
||||||
var info Session
|
var info SessionInfo
|
||||||
info.Key = s.Key
|
info.Key = s.Key
|
||||||
|
info.RXBytes = s.RX
|
||||||
|
info.TXBytes = s.TX
|
||||||
|
info.Uptime = s.Uptime
|
||||||
sessions = append(sessions, info)
|
sessions = append(sessions, info)
|
||||||
}
|
}
|
||||||
return sessions
|
return sessions
|
||||||
|
@ -118,8 +138,17 @@ func (c *Core) GetSessions() []Session {
|
||||||
// Listen starts a new listener (either TCP or TLS). The input should be a url.URL
|
// Listen starts a new listener (either TCP or TLS). The input should be a url.URL
|
||||||
// parsed from a string of the form e.g. "tcp://a.b.c.d:e". In the case of a
|
// parsed from a string of the form e.g. "tcp://a.b.c.d:e". In the case of a
|
||||||
// link-local address, the interface should be provided as the second argument.
|
// link-local address, the interface should be provided as the second argument.
|
||||||
func (c *Core) Listen(u *url.URL, sintf string) (*TcpListener, error) {
|
func (c *Core) Listen(u *url.URL, sintf string) (*Listener, error) {
|
||||||
return c.links.tcp.listenURL(u, sintf)
|
switch u.Scheme {
|
||||||
|
case "tcp":
|
||||||
|
return c.links.tcp.listen(u, sintf)
|
||||||
|
case "tls":
|
||||||
|
return c.links.tls.listen(u, sintf)
|
||||||
|
case "unix":
|
||||||
|
return c.links.unix.listen(u, sintf)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unrecognised scheme %q", u.Scheme)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address gets the IPv6 address of the Yggdrasil node. This is always a /128
|
// Address gets the IPv6 address of the Yggdrasil node. This is always a /128
|
||||||
|
@ -147,7 +176,7 @@ func (c *Core) Subnet() net.IPNet {
|
||||||
// may be useful if you want to redirect the output later. Note that this
|
// may be useful if you want to redirect the output later. Note that this
|
||||||
// expects a Logger from the github.com/gologme/log package and not from Go's
|
// expects a Logger from the github.com/gologme/log package and not from Go's
|
||||||
// built-in log package.
|
// built-in log package.
|
||||||
func (c *Core) SetLogger(log *log.Logger) {
|
func (c *Core) SetLogger(log util.Logger) {
|
||||||
c.log = log
|
c.log = log
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,8 +256,10 @@ func (c *Core) RemovePeer(addr string, sintf string) error {
|
||||||
|
|
||||||
// CallPeer calls a peer once. This should be specified in the peer URI format,
|
// CallPeer calls a peer once. This should be specified in the peer URI format,
|
||||||
// e.g.:
|
// e.g.:
|
||||||
// tcp://a.b.c.d:e
|
//
|
||||||
// socks://a.b.c.d:e/f.g.h.i:j
|
// tcp://a.b.c.d:e
|
||||||
|
// socks://a.b.c.d:e/f.g.h.i:j
|
||||||
|
//
|
||||||
// This does not add the peer to the peer list, so if the connection drops, the
|
// This does not add the peer to the peer list, so if the connection drops, the
|
||||||
// peer will not be called again automatically.
|
// peer will not be called again automatically.
|
||||||
func (c *Core) CallPeer(u *url.URL, sintf string) error {
|
func (c *Core) CallPeer(u *url.URL, sintf string) error {
|
||||||
|
@ -239,62 +270,39 @@ func (c *Core) PublicKey() ed25519.PublicKey {
|
||||||
return c.public
|
return c.public
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) MaxMTU() uint64 {
|
|
||||||
return c.store.maxSessionMTU()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Core) SetMTU(mtu uint64) {
|
|
||||||
if mtu < 1280 {
|
|
||||||
mtu = 1280
|
|
||||||
}
|
|
||||||
c.store.mutex.Lock()
|
|
||||||
c.store.mtu = mtu
|
|
||||||
c.store.mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Core) MTU() uint64 {
|
|
||||||
c.store.mutex.Lock()
|
|
||||||
mtu := c.store.mtu
|
|
||||||
c.store.mutex.Unlock()
|
|
||||||
return mtu
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement io.ReadWriteCloser
|
|
||||||
|
|
||||||
func (c *Core) Read(p []byte) (n int, err error) {
|
|
||||||
n, err = c.store.readPC(p)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Core) Write(p []byte) (n int, err error) {
|
|
||||||
n, err = c.store.writePC(p)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Core) Close() error {
|
|
||||||
c.Stop()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hack to get the admin stuff working, TODO something cleaner
|
// Hack to get the admin stuff working, TODO something cleaner
|
||||||
|
|
||||||
type AddHandler interface {
|
type AddHandler interface {
|
||||||
AddHandler(name string, args []string, handlerfunc func(json.RawMessage) (interface{}, error)) error
|
AddHandler(name, desc string, args []string, handlerfunc AddHandlerFunc) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AddHandlerFunc func(json.RawMessage) (interface{}, error)
|
||||||
|
|
||||||
// SetAdmin must be called after Init and before Start.
|
// SetAdmin must be called after Init and before Start.
|
||||||
// It sets the admin handler for NodeInfo and the Debug admin functions.
|
// It sets the admin handler for NodeInfo and the Debug admin functions.
|
||||||
func (c *Core) SetAdmin(a AddHandler) error {
|
func (c *Core) SetAdmin(a AddHandler) error {
|
||||||
if err := a.AddHandler("getNodeInfo", []string{"key"}, c.proto.nodeinfo.nodeInfoAdminHandler); err != nil {
|
if err := a.AddHandler(
|
||||||
|
"getNodeInfo", "Request nodeinfo from a remote node by its public key", []string{"key"},
|
||||||
|
c.proto.nodeinfo.nodeInfoAdminHandler,
|
||||||
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := a.AddHandler("debug_remoteGetSelf", []string{"key"}, c.proto.getSelfHandler); err != nil {
|
if err := a.AddHandler(
|
||||||
|
"debug_remoteGetSelf", "Debug use only", []string{"key"},
|
||||||
|
c.proto.getSelfHandler,
|
||||||
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := a.AddHandler("debug_remoteGetPeers", []string{"key"}, c.proto.getPeersHandler); err != nil {
|
if err := a.AddHandler(
|
||||||
|
"debug_remoteGetPeers", "Debug use only", []string{"key"},
|
||||||
|
c.proto.getPeersHandler,
|
||||||
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := a.AddHandler("debug_remoteGetDHT", []string{"key"}, c.proto.getDHTHandler); err != nil {
|
if err := a.AddHandler(
|
||||||
|
"debug_remoteGetDHT", "Debug use only", []string{"key"},
|
||||||
|
c.proto.getDHTHandler,
|
||||||
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
244
src/core/core.go
244
src/core/core.go
|
@ -3,20 +3,20 @@ package core
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
iw "github.com/Arceliar/ironwood/encrypted"
|
iwe "github.com/Arceliar/ironwood/encrypted"
|
||||||
|
iwt "github.com/Arceliar/ironwood/types"
|
||||||
"github.com/Arceliar/phony"
|
"github.com/Arceliar/phony"
|
||||||
"github.com/gologme/log"
|
"github.com/gologme/log"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
||||||
|
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The Core object represents the Yggdrasil node. You should create a Core
|
// The Core object represents the Yggdrasil node. You should create a Core
|
||||||
|
@ -26,65 +26,87 @@ type Core struct {
|
||||||
// We're going to keep our own copy of the provided config - that way we can
|
// We're going to keep our own copy of the provided config - that way we can
|
||||||
// guarantee that it will be covered by the mutex
|
// guarantee that it will be covered by the mutex
|
||||||
phony.Inbox
|
phony.Inbox
|
||||||
pc *iw.PacketConn
|
*iwe.PacketConn
|
||||||
config *config.NodeConfig // Config
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
secret ed25519.PrivateKey
|
secret ed25519.PrivateKey
|
||||||
public ed25519.PublicKey
|
public ed25519.PublicKey
|
||||||
links links
|
links links
|
||||||
proto protoHandler
|
proto protoHandler
|
||||||
store keyStore
|
log util.Logger
|
||||||
log *log.Logger
|
|
||||||
addPeerTimer *time.Timer
|
addPeerTimer *time.Timer
|
||||||
ctx context.Context
|
config struct {
|
||||||
ctxCancel context.CancelFunc
|
_peers map[Peer]struct{} // configurable after startup
|
||||||
|
_listeners map[ListenAddress]struct{} // configurable after startup
|
||||||
|
nodeinfo NodeInfo // immutable after startup
|
||||||
|
nodeinfoPrivacy NodeInfoPrivacy // immutable after startup
|
||||||
|
_allowedPublicKeys map[[32]byte]struct{} // configurable after startup
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) _init() error {
|
func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*Core, error) {
|
||||||
// TODO separate init and start functions
|
c := &Core{
|
||||||
// Init sets up structs
|
log: logger,
|
||||||
// Start launches goroutines that depend on structs being set up
|
}
|
||||||
// This is pretty much required to completely avoid race conditions
|
if name := version.BuildName(); name != "unknown" {
|
||||||
c.config.RLock()
|
c.log.Infoln("Build name:", name)
|
||||||
defer c.config.RUnlock()
|
}
|
||||||
|
if version := version.BuildVersion(); version != "unknown" {
|
||||||
|
c.log.Infoln("Build version:", version)
|
||||||
|
}
|
||||||
|
c.ctx, c.cancel = context.WithCancel(context.Background())
|
||||||
|
// Take a copy of the private key so that it is in our own memory space.
|
||||||
|
if len(secret) != ed25519.PrivateKeySize {
|
||||||
|
return nil, fmt.Errorf("private key is incorrect length")
|
||||||
|
}
|
||||||
|
c.secret = make(ed25519.PrivateKey, ed25519.PrivateKeySize)
|
||||||
|
copy(c.secret, secret)
|
||||||
|
c.public = secret.Public().(ed25519.PublicKey)
|
||||||
|
var err error
|
||||||
|
if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating encryption: %w", err)
|
||||||
|
}
|
||||||
|
c.config._peers = map[Peer]struct{}{}
|
||||||
|
c.config._listeners = map[ListenAddress]struct{}{}
|
||||||
|
c.config._allowedPublicKeys = map[[32]byte]struct{}{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
c._applyOption(opt)
|
||||||
|
}
|
||||||
if c.log == nil {
|
if c.log == nil {
|
||||||
c.log = log.New(ioutil.Discard, "", 0)
|
c.log = log.New(io.Discard, "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
sigPriv, err := hex.DecodeString(c.config.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(sigPriv) < ed25519.PrivateKeySize {
|
|
||||||
return errors.New("PrivateKey is incorrect length")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.secret = ed25519.PrivateKey(sigPriv)
|
|
||||||
c.public = c.secret.Public().(ed25519.PublicKey)
|
|
||||||
// TODO check public against current.PublicKey, error if they don't match
|
|
||||||
|
|
||||||
c.pc, err = iw.NewPacketConn(c.secret)
|
|
||||||
c.ctx, c.ctxCancel = context.WithCancel(context.Background())
|
|
||||||
c.store.init(c)
|
|
||||||
c.proto.init(c)
|
c.proto.init(c)
|
||||||
if err := c.proto.nodeinfo.setNodeInfo(c.config.NodeInfo, c.config.NodeInfoPrivacy); err != nil {
|
if err := c.links.init(c); err != nil {
|
||||||
return fmt.Errorf("setNodeInfo: %w", err)
|
return nil, fmt.Errorf("error initialising links: %w", err)
|
||||||
}
|
}
|
||||||
return err
|
if err := c.proto.nodeinfo.setNodeInfo(c.config.nodeinfo, bool(c.config.nodeinfoPrivacy)); err != nil {
|
||||||
|
return nil, fmt.Errorf("error setting node info: %w", err)
|
||||||
|
}
|
||||||
|
for listenaddr := range c.config._listeners {
|
||||||
|
u, err := url.Parse(string(listenaddr))
|
||||||
|
if err != nil {
|
||||||
|
c.log.Errorf("Invalid listener URI %q specified, ignoring\n", listenaddr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err = c.links.listen(u, ""); err != nil {
|
||||||
|
c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Act(nil, c._addPeerLoop)
|
||||||
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any static peers were provided in the configuration above then we should
|
// If any static peers were provided in the configuration above then we should
|
||||||
// configure them. The loop ensures that disconnected peers will eventually
|
// configure them. The loop ensures that disconnected peers will eventually
|
||||||
// be reconnected with.
|
// be reconnected with.
|
||||||
func (c *Core) _addPeerLoop() {
|
func (c *Core) _addPeerLoop() {
|
||||||
c.config.RLock()
|
select {
|
||||||
defer c.config.RUnlock()
|
case <-c.ctx.Done():
|
||||||
|
|
||||||
if c.addPeerTimer == nil {
|
|
||||||
return
|
return
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add peers from the Peers section
|
// Add peers from the Peers section
|
||||||
for _, peer := range c.config.Peers {
|
for peer := range c.config._peers {
|
||||||
go func(peer string, intf string) {
|
go func(peer string, intf string) {
|
||||||
u, err := url.Parse(peer)
|
u, err := url.Parse(peer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -93,22 +115,7 @@ func (c *Core) _addPeerLoop() {
|
||||||
if err := c.CallPeer(u, intf); err != nil {
|
if err := c.CallPeer(u, intf); err != nil {
|
||||||
c.log.Errorln("Failed to add peer:", err)
|
c.log.Errorln("Failed to add peer:", err)
|
||||||
}
|
}
|
||||||
}(peer, "") // TODO: this should be acted and not in a goroutine?
|
}(peer.URI, peer.SourceInterface) // TODO: this should be acted and not in a goroutine?
|
||||||
}
|
|
||||||
|
|
||||||
// Add peers from the InterfacePeers section
|
|
||||||
for intf, intfpeers := range c.config.InterfacePeers {
|
|
||||||
for _, peer := range intfpeers {
|
|
||||||
go func(peer string, intf string) {
|
|
||||||
u, err := url.Parse(peer)
|
|
||||||
if err != nil {
|
|
||||||
c.log.Errorln("Failed to parse peer url:", peer, err)
|
|
||||||
}
|
|
||||||
if err := c.CallPeer(u, intf); err != nil {
|
|
||||||
c.log.Errorln("Failed to add peer:", err)
|
|
||||||
}
|
|
||||||
}(peer, intf) // TODO: this should be acted and not in a goroutine?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
|
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
|
||||||
|
@ -116,68 +123,73 @@ func (c *Core) _addPeerLoop() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts up Yggdrasil using the provided config.NodeConfig, and outputs
|
|
||||||
// debug logging through the provided log.Logger. The started stack will include
|
|
||||||
// TCP and UDP sockets, a multicast discovery socket, an admin socket, router,
|
|
||||||
// switch and DHT node. A config.NodeState is returned which contains both the
|
|
||||||
// current and previous configurations (from reconfigures).
|
|
||||||
func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (err error) {
|
|
||||||
phony.Block(c, func() {
|
|
||||||
err = c._start(nc, log)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function is unsafe and should only be ran by the core actor.
|
|
||||||
func (c *Core) _start(nc *config.NodeConfig, log *log.Logger) error {
|
|
||||||
c.log = log
|
|
||||||
c.config = nc
|
|
||||||
|
|
||||||
if name := version.BuildName(); name != "unknown" {
|
|
||||||
c.log.Infoln("Build name:", name)
|
|
||||||
}
|
|
||||||
if version := version.BuildVersion(); version != "unknown" {
|
|
||||||
c.log.Infoln("Build version:", version)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.log.Infoln("Starting up...")
|
|
||||||
if err := c._init(); err != nil {
|
|
||||||
c.log.Errorln("Failed to initialize core")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.links.init(c); err != nil {
|
|
||||||
c.log.Errorln("Failed to start link interfaces")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.addPeerTimer = time.AfterFunc(0, func() {
|
|
||||||
c.Act(nil, c._addPeerLoop)
|
|
||||||
})
|
|
||||||
|
|
||||||
c.log.Infoln("Startup complete")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop shuts down the Yggdrasil node.
|
// Stop shuts down the Yggdrasil node.
|
||||||
func (c *Core) Stop() {
|
func (c *Core) Stop() {
|
||||||
phony.Block(c, c._stop)
|
phony.Block(c, func() {
|
||||||
|
c.log.Infoln("Stopping...")
|
||||||
|
c._close()
|
||||||
|
c.log.Infoln("Stopped")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is unsafe and should only be ran by the core actor.
|
// This function is unsafe and should only be ran by the core actor.
|
||||||
func (c *Core) _stop() {
|
func (c *Core) _close() error {
|
||||||
c.log.Infoln("Stopping...")
|
c.cancel()
|
||||||
c.ctxCancel()
|
_ = c.links.shutdown()
|
||||||
c.pc.Close()
|
err := c.PacketConn.Close()
|
||||||
if c.addPeerTimer != nil {
|
if c.addPeerTimer != nil {
|
||||||
c.addPeerTimer.Stop()
|
c.addPeerTimer.Stop()
|
||||||
c.addPeerTimer = nil
|
c.addPeerTimer = nil
|
||||||
}
|
}
|
||||||
_ = c.links.stop()
|
return err
|
||||||
/* FIXME this deadlocks, need a waitgroup or something to coordinate shutdown
|
}
|
||||||
for _, peer := range c.GetPeers() {
|
|
||||||
c.DisconnectPeer(peer.Port)
|
func (c *Core) MTU() uint64 {
|
||||||
}
|
const sessionTypeOverhead = 1
|
||||||
*/
|
return c.PacketConn.MTU() - sessionTypeOverhead
|
||||||
c.log.Infoln("Stopped")
|
}
|
||||||
|
|
||||||
|
func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) {
|
||||||
|
buf := make([]byte, c.PacketConn.MTU(), 65535)
|
||||||
|
for {
|
||||||
|
bs := buf
|
||||||
|
n, from, err = c.PacketConn.ReadFrom(bs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, from, err
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch bs[0] {
|
||||||
|
case typeSessionTraffic:
|
||||||
|
// This is what we want to handle here
|
||||||
|
case typeSessionProto:
|
||||||
|
var key keyArray
|
||||||
|
copy(key[:], from.(iwt.Addr))
|
||||||
|
data := append([]byte(nil), bs[1:n]...)
|
||||||
|
c.proto.handleProto(nil, key, data)
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bs = bs[1:n]
|
||||||
|
copy(p, bs)
|
||||||
|
if len(p) < len(bs) {
|
||||||
|
n = len(p)
|
||||||
|
} else {
|
||||||
|
n = len(bs)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
buf := make([]byte, 0, 65535)
|
||||||
|
buf = append(buf, typeSessionTraffic)
|
||||||
|
buf = append(buf, p...)
|
||||||
|
n, err = c.PacketConn.WriteTo(buf, addr)
|
||||||
|
if n > 0 {
|
||||||
|
n -= 1
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ed25519"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -9,21 +10,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gologme/log"
|
"github.com/gologme/log"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateConfig produces default configuration with suitable modifications for tests.
|
|
||||||
func GenerateConfig() *config.NodeConfig {
|
|
||||||
cfg := defaults.GenerateConfig()
|
|
||||||
cfg.AdminListen = "none"
|
|
||||||
cfg.Listen = []string{"tcp://127.0.0.1:0"}
|
|
||||||
cfg.IfName = "none"
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLoggerWithPrefix creates a new logger instance with prefix.
|
// GetLoggerWithPrefix creates a new logger instance with prefix.
|
||||||
// If verbose is set to true, three log levels are enabled: "info", "warn", "error".
|
// If verbose is set to true, three log levels are enabled: "info", "warn", "error".
|
||||||
func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger {
|
func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger {
|
||||||
|
@ -40,17 +28,21 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger {
|
||||||
// CreateAndConnectTwo creates two nodes. nodeB connects to nodeA.
|
// CreateAndConnectTwo creates two nodes. nodeB connects to nodeA.
|
||||||
// Verbosity flag is passed to logger.
|
// Verbosity flag is passed to logger.
|
||||||
func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) {
|
func CreateAndConnectTwo(t testing.TB, verbose bool) (nodeA *Core, nodeB *Core) {
|
||||||
nodeA = new(Core)
|
var err error
|
||||||
if err := nodeA.Start(GenerateConfig(), GetLoggerWithPrefix("A: ", verbose)); err != nil {
|
var skA, skB ed25519.PrivateKey
|
||||||
|
if _, skA, err = ed25519.GenerateKey(nil); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
nodeA.SetMTU(1500)
|
if _, skB, err = ed25519.GenerateKey(nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
nodeB = new(Core)
|
}
|
||||||
if err := nodeB.Start(GenerateConfig(), GetLoggerWithPrefix("B: ", verbose)); err != nil {
|
logger := GetLoggerWithPrefix("", false)
|
||||||
|
if nodeA, err = New(skA, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if nodeB, err = New(skB, logger, ListenAddress("tcp://127.0.0.1:0")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
nodeB.SetMTU(1500)
|
|
||||||
|
|
||||||
u, err := url.Parse("tcp://" + nodeA.links.tcp.getAddr().String())
|
u, err := url.Parse("tcp://" + nodeA.links.tcp.getAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -94,7 +86,7 @@ func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan
|
||||||
buf := make([]byte, bufLen)
|
buf := make([]byte, bufLen)
|
||||||
res := make([]byte, bufLen)
|
res := make([]byte, bufLen)
|
||||||
for i := 0; i < repeats; i++ {
|
for i := 0; i < repeats; i++ {
|
||||||
n, err := nodeA.Read(buf)
|
n, from, err := nodeA.ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -106,7 +98,7 @@ func CreateEchoListener(t testing.TB, nodeA *Core, bufLen int, repeats int) chan
|
||||||
copy(res, buf)
|
copy(res, buf)
|
||||||
copy(res[8:24], buf[24:40])
|
copy(res[8:24], buf[24:40])
|
||||||
copy(res[24:40], buf[8:24])
|
copy(res[24:40], buf[8:24])
|
||||||
_, err = nodeA.Write(res)
|
_, err = nodeA.WriteTo(res, from)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -141,12 +133,12 @@ func TestCore_Start_Transfer(t *testing.T) {
|
||||||
msg[0] = 0x60
|
msg[0] = 0x60
|
||||||
copy(msg[8:24], nodeB.Address())
|
copy(msg[8:24], nodeB.Address())
|
||||||
copy(msg[24:40], nodeA.Address())
|
copy(msg[24:40], nodeA.Address())
|
||||||
_, err := nodeB.Write(msg)
|
_, err := nodeB.WriteTo(msg, nodeA.LocalAddr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
buf := make([]byte, msgLen)
|
buf := make([]byte, msgLen)
|
||||||
_, err = nodeB.Read(buf)
|
_, _, err = nodeB.ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -179,12 +171,13 @@ func BenchmarkCore_Start_Transfer(b *testing.B) {
|
||||||
b.SetBytes(int64(msgLen))
|
b.SetBytes(int64(msgLen))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
|
addr := nodeA.LocalAddr()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := nodeB.Write(msg)
|
_, err := nodeB.WriteTo(msg, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = nodeB.Read(buf)
|
_, _, err = nodeB.ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
|
//go:build debug
|
||||||
// +build debug
|
// +build debug
|
||||||
|
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
import _ "net/http/pprof"
|
"net/http"
|
||||||
import "net/http"
|
_ "net/http/pprof"
|
||||||
import "runtime"
|
"os"
|
||||||
import "os"
|
"runtime"
|
||||||
|
|
||||||
import "github.com/gologme/log"
|
"github.com/gologme/log"
|
||||||
|
)
|
||||||
|
|
||||||
// Start the profiler in debug builds, if the required environment variable is set.
|
// Start the profiler in debug builds, if the required environment variable is set.
|
||||||
func init() {
|
func init() {
|
||||||
|
|
352
src/core/link.go
352
src/core/link.go
|
@ -1,7 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -9,29 +9,31 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
//"sync/atomic"
|
//"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/Arceliar/phony"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
"golang.org/x/net/proxy"
|
|
||||||
//"github.com/Arceliar/phony" // TODO? use instead of mutexes
|
//"github.com/Arceliar/phony" // TODO? use instead of mutexes
|
||||||
)
|
)
|
||||||
|
|
||||||
type links struct {
|
type links struct {
|
||||||
core *Core
|
phony.Inbox
|
||||||
mutex sync.RWMutex // protects links below
|
core *Core
|
||||||
links map[linkInfo]*link
|
tcp *linkTCP // TCP interface support
|
||||||
tcp tcp // TCP interface support
|
tls *linkTLS // TLS interface support
|
||||||
stopped chan struct{}
|
unix *linkUNIX // UNIX interface support
|
||||||
|
socks *linkSOCKS // SOCKS interface support
|
||||||
|
_links map[linkInfo]*link // *link is nil if connection in progress
|
||||||
// TODO timeout (to remove from switch), read from config.ReadTimeout
|
// TODO timeout (to remove from switch), read from config.ReadTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
// linkInfo is used as a map key
|
// linkInfo is used as a map key
|
||||||
type linkInfo struct {
|
type linkInfo struct {
|
||||||
key keyArray
|
|
||||||
linkType string // Type of link, e.g. TCP, AWDL
|
linkType string // Type of link, e.g. TCP, AWDL
|
||||||
local string // Local name or address
|
local string // Local name or address
|
||||||
remote string // Remote name or address
|
remote string // Remote name or address
|
||||||
|
@ -40,100 +42,199 @@ type linkInfo struct {
|
||||||
type link struct {
|
type link struct {
|
||||||
lname string
|
lname string
|
||||||
links *links
|
links *links
|
||||||
conn net.Conn
|
conn *linkConn
|
||||||
options linkOptions
|
options linkOptions
|
||||||
info linkInfo
|
info linkInfo
|
||||||
incoming bool
|
incoming bool
|
||||||
force bool
|
force bool
|
||||||
closed chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type linkOptions struct {
|
type linkOptions struct {
|
||||||
pinnedEd25519Keys map[keyArray]struct{}
|
pinnedEd25519Keys map[keyArray]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Listener struct {
|
||||||
|
net.Listener
|
||||||
|
closed chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
err := l.Listener.Close()
|
||||||
|
<-l.closed
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (l *links) init(c *Core) error {
|
func (l *links) init(c *Core) error {
|
||||||
l.core = c
|
l.core = c
|
||||||
l.mutex.Lock()
|
l.tcp = l.newLinkTCP()
|
||||||
l.links = make(map[linkInfo]*link)
|
l.tls = l.newLinkTLS(l.tcp)
|
||||||
l.mutex.Unlock()
|
l.unix = l.newLinkUNIX()
|
||||||
l.stopped = make(chan struct{})
|
l.socks = l.newLinkSOCKS()
|
||||||
|
l._links = make(map[linkInfo]*link)
|
||||||
|
|
||||||
if err := l.tcp.init(l); err != nil {
|
var listeners []ListenAddress
|
||||||
c.log.Errorln("Failed to start TCP interface")
|
phony.Block(c, func() {
|
||||||
return err
|
listeners = make([]ListenAddress, 0, len(c.config._listeners))
|
||||||
}
|
for listener := range c.config._listeners {
|
||||||
|
listeners = append(listeners, listener)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *links) shutdown() error {
|
||||||
|
phony.Block(l.tcp, func() {
|
||||||
|
for l := range l.tcp._listeners {
|
||||||
|
l.Close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
phony.Block(l.tls, func() {
|
||||||
|
for l := range l.tls._listeners {
|
||||||
|
l.Close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
phony.Block(l.unix, func() {
|
||||||
|
for l := range l.unix._listeners {
|
||||||
|
l.Close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *links) isConnectedTo(info linkInfo) bool {
|
||||||
|
var isConnected bool
|
||||||
|
phony.Block(l, func() {
|
||||||
|
_, isConnected = l._links[info]
|
||||||
|
})
|
||||||
|
return isConnected
|
||||||
|
}
|
||||||
|
|
||||||
func (l *links) call(u *url.URL, sintf string) error {
|
func (l *links) call(u *url.URL, sintf string) error {
|
||||||
//u, err := url.Parse(uri)
|
info := linkInfoFor(u.Scheme, sintf, u.Host)
|
||||||
//if err != nil {
|
if l.isConnectedTo(info) {
|
||||||
// return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err)
|
return nil
|
||||||
//}
|
}
|
||||||
tcpOpts := tcpOptions{}
|
options := linkOptions{
|
||||||
if pubkeys, ok := u.Query()["key"]; ok && len(pubkeys) > 0 {
|
pinnedEd25519Keys: map[keyArray]struct{}{},
|
||||||
tcpOpts.pinnedEd25519Keys = make(map[keyArray]struct{})
|
}
|
||||||
for _, pubkey := range pubkeys {
|
for _, pubkey := range u.Query()["key"] {
|
||||||
if sigPub, err := hex.DecodeString(pubkey); err == nil {
|
sigPub, err := hex.DecodeString(pubkey)
|
||||||
var sigPubKey keyArray
|
if err != nil {
|
||||||
copy(sigPubKey[:], sigPub)
|
return fmt.Errorf("pinned key contains invalid hex characters")
|
||||||
tcpOpts.pinnedEd25519Keys[sigPubKey] = struct{}{}
|
}
|
||||||
|
var sigPubKey keyArray
|
||||||
|
copy(sigPubKey[:], sigPub)
|
||||||
|
options.pinnedEd25519Keys[sigPubKey] = struct{}{}
|
||||||
|
}
|
||||||
|
switch info.linkType {
|
||||||
|
case "tcp":
|
||||||
|
go func() {
|
||||||
|
if err := l.tcp.dial(u, options, sintf); err != nil {
|
||||||
|
l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "socks":
|
||||||
|
go func() {
|
||||||
|
if err := l.socks.dial(u, options); err != nil {
|
||||||
|
l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
case "tls":
|
||||||
|
// SNI headers must contain hostnames and not IP addresses, so we must make sure
|
||||||
|
// that we do not populate the SNI with an IP literal. We do this by splitting
|
||||||
|
// the host-port combo from the query option and then seeing if it parses to an
|
||||||
|
// IP address successfully or not.
|
||||||
|
var tlsSNI string
|
||||||
|
if sni := u.Query().Get("sni"); sni != "" {
|
||||||
|
if net.ParseIP(sni) == nil {
|
||||||
|
tlsSNI = sni
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// If the SNI is not configured still because the above failed then we'll try
|
||||||
switch u.Scheme {
|
// again but this time we'll use the host part of the peering URI instead.
|
||||||
case "tcp":
|
if tlsSNI == "" {
|
||||||
l.tcp.call(u.Host, tcpOpts, sintf)
|
if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil {
|
||||||
case "socks":
|
tlsSNI = host
|
||||||
tcpOpts.socksProxyAddr = u.Host
|
}
|
||||||
if u.User != nil {
|
|
||||||
tcpOpts.socksProxyAuth = &proxy.Auth{}
|
|
||||||
tcpOpts.socksProxyAuth.User = u.User.Username()
|
|
||||||
tcpOpts.socksProxyAuth.Password, _ = u.User.Password()
|
|
||||||
}
|
}
|
||||||
tcpOpts.upgrade = l.tcp.tls.forDialer // TODO make this configurable
|
go func() {
|
||||||
pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/")
|
if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil {
|
||||||
l.tcp.call(pathtokens[0], tcpOpts, sintf)
|
l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err)
|
||||||
case "tls":
|
}
|
||||||
tcpOpts.upgrade = l.tcp.tls.forDialer
|
}()
|
||||||
l.tcp.call(u.Host, tcpOpts, sintf)
|
|
||||||
|
case "unix":
|
||||||
|
go func() {
|
||||||
|
if err := l.unix.dial(u, options, sintf); err != nil {
|
||||||
|
l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.New("unknown call scheme: " + u.Scheme)
|
return errors.New("unknown call scheme: " + u.Scheme)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *links) create(conn net.Conn, name, linkType, local, remote string, incoming, force bool, options linkOptions) (*link, error) {
|
func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||||
// Technically anything unique would work for names, but let's pick something human readable, just for debugging
|
var listener *Listener
|
||||||
|
var err error
|
||||||
|
switch u.Scheme {
|
||||||
|
case "tcp":
|
||||||
|
listener, err = l.tcp.listen(u, sintf)
|
||||||
|
case "tls":
|
||||||
|
listener, err = l.tls.listen(u, sintf)
|
||||||
|
case "unix":
|
||||||
|
listener, err = l.unix.listen(u, sintf)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unrecognised scheme %q", u.Scheme)
|
||||||
|
}
|
||||||
|
return listener, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *links) create(conn net.Conn, name string, info linkInfo, incoming, force bool, options linkOptions) error {
|
||||||
intf := link{
|
intf := link{
|
||||||
conn: conn,
|
conn: &linkConn{
|
||||||
lname: name,
|
Conn: conn,
|
||||||
links: l,
|
up: time.Now(),
|
||||||
options: options,
|
|
||||||
info: linkInfo{
|
|
||||||
linkType: linkType,
|
|
||||||
local: local,
|
|
||||||
remote: remote,
|
|
||||||
},
|
},
|
||||||
|
lname: name,
|
||||||
|
links: l,
|
||||||
|
options: options,
|
||||||
|
info: info,
|
||||||
incoming: incoming,
|
incoming: incoming,
|
||||||
force: force,
|
force: force,
|
||||||
}
|
}
|
||||||
return &intf, nil
|
go func() {
|
||||||
}
|
if err := intf.handler(); err != nil {
|
||||||
|
l.core.log.Errorf("Link handler %s error (%s): %s", name, conn.RemoteAddr(), err)
|
||||||
func (l *links) stop() error {
|
}
|
||||||
close(l.stopped)
|
}()
|
||||||
if err := l.tcp.stop(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (intf *link) handler() (chan struct{}, error) {
|
func (intf *link) handler() error {
|
||||||
// TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later
|
|
||||||
defer intf.conn.Close()
|
defer intf.conn.Close()
|
||||||
|
|
||||||
|
// Don't connect to this link more than once.
|
||||||
|
if intf.links.isConnectedTo(intf.info) {
|
||||||
|
return fmt.Errorf("already connected to this node")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the connection as in progress.
|
||||||
|
phony.Block(intf.links, func() {
|
||||||
|
intf.links._links[intf.info] = nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// When we're done, clean up the connection entry.
|
||||||
|
defer phony.Block(intf.links, func() {
|
||||||
|
delete(intf.links._links, intf.info)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later
|
||||||
meta := version_getBaseMetadata()
|
meta := version_getBaseMetadata()
|
||||||
meta.key = intf.links.core.public
|
meta.key = intf.links.core.public
|
||||||
metaBytes := meta.encode()
|
metaBytes := meta.encode()
|
||||||
|
@ -146,10 +247,10 @@ func (intf *link) handler() (chan struct{}, error) {
|
||||||
err = errors.New("incomplete metadata send")
|
err = errors.New("incomplete metadata send")
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
return nil, errors.New("timeout on metadata send")
|
return errors.New("timeout on metadata send")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return fmt.Errorf("write handshake: %w", err)
|
||||||
}
|
}
|
||||||
if !util.FuncTimeout(30*time.Second, func() {
|
if !util.FuncTimeout(30*time.Second, func() {
|
||||||
var n int
|
var n int
|
||||||
|
@ -158,41 +259,46 @@ func (intf *link) handler() (chan struct{}, error) {
|
||||||
err = errors.New("incomplete metadata recv")
|
err = errors.New("incomplete metadata recv")
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
return nil, errors.New("timeout on metadata recv")
|
return errors.New("timeout on metadata recv")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return fmt.Errorf("read handshake: %w", err)
|
||||||
}
|
}
|
||||||
meta = version_metadata{}
|
meta = version_metadata{}
|
||||||
base := version_getBaseMetadata()
|
base := version_getBaseMetadata()
|
||||||
if !meta.decode(metaBytes) {
|
if !meta.decode(metaBytes) {
|
||||||
return nil, errors.New("failed to decode metadata")
|
return errors.New("failed to decode metadata")
|
||||||
}
|
}
|
||||||
if !meta.check() {
|
if !meta.check() {
|
||||||
intf.links.core.log.Errorf("Failed to connect to node: %s is incompatible version (local %s, remote %s)",
|
var connectError string
|
||||||
|
if intf.incoming {
|
||||||
|
connectError = "Rejected incoming connection"
|
||||||
|
} else {
|
||||||
|
connectError = "Failed to connect"
|
||||||
|
}
|
||||||
|
intf.links.core.log.Debugf("%s: %s is incompatible version (local %s, remote %s)",
|
||||||
|
connectError,
|
||||||
intf.lname,
|
intf.lname,
|
||||||
fmt.Sprintf("%d.%d", base.ver, base.minorVer),
|
fmt.Sprintf("%d.%d", base.ver, base.minorVer),
|
||||||
fmt.Sprintf("%d.%d", meta.ver, meta.minorVer),
|
fmt.Sprintf("%d.%d", meta.ver, meta.minorVer),
|
||||||
)
|
)
|
||||||
return nil, errors.New("remote node is incompatible version")
|
return errors.New("remote node is incompatible version")
|
||||||
}
|
}
|
||||||
// Check if the remote side matches the keys we expected. This is a bit of a weak
|
// Check if the remote side matches the keys we expected. This is a bit of a weak
|
||||||
// check - in future versions we really should check a signature or something like that.
|
// check - in future versions we really should check a signature or something like that.
|
||||||
if pinned := intf.options.pinnedEd25519Keys; pinned != nil {
|
if pinned := intf.options.pinnedEd25519Keys; len(pinned) > 0 {
|
||||||
var key keyArray
|
var key keyArray
|
||||||
copy(key[:], meta.key)
|
copy(key[:], meta.key)
|
||||||
if _, allowed := pinned[key]; !allowed {
|
if _, allowed := pinned[key]; !allowed {
|
||||||
intf.links.core.log.Errorf("Failed to connect to node: %q sent ed25519 key that does not match pinned keys", intf.name())
|
intf.links.core.log.Errorf("Failed to connect to node: %q sent ed25519 key that does not match pinned keys", intf.name())
|
||||||
return nil, fmt.Errorf("failed to connect: host sent ed25519 key that does not match pinned keys")
|
return fmt.Errorf("failed to connect: host sent ed25519 key that does not match pinned keys")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if we're authorized to connect to this key / IP
|
// Check if we're authorized to connect to this key / IP
|
||||||
intf.links.core.config.RLock()
|
allowed := intf.links.core.config._allowedPublicKeys
|
||||||
allowed := intf.links.core.config.AllowedPublicKeys
|
|
||||||
intf.links.core.config.RUnlock()
|
|
||||||
isallowed := len(allowed) == 0
|
isallowed := len(allowed) == 0
|
||||||
for _, k := range allowed {
|
for k := range allowed {
|
||||||
if k == hex.EncodeToString(meta.key) { // TODO: this is yuck
|
if bytes.Equal(k[:], meta.key) {
|
||||||
isallowed = true
|
isallowed = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -201,51 +307,67 @@ func (intf *link) handler() (chan struct{}, error) {
|
||||||
intf.links.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s",
|
intf.links.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s",
|
||||||
strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.key))
|
strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.key))
|
||||||
intf.close()
|
intf.close()
|
||||||
return nil, nil
|
return fmt.Errorf("forbidden connection")
|
||||||
}
|
}
|
||||||
// Check if we already have a link to this node
|
|
||||||
copy(intf.info.key[:], meta.key)
|
phony.Block(intf.links, func() {
|
||||||
intf.links.mutex.Lock()
|
intf.links._links[intf.info] = intf
|
||||||
if oldIntf, isIn := intf.links.links[intf.info]; isIn {
|
})
|
||||||
intf.links.mutex.Unlock()
|
|
||||||
// FIXME we should really return an error and let the caller block instead
|
remoteAddr := net.IP(address.AddrForKey(meta.key)[:]).String()
|
||||||
// That lets them do things like close connections on its own, avoid printing a connection message in the first place, etc.
|
remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote)
|
||||||
intf.links.core.log.Debugln("DEBUG: found existing interface for", intf.name())
|
localStr := intf.conn.LocalAddr()
|
||||||
return oldIntf.closed, nil
|
|
||||||
} else {
|
|
||||||
intf.closed = make(chan struct{})
|
|
||||||
intf.links.links[intf.info] = intf
|
|
||||||
defer func() {
|
|
||||||
intf.links.mutex.Lock()
|
|
||||||
delete(intf.links.links, intf.info)
|
|
||||||
intf.links.mutex.Unlock()
|
|
||||||
close(intf.closed)
|
|
||||||
}()
|
|
||||||
intf.links.core.log.Debugln("DEBUG: registered interface for", intf.name())
|
|
||||||
}
|
|
||||||
intf.links.mutex.Unlock()
|
|
||||||
themAddr := address.AddrForKey(ed25519.PublicKey(intf.info.key[:]))
|
|
||||||
themAddrString := net.IP(themAddr[:]).String()
|
|
||||||
themString := fmt.Sprintf("%s@%s", themAddrString, intf.info.remote)
|
|
||||||
intf.links.core.log.Infof("Connected %s: %s, source %s",
|
intf.links.core.log.Infof("Connected %s: %s, source %s",
|
||||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local)
|
strings.ToUpper(intf.info.linkType), remoteStr, localStr)
|
||||||
// Run the handler
|
|
||||||
err = intf.links.core.pc.HandleConn(ed25519.PublicKey(intf.info.key[:]), intf.conn)
|
|
||||||
// TODO don't report an error if it's just a 'use of closed network connection'
|
// TODO don't report an error if it's just a 'use of closed network connection'
|
||||||
if err != nil {
|
if err = intf.links.core.HandleConn(meta.key, intf.conn); err != nil && err != io.EOF {
|
||||||
intf.links.core.log.Infof("Disconnected %s: %s, source %s; error: %s",
|
intf.links.core.log.Infof("Disconnected %s: %s, source %s; error: %s",
|
||||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local, err)
|
strings.ToUpper(intf.info.linkType), remoteStr, localStr, err)
|
||||||
} else {
|
} else {
|
||||||
intf.links.core.log.Infof("Disconnected %s: %s, source %s",
|
intf.links.core.log.Infof("Disconnected %s: %s, source %s",
|
||||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local)
|
strings.ToUpper(intf.info.linkType), remoteStr, localStr)
|
||||||
}
|
}
|
||||||
return nil, err
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (intf *link) close() {
|
func (intf *link) close() error {
|
||||||
intf.conn.Close()
|
return intf.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (intf *link) name() string {
|
func (intf *link) name() string {
|
||||||
return intf.lname
|
return intf.lname
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func linkInfoFor(linkType, sintf, remote string) linkInfo {
|
||||||
|
if h, _, err := net.SplitHostPort(remote); err == nil {
|
||||||
|
remote = h
|
||||||
|
}
|
||||||
|
return linkInfo{
|
||||||
|
linkType: linkType,
|
||||||
|
local: sintf,
|
||||||
|
remote: remote,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type linkConn struct {
|
||||||
|
// tx and rx are at the beginning of the struct to ensure 64-bit alignment
|
||||||
|
// on 32-bit platforms, see https://pkg.go.dev/sync/atomic#pkg-note-BUG
|
||||||
|
rx uint64
|
||||||
|
tx uint64
|
||||||
|
up time.Time
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linkConn) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = c.Conn.Read(p)
|
||||||
|
atomic.AddUint64(&c.rx, uint64(n))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *linkConn) Write(p []byte) (n int, err error) {
|
||||||
|
n, err = c.Conn.Write(p)
|
||||||
|
atomic.AddUint64(&c.tx, uint64(n))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
52
src/core/link_socks.go
Normal file
52
src/core/link_socks.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
type linkSOCKS struct {
|
||||||
|
*links
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *links) newLinkSOCKS() *linkSOCKS {
|
||||||
|
lt := &linkSOCKS{
|
||||||
|
links: l,
|
||||||
|
}
|
||||||
|
return lt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkSOCKS) dial(url *url.URL, options linkOptions) error {
|
||||||
|
info := linkInfoFor("socks", "", url.Path)
|
||||||
|
if l.links.isConnectedTo(info) {
|
||||||
|
return fmt.Errorf("duplicate connection attempt")
|
||||||
|
}
|
||||||
|
proxyAuth := &proxy.Auth{}
|
||||||
|
proxyAuth.User = url.User.Username()
|
||||||
|
proxyAuth.Password, _ = url.User.Password()
|
||||||
|
dialer, err := proxy.SOCKS5("tcp", url.Host, proxyAuth, proxy.Direct)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to configure proxy")
|
||||||
|
}
|
||||||
|
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
|
||||||
|
conn, err := dialer.Dial("tcp", pathtokens[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return l.handler(url.String(), info, conn, options, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkSOCKS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error {
|
||||||
|
return l.links.create(
|
||||||
|
conn, // connection
|
||||||
|
name, // connection name
|
||||||
|
info, // connection info
|
||||||
|
incoming, // not incoming
|
||||||
|
false, // not forced
|
||||||
|
options, // connection options
|
||||||
|
)
|
||||||
|
}
|
183
src/core/link_tcp.go
Normal file
183
src/core/link_tcp.go
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Arceliar/phony"
|
||||||
|
)
|
||||||
|
|
||||||
|
type linkTCP struct {
|
||||||
|
phony.Inbox
|
||||||
|
*links
|
||||||
|
listener *net.ListenConfig
|
||||||
|
_listeners map[*Listener]context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *links) newLinkTCP() *linkTCP {
|
||||||
|
lt := &linkTCP{
|
||||||
|
links: l,
|
||||||
|
listener: &net.ListenConfig{
|
||||||
|
KeepAlive: -1,
|
||||||
|
},
|
||||||
|
_listeners: map[*Listener]context.CancelFunc{},
|
||||||
|
}
|
||||||
|
lt.listener.Control = lt.tcpContext
|
||||||
|
return lt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error {
|
||||||
|
info := linkInfoFor("tcp", sintf, strings.SplitN(url.Host, "%", 2)[0])
|
||||||
|
if l.links.isConnectedTo(info) {
|
||||||
|
return fmt.Errorf("duplicate connection attempt")
|
||||||
|
}
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", url.Host)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
addr.Zone = sintf
|
||||||
|
dialer, err := l.dialerFor(addr.String(), sintf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return l.handler(url.String(), info, conn, options, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) {
|
||||||
|
ctx, cancel := context.WithCancel(l.core.ctx)
|
||||||
|
hostport := url.Host
|
||||||
|
if sintf != "" {
|
||||||
|
if host, port, err := net.SplitHostPort(hostport); err == nil {
|
||||||
|
hostport = fmt.Sprintf("[%s%%%s]:%s", host, sintf, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener, err := l.listener.Listen(ctx, "tcp", hostport)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
entry := &Listener{
|
||||||
|
Listener: listener,
|
||||||
|
closed: make(chan struct{}),
|
||||||
|
}
|
||||||
|
phony.Block(l, func() {
|
||||||
|
l._listeners[entry] = cancel
|
||||||
|
})
|
||||||
|
l.core.log.Printf("TCP listener started on %s", listener.Addr())
|
||||||
|
go func() {
|
||||||
|
defer phony.Block(l, func() {
|
||||||
|
delete(l._listeners, entry)
|
||||||
|
})
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
addr := conn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
name := fmt.Sprintf("tls://%s", addr)
|
||||||
|
info := linkInfoFor("tcp", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0])
|
||||||
|
if err = l.handler(name, info, conn, linkOptions{}, true); err != nil {
|
||||||
|
l.core.log.Errorln("Failed to create inbound link:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.Close()
|
||||||
|
close(entry.closed)
|
||||||
|
l.core.log.Printf("TCP listener stopped on %s", listener.Addr())
|
||||||
|
}()
|
||||||
|
return entry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error {
|
||||||
|
return l.links.create(
|
||||||
|
conn, // connection
|
||||||
|
name, // connection name
|
||||||
|
info, // connection info
|
||||||
|
incoming, // not incoming
|
||||||
|
false, // not forced
|
||||||
|
options, // connection options
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the address of the listener.
|
||||||
|
func (l *linkTCP) getAddr() *net.TCPAddr {
|
||||||
|
// TODO: Fix this, because this will currently only give a single address
|
||||||
|
// to multicast.go, which obviously is not great, but right now multicast.go
|
||||||
|
// doesn't have the ability to send more than one address in a packet either
|
||||||
|
var addr *net.TCPAddr
|
||||||
|
phony.Block(l, func() {
|
||||||
|
for listener := range l._listeners {
|
||||||
|
addr = listener.Addr().(*net.TCPAddr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTCP) dialerFor(saddr, sintf string) (*net.Dialer, error) {
|
||||||
|
dst, err := net.ResolveTCPAddr("tcp", saddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dst.IP.IsLinkLocalUnicast() {
|
||||||
|
dst.Zone = sintf
|
||||||
|
if dst.Zone == "" {
|
||||||
|
return nil, fmt.Errorf("link-local address requires a zone")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialer := &net.Dialer{
|
||||||
|
Timeout: time.Second * 5,
|
||||||
|
KeepAlive: -1,
|
||||||
|
Control: l.tcpContext,
|
||||||
|
}
|
||||||
|
if sintf != "" {
|
||||||
|
dialer.Control = l.getControl(sintf)
|
||||||
|
ief, err := net.InterfaceByName(sintf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("interface %q not found", sintf)
|
||||||
|
}
|
||||||
|
if ief.Flags&net.FlagUp == 0 {
|
||||||
|
return nil, fmt.Errorf("interface %q is not up", sintf)
|
||||||
|
}
|
||||||
|
addrs, err := ief.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("interface %q addresses not available: %w", sintf, err)
|
||||||
|
}
|
||||||
|
for addrindex, addr := range addrs {
|
||||||
|
src, _, err := net.ParseCIDR(addr.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !src.IsGlobalUnicast() && !src.IsLinkLocalUnicast() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bothglobal := src.IsGlobalUnicast() == dst.IP.IsGlobalUnicast()
|
||||||
|
bothlinklocal := src.IsLinkLocalUnicast() == dst.IP.IsLinkLocalUnicast()
|
||||||
|
if !bothglobal && !bothlinklocal {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (src.To4() != nil) != (dst.IP.To4() != nil) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if bothglobal || bothlinklocal || addrindex == len(addrs)-1 {
|
||||||
|
dialer.LocalAddr = &net.TCPAddr{
|
||||||
|
IP: src,
|
||||||
|
Port: 0,
|
||||||
|
Zone: sintf,
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dialer.LocalAddr == nil {
|
||||||
|
return nil, fmt.Errorf("no suitable source address found on interface %q", sintf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dialer, nil
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build darwin
|
||||||
// +build darwin
|
// +build darwin
|
||||||
|
|
||||||
package core
|
package core
|
||||||
|
@ -10,7 +11,7 @@ import (
|
||||||
|
|
||||||
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
|
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
|
||||||
|
|
||||||
func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error {
|
func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error {
|
||||||
var control error
|
var control error
|
||||||
var recvanyif error
|
var recvanyif error
|
||||||
|
|
||||||
|
@ -27,6 +28,6 @@ func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tcp) getControl(sintf string) func(string, string, syscall.RawConn) error {
|
func (t *linkTCP) getControl(sintf string) func(string, string, syscall.RawConn) error {
|
||||||
return t.tcpContext
|
return t.tcpContext
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package core
|
package core
|
||||||
|
@ -10,7 +11,7 @@ import (
|
||||||
|
|
||||||
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
|
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
|
||||||
|
|
||||||
func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error {
|
func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error {
|
||||||
var control error
|
var control error
|
||||||
var bbr error
|
var bbr error
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tcp) getControl(sintf string) func(string, string, syscall.RawConn) error {
|
func (t *linkTCP) getControl(sintf string) func(string, string, syscall.RawConn) error {
|
||||||
return func(network, address string, c syscall.RawConn) error {
|
return func(network, address string, c syscall.RawConn) error {
|
||||||
var err error
|
var err error
|
||||||
btd := func(fd uintptr) {
|
btd := func(fd uintptr) {
|
18
src/core/link_tcp_other.go
Normal file
18
src/core/link_tcp_other.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
//go:build !darwin && !linux
|
||||||
|
// +build !darwin,!linux
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
|
||||||
|
|
||||||
|
func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *linkTCP) getControl(sintf string) func(string, string, syscall.RawConn) error {
|
||||||
|
return t.tcpContext
|
||||||
|
}
|
171
src/core/link_tls.go
Normal file
171
src/core/link_tls.go
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Arceliar/phony"
|
||||||
|
)
|
||||||
|
|
||||||
|
type linkTLS struct {
|
||||||
|
phony.Inbox
|
||||||
|
*links
|
||||||
|
tcp *linkTCP
|
||||||
|
listener *net.ListenConfig
|
||||||
|
config *tls.Config
|
||||||
|
_listeners map[*Listener]context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
|
||||||
|
lt := &linkTLS{
|
||||||
|
links: l,
|
||||||
|
tcp: tcp,
|
||||||
|
listener: &net.ListenConfig{
|
||||||
|
Control: tcp.tcpContext,
|
||||||
|
KeepAlive: -1,
|
||||||
|
},
|
||||||
|
_listeners: map[*Listener]context.CancelFunc{},
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
lt.config, err = lt.generateConfig()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return lt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error {
|
||||||
|
info := linkInfoFor("tls", sintf, strings.SplitN(url.Host, "%", 2)[0])
|
||||||
|
if l.links.isConnectedTo(info) {
|
||||||
|
return fmt.Errorf("duplicate connection attempt")
|
||||||
|
}
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", url.Host)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
addr.Zone = sintf
|
||||||
|
dialer, err := l.tcp.dialerFor(addr.String(), sintf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tlsconfig := l.config.Clone()
|
||||||
|
tlsconfig.ServerName = sni
|
||||||
|
tlsdialer := &tls.Dialer{
|
||||||
|
NetDialer: dialer,
|
||||||
|
Config: tlsconfig,
|
||||||
|
}
|
||||||
|
conn, err := tlsdialer.DialContext(l.core.ctx, "tcp", addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return l.handler(url.String(), info, conn, options, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) {
|
||||||
|
ctx, cancel := context.WithCancel(l.core.ctx)
|
||||||
|
hostport := url.Host
|
||||||
|
if sintf != "" {
|
||||||
|
if host, port, err := net.SplitHostPort(hostport); err == nil {
|
||||||
|
hostport = fmt.Sprintf("[%s%%%s]:%s", host, sintf, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener, err := l.listener.Listen(ctx, "tcp", hostport)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlslistener := tls.NewListener(listener, l.config)
|
||||||
|
entry := &Listener{
|
||||||
|
Listener: tlslistener,
|
||||||
|
closed: make(chan struct{}),
|
||||||
|
}
|
||||||
|
phony.Block(l, func() {
|
||||||
|
l._listeners[entry] = cancel
|
||||||
|
})
|
||||||
|
l.core.log.Printf("TLS listener started on %s", listener.Addr())
|
||||||
|
go func() {
|
||||||
|
defer phony.Block(l, func() {
|
||||||
|
delete(l._listeners, entry)
|
||||||
|
})
|
||||||
|
for {
|
||||||
|
conn, err := tlslistener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
addr := conn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
name := fmt.Sprintf("tls://%s", addr)
|
||||||
|
info := linkInfoFor("tls", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0])
|
||||||
|
if err = l.handler(name, info, conn, linkOptions{}, true); err != nil {
|
||||||
|
l.core.log.Errorln("Failed to create inbound link:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tlslistener.Close()
|
||||||
|
close(entry.closed)
|
||||||
|
l.core.log.Printf("TLS listener stopped on %s", listener.Addr())
|
||||||
|
}()
|
||||||
|
return entry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTLS) generateConfig() (*tls.Config, error) {
|
||||||
|
certBuf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// TODO: because NotAfter is finite, we should add some mechanism to
|
||||||
|
// regenerate the certificate and restart the listeners periodically
|
||||||
|
// for nodes with very high uptimes. Perhaps regenerate certs and restart
|
||||||
|
// listeners every few months or so.
|
||||||
|
cert := x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: hex.EncodeToString(l.links.core.public[:]),
|
||||||
|
},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().Add(time.Hour * 24 * 365),
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
certbytes, err := x509.CreateCertificate(rand.Reader, &cert, &cert, l.links.core.public, l.links.core.secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := pem.Encode(certBuf, &pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: certbytes,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCAs := x509.NewCertPool()
|
||||||
|
rootCAs.AppendCertsFromPEM(certbytes)
|
||||||
|
|
||||||
|
return &tls.Config{
|
||||||
|
RootCAs: rootCAs,
|
||||||
|
Certificates: []tls.Certificate{
|
||||||
|
{
|
||||||
|
Certificate: [][]byte{certbytes},
|
||||||
|
PrivateKey: l.links.core.secret,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
MinVersion: tls.VersionTLS13,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error {
|
||||||
|
return l.tcp.handler(name, info, conn, options, incoming)
|
||||||
|
}
|
98
src/core/link_unix.go
Normal file
98
src/core/link_unix.go
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Arceliar/phony"
|
||||||
|
)
|
||||||
|
|
||||||
|
type linkUNIX struct {
|
||||||
|
phony.Inbox
|
||||||
|
*links
|
||||||
|
dialer *net.Dialer
|
||||||
|
listener *net.ListenConfig
|
||||||
|
_listeners map[*Listener]context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *links) newLinkUNIX() *linkUNIX {
|
||||||
|
lt := &linkUNIX{
|
||||||
|
links: l,
|
||||||
|
dialer: &net.Dialer{
|
||||||
|
Timeout: time.Second * 5,
|
||||||
|
KeepAlive: -1,
|
||||||
|
},
|
||||||
|
listener: &net.ListenConfig{
|
||||||
|
KeepAlive: -1,
|
||||||
|
},
|
||||||
|
_listeners: map[*Listener]context.CancelFunc{},
|
||||||
|
}
|
||||||
|
return lt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkUNIX) dial(url *url.URL, options linkOptions, _ string) error {
|
||||||
|
info := linkInfoFor("unix", "", url.Path)
|
||||||
|
if l.links.isConnectedTo(info) {
|
||||||
|
return fmt.Errorf("duplicate connection attempt")
|
||||||
|
}
|
||||||
|
addr, err := net.ResolveUnixAddr("unix", url.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn, err := l.dialer.DialContext(l.core.ctx, "unix", addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return l.handler(url.String(), info, conn, options, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) {
|
||||||
|
ctx, cancel := context.WithCancel(l.core.ctx)
|
||||||
|
listener, err := l.listener.Listen(ctx, "unix", url.Path)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
entry := &Listener{
|
||||||
|
Listener: listener,
|
||||||
|
closed: make(chan struct{}),
|
||||||
|
}
|
||||||
|
phony.Block(l, func() {
|
||||||
|
l._listeners[entry] = cancel
|
||||||
|
})
|
||||||
|
l.core.log.Printf("UNIX listener started on %s", listener.Addr())
|
||||||
|
go func() {
|
||||||
|
defer phony.Block(l, func() {
|
||||||
|
delete(l._listeners, entry)
|
||||||
|
})
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
info := linkInfoFor("unix", "", url.String())
|
||||||
|
if err = l.handler(url.String(), info, conn, linkOptions{}, true); err != nil {
|
||||||
|
l.core.log.Errorln("Failed to create inbound link:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.Close()
|
||||||
|
close(entry.closed)
|
||||||
|
l.core.log.Printf("UNIX listener stopped on %s", listener.Addr())
|
||||||
|
}()
|
||||||
|
return entry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkUNIX) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error {
|
||||||
|
return l.links.create(
|
||||||
|
conn, // connection
|
||||||
|
name, // connection name
|
||||||
|
info, // connection info
|
||||||
|
incoming, // not incoming
|
||||||
|
false, // not forced
|
||||||
|
options, // connection options
|
||||||
|
)
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -13,7 +12,7 @@ import (
|
||||||
"github.com/Arceliar/phony"
|
"github.com/Arceliar/phony"
|
||||||
|
|
||||||
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -129,7 +128,7 @@ func (m *nodeinfo) _sendReq(key keyArray, callback func(nodeinfo NodeInfoPayload
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
m._addCallback(key, callback)
|
m._addCallback(key, callback)
|
||||||
}
|
}
|
||||||
_, _ = m.proto.core.pc.WriteTo([]byte{typeSessionProto, typeProtoNodeInfoRequest}, iwt.Addr(key[:]))
|
_, _ = m.proto.core.PacketConn.WriteTo([]byte{typeSessionProto, typeProtoNodeInfoRequest}, iwt.Addr(key[:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *nodeinfo) handleReq(from phony.Actor, key keyArray) {
|
func (m *nodeinfo) handleReq(from phony.Actor, key keyArray) {
|
||||||
|
@ -146,7 +145,7 @@ func (m *nodeinfo) handleRes(from phony.Actor, key keyArray, info NodeInfoPayloa
|
||||||
|
|
||||||
func (m *nodeinfo) _sendRes(key keyArray) {
|
func (m *nodeinfo) _sendRes(key keyArray) {
|
||||||
bs := append([]byte{typeSessionProto, typeProtoNodeInfoResponse}, m._getNodeInfo()...)
|
bs := append([]byte{typeSessionProto, typeProtoNodeInfoResponse}, m._getNodeInfo()...)
|
||||||
_, _ = m.proto.core.pc.WriteTo(bs, iwt.Addr(key[:]))
|
_, _ = m.proto.core.PacketConn.WriteTo(bs, iwt.Addr(key[:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin socket stuff
|
// Admin socket stuff
|
||||||
|
@ -154,7 +153,7 @@ func (m *nodeinfo) _sendRes(key keyArray) {
|
||||||
type GetNodeInfoRequest struct {
|
type GetNodeInfoRequest struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
}
|
}
|
||||||
type GetNodeInfoResponse map[string]interface{}
|
type GetNodeInfoResponse map[string]json.RawMessage
|
||||||
|
|
||||||
func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error) {
|
func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error) {
|
||||||
var req GetNodeInfoRequest
|
var req GetNodeInfoRequest
|
||||||
|
@ -182,8 +181,8 @@ func (m *nodeinfo) nodeInfoAdminHandler(in json.RawMessage) (interface{}, error)
|
||||||
if err := msg.UnmarshalJSON(info); err != nil {
|
if err := msg.UnmarshalJSON(info); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ip := net.IP(address.AddrForKey(kbs)[:])
|
key := hex.EncodeToString(kbs[:])
|
||||||
res := GetNodeInfoResponse{ip.String(): msg}
|
res := GetNodeInfoResponse{key: msg}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
41
src/core/options.go
Normal file
41
src/core/options.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Core) _applyOption(opt SetupOption) {
|
||||||
|
switch v := opt.(type) {
|
||||||
|
case Peer:
|
||||||
|
c.config._peers[v] = struct{}{}
|
||||||
|
case ListenAddress:
|
||||||
|
c.config._listeners[v] = struct{}{}
|
||||||
|
case NodeInfo:
|
||||||
|
c.config.nodeinfo = v
|
||||||
|
case NodeInfoPrivacy:
|
||||||
|
c.config.nodeinfoPrivacy = v
|
||||||
|
case AllowedPublicKey:
|
||||||
|
pk := [32]byte{}
|
||||||
|
copy(pk[:], v)
|
||||||
|
c.config._allowedPublicKeys[pk] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetupOption interface {
|
||||||
|
isSetupOption()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListenAddress string
|
||||||
|
type Peer struct {
|
||||||
|
URI string
|
||||||
|
SourceInterface string
|
||||||
|
}
|
||||||
|
type NodeInfo map[string]interface{}
|
||||||
|
type NodeInfoPrivacy bool
|
||||||
|
type AllowedPublicKey ed25519.PublicKey
|
||||||
|
|
||||||
|
func (a ListenAddress) isSetupOption() {}
|
||||||
|
func (a Peer) isSetupOption() {}
|
||||||
|
func (a NodeInfo) isSetupOption() {}
|
||||||
|
func (a NodeInfoPrivacy) isSetupOption() {}
|
||||||
|
func (a AllowedPublicKey) isSetupOption() {}
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -29,23 +30,30 @@ type reqInfo struct {
|
||||||
timer *time.Timer // time.AfterFunc cleanup
|
timer *time.Timer // time.AfterFunc cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type keyArray [ed25519.PublicKeySize]byte
|
||||||
|
|
||||||
type protoHandler struct {
|
type protoHandler struct {
|
||||||
phony.Inbox
|
phony.Inbox
|
||||||
|
|
||||||
core *Core
|
core *Core
|
||||||
nodeinfo nodeinfo
|
nodeinfo nodeinfo
|
||||||
sreqs map[keyArray]*reqInfo
|
|
||||||
preqs map[keyArray]*reqInfo
|
selfRequests map[keyArray]*reqInfo
|
||||||
dreqs map[keyArray]*reqInfo
|
peersRequests map[keyArray]*reqInfo
|
||||||
|
dhtRequests map[keyArray]*reqInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *protoHandler) init(core *Core) {
|
func (p *protoHandler) init(core *Core) {
|
||||||
p.core = core
|
p.core = core
|
||||||
p.nodeinfo.init(p)
|
p.nodeinfo.init(p)
|
||||||
p.sreqs = make(map[keyArray]*reqInfo)
|
|
||||||
p.preqs = make(map[keyArray]*reqInfo)
|
p.selfRequests = make(map[keyArray]*reqInfo)
|
||||||
p.dreqs = make(map[keyArray]*reqInfo)
|
p.peersRequests = make(map[keyArray]*reqInfo)
|
||||||
|
p.dhtRequests = make(map[keyArray]*reqInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Common functions
|
||||||
|
|
||||||
func (p *protoHandler) handleProto(from phony.Actor, key keyArray, bs []byte) {
|
func (p *protoHandler) handleProto(from phony.Actor, key keyArray, bs []byte) {
|
||||||
if len(bs) == 0 {
|
if len(bs) == 0 {
|
||||||
return
|
return
|
||||||
|
@ -57,10 +65,16 @@ func (p *protoHandler) handleProto(from phony.Actor, key keyArray, bs []byte) {
|
||||||
case typeProtoNodeInfoResponse:
|
case typeProtoNodeInfoResponse:
|
||||||
p.nodeinfo.handleRes(p, key, bs[1:])
|
p.nodeinfo.handleRes(p, key, bs[1:])
|
||||||
case typeProtoDebug:
|
case typeProtoDebug:
|
||||||
p._handleDebug(key, bs[1:])
|
p.handleDebug(from, key, bs[1:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *protoHandler) handleDebug(from phony.Actor, key keyArray, bs []byte) {
|
||||||
|
p.Act(from, func() {
|
||||||
|
p._handleDebug(key, bs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (p *protoHandler) _handleDebug(key keyArray, bs []byte) {
|
func (p *protoHandler) _handleDebug(key keyArray, bs []byte) {
|
||||||
if len(bs) == 0 {
|
if len(bs) == 0 {
|
||||||
return
|
return
|
||||||
|
@ -82,22 +96,29 @@ func (p *protoHandler) _handleDebug(key keyArray, bs []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *protoHandler) _sendDebug(key keyArray, dType uint8, data []byte) {
|
||||||
|
bs := append([]byte{typeSessionProto, typeProtoDebug, dType}, data...)
|
||||||
|
_, _ = p.core.PacketConn.WriteTo(bs, iwt.Addr(key[:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get self
|
||||||
|
|
||||||
func (p *protoHandler) sendGetSelfRequest(key keyArray, callback func([]byte)) {
|
func (p *protoHandler) sendGetSelfRequest(key keyArray, callback func([]byte)) {
|
||||||
p.Act(nil, func() {
|
p.Act(nil, func() {
|
||||||
if info := p.sreqs[key]; info != nil {
|
if info := p.selfRequests[key]; info != nil {
|
||||||
info.timer.Stop()
|
info.timer.Stop()
|
||||||
delete(p.sreqs, key)
|
delete(p.selfRequests, key)
|
||||||
}
|
}
|
||||||
info := new(reqInfo)
|
info := new(reqInfo)
|
||||||
info.callback = callback
|
info.callback = callback
|
||||||
info.timer = time.AfterFunc(time.Minute, func() {
|
info.timer = time.AfterFunc(time.Minute, func() {
|
||||||
p.Act(nil, func() {
|
p.Act(nil, func() {
|
||||||
if p.sreqs[key] == info {
|
if p.selfRequests[key] == info {
|
||||||
delete(p.sreqs, key)
|
delete(p.selfRequests, key)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
p.sreqs[key] = info
|
p.selfRequests[key] = info
|
||||||
p._sendDebug(key, typeDebugGetSelfRequest, nil)
|
p._sendDebug(key, typeDebugGetSelfRequest, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -116,29 +137,31 @@ func (p *protoHandler) _handleGetSelfRequest(key keyArray) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *protoHandler) _handleGetSelfResponse(key keyArray, bs []byte) {
|
func (p *protoHandler) _handleGetSelfResponse(key keyArray, bs []byte) {
|
||||||
if info := p.sreqs[key]; info != nil {
|
if info := p.selfRequests[key]; info != nil {
|
||||||
info.timer.Stop()
|
info.timer.Stop()
|
||||||
info.callback(bs)
|
info.callback(bs)
|
||||||
delete(p.sreqs, key)
|
delete(p.selfRequests, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get peers
|
||||||
|
|
||||||
func (p *protoHandler) sendGetPeersRequest(key keyArray, callback func([]byte)) {
|
func (p *protoHandler) sendGetPeersRequest(key keyArray, callback func([]byte)) {
|
||||||
p.Act(nil, func() {
|
p.Act(nil, func() {
|
||||||
if info := p.preqs[key]; info != nil {
|
if info := p.peersRequests[key]; info != nil {
|
||||||
info.timer.Stop()
|
info.timer.Stop()
|
||||||
delete(p.preqs, key)
|
delete(p.peersRequests, key)
|
||||||
}
|
}
|
||||||
info := new(reqInfo)
|
info := new(reqInfo)
|
||||||
info.callback = callback
|
info.callback = callback
|
||||||
info.timer = time.AfterFunc(time.Minute, func() {
|
info.timer = time.AfterFunc(time.Minute, func() {
|
||||||
p.Act(nil, func() {
|
p.Act(nil, func() {
|
||||||
if p.preqs[key] == info {
|
if p.peersRequests[key] == info {
|
||||||
delete(p.preqs, key)
|
delete(p.peersRequests, key)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
p.preqs[key] = info
|
p.peersRequests[key] = info
|
||||||
p._sendDebug(key, typeDebugGetPeersRequest, nil)
|
p._sendDebug(key, typeDebugGetPeersRequest, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -149,7 +172,7 @@ func (p *protoHandler) _handleGetPeersRequest(key keyArray) {
|
||||||
for _, pinfo := range peers {
|
for _, pinfo := range peers {
|
||||||
tmp := append(bs, pinfo.Key[:]...)
|
tmp := append(bs, pinfo.Key[:]...)
|
||||||
const responseOverhead = 2 // 1 debug type, 1 getpeers type
|
const responseOverhead = 2 // 1 debug type, 1 getpeers type
|
||||||
if uint64(len(tmp))+responseOverhead > p.core.store.maxSessionMTU() {
|
if uint64(len(tmp))+responseOverhead > p.core.MTU() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
bs = tmp
|
bs = tmp
|
||||||
|
@ -158,29 +181,31 @@ func (p *protoHandler) _handleGetPeersRequest(key keyArray) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *protoHandler) _handleGetPeersResponse(key keyArray, bs []byte) {
|
func (p *protoHandler) _handleGetPeersResponse(key keyArray, bs []byte) {
|
||||||
if info := p.preqs[key]; info != nil {
|
if info := p.peersRequests[key]; info != nil {
|
||||||
info.timer.Stop()
|
info.timer.Stop()
|
||||||
info.callback(bs)
|
info.callback(bs)
|
||||||
delete(p.preqs, key)
|
delete(p.peersRequests, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get DHT
|
||||||
|
|
||||||
func (p *protoHandler) sendGetDHTRequest(key keyArray, callback func([]byte)) {
|
func (p *protoHandler) sendGetDHTRequest(key keyArray, callback func([]byte)) {
|
||||||
p.Act(nil, func() {
|
p.Act(nil, func() {
|
||||||
if info := p.dreqs[key]; info != nil {
|
if info := p.dhtRequests[key]; info != nil {
|
||||||
info.timer.Stop()
|
info.timer.Stop()
|
||||||
delete(p.dreqs, key)
|
delete(p.dhtRequests, key)
|
||||||
}
|
}
|
||||||
info := new(reqInfo)
|
info := new(reqInfo)
|
||||||
info.callback = callback
|
info.callback = callback
|
||||||
info.timer = time.AfterFunc(time.Minute, func() {
|
info.timer = time.AfterFunc(time.Minute, func() {
|
||||||
p.Act(nil, func() {
|
p.Act(nil, func() {
|
||||||
if p.dreqs[key] == info {
|
if p.dhtRequests[key] == info {
|
||||||
delete(p.dreqs, key)
|
delete(p.dhtRequests, key)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
p.dreqs[key] = info
|
p.dhtRequests[key] = info
|
||||||
p._sendDebug(key, typeDebugGetDHTRequest, nil)
|
p._sendDebug(key, typeDebugGetDHTRequest, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -191,7 +216,7 @@ func (p *protoHandler) _handleGetDHTRequest(key keyArray) {
|
||||||
for _, dinfo := range dinfos {
|
for _, dinfo := range dinfos {
|
||||||
tmp := append(bs, dinfo.Key[:]...)
|
tmp := append(bs, dinfo.Key[:]...)
|
||||||
const responseOverhead = 2 // 1 debug type, 1 getdht type
|
const responseOverhead = 2 // 1 debug type, 1 getdht type
|
||||||
if uint64(len(tmp))+responseOverhead > p.core.store.maxSessionMTU() {
|
if uint64(len(tmp))+responseOverhead > p.core.MTU() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
bs = tmp
|
bs = tmp
|
||||||
|
@ -200,19 +225,14 @@ func (p *protoHandler) _handleGetDHTRequest(key keyArray) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *protoHandler) _handleGetDHTResponse(key keyArray, bs []byte) {
|
func (p *protoHandler) _handleGetDHTResponse(key keyArray, bs []byte) {
|
||||||
if info := p.dreqs[key]; info != nil {
|
if info := p.dhtRequests[key]; info != nil {
|
||||||
info.timer.Stop()
|
info.timer.Stop()
|
||||||
info.callback(bs)
|
info.callback(bs)
|
||||||
delete(p.dreqs, key)
|
delete(p.dhtRequests, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *protoHandler) _sendDebug(key keyArray, dType uint8, data []byte) {
|
// Admin socket stuff for "Get self"
|
||||||
bs := append([]byte{typeSessionProto, typeProtoDebug, dType}, data...)
|
|
||||||
_, _ = p.core.pc.WriteTo(bs, iwt.Addr(key[:]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Admin socket stuff
|
|
||||||
|
|
||||||
type DebugGetSelfRequest struct {
|
type DebugGetSelfRequest struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
|
@ -252,6 +272,8 @@ func (p *protoHandler) getSelfHandler(in json.RawMessage) (interface{}, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Admin socket stuff for "Get peers"
|
||||||
|
|
||||||
type DebugGetPeersRequest struct {
|
type DebugGetPeersRequest struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
}
|
}
|
||||||
|
@ -300,6 +322,8 @@ func (p *protoHandler) getPeersHandler(in json.RawMessage) (interface{}, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Admin socket stuff for "Get DHT"
|
||||||
|
|
||||||
type DebugGetDHTRequest struct {
|
type DebugGetDHTRequest struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
}
|
}
|
||||||
|
|
417
src/core/tcp.go
417
src/core/tcp.go
|
@ -1,417 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
// This sends packets to peers using TCP as a transport
|
|
||||||
// It's generally better tested than the UDP implementation
|
|
||||||
// Using it regularly is insane, but I find TCP easier to test/debug with it
|
|
||||||
// Updating and optimizing the UDP version is a higher priority
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// Something needs to make sure we're getting *valid* packets
|
|
||||||
// Could be used to DoS (connect, give someone else's keys, spew garbage)
|
|
||||||
// I guess the "peer" part should watch for link packets, disconnect?
|
|
||||||
|
|
||||||
// TCP connections start with a metadata exchange.
|
|
||||||
// It involves exchanging version numbers and crypto keys
|
|
||||||
// See version.go for version metadata format
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/proxy"
|
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
|
||||||
//"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
const default_timeout = 6 * time.Second
|
|
||||||
|
|
||||||
// The TCP listener and information about active TCP connections, to avoid duplication.
|
|
||||||
type tcp struct {
|
|
||||||
links *links
|
|
||||||
waitgroup sync.WaitGroup
|
|
||||||
mutex sync.Mutex // Protecting the below
|
|
||||||
listeners map[string]*TcpListener
|
|
||||||
calls map[string]struct{}
|
|
||||||
conns map[linkInfo](chan struct{})
|
|
||||||
tls tcptls
|
|
||||||
}
|
|
||||||
|
|
||||||
// TcpListener is a stoppable TCP listener interface. These are typically
|
|
||||||
// returned from calls to the ListenTCP() function and are also used internally
|
|
||||||
// to represent listeners created by the "Listen" configuration option and for
|
|
||||||
// multicast interfaces.
|
|
||||||
type TcpListener struct {
|
|
||||||
Listener net.Listener
|
|
||||||
opts tcpOptions
|
|
||||||
stop chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TcpUpgrade struct {
|
|
||||||
upgrade func(c net.Conn, o *tcpOptions) (net.Conn, error)
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type tcpOptions struct {
|
|
||||||
linkOptions
|
|
||||||
upgrade *TcpUpgrade
|
|
||||||
socksProxyAddr string
|
|
||||||
socksProxyAuth *proxy.Auth
|
|
||||||
socksPeerAddr string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *TcpListener) Stop() {
|
|
||||||
defer func() { _ = recover() }()
|
|
||||||
close(l.stop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper function to set additional options for specific connection types.
|
|
||||||
func (t *tcp) setExtraOptions(c net.Conn) {
|
|
||||||
switch sock := c.(type) {
|
|
||||||
case *net.TCPConn:
|
|
||||||
_ = sock.SetNoDelay(true)
|
|
||||||
// TODO something for socks5
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the address of the listener.
|
|
||||||
func (t *tcp) getAddr() *net.TCPAddr {
|
|
||||||
// TODO: Fix this, because this will currently only give a single address
|
|
||||||
// to multicast.go, which obviously is not great, but right now multicast.go
|
|
||||||
// doesn't have the ability to send more than one address in a packet either
|
|
||||||
t.mutex.Lock()
|
|
||||||
defer t.mutex.Unlock()
|
|
||||||
for _, l := range t.listeners {
|
|
||||||
return l.Listener.Addr().(*net.TCPAddr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initializes the struct.
|
|
||||||
func (t *tcp) init(l *links) error {
|
|
||||||
t.links = l
|
|
||||||
t.tls.init(t)
|
|
||||||
t.mutex.Lock()
|
|
||||||
t.calls = make(map[string]struct{})
|
|
||||||
t.conns = make(map[linkInfo](chan struct{}))
|
|
||||||
t.listeners = make(map[string]*TcpListener)
|
|
||||||
t.mutex.Unlock()
|
|
||||||
|
|
||||||
t.links.core.config.RLock()
|
|
||||||
defer t.links.core.config.RUnlock()
|
|
||||||
for _, listenaddr := range t.links.core.config.Listen {
|
|
||||||
u, err := url.Parse(listenaddr)
|
|
||||||
if err != nil {
|
|
||||||
t.links.core.log.Errorln("Failed to parse listener: listener", listenaddr, "is not correctly formatted, ignoring")
|
|
||||||
}
|
|
||||||
if _, err := t.listenURL(u, ""); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcp) stop() error {
|
|
||||||
t.mutex.Lock()
|
|
||||||
for _, listener := range t.listeners {
|
|
||||||
listener.Stop()
|
|
||||||
}
|
|
||||||
t.mutex.Unlock()
|
|
||||||
t.waitgroup.Wait()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcp) listenURL(u *url.URL, sintf string) (*TcpListener, error) {
|
|
||||||
var listener *TcpListener
|
|
||||||
var err error
|
|
||||||
hostport := u.Host // Used for tcp and tls
|
|
||||||
if len(sintf) != 0 {
|
|
||||||
host, port, err := net.SplitHostPort(hostport)
|
|
||||||
if err == nil {
|
|
||||||
hostport = fmt.Sprintf("[%s%%%s]:%s", host, sintf, port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch u.Scheme {
|
|
||||||
case "tcp":
|
|
||||||
listener, err = t.listen(hostport, nil)
|
|
||||||
case "tls":
|
|
||||||
listener, err = t.listen(hostport, t.tls.forListener)
|
|
||||||
default:
|
|
||||||
t.links.core.log.Errorln("Failed to add listener: listener", u.String(), "is not correctly formatted, ignoring")
|
|
||||||
}
|
|
||||||
return listener, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcp) listen(listenaddr string, upgrade *TcpUpgrade) (*TcpListener, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
ctx := t.links.core.ctx
|
|
||||||
lc := net.ListenConfig{
|
|
||||||
Control: t.tcpContext,
|
|
||||||
}
|
|
||||||
listener, err := lc.Listen(ctx, "tcp", listenaddr)
|
|
||||||
if err == nil {
|
|
||||||
l := TcpListener{
|
|
||||||
Listener: listener,
|
|
||||||
opts: tcpOptions{upgrade: upgrade},
|
|
||||||
stop: make(chan struct{}),
|
|
||||||
}
|
|
||||||
t.waitgroup.Add(1)
|
|
||||||
go t.listener(&l, listenaddr)
|
|
||||||
return &l, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs the listener, which spawns off goroutines for incoming connections.
|
|
||||||
func (t *tcp) listener(l *TcpListener, listenaddr string) {
|
|
||||||
defer t.waitgroup.Done()
|
|
||||||
if l == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Track the listener so that we can find it again in future
|
|
||||||
t.mutex.Lock()
|
|
||||||
if _, isIn := t.listeners[listenaddr]; isIn {
|
|
||||||
t.mutex.Unlock()
|
|
||||||
l.Listener.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
callproto := "TCP"
|
|
||||||
if l.opts.upgrade != nil {
|
|
||||||
callproto = strings.ToUpper(l.opts.upgrade.name)
|
|
||||||
}
|
|
||||||
t.listeners[listenaddr] = l
|
|
||||||
t.mutex.Unlock()
|
|
||||||
// And here we go!
|
|
||||||
defer func() {
|
|
||||||
t.links.core.log.Infoln("Stopping", callproto, "listener on:", l.Listener.Addr().String())
|
|
||||||
l.Listener.Close()
|
|
||||||
t.mutex.Lock()
|
|
||||||
delete(t.listeners, listenaddr)
|
|
||||||
t.mutex.Unlock()
|
|
||||||
}()
|
|
||||||
t.links.core.log.Infoln("Listening for", callproto, "on:", l.Listener.Addr().String())
|
|
||||||
go func() {
|
|
||||||
<-l.stop
|
|
||||||
l.Listener.Close()
|
|
||||||
}()
|
|
||||||
defer l.Stop()
|
|
||||||
for {
|
|
||||||
sock, err := l.Listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
t.links.core.log.Errorln("Failed to accept connection:", err)
|
|
||||||
select {
|
|
||||||
case <-l.stop:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second) // So we don't busy loop
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.waitgroup.Add(1)
|
|
||||||
options := l.opts
|
|
||||||
go t.handler(sock, true, options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if we already are calling this address
|
|
||||||
func (t *tcp) startCalling(saddr string) bool {
|
|
||||||
t.mutex.Lock()
|
|
||||||
defer t.mutex.Unlock()
|
|
||||||
_, isIn := t.calls[saddr]
|
|
||||||
t.calls[saddr] = struct{}{}
|
|
||||||
return !isIn
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if a connection already exists.
|
|
||||||
// If not, it adds it to the list of active outgoing calls (to block future attempts) and dials the address.
|
|
||||||
// If the dial is successful, it launches the handler.
|
|
||||||
// When finished, it removes the outgoing call, so reconnection attempts can be made later.
|
|
||||||
// This all happens in a separate goroutine that it spawns.
|
|
||||||
func (t *tcp) call(saddr string, options tcpOptions, sintf string) {
|
|
||||||
go func() {
|
|
||||||
callname := saddr
|
|
||||||
callproto := "TCP"
|
|
||||||
if options.upgrade != nil {
|
|
||||||
callproto = strings.ToUpper(options.upgrade.name)
|
|
||||||
}
|
|
||||||
if sintf != "" {
|
|
||||||
callname = fmt.Sprintf("%s/%s/%s", callproto, saddr, sintf)
|
|
||||||
}
|
|
||||||
if !t.startCalling(callname) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
// Block new calls for a little while, to mitigate livelock scenarios
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
delay := default_timeout + time.Duration(rand.Intn(10000))*time.Millisecond
|
|
||||||
time.Sleep(delay)
|
|
||||||
t.mutex.Lock()
|
|
||||||
delete(t.calls, callname)
|
|
||||||
t.mutex.Unlock()
|
|
||||||
}()
|
|
||||||
var conn net.Conn
|
|
||||||
var err error
|
|
||||||
if options.socksProxyAddr != "" {
|
|
||||||
if sintf != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dialerdst, er := net.ResolveTCPAddr("tcp", options.socksProxyAddr)
|
|
||||||
if er != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var dialer proxy.Dialer
|
|
||||||
dialer, err = proxy.SOCKS5("tcp", dialerdst.String(), options.socksProxyAuth, proxy.Direct)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx, done := context.WithTimeout(t.links.core.ctx, default_timeout)
|
|
||||||
conn, err = dialer.(proxy.ContextDialer).DialContext(ctx, "tcp", saddr)
|
|
||||||
done()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.waitgroup.Add(1)
|
|
||||||
options.socksPeerAddr = saddr
|
|
||||||
if ch := t.handler(conn, false, options); ch != nil {
|
|
||||||
<-ch
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dst, err := net.ResolveTCPAddr("tcp", saddr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if dst.IP.IsLinkLocalUnicast() {
|
|
||||||
dst.Zone = sintf
|
|
||||||
if dst.Zone == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dialer := net.Dialer{
|
|
||||||
Control: t.tcpContext,
|
|
||||||
}
|
|
||||||
if sintf != "" {
|
|
||||||
dialer.Control = t.getControl(sintf)
|
|
||||||
ief, err := net.InterfaceByName(sintf)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ief.Flags&net.FlagUp == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
addrs, err := ief.Addrs()
|
|
||||||
if err == nil {
|
|
||||||
for addrindex, addr := range addrs {
|
|
||||||
src, _, err := net.ParseCIDR(addr.String())
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if src.Equal(dst.IP) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !src.IsGlobalUnicast() && !src.IsLinkLocalUnicast() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
bothglobal := src.IsGlobalUnicast() == dst.IP.IsGlobalUnicast()
|
|
||||||
bothlinklocal := src.IsLinkLocalUnicast() == dst.IP.IsLinkLocalUnicast()
|
|
||||||
if !bothglobal && !bothlinklocal {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (src.To4() != nil) != (dst.IP.To4() != nil) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if bothglobal || bothlinklocal || addrindex == len(addrs)-1 {
|
|
||||||
dialer.LocalAddr = &net.TCPAddr{
|
|
||||||
IP: src,
|
|
||||||
Port: 0,
|
|
||||||
Zone: sintf,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dialer.LocalAddr == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx, done := context.WithTimeout(t.links.core.ctx, default_timeout)
|
|
||||||
conn, err = dialer.DialContext(ctx, "tcp", dst.String())
|
|
||||||
done()
|
|
||||||
if err != nil {
|
|
||||||
t.links.core.log.Debugf("Failed to dial %s: %s", callproto, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.waitgroup.Add(1)
|
|
||||||
if ch := t.handler(conn, false, options); ch != nil {
|
|
||||||
<-ch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcp) handler(sock net.Conn, incoming bool, options tcpOptions) chan struct{} {
|
|
||||||
defer t.waitgroup.Done() // Happens after sock.close
|
|
||||||
defer sock.Close()
|
|
||||||
t.setExtraOptions(sock)
|
|
||||||
var upgraded bool
|
|
||||||
if options.upgrade != nil {
|
|
||||||
var err error
|
|
||||||
if sock, err = options.upgrade.upgrade(sock, &options); err != nil {
|
|
||||||
t.links.core.log.Errorln("TCP handler upgrade failed:", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
upgraded = true
|
|
||||||
}
|
|
||||||
var name, proto, local, remote string
|
|
||||||
if options.socksProxyAddr != "" {
|
|
||||||
name = "socks://" + sock.RemoteAddr().String() + "/" + options.socksPeerAddr
|
|
||||||
proto = "socks"
|
|
||||||
local, _, _ = net.SplitHostPort(sock.LocalAddr().String())
|
|
||||||
remote, _, _ = net.SplitHostPort(options.socksPeerAddr)
|
|
||||||
} else {
|
|
||||||
if upgraded {
|
|
||||||
proto = options.upgrade.name
|
|
||||||
name = proto + "://" + sock.RemoteAddr().String()
|
|
||||||
} else {
|
|
||||||
proto = "tcp"
|
|
||||||
name = proto + "://" + sock.RemoteAddr().String()
|
|
||||||
}
|
|
||||||
local, _, _ = net.SplitHostPort(sock.LocalAddr().String())
|
|
||||||
remote, _, _ = net.SplitHostPort(sock.RemoteAddr().String())
|
|
||||||
}
|
|
||||||
localIP := net.ParseIP(local)
|
|
||||||
if localIP = localIP.To16(); localIP != nil {
|
|
||||||
var laddr address.Address
|
|
||||||
var lsubnet address.Subnet
|
|
||||||
copy(laddr[:], localIP)
|
|
||||||
copy(lsubnet[:], localIP)
|
|
||||||
if laddr.IsValid() || lsubnet.IsValid() {
|
|
||||||
// The local address is with the network address/prefix range
|
|
||||||
// This would route ygg over ygg, which we don't want
|
|
||||||
// FIXME ideally this check should happen outside of the core library
|
|
||||||
// Maybe dial/listen at the application level
|
|
||||||
// Then pass a net.Conn to the core library (after these kinds of checks are done)
|
|
||||||
t.links.core.log.Debugln("Dropping ygg-tunneled connection", local, remote)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
force := net.ParseIP(strings.Split(remote, "%")[0]).IsLinkLocalUnicast()
|
|
||||||
link, err := t.links.create(sock, name, proto, local, remote, incoming, force, options.linkOptions)
|
|
||||||
if err != nil {
|
|
||||||
t.links.core.log.Println(err)
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
t.links.core.log.Debugln("DEBUG: starting handler for", name)
|
|
||||||
ch, err := link.handler()
|
|
||||||
t.links.core.log.Debugln("DEBUG: stopped handler for", name, err)
|
|
||||||
return ch
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
// +build !darwin,!linux
|
|
||||||
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WARNING: This context is used both by net.Dialer and net.Listen in tcp.go
|
|
||||||
|
|
||||||
func (t *tcp) tcpContext(network, address string, c syscall.RawConn) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcp) getControl(sintf string) func(string, string, syscall.RawConn) error {
|
|
||||||
return t.tcpContext
|
|
||||||
}
|
|
125
src/core/tls.go
125
src/core/tls.go
|
@ -1,125 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/ed25519"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
"math/big"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tcptls struct {
|
|
||||||
tcp *tcp
|
|
||||||
config *tls.Config
|
|
||||||
forDialer *TcpUpgrade
|
|
||||||
forListener *TcpUpgrade
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcptls) init(tcp *tcp) {
|
|
||||||
t.tcp = tcp
|
|
||||||
t.forDialer = &TcpUpgrade{
|
|
||||||
upgrade: t.upgradeDialer,
|
|
||||||
name: "tls",
|
|
||||||
}
|
|
||||||
t.forListener = &TcpUpgrade{
|
|
||||||
upgrade: t.upgradeListener,
|
|
||||||
name: "tls",
|
|
||||||
}
|
|
||||||
|
|
||||||
edpriv := make(ed25519.PrivateKey, ed25519.PrivateKeySize)
|
|
||||||
copy(edpriv[:], tcp.links.core.secret[:])
|
|
||||||
|
|
||||||
certBuf := &bytes.Buffer{}
|
|
||||||
|
|
||||||
// TODO: because NotAfter is finite, we should add some mechanism to regenerate the certificate and restart the listeners periodically for nodes with very high uptimes. Perhaps regenerate certs and restart listeners every few months or so.
|
|
||||||
pubtemp := x509.Certificate{
|
|
||||||
SerialNumber: big.NewInt(1),
|
|
||||||
Subject: pkix.Name{
|
|
||||||
CommonName: hex.EncodeToString(tcp.links.core.public[:]),
|
|
||||||
},
|
|
||||||
NotBefore: time.Now(),
|
|
||||||
NotAfter: time.Now().Add(time.Hour * 24 * 365),
|
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
derbytes, err := x509.CreateCertificate(rand.Reader, &pubtemp, &pubtemp, edpriv.Public(), edpriv)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to create certificate: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: derbytes}); err != nil {
|
|
||||||
panic("failed to encode certificate into PEM")
|
|
||||||
}
|
|
||||||
|
|
||||||
cpool := x509.NewCertPool()
|
|
||||||
cpool.AppendCertsFromPEM(derbytes)
|
|
||||||
|
|
||||||
t.config = &tls.Config{
|
|
||||||
RootCAs: cpool,
|
|
||||||
Certificates: []tls.Certificate{
|
|
||||||
{
|
|
||||||
Certificate: [][]byte{derbytes},
|
|
||||||
PrivateKey: edpriv,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
MinVersion: tls.VersionTLS13,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcptls) configForOptions(options *tcpOptions) *tls.Config {
|
|
||||||
config := *t.config
|
|
||||||
config.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
|
|
||||||
if len(rawCerts) != 1 {
|
|
||||||
return errors.New("tls not exactly 1 cert")
|
|
||||||
}
|
|
||||||
cert, err := x509.ParseCertificate(rawCerts[0])
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("tls failed to parse cert")
|
|
||||||
}
|
|
||||||
if cert.PublicKeyAlgorithm != x509.Ed25519 {
|
|
||||||
return errors.New("tls wrong cert algorithm")
|
|
||||||
}
|
|
||||||
pk := cert.PublicKey.(ed25519.PublicKey)
|
|
||||||
var key keyArray
|
|
||||||
copy(key[:], pk)
|
|
||||||
// If options does not have a pinned key, then pin one now
|
|
||||||
if options.pinnedEd25519Keys == nil {
|
|
||||||
options.pinnedEd25519Keys = make(map[keyArray]struct{})
|
|
||||||
options.pinnedEd25519Keys[key] = struct{}{}
|
|
||||||
}
|
|
||||||
if _, isIn := options.pinnedEd25519Keys[key]; !isIn {
|
|
||||||
return errors.New("tls key does not match pinned key")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcptls) upgradeListener(c net.Conn, options *tcpOptions) (net.Conn, error) {
|
|
||||||
config := t.configForOptions(options)
|
|
||||||
conn := tls.Server(c, config)
|
|
||||||
if err := conn.Handshake(); err != nil {
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tcptls) upgradeDialer(c net.Conn, options *tcpOptions) (net.Conn, error) {
|
|
||||||
config := t.configForOptions(options)
|
|
||||||
conn := tls.Client(c, config)
|
|
||||||
if err := conn.Handshake(); err != nil {
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
|
@ -1,12 +1,5 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
// Out-of-band packet types
|
|
||||||
const (
|
|
||||||
typeKeyDummy = iota // nolint:deadcode,varcheck
|
|
||||||
typeKeyLookup
|
|
||||||
typeKeyResponse
|
|
||||||
)
|
|
||||||
|
|
||||||
// In-band packet types
|
// In-band packet types
|
||||||
const (
|
const (
|
||||||
typeSessionDummy = iota // nolint:deadcode,varcheck
|
typeSessionDummy = iota // nolint:deadcode,varcheck
|
||||||
|
|
|
@ -4,6 +4,9 @@ import "github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
|
|
||||||
type MulticastInterfaceConfig = config.MulticastInterfaceConfig
|
type MulticastInterfaceConfig = config.MulticastInterfaceConfig
|
||||||
|
|
||||||
|
var defaultConfig = "" // LDFLAGS='-X github.com/yggdrasil-network/yggdrasil-go/src/defaults.defaultConfig=/path/to/config
|
||||||
|
var defaultAdminListen = "" // LDFLAGS='-X github.com/yggdrasil-network/yggdrasil-go/src/defaults.defaultAdminListen=unix://path/to/sock'
|
||||||
|
|
||||||
// Defines which parameters are expected by default for configuration on a
|
// Defines which parameters are expected by default for configuration on a
|
||||||
// specific platform. These values are populated in the relevant defaults_*.go
|
// specific platform. These values are populated in the relevant defaults_*.go
|
||||||
// for the platform being targeted. They must be set.
|
// for the platform being targeted. They must be set.
|
||||||
|
@ -23,21 +26,34 @@ type platformDefaultParameters struct {
|
||||||
DefaultIfName string
|
DefaultIfName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDefaults() platformDefaultParameters {
|
||||||
|
defaults := getDefaults()
|
||||||
|
if defaultConfig != "" {
|
||||||
|
defaults.DefaultConfigFile = defaultConfig
|
||||||
|
}
|
||||||
|
if defaultAdminListen != "" {
|
||||||
|
defaults.DefaultAdminListen = defaultAdminListen
|
||||||
|
}
|
||||||
|
return defaults
|
||||||
|
}
|
||||||
|
|
||||||
// Generates default configuration and returns a pointer to the resulting
|
// Generates default configuration and returns a pointer to the resulting
|
||||||
// NodeConfig. This is used when outputting the -genconf parameter and also when
|
// NodeConfig. This is used when outputting the -genconf parameter and also when
|
||||||
// using -autoconf.
|
// using -autoconf.
|
||||||
func GenerateConfig() *config.NodeConfig {
|
func GenerateConfig() *config.NodeConfig {
|
||||||
|
// Get the defaults for the platform.
|
||||||
|
defaults := GetDefaults()
|
||||||
// Create a node configuration and populate it.
|
// Create a node configuration and populate it.
|
||||||
cfg := new(config.NodeConfig)
|
cfg := new(config.NodeConfig)
|
||||||
cfg.NewKeys()
|
cfg.NewKeys()
|
||||||
cfg.Listen = []string{}
|
cfg.Listen = []string{}
|
||||||
cfg.AdminListen = GetDefaults().DefaultAdminListen
|
cfg.AdminListen = defaults.DefaultAdminListen
|
||||||
cfg.Peers = []string{}
|
cfg.Peers = []string{}
|
||||||
cfg.InterfacePeers = map[string][]string{}
|
cfg.InterfacePeers = map[string][]string{}
|
||||||
cfg.AllowedPublicKeys = []string{}
|
cfg.AllowedPublicKeys = []string{}
|
||||||
cfg.MulticastInterfaces = GetDefaults().DefaultMulticastInterfaces
|
cfg.MulticastInterfaces = defaults.DefaultMulticastInterfaces
|
||||||
cfg.IfName = GetDefaults().DefaultIfName
|
cfg.IfName = defaults.DefaultIfName
|
||||||
cfg.IfMTU = GetDefaults().DefaultIfMTU
|
cfg.IfMTU = defaults.DefaultIfMTU
|
||||||
cfg.NodeInfoPrivacy = false
|
cfg.NodeInfoPrivacy = false
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
//go:build darwin
|
||||||
// +build darwin
|
// +build darwin
|
||||||
|
|
||||||
package defaults
|
package defaults
|
||||||
|
|
||||||
// Sane defaults for the macOS/Darwin platform. The "default" options may be
|
// Sane defaults for the macOS/Darwin platform. The "default" options may be
|
||||||
// may be replaced by the running configuration.
|
// may be replaced by the running configuration.
|
||||||
func GetDefaults() platformDefaultParameters {
|
func getDefaults() platformDefaultParameters {
|
||||||
return platformDefaultParameters{
|
return platformDefaultParameters{
|
||||||
// Admin
|
// Admin
|
||||||
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
|
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
//go:build freebsd
|
||||||
// +build freebsd
|
// +build freebsd
|
||||||
|
|
||||||
package defaults
|
package defaults
|
||||||
|
|
||||||
// Sane defaults for the BSD platforms. The "default" options may be
|
// Sane defaults for the BSD platforms. The "default" options may be
|
||||||
// may be replaced by the running configuration.
|
// may be replaced by the running configuration.
|
||||||
func GetDefaults() platformDefaultParameters {
|
func getDefaults() platformDefaultParameters {
|
||||||
return platformDefaultParameters{
|
return platformDefaultParameters{
|
||||||
// Admin
|
// Admin
|
||||||
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
|
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
//go:build linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package defaults
|
package defaults
|
||||||
|
|
||||||
// Sane defaults for the Linux platform. The "default" options may be
|
// Sane defaults for the Linux platform. The "default" options may be
|
||||||
// may be replaced by the running configuration.
|
// may be replaced by the running configuration.
|
||||||
func GetDefaults() platformDefaultParameters {
|
func getDefaults() platformDefaultParameters {
|
||||||
return platformDefaultParameters{
|
return platformDefaultParameters{
|
||||||
// Admin
|
// Admin
|
||||||
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
|
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
//go:build openbsd
|
||||||
// +build openbsd
|
// +build openbsd
|
||||||
|
|
||||||
package defaults
|
package defaults
|
||||||
|
|
||||||
// Sane defaults for the BSD platforms. The "default" options may be
|
// Sane defaults for the BSD platforms. The "default" options may be
|
||||||
// may be replaced by the running configuration.
|
// may be replaced by the running configuration.
|
||||||
func GetDefaults() platformDefaultParameters {
|
func getDefaults() platformDefaultParameters {
|
||||||
return platformDefaultParameters{
|
return platformDefaultParameters{
|
||||||
// Admin
|
// Admin
|
||||||
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
|
DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
//go:build !linux && !darwin && !windows && !openbsd && !freebsd
|
||||||
// +build !linux,!darwin,!windows,!openbsd,!freebsd
|
// +build !linux,!darwin,!windows,!openbsd,!freebsd
|
||||||
|
|
||||||
package defaults
|
package defaults
|
||||||
|
|
||||||
// Sane defaults for the other platforms. The "default" options may be
|
// Sane defaults for the other platforms. The "default" options may be
|
||||||
// may be replaced by the running configuration.
|
// may be replaced by the running configuration.
|
||||||
func GetDefaults() platformDefaultParameters {
|
func getDefaults() platformDefaultParameters {
|
||||||
return platformDefaultParameters{
|
return platformDefaultParameters{
|
||||||
// Admin
|
// Admin
|
||||||
DefaultAdminListen: "tcp://localhost:9001",
|
DefaultAdminListen: "tcp://localhost:9001",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package defaults
|
package defaults
|
||||||
|
|
||||||
// Sane defaults for the Windows platform. The "default" options may be
|
// Sane defaults for the Windows platform. The "default" options may be
|
||||||
// may be replaced by the running configuration.
|
// may be replaced by the running configuration.
|
||||||
func GetDefaults() platformDefaultParameters {
|
func getDefaults() platformDefaultParameters {
|
||||||
return platformDefaultParameters{
|
return platformDefaultParameters{
|
||||||
// Admin
|
// Admin
|
||||||
DefaultAdminListen: "tcp://localhost:9001",
|
DefaultAdminListen: "tcp://localhost:9001",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package core
|
package ipv6rwc
|
||||||
|
|
||||||
// The ICMPv6 module implements functions to easily create ICMPv6
|
// The ICMPv6 module implements functions to easily create ICMPv6
|
||||||
// packets. These functions, when mixed with the built-in Go IPv6
|
// packets. These functions, when mixed with the built-in Go IPv6
|
|
@ -1,4 +1,4 @@
|
||||||
package core
|
package ipv6rwc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
|
@ -14,14 +14,22 @@ import (
|
||||||
iwt "github.com/Arceliar/ironwood/types"
|
iwt "github.com/Arceliar/ironwood/types"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
const keyStoreTimeout = 2 * time.Minute
|
const keyStoreTimeout = 2 * time.Minute
|
||||||
|
|
||||||
|
// Out-of-band packet types
|
||||||
|
const (
|
||||||
|
typeKeyDummy = iota // nolint:deadcode,varcheck
|
||||||
|
typeKeyLookup
|
||||||
|
typeKeyResponse
|
||||||
|
)
|
||||||
|
|
||||||
type keyArray [ed25519.PublicKeySize]byte
|
type keyArray [ed25519.PublicKeySize]byte
|
||||||
|
|
||||||
type keyStore struct {
|
type keyStore struct {
|
||||||
core *Core
|
core *core.Core
|
||||||
address address.Address
|
address address.Address
|
||||||
subnet address.Subnet
|
subnet address.Subnet
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
|
@ -45,11 +53,11 @@ type buffer struct {
|
||||||
timeout *time.Timer
|
timeout *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyStore) init(core *Core) {
|
func (k *keyStore) init(c *core.Core) {
|
||||||
k.core = core
|
k.core = c
|
||||||
k.address = *address.AddrForKey(k.core.public)
|
k.address = *address.AddrForKey(k.core.PublicKey())
|
||||||
k.subnet = *address.SubnetForKey(k.core.public)
|
k.subnet = *address.SubnetForKey(k.core.PublicKey())
|
||||||
if err := k.core.pc.SetOutOfBandHandler(k.oobHandler); err != nil {
|
if err := k.core.SetOutOfBandHandler(k.oobHandler); err != nil {
|
||||||
err = fmt.Errorf("tun.core.SetOutOfBandHander: %w", err)
|
err = fmt.Errorf("tun.core.SetOutOfBandHander: %w", err)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +74,7 @@ func (k *keyStore) sendToAddress(addr address.Address, bs []byte) {
|
||||||
if info := k.addrToInfo[addr]; info != nil {
|
if info := k.addrToInfo[addr]; info != nil {
|
||||||
k.resetTimeout(info)
|
k.resetTimeout(info)
|
||||||
k.mutex.Unlock()
|
k.mutex.Unlock()
|
||||||
_, _ = k.core.pc.WriteTo(bs, iwt.Addr(info.key[:]))
|
_, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:]))
|
||||||
} else {
|
} else {
|
||||||
var buf *buffer
|
var buf *buffer
|
||||||
if buf = k.addrBuffer[addr]; buf == nil {
|
if buf = k.addrBuffer[addr]; buf == nil {
|
||||||
|
@ -95,7 +103,7 @@ func (k *keyStore) sendToSubnet(subnet address.Subnet, bs []byte) {
|
||||||
if info := k.subnetToInfo[subnet]; info != nil {
|
if info := k.subnetToInfo[subnet]; info != nil {
|
||||||
k.resetTimeout(info)
|
k.resetTimeout(info)
|
||||||
k.mutex.Unlock()
|
k.mutex.Unlock()
|
||||||
_, _ = k.core.pc.WriteTo(bs, iwt.Addr(info.key[:]))
|
_, _ = k.core.WriteTo(bs, iwt.Addr(info.key[:]))
|
||||||
} else {
|
} else {
|
||||||
var buf *buffer
|
var buf *buffer
|
||||||
if buf = k.subnetBuffer[subnet]; buf == nil {
|
if buf = k.subnetBuffer[subnet]; buf == nil {
|
||||||
|
@ -124,6 +132,7 @@ func (k *keyStore) update(key ed25519.PublicKey) *keyInfo {
|
||||||
var kArray keyArray
|
var kArray keyArray
|
||||||
copy(kArray[:], key)
|
copy(kArray[:], key)
|
||||||
var info *keyInfo
|
var info *keyInfo
|
||||||
|
var packets [][]byte
|
||||||
if info = k.keyToInfo[kArray]; info == nil {
|
if info = k.keyToInfo[kArray]; info == nil {
|
||||||
info = new(keyInfo)
|
info = new(keyInfo)
|
||||||
info.key = kArray
|
info.key = kArray
|
||||||
|
@ -132,19 +141,19 @@ func (k *keyStore) update(key ed25519.PublicKey) *keyInfo {
|
||||||
k.keyToInfo[info.key] = info
|
k.keyToInfo[info.key] = info
|
||||||
k.addrToInfo[info.address] = info
|
k.addrToInfo[info.address] = info
|
||||||
k.subnetToInfo[info.subnet] = info
|
k.subnetToInfo[info.subnet] = info
|
||||||
k.resetTimeout(info)
|
|
||||||
k.mutex.Unlock()
|
|
||||||
if buf := k.addrBuffer[info.address]; buf != nil {
|
if buf := k.addrBuffer[info.address]; buf != nil {
|
||||||
k.core.pc.WriteTo(buf.packet, iwt.Addr(info.key[:]))
|
packets = append(packets, buf.packet)
|
||||||
delete(k.addrBuffer, info.address)
|
delete(k.addrBuffer, info.address)
|
||||||
}
|
}
|
||||||
if buf := k.subnetBuffer[info.subnet]; buf != nil {
|
if buf := k.subnetBuffer[info.subnet]; buf != nil {
|
||||||
k.core.pc.WriteTo(buf.packet, iwt.Addr(info.key[:]))
|
packets = append(packets, buf.packet)
|
||||||
delete(k.subnetBuffer, info.subnet)
|
delete(k.subnetBuffer, info.subnet)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
k.resetTimeout(info)
|
k.resetTimeout(info)
|
||||||
k.mutex.Unlock()
|
k.mutex.Unlock()
|
||||||
|
for _, packet := range packets {
|
||||||
|
_, _ = k.core.WriteTo(packet, iwt.Addr(info.key[:]))
|
||||||
}
|
}
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
@ -191,46 +200,29 @@ func (k *keyStore) oobHandler(fromKey, toKey ed25519.PublicKey, data []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyStore) sendKeyLookup(partial ed25519.PublicKey) {
|
func (k *keyStore) sendKeyLookup(partial ed25519.PublicKey) {
|
||||||
sig := ed25519.Sign(k.core.secret, partial[:])
|
sig := ed25519.Sign(k.core.PrivateKey(), partial[:])
|
||||||
bs := append([]byte{typeKeyLookup}, sig...)
|
bs := append([]byte{typeKeyLookup}, sig...)
|
||||||
_ = k.core.pc.SendOutOfBand(partial, bs)
|
_ = k.core.SendOutOfBand(partial, bs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyStore) sendKeyResponse(dest ed25519.PublicKey) {
|
func (k *keyStore) sendKeyResponse(dest ed25519.PublicKey) {
|
||||||
sig := ed25519.Sign(k.core.secret, dest[:])
|
sig := ed25519.Sign(k.core.PrivateKey(), dest[:])
|
||||||
bs := append([]byte{typeKeyResponse}, sig...)
|
bs := append([]byte{typeKeyResponse}, sig...)
|
||||||
_ = k.core.pc.SendOutOfBand(dest, bs)
|
_ = k.core.SendOutOfBand(dest, bs)
|
||||||
}
|
|
||||||
|
|
||||||
func (k *keyStore) maxSessionMTU() uint64 {
|
|
||||||
const sessionTypeOverhead = 1
|
|
||||||
return k.core.pc.MTU() - sessionTypeOverhead
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyStore) readPC(p []byte) (int, error) {
|
func (k *keyStore) readPC(p []byte) (int, error) {
|
||||||
buf := make([]byte, k.core.pc.MTU(), 65535)
|
buf := make([]byte, k.core.MTU(), 65535)
|
||||||
for {
|
for {
|
||||||
bs := buf
|
bs := buf
|
||||||
n, from, err := k.core.pc.ReadFrom(bs)
|
n, from, err := k.core.ReadFrom(bs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch bs[0] {
|
bs = bs[:n]
|
||||||
case typeSessionTraffic:
|
|
||||||
// This is what we want to handle here
|
|
||||||
case typeSessionProto:
|
|
||||||
var key keyArray
|
|
||||||
copy(key[:], from.(iwt.Addr))
|
|
||||||
data := append([]byte(nil), bs[1:n]...)
|
|
||||||
k.core.proto.handleProto(nil, key, data)
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
bs = bs[1:n]
|
|
||||||
if len(bs) == 0 {
|
if len(bs) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -245,11 +237,11 @@ func (k *keyStore) readPC(p []byte) (int, error) {
|
||||||
k.mutex.Unlock()
|
k.mutex.Unlock()
|
||||||
if len(bs) > mtu {
|
if len(bs) > mtu {
|
||||||
// Using bs would make it leak off the stack, so copy to buf
|
// Using bs would make it leak off the stack, so copy to buf
|
||||||
buf := make([]byte, 40)
|
buf := make([]byte, 512)
|
||||||
copy(buf, bs)
|
cn := copy(buf, bs)
|
||||||
ptb := &icmp.PacketTooBig{
|
ptb := &icmp.PacketTooBig{
|
||||||
MTU: mtu,
|
MTU: mtu,
|
||||||
Data: buf[:40],
|
Data: buf[:cn],
|
||||||
}
|
}
|
||||||
if packet, err := CreateICMPv6(buf[8:24], buf[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil {
|
if packet, err := CreateICMPv6(buf[8:24], buf[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil {
|
||||||
_, _ = k.writePC(packet)
|
_, _ = k.writePC(packet)
|
||||||
|
@ -294,15 +286,69 @@ func (k *keyStore) writePC(bs []byte) (int, error) {
|
||||||
strErr := fmt.Sprint("incorrect source address: ", net.IP(srcAddr[:]).String())
|
strErr := fmt.Sprint("incorrect source address: ", net.IP(srcAddr[:]).String())
|
||||||
return 0, errors.New(strErr)
|
return 0, errors.New(strErr)
|
||||||
}
|
}
|
||||||
buf := make([]byte, 1+len(bs), 65535)
|
|
||||||
buf[0] = typeSessionTraffic
|
|
||||||
copy(buf[1:], bs)
|
|
||||||
if dstAddr.IsValid() {
|
if dstAddr.IsValid() {
|
||||||
k.sendToAddress(dstAddr, buf)
|
k.sendToAddress(dstAddr, bs)
|
||||||
} else if dstSubnet.IsValid() {
|
} else if dstSubnet.IsValid() {
|
||||||
k.sendToSubnet(dstSubnet, buf)
|
k.sendToSubnet(dstSubnet, bs)
|
||||||
} else {
|
} else {
|
||||||
return 0, errors.New("invalid destination address")
|
return 0, errors.New("invalid destination address")
|
||||||
}
|
}
|
||||||
return len(bs), nil
|
return len(bs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exported API
|
||||||
|
|
||||||
|
func (k *keyStore) MaxMTU() uint64 {
|
||||||
|
return k.core.MTU()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *keyStore) SetMTU(mtu uint64) {
|
||||||
|
if mtu > k.MaxMTU() {
|
||||||
|
mtu = k.MaxMTU()
|
||||||
|
}
|
||||||
|
if mtu < 1280 {
|
||||||
|
mtu = 1280
|
||||||
|
}
|
||||||
|
k.mutex.Lock()
|
||||||
|
k.mtu = mtu
|
||||||
|
k.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *keyStore) MTU() uint64 {
|
||||||
|
k.mutex.Lock()
|
||||||
|
mtu := k.mtu
|
||||||
|
k.mutex.Unlock()
|
||||||
|
return mtu
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReadWriteCloser struct {
|
||||||
|
keyStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReadWriteCloser(c *core.Core) *ReadWriteCloser {
|
||||||
|
rwc := new(ReadWriteCloser)
|
||||||
|
rwc.init(c)
|
||||||
|
return rwc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rwc *ReadWriteCloser) Address() address.Address {
|
||||||
|
return rwc.address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rwc *ReadWriteCloser) Subnet() address.Subnet {
|
||||||
|
return rwc.subnet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rwc *ReadWriteCloser) Read(p []byte) (n int, err error) {
|
||||||
|
return rwc.readPC(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rwc *ReadWriteCloser) Write(p []byte) (n int, err error) {
|
||||||
|
return rwc.writePC(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rwc *ReadWriteCloser) Close() error {
|
||||||
|
err := rwc.core.Close()
|
||||||
|
rwc.core.Stop()
|
||||||
|
return err
|
||||||
|
}
|
|
@ -20,15 +20,18 @@ func (m *Multicast) getMulticastInterfacesHandler(req *GetMulticastInterfacesReq
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) SetupAdminHandlers(a *admin.AdminSocket) {
|
func (m *Multicast) SetupAdminHandlers(a *admin.AdminSocket) {
|
||||||
_ = a.AddHandler("getMulticastInterfaces", []string{}, func(in json.RawMessage) (interface{}, error) {
|
_ = a.AddHandler(
|
||||||
req := &GetMulticastInterfacesRequest{}
|
"getMulticastInterfaces", "Show which interfaces multicast is enabled on", []string{},
|
||||||
res := &GetMulticastInterfacesResponse{}
|
func(in json.RawMessage) (interface{}, error) {
|
||||||
if err := json.Unmarshal(in, &req); err != nil {
|
req := &GetMulticastInterfacesRequest{}
|
||||||
return nil, err
|
res := &GetMulticastInterfacesResponse{}
|
||||||
}
|
if err := json.Unmarshal(in, &req); err != nil {
|
||||||
if err := m.getMulticastInterfacesHandler(req, res); err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
if err := m.getMulticastInterfacesHandler(req, res); err != nil {
|
||||||
return res, nil
|
return nil, err
|
||||||
})
|
}
|
||||||
|
return res, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Arceliar/phony"
|
"github.com/Arceliar/phony"
|
||||||
"github.com/gologme/log"
|
"github.com/gologme/log"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
"golang.org/x/net/ipv6"
|
"golang.org/x/net/ipv6"
|
||||||
)
|
)
|
||||||
|
@ -27,13 +25,15 @@ import (
|
||||||
type Multicast struct {
|
type Multicast struct {
|
||||||
phony.Inbox
|
phony.Inbox
|
||||||
core *core.Core
|
core *core.Core
|
||||||
config *config.NodeConfig
|
|
||||||
log *log.Logger
|
log *log.Logger
|
||||||
sock *ipv6.PacketConn
|
sock *ipv6.PacketConn
|
||||||
groupAddr string
|
_isOpen bool
|
||||||
listeners map[string]*listenerInfo
|
_listeners map[string]*listenerInfo
|
||||||
isOpen bool
|
_interfaces map[string]*interfaceInfo
|
||||||
_interfaces map[string]interfaceInfo
|
config struct {
|
||||||
|
_groupAddr GroupAddress
|
||||||
|
_interfaces map[MulticastInterface]struct{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type interfaceInfo struct {
|
type interfaceInfo struct {
|
||||||
|
@ -45,46 +45,44 @@ type interfaceInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type listenerInfo struct {
|
type listenerInfo struct {
|
||||||
listener *core.TcpListener
|
listener *core.Listener
|
||||||
time time.Time
|
time time.Time
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
port uint16
|
port uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init prepares the multicast interface for use.
|
|
||||||
func (m *Multicast) Init(core *core.Core, nc *config.NodeConfig, log *log.Logger, options interface{}) error {
|
|
||||||
m.core = core
|
|
||||||
m.config = nc
|
|
||||||
m.log = log
|
|
||||||
m.listeners = make(map[string]*listenerInfo)
|
|
||||||
m._interfaces = make(map[string]interfaceInfo)
|
|
||||||
m.groupAddr = "[ff02::114]:9001"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start starts the multicast interface. This launches goroutines which will
|
// Start starts the multicast interface. This launches goroutines which will
|
||||||
// listen for multicast beacons from other hosts and will advertise multicast
|
// listen for multicast beacons from other hosts and will advertise multicast
|
||||||
// beacons out to the network.
|
// beacons out to the network.
|
||||||
func (m *Multicast) Start() error {
|
func New(core *core.Core, log *log.Logger, opts ...SetupOption) (*Multicast, error) {
|
||||||
|
m := &Multicast{
|
||||||
|
core: core,
|
||||||
|
log: log,
|
||||||
|
_listeners: make(map[string]*listenerInfo),
|
||||||
|
_interfaces: make(map[string]*interfaceInfo),
|
||||||
|
}
|
||||||
|
m.config._interfaces = map[MulticastInterface]struct{}{}
|
||||||
|
m.config._groupAddr = GroupAddress("[ff02::114]:9001")
|
||||||
|
for _, opt := range opts {
|
||||||
|
m._applyOption(opt)
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
phony.Block(m, func() {
|
phony.Block(m, func() {
|
||||||
err = m._start()
|
err = m._start()
|
||||||
})
|
})
|
||||||
m.log.Debugln("Started multicast module")
|
return m, err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) _start() error {
|
func (m *Multicast) _start() error {
|
||||||
if m.isOpen {
|
if m._isOpen {
|
||||||
return fmt.Errorf("multicast module is already started")
|
return fmt.Errorf("multicast module is already started")
|
||||||
}
|
}
|
||||||
m.config.RLock()
|
if len(m.config._interfaces) == 0 {
|
||||||
defer m.config.RUnlock()
|
|
||||||
if len(m.config.MulticastInterfaces) == 0 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
m.log.Infoln("Starting multicast module")
|
m.log.Debugln("Starting multicast module")
|
||||||
addr, err := net.ResolveUDPAddr("udp", m.groupAddr)
|
defer m.log.Debugln("Started multicast module")
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", string(m.config._groupAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -101,7 +99,7 @@ func (m *Multicast) _start() error {
|
||||||
// Windows can't set this flag, so we need to handle it in other ways
|
// Windows can't set this flag, so we need to handle it in other ways
|
||||||
}
|
}
|
||||||
|
|
||||||
m.isOpen = true
|
m._isOpen = true
|
||||||
go m.listen()
|
go m.listen()
|
||||||
m.Act(nil, m._multicastStarted)
|
m.Act(nil, m._multicastStarted)
|
||||||
m.Act(nil, m._announce)
|
m.Act(nil, m._announce)
|
||||||
|
@ -113,7 +111,7 @@ func (m *Multicast) _start() error {
|
||||||
func (m *Multicast) IsStarted() bool {
|
func (m *Multicast) IsStarted() bool {
|
||||||
var isOpen bool
|
var isOpen bool
|
||||||
phony.Block(m, func() {
|
phony.Block(m, func() {
|
||||||
isOpen = m.isOpen
|
isOpen = m._isOpen
|
||||||
})
|
})
|
||||||
return isOpen
|
return isOpen
|
||||||
}
|
}
|
||||||
|
@ -130,7 +128,7 @@ func (m *Multicast) Stop() error {
|
||||||
|
|
||||||
func (m *Multicast) _stop() error {
|
func (m *Multicast) _stop() error {
|
||||||
m.log.Infoln("Stopping multicast module")
|
m.log.Infoln("Stopping multicast module")
|
||||||
m.isOpen = false
|
m._isOpen = false
|
||||||
if m.sock != nil {
|
if m.sock != nil {
|
||||||
m.sock.Close()
|
m.sock.Close()
|
||||||
}
|
}
|
||||||
|
@ -138,7 +136,7 @@ func (m *Multicast) _stop() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) _updateInterfaces() {
|
func (m *Multicast) _updateInterfaces() {
|
||||||
interfaces := m.getAllowedInterfaces()
|
interfaces := m._getAllowedInterfaces()
|
||||||
for name, info := range interfaces {
|
for name, info := range interfaces {
|
||||||
addrs, err := info.iface.Addrs()
|
addrs, err := info.iface.Addrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -163,10 +161,8 @@ func (m *Multicast) Interfaces() map[string]net.Interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAllowedInterfaces returns the currently known/enabled multicast interfaces.
|
// getAllowedInterfaces returns the currently known/enabled multicast interfaces.
|
||||||
func (m *Multicast) getAllowedInterfaces() map[string]interfaceInfo {
|
func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
|
||||||
interfaces := make(map[string]interfaceInfo)
|
interfaces := make(map[string]*interfaceInfo)
|
||||||
// Get interface expressions from config
|
|
||||||
ifcfgs := m.config.MulticastInterfaces
|
|
||||||
// Ask the system for network interfaces
|
// Ask the system for network interfaces
|
||||||
allifaces, err := net.Interfaces()
|
allifaces, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -176,62 +172,55 @@ func (m *Multicast) getAllowedInterfaces() map[string]interfaceInfo {
|
||||||
}
|
}
|
||||||
// Work out which interfaces to announce on
|
// Work out which interfaces to announce on
|
||||||
for _, iface := range allifaces {
|
for _, iface := range allifaces {
|
||||||
if iface.Flags&net.FlagUp == 0 {
|
switch {
|
||||||
// Ignore interfaces that are down
|
case iface.Flags&net.FlagUp == 0:
|
||||||
continue
|
continue // Ignore interfaces that are down
|
||||||
|
case iface.Flags&net.FlagMulticast == 0:
|
||||||
|
continue // Ignore non-multicast interfaces
|
||||||
|
case iface.Flags&net.FlagPointToPoint != 0:
|
||||||
|
continue // Ignore point-to-point interfaces
|
||||||
}
|
}
|
||||||
if iface.Flags&net.FlagMulticast == 0 {
|
for ifcfg := range m.config._interfaces {
|
||||||
// Ignore non-multicast interfaces
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if iface.Flags&net.FlagPointToPoint != 0 {
|
|
||||||
// Ignore point-to-point interfaces
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, ifcfg := range ifcfgs {
|
|
||||||
// Compile each regular expression
|
// Compile each regular expression
|
||||||
e, err := regexp.Compile(ifcfg.Regex)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// Does the interface match the regular expression? Store it if so
|
// Does the interface match the regular expression? Store it if so
|
||||||
if e.MatchString(iface.Name) {
|
if !ifcfg.Beacon && !ifcfg.Listen {
|
||||||
if ifcfg.Beacon || ifcfg.Listen {
|
continue
|
||||||
info := interfaceInfo{
|
|
||||||
iface: iface,
|
|
||||||
beacon: ifcfg.Beacon,
|
|
||||||
listen: ifcfg.Listen,
|
|
||||||
port: ifcfg.Port,
|
|
||||||
}
|
|
||||||
interfaces[iface.Name] = info
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
if !ifcfg.Regex.MatchString(iface.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
interfaces[iface.Name] = &interfaceInfo{
|
||||||
|
iface: iface,
|
||||||
|
beacon: ifcfg.Beacon,
|
||||||
|
listen: ifcfg.Listen,
|
||||||
|
port: ifcfg.Port,
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return interfaces
|
return interfaces
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) _announce() {
|
func (m *Multicast) _announce() {
|
||||||
if !m.isOpen {
|
if !m._isOpen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m._updateInterfaces()
|
m._updateInterfaces()
|
||||||
groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
|
groupAddr, err := net.ResolveUDPAddr("udp6", string(m.config._groupAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
destAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
|
destAddr, err := net.ResolveUDPAddr("udp6", string(m.config._groupAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
// There might be interfaces that we configured listeners for but are no
|
// There might be interfaces that we configured listeners for but are no
|
||||||
// longer up - if that's the case then we should stop the listeners
|
// longer up - if that's the case then we should stop the listeners
|
||||||
for name, info := range m.listeners {
|
for name, info := range m._listeners {
|
||||||
// Prepare our stop function!
|
// Prepare our stop function!
|
||||||
stop := func() {
|
stop := func() {
|
||||||
info.listener.Stop()
|
info.listener.Close()
|
||||||
delete(m.listeners, name)
|
delete(m._listeners, name)
|
||||||
m.log.Debugln("No longer multicasting on", name)
|
m.log.Debugln("No longer multicasting on", name)
|
||||||
}
|
}
|
||||||
// If the interface is no longer visible on the system then stop the
|
// If the interface is no longer visible on the system then stop the
|
||||||
|
@ -290,7 +279,7 @@ func (m *Multicast) _announce() {
|
||||||
}
|
}
|
||||||
// Try and see if we already have a TCP listener for this interface
|
// Try and see if we already have a TCP listener for this interface
|
||||||
var linfo *listenerInfo
|
var linfo *listenerInfo
|
||||||
if nfo, ok := m.listeners[iface.Name]; !ok || nfo.listener.Listener == nil {
|
if nfo, ok := m._listeners[iface.Name]; !ok || nfo.listener.Listener == nil {
|
||||||
// No listener was found - let's create one
|
// No listener was found - let's create one
|
||||||
urlString := fmt.Sprintf("tls://[%s]:%d", addrIP, info.port)
|
urlString := fmt.Sprintf("tls://[%s]:%d", addrIP, info.port)
|
||||||
u, err := url.Parse(urlString)
|
u, err := url.Parse(urlString)
|
||||||
|
@ -300,14 +289,14 @@ func (m *Multicast) _announce() {
|
||||||
if li, err := m.core.Listen(u, iface.Name); err == nil {
|
if li, err := m.core.Listen(u, iface.Name); err == nil {
|
||||||
m.log.Debugln("Started multicasting on", iface.Name)
|
m.log.Debugln("Started multicasting on", iface.Name)
|
||||||
// Store the listener so that we can stop it later if needed
|
// Store the listener so that we can stop it later if needed
|
||||||
linfo = &listenerInfo{listener: li, time: time.Now()}
|
linfo = &listenerInfo{listener: li, time: time.Now(), port: info.port}
|
||||||
m.listeners[iface.Name] = linfo
|
m._listeners[iface.Name] = linfo
|
||||||
} else {
|
} else {
|
||||||
m.log.Warnln("Not multicasting on", iface.Name, "due to error:", err)
|
m.log.Warnln("Not multicasting on", iface.Name, "due to error:", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// An existing listener was found
|
// An existing listener was found
|
||||||
linfo = m.listeners[iface.Name]
|
linfo = m._listeners[iface.Name]
|
||||||
}
|
}
|
||||||
// Make sure nothing above failed for some reason
|
// Make sure nothing above failed for some reason
|
||||||
if linfo == nil {
|
if linfo == nil {
|
||||||
|
@ -340,7 +329,7 @@ func (m *Multicast) _announce() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) listen() {
|
func (m *Multicast) listen() {
|
||||||
groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
|
groupAddr, err := net.ResolveUDPAddr("udp6", string(m.config._groupAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -388,7 +377,7 @@ func (m *Multicast) listen() {
|
||||||
if !from.IP.Equal(addr.IP) {
|
if !from.IP.Equal(addr.IP) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var interfaces map[string]interfaceInfo
|
var interfaces map[string]*interfaceInfo
|
||||||
phony.Block(m, func() {
|
phony.Block(m, func() {
|
||||||
interfaces = m._interfaces
|
interfaces = m._interfaces
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,48 +1,17 @@
|
||||||
// +build darwin
|
//go:build !cgo && (darwin || ios)
|
||||||
|
// +build !cgo
|
||||||
|
// +build darwin ios
|
||||||
|
|
||||||
package multicast
|
package multicast
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo CFLAGS: -x objective-c
|
|
||||||
#cgo LDFLAGS: -framework Foundation
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
NSNetServiceBrowser *serviceBrowser;
|
|
||||||
void StartAWDLBrowsing() {
|
|
||||||
if (serviceBrowser == nil) {
|
|
||||||
serviceBrowser = [[NSNetServiceBrowser alloc] init];
|
|
||||||
serviceBrowser.includesPeerToPeer = YES;
|
|
||||||
}
|
|
||||||
[serviceBrowser searchForServicesOfType:@"_yggdrasil._tcp" inDomain:@""];
|
|
||||||
}
|
|
||||||
void StopAWDLBrowsing() {
|
|
||||||
if (serviceBrowser == nil) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[serviceBrowser stop];
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *Multicast) _multicastStarted() {
|
func (m *Multicast) _multicastStarted() {
|
||||||
if !m.isOpen {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
C.StopAWDLBrowsing()
|
|
||||||
for intf := range m._interfaces {
|
|
||||||
if intf == "awdl0" {
|
|
||||||
C.StartAWDLBrowsing()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time.AfterFunc(time.Minute, func() {
|
|
||||||
m.Act(nil, m._multicastStarted)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error {
|
func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error {
|
||||||
|
|
69
src/multicast/multicast_darwin_cgo.go
Normal file
69
src/multicast/multicast_darwin_cgo.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
//go:build (darwin && cgo) || (ios && cgo)
|
||||||
|
// +build darwin,cgo ios,cgo
|
||||||
|
|
||||||
|
package multicast
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -x objective-c
|
||||||
|
#cgo LDFLAGS: -framework Foundation
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
NSNetServiceBrowser *serviceBrowser;
|
||||||
|
void StartAWDLBrowsing() {
|
||||||
|
if (serviceBrowser == nil) {
|
||||||
|
serviceBrowser = [[NSNetServiceBrowser alloc] init];
|
||||||
|
serviceBrowser.includesPeerToPeer = YES;
|
||||||
|
}
|
||||||
|
[serviceBrowser searchForServicesOfType:@"_yggdrasil._tcp" inDomain:@""];
|
||||||
|
}
|
||||||
|
void StopAWDLBrowsing() {
|
||||||
|
if (serviceBrowser == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[serviceBrowser stop];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *Multicast) _multicastStarted() {
|
||||||
|
if !m._isOpen {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
C.StopAWDLBrowsing()
|
||||||
|
for intf := range m._interfaces {
|
||||||
|
if intf == "awdl0" {
|
||||||
|
C.StartAWDLBrowsing()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.AfterFunc(time.Minute, func() {
|
||||||
|
m.Act(nil, m._multicastStarted)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error {
|
||||||
|
var control error
|
||||||
|
var reuseport error
|
||||||
|
var recvanyif error
|
||||||
|
|
||||||
|
control = c.Control(func(fd uintptr) {
|
||||||
|
reuseport = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
|
||||||
|
|
||||||
|
// sys/socket.h: #define SO_RECV_ANYIF 0x1104
|
||||||
|
recvanyif = unix.SetsockoptInt(int(fd), syscall.SOL_SOCKET, 0x1104, 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case reuseport != nil:
|
||||||
|
return reuseport
|
||||||
|
case recvanyif != nil:
|
||||||
|
return recvanyif
|
||||||
|
default:
|
||||||
|
return control
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
// +build !linux,!darwin,!netbsd,!freebsd,!openbsd,!dragonflybsd,!windows
|
//go:build !linux && !darwin && !ios && !netbsd && !freebsd && !openbsd && !dragonflybsd && !windows
|
||||||
|
// +build !linux,!darwin,!ios,!netbsd,!freebsd,!openbsd,!dragonflybsd,!windows
|
||||||
|
|
||||||
package multicast
|
package multicast
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
//go:build linux || netbsd || freebsd || openbsd || dragonflybsd
|
||||||
// +build linux netbsd freebsd openbsd dragonflybsd
|
// +build linux netbsd freebsd openbsd dragonflybsd
|
||||||
|
|
||||||
package multicast
|
package multicast
|
||||||
|
|
||||||
import "syscall"
|
import (
|
||||||
import "golang.org/x/sys/unix"
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
func (m *Multicast) _multicastStarted() {
|
func (m *Multicast) _multicastStarted() {
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package multicast
|
package multicast
|
||||||
|
|
||||||
import "syscall"
|
import (
|
||||||
import "golang.org/x/sys/windows"
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
func (m *Multicast) _multicastStarted() {
|
func (m *Multicast) _multicastStarted() {
|
||||||
|
|
||||||
|
|
28
src/multicast/options.go
Normal file
28
src/multicast/options.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package multicast
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
func (m *Multicast) _applyOption(opt SetupOption) {
|
||||||
|
switch v := opt.(type) {
|
||||||
|
case MulticastInterface:
|
||||||
|
m.config._interfaces[v] = struct{}{}
|
||||||
|
case GroupAddress:
|
||||||
|
m.config._groupAddr = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetupOption interface {
|
||||||
|
isSetupOption()
|
||||||
|
}
|
||||||
|
|
||||||
|
type MulticastInterface struct {
|
||||||
|
Regex *regexp.Regexp
|
||||||
|
Beacon bool
|
||||||
|
Listen bool
|
||||||
|
Port uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupAddress string
|
||||||
|
|
||||||
|
func (a MulticastInterface) isSetupOption() {}
|
||||||
|
func (a GroupAddress) isSetupOption() {}
|
|
@ -7,31 +7,39 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type GetTUNRequest struct{}
|
type GetTUNRequest struct{}
|
||||||
type GetTUNResponse map[string]TUNEntry
|
type GetTUNResponse struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
MTU uint64 `json:"mtu,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type TUNEntry struct {
|
type TUNEntry struct {
|
||||||
MTU uint64 `json:"mtu"`
|
MTU uint64 `json:"mtu"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TunAdapter) getTUNHandler(req *GetTUNRequest, res *GetTUNResponse) error {
|
func (t *TunAdapter) getTUNHandler(req *GetTUNRequest, res *GetTUNResponse) error {
|
||||||
*res = GetTUNResponse{
|
res.Enabled = t.isEnabled
|
||||||
t.Name(): TUNEntry{
|
if !t.isEnabled {
|
||||||
MTU: t.MTU(),
|
return nil
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
res.Name = t.Name()
|
||||||
|
res.MTU = t.MTU()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TunAdapter) SetupAdminHandlers(a *admin.AdminSocket) {
|
func (t *TunAdapter) SetupAdminHandlers(a *admin.AdminSocket) {
|
||||||
_ = a.AddHandler("getTunTap", []string{}, func(in json.RawMessage) (interface{}, error) {
|
_ = a.AddHandler(
|
||||||
req := &GetTUNRequest{}
|
"getTun", "Show information about the node's TUN interface", []string{},
|
||||||
res := &GetTUNResponse{}
|
func(in json.RawMessage) (interface{}, error) {
|
||||||
if err := json.Unmarshal(in, &req); err != nil {
|
req := &GetTUNRequest{}
|
||||||
return nil, err
|
res := &GetTUNResponse{}
|
||||||
}
|
if err := json.Unmarshal(in, &req); err != nil {
|
||||||
if err := t.getTUNHandler(req, res); err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
if err := t.getTUNHandler(req, res); err != nil {
|
||||||
return res, nil
|
return nil, err
|
||||||
})
|
}
|
||||||
|
return res, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ func (tun *TunAdapter) read() {
|
||||||
begin := TUN_OFFSET_BYTES
|
begin := TUN_OFFSET_BYTES
|
||||||
end := begin + n
|
end := begin + n
|
||||||
bs := buf[begin:end]
|
bs := buf[begin:end]
|
||||||
if _, err := tun.core.Write(bs); err != nil {
|
if _, err := tun.rwc.Write(bs); err != nil {
|
||||||
tun.log.Debugln("Unable to send packet:", err)
|
tun.log.Debugln("Unable to send packet:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ func (tun *TunAdapter) write() {
|
||||||
var buf [TUN_OFFSET_BYTES + 65535]byte
|
var buf [TUN_OFFSET_BYTES + 65535]byte
|
||||||
for {
|
for {
|
||||||
bs := buf[TUN_OFFSET_BYTES:]
|
bs := buf[TUN_OFFSET_BYTES:]
|
||||||
n, err := tun.core.Read(bs)
|
n, err := tun.rwc.Read(bs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.log.Errorln("Exiting tun writer due to core read error:", err)
|
tun.log.Errorln("Exiting tun writer due to core read error:", err)
|
||||||
return
|
return
|
||||||
|
|
20
src/tuntap/options.go
Normal file
20
src/tuntap/options.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package tuntap
|
||||||
|
|
||||||
|
func (m *TunAdapter) _applyOption(opt SetupOption) {
|
||||||
|
switch v := opt.(type) {
|
||||||
|
case InterfaceName:
|
||||||
|
m.config.name = v
|
||||||
|
case InterfaceMTU:
|
||||||
|
m.config.mtu = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetupOption interface {
|
||||||
|
isSetupOption()
|
||||||
|
}
|
||||||
|
|
||||||
|
type InterfaceName string
|
||||||
|
type InterfaceMTU uint64
|
||||||
|
|
||||||
|
func (a InterfaceName) isSetupOption() {}
|
||||||
|
func (a InterfaceMTU) isSetupOption() {}
|
|
@ -16,13 +16,12 @@ import (
|
||||||
//"sync"
|
//"sync"
|
||||||
|
|
||||||
"github.com/Arceliar/phony"
|
"github.com/Arceliar/phony"
|
||||||
"github.com/gologme/log"
|
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
"github.com/yggdrasil-network/yggdrasil-go/src/defaults"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MTU uint16
|
type MTU uint16
|
||||||
|
@ -32,9 +31,8 @@ type MTU uint16
|
||||||
// should pass this object to the yggdrasil.SetRouterAdapter() function before
|
// should pass this object to the yggdrasil.SetRouterAdapter() function before
|
||||||
// calling yggdrasil.Start().
|
// calling yggdrasil.Start().
|
||||||
type TunAdapter struct {
|
type TunAdapter struct {
|
||||||
core *core.Core
|
rwc *ipv6rwc.ReadWriteCloser
|
||||||
config *config.NodeConfig
|
log util.Logger
|
||||||
log *log.Logger
|
|
||||||
addr address.Address
|
addr address.Address
|
||||||
subnet address.Subnet
|
subnet address.Subnet
|
||||||
mtu uint64
|
mtu uint64
|
||||||
|
@ -43,6 +41,10 @@ type TunAdapter struct {
|
||||||
//mutex sync.RWMutex // Protects the below
|
//mutex sync.RWMutex // Protects the below
|
||||||
isOpen bool
|
isOpen bool
|
||||||
isEnabled bool // Used by the writer to drop sessionTraffic if not enabled
|
isEnabled bool // Used by the writer to drop sessionTraffic if not enabled
|
||||||
|
config struct {
|
||||||
|
name InterfaceName
|
||||||
|
mtu InterfaceMTU
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the maximum supported MTU for the platform based on the defaults in
|
// Gets the maximum supported MTU for the platform based on the defaults in
|
||||||
|
@ -93,53 +95,41 @@ func MaximumMTU() uint64 {
|
||||||
|
|
||||||
// Init initialises the TUN module. You must have acquired a Listener from
|
// Init initialises the TUN module. You must have acquired a Listener from
|
||||||
// the Yggdrasil core before this point and it must not be in use elsewhere.
|
// the Yggdrasil core before this point and it must not be in use elsewhere.
|
||||||
func (tun *TunAdapter) Init(core *core.Core, config *config.NodeConfig, log *log.Logger, options interface{}) error {
|
func New(rwc *ipv6rwc.ReadWriteCloser, log util.Logger, opts ...SetupOption) (*TunAdapter, error) {
|
||||||
tun.core = core
|
tun := &TunAdapter{
|
||||||
tun.config = config
|
rwc: rwc,
|
||||||
tun.log = log
|
log: log,
|
||||||
return nil
|
}
|
||||||
}
|
for _, opt := range opts {
|
||||||
|
tun._applyOption(opt)
|
||||||
// Start the setup process for the TUN adapter. If successful, starts the
|
}
|
||||||
// reader actor to handle packets on that interface.
|
return tun, tun._start()
|
||||||
func (tun *TunAdapter) Start() error {
|
|
||||||
var err error
|
|
||||||
phony.Block(tun, func() {
|
|
||||||
err = tun._start()
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tun *TunAdapter) _start() error {
|
func (tun *TunAdapter) _start() error {
|
||||||
if tun.isOpen {
|
if tun.isOpen {
|
||||||
return errors.New("TUN module is already started")
|
return errors.New("TUN module is already started")
|
||||||
}
|
}
|
||||||
tun.config.RLock()
|
tun.addr = tun.rwc.Address()
|
||||||
defer tun.config.RUnlock()
|
tun.subnet = tun.rwc.Subnet()
|
||||||
if tun.config == nil {
|
|
||||||
return errors.New("no configuration available to TUN")
|
|
||||||
}
|
|
||||||
pk := tun.core.PublicKey()
|
|
||||||
tun.addr = *address.AddrForKey(pk)
|
|
||||||
tun.subnet = *address.SubnetForKey(pk)
|
|
||||||
addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1)
|
addr := fmt.Sprintf("%s/%d", net.IP(tun.addr[:]).String(), 8*len(address.GetPrefix())-1)
|
||||||
if tun.config.IfName == "none" || tun.config.IfName == "dummy" {
|
if tun.config.name == "none" || tun.config.name == "dummy" {
|
||||||
tun.log.Debugln("Not starting TUN as ifname is none or dummy")
|
tun.log.Debugln("Not starting TUN as ifname is none or dummy")
|
||||||
tun.isEnabled = false
|
tun.isEnabled = false
|
||||||
go tun.write()
|
go tun.write()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
mtu := tun.config.IfMTU
|
mtu := uint64(tun.config.mtu)
|
||||||
if tun.core.MaxMTU() < mtu {
|
if tun.rwc.MaxMTU() < mtu {
|
||||||
mtu = tun.core.MaxMTU()
|
mtu = tun.rwc.MaxMTU()
|
||||||
}
|
}
|
||||||
if err := tun.setup(tun.config.IfName, addr, mtu); err != nil {
|
if err := tun.setup(string(tun.config.name), addr, mtu); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if tun.MTU() != mtu {
|
if tun.MTU() != mtu {
|
||||||
tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", tun.config.IfMTU, tun.MTU(), MaximumMTU())
|
tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", tun.config.mtu, tun.MTU(), MaximumMTU())
|
||||||
}
|
}
|
||||||
tun.core.SetMTU(tun.MTU())
|
tun.rwc.SetMTU(tun.MTU())
|
||||||
tun.isOpen = true
|
tun.isOpen = true
|
||||||
tun.isEnabled = true
|
tun.isEnabled = true
|
||||||
go tun.read()
|
go tun.read()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build openbsd || freebsd
|
||||||
// +build openbsd freebsd
|
// +build openbsd freebsd
|
||||||
|
|
||||||
package tuntap
|
package tuntap
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !mobile
|
||||||
// +build !mobile
|
// +build !mobile
|
||||||
|
|
||||||
package tuntap
|
package tuntap
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !mobile
|
||||||
// +build !mobile
|
// +build !mobile
|
||||||
|
|
||||||
package tuntap
|
package tuntap
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !linux && !darwin && !windows && !openbsd && !freebsd && !mobile
|
||||||
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!mobile
|
// +build !linux,!darwin,!windows,!openbsd,!freebsd,!mobile
|
||||||
|
|
||||||
package tuntap
|
package tuntap
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package tuntap
|
package tuntap
|
||||||
|
|
|
@ -8,6 +8,20 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Any logger that satisfies this interface is suitable for Yggdrasil.
|
||||||
|
type Logger interface {
|
||||||
|
Printf(string, ...interface{})
|
||||||
|
Println(...interface{})
|
||||||
|
Infof(string, ...interface{})
|
||||||
|
Infoln(...interface{})
|
||||||
|
Warnf(string, ...interface{})
|
||||||
|
Warnln(...interface{})
|
||||||
|
Errorf(string, ...interface{})
|
||||||
|
Errorln(...interface{})
|
||||||
|
Debugf(string, ...interface{})
|
||||||
|
Debugln(...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
// TimerStop stops a timer and makes sure the channel is drained, returns true if the timer was stopped before firing.
|
// TimerStop stops a timer and makes sure the channel is drained, returns true if the timer was stopped before firing.
|
||||||
func TimerStop(t *time.Timer) bool {
|
func TimerStop(t *time.Timer) bool {
|
||||||
stopped := t.Stop()
|
stopped := t.Stop()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue