mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-04-28 14:15:06 +03:00
Compare commits
No commits in common. "develop" and "v0.5.3" have entirely different histories.
57 changed files with 619 additions and 1681 deletions
34
.github/workflows/ci.yml
vendored
34
.github/workflows/ci.yml
vendored
|
@ -15,12 +15,12 @@ jobs:
|
||||||
name: Lint
|
name: Lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: stable
|
go-version: 1.21
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v3
|
||||||
with:
|
with:
|
||||||
args: --issues-exit-code=1
|
args: --issues-exit-code=1
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v2
|
||||||
|
@ -51,17 +51,17 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
goversion: ["1.22", "1.23", "1.24"]
|
goversion: ["1.20", "1.21"]
|
||||||
|
|
||||||
name: Build & Test (Linux, Go ${{ matrix.goversion }})
|
name: Build & Test (Linux, Go ${{ matrix.goversion }})
|
||||||
needs: [lint]
|
needs: [lint]
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.goversion }}
|
go-version: ${{ matrix.goversion }}
|
||||||
|
|
||||||
|
@ -75,17 +75,17 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
goversion: ["1.22", "1.23", "1.24"]
|
goversion: ["1.20", "1.21"]
|
||||||
|
|
||||||
name: Build & Test (Windows, Go ${{ matrix.goversion }})
|
name: Build & Test (Windows, Go ${{ matrix.goversion }})
|
||||||
needs: [lint]
|
needs: [lint]
|
||||||
|
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.goversion }}
|
go-version: ${{ matrix.goversion }}
|
||||||
|
|
||||||
|
@ -99,17 +99,17 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
goversion: ["1.22", "1.23", "1.24"]
|
goversion: ["1.20", "1.21"]
|
||||||
|
|
||||||
name: Build & Test (macOS, Go ${{ matrix.goversion }})
|
name: Build & Test (macOS, Go ${{ matrix.goversion }})
|
||||||
needs: [lint]
|
needs: [lint]
|
||||||
|
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.goversion }}
|
go-version: ${{ matrix.goversion }}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
goversion: ["1.22", "1.23", "1.24"]
|
goversion: ["1.20", "1.21"]
|
||||||
goos:
|
goos:
|
||||||
- freebsd
|
- freebsd
|
||||||
- openbsd
|
- openbsd
|
||||||
|
@ -133,10 +133,10 @@ jobs:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.goversion }}
|
go-version: ${{ matrix.goversion }}
|
||||||
|
|
||||||
|
|
37
.github/workflows/pkg.yml
vendored
37
.github/workflows/pkg.yml
vendored
|
@ -18,14 +18,14 @@ jobs:
|
||||||
|
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "1.20"
|
||||||
|
|
||||||
- name: Build package
|
- name: Build package
|
||||||
env:
|
env:
|
||||||
|
@ -33,7 +33,7 @@ jobs:
|
||||||
run: sh contrib/deb/generate.sh
|
run: sh contrib/deb/generate.sh
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Debian package (${{ matrix.pkgarch }})
|
name: Debian package (${{ matrix.pkgarch }})
|
||||||
path: "*.deb"
|
path: "*.deb"
|
||||||
|
@ -49,14 +49,14 @@ jobs:
|
||||||
|
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "1.20"
|
||||||
|
|
||||||
- name: Build package
|
- name: Build package
|
||||||
env:
|
env:
|
||||||
|
@ -64,7 +64,7 @@ jobs:
|
||||||
run: sh contrib/macos/create-pkg.sh
|
run: sh contrib/macos/create-pkg.sh
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: macOS package (${{ matrix.pkgarch }})
|
name: macOS package (${{ matrix.pkgarch }})
|
||||||
path: "*.pkg"
|
path: "*.pkg"
|
||||||
|
@ -80,23 +80,20 @@ jobs:
|
||||||
|
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "1.20"
|
||||||
|
|
||||||
- name: Setup .NET Core SDK
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
|
|
||||||
- name: Build package
|
- name: Build package
|
||||||
run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }}
|
run: sh contrib/msi/build-msi.sh ${{ matrix.pkgarch }}
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Windows package (${{ matrix.pkgarch }})
|
name: Windows package (${{ matrix.pkgarch }})
|
||||||
path: "*.msi"
|
path: "*.msi"
|
||||||
|
@ -112,20 +109,20 @@ jobs:
|
||||||
|
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
path: yggdrasil
|
path: yggdrasil
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: neilalexander/vyatta-yggdrasil
|
repository: neilalexander/vyatta-yggdrasil
|
||||||
path: vyatta-yggdrasil
|
path: vyatta-yggdrasil
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "1.20"
|
||||||
|
|
||||||
- name: Build package
|
- name: Build package
|
||||||
env:
|
env:
|
||||||
|
@ -133,7 +130,7 @@ jobs:
|
||||||
run: cd /home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil && ./build-${{ matrix.pkgarch }}
|
run: cd /home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil && ./build-${{ matrix.pkgarch }}
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Router package (${{ matrix.pkgarch }})
|
name: Router package (${{ matrix.pkgarch }})
|
||||||
path: "/home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil/*.deb"
|
path: "/home/runner/work/yggdrasil-go/yggdrasil-go/vyatta-yggdrasil/*.deb"
|
||||||
|
|
|
@ -2,10 +2,9 @@ run:
|
||||||
build-tags:
|
build-tags:
|
||||||
- lint
|
- lint
|
||||||
issues-exit-code: 0 # TODO: change this to 1 when we want it to fail builds
|
issues-exit-code: 0 # TODO: change this to 1 when we want it to fail builds
|
||||||
issues:
|
skip-dirs:
|
||||||
exclude-dirs:
|
|
||||||
- contrib/
|
- contrib/
|
||||||
- misc/
|
- misc/
|
||||||
linters:
|
linters:
|
||||||
disable:
|
disable:
|
||||||
- gocyclo
|
- gocyclo
|
155
CHANGELOG.md
155
CHANGELOG.md
|
@ -26,159 +26,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- in case of vulnerabilities.
|
- in case of vulnerabilities.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## [0.5.12] - 2024-12-18
|
|
||||||
|
|
||||||
* Go 1.22 is now required to build Yggdrasil
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* The `latency_ms` field in the admin socket `getPeers` response has been renamed to `latency`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* A timing regression which causes a higher level of idle protocol traffic on each peering has been fixed
|
|
||||||
* The `-user` flag now correctly detects an empty user/group specification
|
|
||||||
|
|
||||||
## [0.5.11] - 2024-12-12
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* Support for `unveil` and `pledge` on OpenBSD
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* The parent selection algorithm now only chooses a new parent if there is a larger cost benefit to doing so, which should help to stabilise the tree
|
|
||||||
* The bloom filters are now repropagated periodically, to avoid nodes getting stuck with bad state
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* A memory leak caused by missed cleanup of the peer response map has been fixed
|
|
||||||
* Other bug fixes with bloom filter propagation for off-tree filters and zero vs one bits
|
|
||||||
* TLS-based peering connections now support TLS 1.2 again
|
|
||||||
|
|
||||||
## [0.5.10] - 2024-11-24
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* The `getPeers` admin endpoint will now report the current transmit/receive rate for each given peer
|
|
||||||
* The `getMulticastInterfaces` admin endpoint now reports much more useful information about each interface, rather than just a list of interface names
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Minor tweaks to the routing algorithm:
|
|
||||||
* The next-hop selection will now prefer shorter paths when the costed distance is otherwise equal, tiebreaking on peering uptime to fall back to more stable paths
|
|
||||||
* Link cost calculations have been smoothed out, making the costs less sensitive to sudden spikes in latency
|
|
||||||
* Reusable name lookup and peer connection logic across different peering types for more consistent behaviour
|
|
||||||
* Some comments in the configuration file have been revised for clarity
|
|
||||||
* Upgrade dependencies
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* Nodes with `IfName` set to `none` will now correctly respond to debug RPC requests
|
|
||||||
* The admin socket will now be created reliably before dropping privileges with `-user`
|
|
||||||
* Clear supplementary groups when providing a group ID as well as a user ID to `-user`
|
|
||||||
* SOCKS and WebSocket peerings should now use the correct source interface when specified in `InterfacePeers`
|
|
||||||
* `Peers` and `InterfacePeers` addresses that are obviously invalid (such as unspecified or multicast addresses) will now be correctly ignored
|
|
||||||
* Listeners should now shut down correctly, which should resolve issues where multicast listeners for specific interfaces would not come back up or would log errors
|
|
||||||
|
|
||||||
## [0.5.9] - 2024-10-19
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* New command line option `-user` for changing the process UID/GID
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* The routing algorithm has been updated with RTT-aware link costing, which should prefer lower latency links over higher latency links where possible
|
|
||||||
* The calculated cost is an average of the link RTT, but newly established links are costed higher to begin with, such that unstable peerings can be avoided
|
|
||||||
* Link costs are only used where multiple next-hops are available and will be ignored if there is only one loop-free path to the destination
|
|
||||||
* This is protocol-compatible with existing v0.5.x nodes but will have the best results when peering with nodes that are also running the latest version
|
|
||||||
* The `getPeers` endpoint will now report the calculated link cost for each given peer
|
|
||||||
* Upgrade dependencies
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* Multicast discovery should now work again when building Yggdrasil as an Android framework
|
|
||||||
* Multicast discovery will now correctly ignore interfaces that are not marked as running
|
|
||||||
* Ephemeral links, such as those added by multicast, will no longer try to reconnect in a fast loop, fixing a high CPU issue
|
|
||||||
* The TUN interface will no longer stop working when hitting a segment read error from vectorised reads
|
|
||||||
* The `AllowedPublicKeys` option will once again no longer apply to multicast peerings, as was originally intended
|
|
||||||
* A potential panic when shutting down peering links has been fixed
|
|
||||||
* A redundant system call for setting MTU on OpenBSD has been removed
|
|
||||||
|
|
||||||
## [0.5.8] - 2024-08-12
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* A bug which caused startup problems on Windows and FreeBSD should be fixed
|
|
||||||
* Resolved some minor link state and listener management bugs during shutdown
|
|
||||||
|
|
||||||
## [0.5.7] - 2024-08-05
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* WebSocket support for peerings, by using the new `ws://` scheme in `Listen` and `Peers`
|
|
||||||
* Additionally, the `wss://` scheme can be used to connect to a WebSocket peer behind a HTTPS reverse proxy
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* On Linux, the TUN adapter now uses vectorised reads/writes where possible, which should reduce the amount of CPU time spent on syscalls and potentially improve throughput
|
|
||||||
* Link error handling has been improved and various link error messages have been rewritten to be clearer
|
|
||||||
* Upgrade dependencies
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* Multiple multicast connections to the same remote machine should now work correctly
|
|
||||||
* You may get two connections in some cases, one inbound and one outbound, this is known and will not cause problems
|
|
||||||
* Running as a Windows service should be more reliable with service startup and shutdown bugs fixed
|
|
||||||
|
|
||||||
## [0.5.6] - 2024-05-30
|
|
||||||
|
|
||||||
* Go 1.21 is now required to build Yggdrasil
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* The `getPeers` endpoint now reports the RTT/latency of directly connected peers
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* The tree parent selection algorithm now prefers the lowest latency peers instead of the most stable
|
|
||||||
* Session key exchange logic has been changed to improve throughput and reduce occasional jitter
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* Bloom filter hashing now works correctly on big-endian architectures
|
|
||||||
* Incorrect buffer pool usage has been fixed, reducing memory allocations
|
|
||||||
* The multicast beacon interval now backs off correctly, reducing the number of beacons sent
|
|
||||||
* A denial-of-service vulnerability in the QUIC library has been fixed with a dependency update
|
|
||||||
|
|
||||||
## [0.5.5] - 2024-01-27
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* A new peer option `?maxbackoff=X` has been added to control the maximum backoff time for a given peer, supports duration values like `5m`, `1h` etc
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* The maximum backoff period for failing peer connections has been reduced to just over 1 hour, compared to 4.5 hours before
|
|
||||||
* The `getPeers` endpoint now sorts peers in a more stable fashion
|
|
||||||
* Upgrade dependencies
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* A bug where QUIC listeners could stop listening for incoming connections unexpectedly has been fixed
|
|
||||||
* The priority tiebreak between multiple peerings to the same node has been fixed
|
|
||||||
* Peer connection ordering is no longer sensitive to poor system time resolution
|
|
||||||
* The admin socket now verifies the length of input public keys
|
|
||||||
* The `PPROFLISTEN` environment variable has been fixed and now starts the pprof listener correctly
|
|
||||||
* A panic in `getPeers` has been fixed when using abstract UNIX sockets on Linux
|
|
||||||
|
|
||||||
## [0.5.4] - 2023-11-27
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* Fixed a crash that could happen when calculating the size of bloom filters during encoding
|
|
||||||
|
|
||||||
## [0.5.3] - 2023-11-26
|
## [0.5.3] - 2023-11-26
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -315,7 +162,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Go 1.20 is now required to build Yggdrasil
|
* Go 1.17 is now required to build Yggdrasil
|
||||||
|
|
||||||
## [0.4.3] - 2022-02-06
|
## [0.4.3] - 2022-02-06
|
||||||
|
|
||||||
|
|
|
@ -24,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.22 or later)
|
1. Install [Go](https://golang.org) (requires Go 1.17 or later)
|
||||||
2. Clone this repository
|
2. Clone this repository
|
||||||
2. Run `./build`
|
2. Run `./build`
|
||||||
|
|
||||||
|
|
|
@ -18,27 +18,18 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"suah.dev/protect"
|
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
)
|
)
|
||||||
|
|
||||||
type keySet struct {
|
type keySet struct {
|
||||||
priv ed25519.PrivateKey
|
priv ed25519.PrivateKey
|
||||||
pub ed25519.PublicKey
|
pub ed25519.PublicKey
|
||||||
count uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := protect.Pledge("stdio"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
threads := runtime.GOMAXPROCS(0)
|
threads := runtime.GOMAXPROCS(0)
|
||||||
fmt.Println("Threads:", threads)
|
fmt.Println("Threads:", threads)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
var totalKeys uint64
|
|
||||||
totalKeys = 0
|
|
||||||
var currentBest ed25519.PublicKey
|
var currentBest ed25519.PublicKey
|
||||||
newKeys := make(chan keySet, threads)
|
newKeys := make(chan keySet, threads)
|
||||||
for i := 0; i < threads; i++ {
|
for i := 0; i < threads; i++ {
|
||||||
|
@ -47,9 +38,8 @@ func main() {
|
||||||
for {
|
for {
|
||||||
newKey := <-newKeys
|
newKey := <-newKeys
|
||||||
if isBetter(currentBest, newKey.pub) || len(currentBest) == 0 {
|
if isBetter(currentBest, newKey.pub) || len(currentBest) == 0 {
|
||||||
totalKeys += newKey.count
|
|
||||||
currentBest = newKey.pub
|
currentBest = newKey.pub
|
||||||
fmt.Println("-----", time.Since(start), "---", totalKeys, "keys tried")
|
fmt.Println("-----", time.Since(start))
|
||||||
fmt.Println("Priv:", hex.EncodeToString(newKey.priv))
|
fmt.Println("Priv:", hex.EncodeToString(newKey.priv))
|
||||||
fmt.Println("Pub:", hex.EncodeToString(newKey.pub))
|
fmt.Println("Pub:", hex.EncodeToString(newKey.pub))
|
||||||
addr := address.AddrForKey(newKey.pub)
|
addr := address.AddrForKey(newKey.pub)
|
||||||
|
@ -72,14 +62,11 @@ func isBetter(oldPub, newPub ed25519.PublicKey) bool {
|
||||||
|
|
||||||
func doKeys(out chan<- keySet) {
|
func doKeys(out chan<- keySet) {
|
||||||
bestKey := make(ed25519.PublicKey, ed25519.PublicKeySize)
|
bestKey := make(ed25519.PublicKey, ed25519.PublicKeySize)
|
||||||
var count uint64
|
|
||||||
count = 0
|
|
||||||
for idx := range bestKey {
|
for idx := range bestKey {
|
||||||
bestKey[idx] = 0xff
|
bestKey[idx] = 0xff
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
pub, priv, err := ed25519.GenerateKey(nil)
|
pub, priv, err := ed25519.GenerateKey(nil)
|
||||||
count++
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -87,7 +74,6 @@ func doKeys(out chan<- keySet) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bestKey = pub
|
bestKey = pub
|
||||||
out <- keySet{priv, pub, count}
|
out <- keySet{priv, pub}
|
||||||
count = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris
|
|
||||||
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
func chuser(user string) error {
|
|
||||||
return errors.New("setting uid/gid is not supported on this platform")
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/user"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
func chuser(input string) error {
|
|
||||||
givenUser, givenGroup, _ := strings.Cut(input, ":")
|
|
||||||
if givenUser == "" {
|
|
||||||
return fmt.Errorf("user is empty")
|
|
||||||
}
|
|
||||||
if strings.Contains(input, ":") && givenGroup == "" {
|
|
||||||
return fmt.Errorf("group is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
usr *user.User
|
|
||||||
grp *user.Group
|
|
||||||
uid, gid int
|
|
||||||
)
|
|
||||||
|
|
||||||
if usr, err = user.LookupId(givenUser); err != nil {
|
|
||||||
if usr, err = user.Lookup(givenUser); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if uid, err = strconv.Atoi(usr.Uid); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if givenGroup != "" {
|
|
||||||
if grp, err = user.LookupGroupId(givenGroup); err != nil {
|
|
||||||
if grp, err = user.LookupGroup(givenGroup); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gid, _ = strconv.Atoi(grp.Gid)
|
|
||||||
} else {
|
|
||||||
gid, _ = strconv.Atoi(usr.Gid)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := unix.Setgroups([]int{gid}); err != nil {
|
|
||||||
return fmt.Errorf("setgroups: %d: %v", gid, err)
|
|
||||||
}
|
|
||||||
if err := unix.Setgid(gid); err != nil {
|
|
||||||
return fmt.Errorf("setgid: %d: %v", gid, err)
|
|
||||||
}
|
|
||||||
if err := unix.Setuid(uid); err != nil {
|
|
||||||
return fmt.Errorf("setuid: %d: %v", uid, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os/user"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Usernames must not contain a number sign.
|
|
||||||
func TestEmptyString(t *testing.T) {
|
|
||||||
if chuser("") == nil {
|
|
||||||
t.Fatal("the empty string is not a valid user")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Either omit delimiter and group, or omit both.
|
|
||||||
func TestEmptyGroup(t *testing.T) {
|
|
||||||
if chuser("0:") == nil {
|
|
||||||
t.Fatal("the empty group is not allowed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Either user only or user and group.
|
|
||||||
func TestGroupOnly(t *testing.T) {
|
|
||||||
if chuser(":0") == nil {
|
|
||||||
t.Fatal("group only is not allowed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usenames must not contain the number sign.
|
|
||||||
func TestInvalidUsername(t *testing.T) {
|
|
||||||
const username = "#user"
|
|
||||||
if chuser(username) == nil {
|
|
||||||
t.Fatalf("'%s' is not a valid username", username)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// User IDs must be non-negative.
|
|
||||||
func TestInvalidUserid(t *testing.T) {
|
|
||||||
if chuser("-1") == nil {
|
|
||||||
t.Fatal("User ID cannot be negative")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change to the current user by ID.
|
|
||||||
func TestCurrentUserid(t *testing.T) {
|
|
||||||
usr, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if usr.Uid != "0" {
|
|
||||||
t.Skip("setgroups(2): Only the superuser may set new groups.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = chuser(usr.Uid); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change to a common user by name.
|
|
||||||
func TestCommonUsername(t *testing.T) {
|
|
||||||
usr, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if usr.Uid != "0" {
|
|
||||||
t.Skip("setgroups(2): Only the superuser may set new groups.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := chuser("nobody"); err != nil {
|
|
||||||
if _, ok := err.(user.UnknownUserError); ok {
|
|
||||||
t.Skip(err)
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,8 +14,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"suah.dev/protect"
|
|
||||||
|
|
||||||
"github.com/gologme/log"
|
"github.com/gologme/log"
|
||||||
gsyslog "github.com/hashicorp/go-syslog"
|
gsyslog "github.com/hashicorp/go-syslog"
|
||||||
"github.com/hjson/hjson-go/v4"
|
"github.com/hjson/hjson-go/v4"
|
||||||
|
@ -41,20 +39,6 @@ type node struct {
|
||||||
|
|
||||||
// The main function is responsible for configuring and starting Yggdrasil.
|
// The main function is responsible for configuring and starting Yggdrasil.
|
||||||
func main() {
|
func main() {
|
||||||
// Not all operations are coverable with pledge(2), so immediately
|
|
||||||
// limit file system access with unveil(2), effectively preventing
|
|
||||||
// "proc exec" promises right from the start:
|
|
||||||
//
|
|
||||||
// - read arbitrary config file
|
|
||||||
// - create/write arbitrary log file
|
|
||||||
// - read/write/chmod/remove admin socket, if at all
|
|
||||||
if err := protect.Unveil("/", "rwc"); err != nil {
|
|
||||||
panic(fmt.Sprintf("unveil: / rwc: %v", err))
|
|
||||||
}
|
|
||||||
if err := protect.UnveilBlock(); err != nil {
|
|
||||||
panic(fmt.Sprintf("unveil: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
genconf := flag.Bool("genconf", false, "print a new config to stdout")
|
genconf := flag.Bool("genconf", false, "print a new config to stdout")
|
||||||
useconf := flag.Bool("useconf", false, "read HJSON/JSON config from stdin")
|
useconf := flag.Bool("useconf", false, "read HJSON/JSON config from stdin")
|
||||||
useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path")
|
useconffile := flag.String("useconffile", "", "read HJSON/JSON config from specified file path")
|
||||||
|
@ -68,15 +52,14 @@ func main() {
|
||||||
getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet")
|
getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet")
|
||||||
getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key")
|
getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key")
|
||||||
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
||||||
chuserto := flag.String("user", "", "user (and, optionally, group) to set UID/GID to")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
done := make(chan struct{})
|
|
||||||
defer close(done)
|
|
||||||
|
|
||||||
// Catch interrupts from the operating system to exit gracefully.
|
// Catch interrupts from the operating system to exit gracefully.
|
||||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
|
// Capture the service being stopped on Windows.
|
||||||
|
minwinsvc.SetOnExit(cancel)
|
||||||
|
|
||||||
// Create a new logger that logs output to stdout.
|
// Create a new logger that logs output to stdout.
|
||||||
var logger *log.Logger
|
var logger *log.Logger
|
||||||
switch *logto {
|
switch *logto {
|
||||||
|
@ -205,18 +188,11 @@ func main() {
|
||||||
|
|
||||||
n := &node{}
|
n := &node{}
|
||||||
|
|
||||||
// Set up the Yggdrasil node itself.
|
// Setup the Yggdrasil node itself.
|
||||||
{
|
{
|
||||||
iprange := net.IPNet{
|
|
||||||
IP: net.ParseIP("200::"),
|
|
||||||
Mask: net.CIDRMask(7, 128),
|
|
||||||
}
|
|
||||||
options := []core.SetupOption{
|
options := []core.SetupOption{
|
||||||
core.NodeInfo(cfg.NodeInfo),
|
core.NodeInfo(cfg.NodeInfo),
|
||||||
core.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
|
core.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
|
||||||
core.PeerFilter(func(ip net.IP) bool {
|
|
||||||
return !iprange.Contains(ip)
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
for _, addr := range cfg.Listen {
|
for _, addr := range cfg.Listen {
|
||||||
options = append(options, core.ListenAddress(addr))
|
options = append(options, core.ListenAddress(addr))
|
||||||
|
@ -245,7 +221,7 @@ func main() {
|
||||||
logger.Printf("Your IPv6 subnet is %s", subnet.String())
|
logger.Printf("Your IPv6 subnet is %s", subnet.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the admin socket.
|
// Setup the admin socket.
|
||||||
{
|
{
|
||||||
options := []admin.SetupOption{
|
options := []admin.SetupOption{
|
||||||
admin.ListenAddress(cfg.AdminListen),
|
admin.ListenAddress(cfg.AdminListen),
|
||||||
|
@ -261,7 +237,7 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the multicast module.
|
// Setup the multicast module.
|
||||||
{
|
{
|
||||||
options := []multicast.SetupOption{}
|
options := []multicast.SetupOption{}
|
||||||
for _, intf := range cfg.MulticastInterfaces {
|
for _, intf := range cfg.MulticastInterfaces {
|
||||||
|
@ -282,7 +258,7 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the TUN module.
|
// Setup the TUN module.
|
||||||
{
|
{
|
||||||
options := []tun.SetupOption{
|
options := []tun.SetupOption{
|
||||||
tun.InterfaceName(cfg.IfName),
|
tun.InterfaceName(cfg.IfName),
|
||||||
|
@ -296,37 +272,6 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Windows service shutdown
|
|
||||||
minwinsvc.SetOnExit(func() {
|
|
||||||
logger.Infof("Shutting down service ...")
|
|
||||||
cancel()
|
|
||||||
// Wait for all parts to shutdown properly
|
|
||||||
<-done
|
|
||||||
})
|
|
||||||
|
|
||||||
// Change user if requested
|
|
||||||
if *chuserto != "" {
|
|
||||||
err = chuser(*chuserto)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Promise final modes of operation. At this point, if at all:
|
|
||||||
// - raw socket is created/open
|
|
||||||
// - admin socket is created/open
|
|
||||||
// - privileges are dropped to non-root user
|
|
||||||
//
|
|
||||||
// Peers, InterfacePeers, Listen can be UNIX sockets;
|
|
||||||
// Go's net.Listen.Close() deletes files on shutdown.
|
|
||||||
promises := []string{"stdio", "cpath", "inet", "unix", "dns"}
|
|
||||||
if len(cfg.MulticastInterfaces) > 0 {
|
|
||||||
promises = append(promises, "mcast")
|
|
||||||
}
|
|
||||||
if err := protect.Pledge(strings.Join(promises, " ")); err != nil {
|
|
||||||
panic(fmt.Sprintf("pledge: %v: %v", promises, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block until we are told to shut down.
|
// Block until we are told to shut down.
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() {
|
||||||
fmt.Println("Examples:")
|
fmt.Println("Examples:")
|
||||||
fmt.Println(" - ", os.Args[0], "list")
|
fmt.Println(" - ", os.Args[0], "list")
|
||||||
fmt.Println(" - ", os.Args[0], "getPeers")
|
fmt.Println(" - ", os.Args[0], "getPeers")
|
||||||
|
fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
|
||||||
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getPeers")
|
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getPeers")
|
||||||
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getPeers")
|
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getPeers")
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"suah.dev/protect"
|
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
|
@ -24,11 +22,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// read config, speak DNS/TCP and/or over a UNIX socket
|
|
||||||
if err := protect.Pledge("stdio rpath inet unix dns"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
||||||
}
|
}
|
||||||
|
@ -85,11 +78,6 @@ func run() int {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// config and socket are done, work without unprivileges
|
|
||||||
if err := protect.Pledge("stdio"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Println("Connected")
|
logger.Println("Connected")
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
@ -186,41 +174,29 @@ func run() int {
|
||||||
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Down", "Up", "Pr", "Cost", "Last Error"})
|
table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RX", "TX", "Pr", "Last Error"})
|
||||||
for _, peer := range resp.Peers {
|
for _, peer := range resp.Peers {
|
||||||
state, lasterr, dir, rtt, rxr, txr := "Up", "-", "Out", "-", "-", "-"
|
state, lasterr, dir := "Up", "-", "Out"
|
||||||
if !peer.Up {
|
if !peer.Up {
|
||||||
state, lasterr = "Down", fmt.Sprintf("%s ago: %s", peer.LastErrorTime.Round(time.Second), peer.LastError)
|
state, lasterr = "Down", fmt.Sprintf("%s ago: %s", peer.LastErrorTime.Round(time.Second), peer.LastError)
|
||||||
} else if rttms := float64(peer.Latency.Microseconds()) / 1000; rttms > 0 {
|
|
||||||
rtt = fmt.Sprintf("%.02fms", rttms)
|
|
||||||
}
|
}
|
||||||
if peer.Inbound {
|
if peer.Inbound {
|
||||||
dir = "In"
|
dir = "In"
|
||||||
}
|
}
|
||||||
uristring := peer.URI
|
uri, err := url.Parse(peer.URI)
|
||||||
if uri, err := url.Parse(peer.URI); err == nil {
|
if err != nil {
|
||||||
uri.RawQuery = ""
|
panic(err)
|
||||||
uristring = uri.String()
|
|
||||||
}
|
|
||||||
if peer.RXRate > 0 {
|
|
||||||
rxr = peer.RXRate.String() + "/s"
|
|
||||||
}
|
|
||||||
if peer.TXRate > 0 {
|
|
||||||
txr = peer.TXRate.String() + "/s"
|
|
||||||
}
|
}
|
||||||
|
uri.RawQuery = ""
|
||||||
table.Append([]string{
|
table.Append([]string{
|
||||||
uristring,
|
uri.String(),
|
||||||
state,
|
state,
|
||||||
dir,
|
dir,
|
||||||
peer.IPAddress,
|
peer.IPAddress,
|
||||||
(time.Duration(peer.Uptime) * time.Second).String(),
|
(time.Duration(peer.Uptime) * time.Second).String(),
|
||||||
rtt,
|
|
||||||
peer.RXBytes.String(),
|
peer.RXBytes.String(),
|
||||||
peer.TXBytes.String(),
|
peer.TXBytes.String(),
|
||||||
rxr,
|
|
||||||
txr,
|
|
||||||
fmt.Sprintf("%d", peer.Priority),
|
fmt.Sprintf("%d", peer.Priority),
|
||||||
fmt.Sprintf("%d", peer.Cost),
|
|
||||||
lasterr,
|
lasterr,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -293,21 +269,9 @@ func run() int {
|
||||||
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
if err := json.Unmarshal(recv.Response, &resp); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmtBool := func(b bool) string {
|
table.SetHeader([]string{"Interface"})
|
||||||
if b {
|
|
||||||
return "Yes"
|
|
||||||
}
|
|
||||||
return "-"
|
|
||||||
}
|
|
||||||
table.SetHeader([]string{"Name", "Listen Address", "Beacon", "Listen", "Password"})
|
|
||||||
for _, p := range resp.Interfaces {
|
for _, p := range resp.Interfaces {
|
||||||
table.Append([]string{
|
table.Append([]string{p})
|
||||||
p.Name,
|
|
||||||
p.Address,
|
|
||||||
fmtBool(p.Beacon),
|
|
||||||
fmtBool(p.Listen),
|
|
||||||
fmtBool(p.Password),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
table.Render()
|
table.Render()
|
||||||
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# Last Modified: Mon Feb 3 22:19:45 2025
|
|
||||||
include <tunables/global>
|
|
||||||
|
|
||||||
/usr/bin/yggdrasilctl {
|
|
||||||
include <abstractions/base>
|
|
||||||
|
|
||||||
/etc/yggdrasil.conf rw,
|
|
||||||
/run/yggdrasil.sock rw,
|
|
||||||
owner /sys/kernel/mm/transparent_hugepage/hpage_pmd_size r,
|
|
||||||
|
|
||||||
}
|
|
|
@ -50,12 +50,11 @@ echo 9 > /tmp/$PKGNAME/debian/compat
|
||||||
cat > /tmp/$PKGNAME/debian/control << EOF
|
cat > /tmp/$PKGNAME/debian/control << EOF
|
||||||
Package: $PKGNAME
|
Package: $PKGNAME
|
||||||
Version: $PKGVERSION
|
Version: $PKGVERSION
|
||||||
Section: golang
|
Section: contrib/net
|
||||||
Priority: optional
|
Priority: extra
|
||||||
Architecture: $PKGARCH
|
Architecture: $PKGARCH
|
||||||
Replaces: $PKGREPLACES
|
Replaces: $PKGREPLACES
|
||||||
Conflicts: $PKGREPLACES
|
Conflicts: $PKGREPLACES
|
||||||
Depends: systemd
|
|
||||||
Maintainer: Neil Alexander <neilalexander@users.noreply.github.com>
|
Maintainer: Neil Alexander <neilalexander@users.noreply.github.com>
|
||||||
Description: Yggdrasil Network
|
Description: Yggdrasil Network
|
||||||
Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6
|
Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6
|
||||||
|
@ -103,7 +102,7 @@ then
|
||||||
|
|
||||||
echo "Normalising and updating /etc/yggdrasil/yggdrasil.conf"
|
echo "Normalising and updating /etc/yggdrasil/yggdrasil.conf"
|
||||||
/usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil/yggdrasil.conf
|
/usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil/yggdrasil.conf
|
||||||
|
|
||||||
chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf
|
chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf
|
||||||
chmod 640 /etc/yggdrasil/yggdrasil.conf
|
chmod 640 /etc/yggdrasil/yggdrasil.conf
|
||||||
else
|
else
|
||||||
|
|
|
@ -7,7 +7,6 @@ set -ef
|
||||||
PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version}
|
PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version}
|
||||||
PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
|
PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
|
||||||
PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
|
PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
|
||||||
GOVER=$(go version | { read _ _ version _; echo ${version#go}; })
|
|
||||||
|
|
||||||
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
|
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
|
||||||
ARGS="-v"
|
ARGS="-v"
|
||||||
|
@ -34,15 +33,6 @@ if [ ! $IOS ] && [ ! $ANDROID ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ver_le() {
|
|
||||||
printf "$1\n$2\n" | sort -VC
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ $ANDROID ] && ver_le 1.23.0 $GOVER ; then
|
|
||||||
# github.com/wlynxg/anet library relies on //go:linkname
|
|
||||||
LDFLAGS="$LDFLAGS -checklinkname=0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $IOS ]; then
|
if [ $IOS ]; then
|
||||||
echo "Building framework for iOS"
|
echo "Building framework for iOS"
|
||||||
go get golang.org/x/mobile/bind
|
go get golang.org/x/mobile/bind
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package mobile
|
package mobile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net"
|
"net"
|
||||||
|
@ -16,6 +15,8 @@ import (
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/tun"
|
"github.com/yggdrasil-network/yggdrasil-go/src/tun"
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/version"
|
"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
|
// Yggdrasil mobile package is meant to "plug the gap" for mobile support, as
|
||||||
|
@ -52,17 +53,9 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
||||||
if err := m.config.UnmarshalHJSON(configjson); err != nil {
|
if err := m.config.UnmarshalHJSON(configjson); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Set up the Yggdrasil node itself.
|
// Setup the Yggdrasil node itself.
|
||||||
{
|
{
|
||||||
iprange := net.IPNet{
|
options := []core.SetupOption{}
|
||||||
IP: net.ParseIP("200::"),
|
|
||||||
Mask: net.CIDRMask(7, 128),
|
|
||||||
}
|
|
||||||
options := []core.SetupOption{
|
|
||||||
core.PeerFilter(func(ip net.IP) bool {
|
|
||||||
return !iprange.Contains(ip)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
for _, peer := range m.config.Peers {
|
for _, peer := range m.config.Peers {
|
||||||
options = append(options, core.Peer{URI: peer})
|
options = append(options, core.Peer{URI: peer})
|
||||||
}
|
}
|
||||||
|
@ -92,7 +85,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
||||||
logger.Infof("Your IPv6 subnet is %s", subnet.String())
|
logger.Infof("Your IPv6 subnet is %s", subnet.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the multicast module.
|
// Setup the multicast module.
|
||||||
if len(m.config.MulticastInterfaces) > 0 {
|
if len(m.config.MulticastInterfaces) > 0 {
|
||||||
var err error
|
var err error
|
||||||
logger.Infof("Initializing multicast %s", "")
|
logger.Infof("Initializing multicast %s", "")
|
||||||
|
@ -274,28 +267,3 @@ func (m *Yggdrasil) GetMTU() int {
|
||||||
func GetVersion() string {
|
func GetVersion() string {
|
||||||
return version.BuildVersion()
|
return version.BuildVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigSummary struct {
|
|
||||||
PublicKey string
|
|
||||||
IPv6Address string
|
|
||||||
IPv6Subnet string
|
|
||||||
}
|
|
||||||
|
|
||||||
func SummaryForConfig(b []byte) *ConfigSummary {
|
|
||||||
cfg := config.GenerateConfig()
|
|
||||||
if err := cfg.UnmarshalHJSON(b); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pub := ed25519.PrivateKey(cfg.PrivateKey).Public().(ed25519.PublicKey)
|
|
||||||
hpub := hex.EncodeToString(pub)
|
|
||||||
addr := net.IP(address.AddrForKey(pub)[:])
|
|
||||||
snet := net.IPNet{
|
|
||||||
IP: append(address.SubnetForKey(pub)[:], 0, 0, 0, 0, 0, 0, 0, 0),
|
|
||||||
Mask: net.CIDRMask(64, 128),
|
|
||||||
}
|
|
||||||
return &ConfigSummary{
|
|
||||||
PublicKey: hpub,
|
|
||||||
IPv6Address: addr.String(),
|
|
||||||
IPv6Subnet: snet.String(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build ios || darwin
|
//go:build ios
|
||||||
// +build ios darwin
|
// +build ios
|
||||||
|
|
||||||
package mobile
|
package mobile
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build !android && !ios && !darwin
|
//go:build !android && !ios
|
||||||
// +build !android,!ios,!darwin
|
// +build !android,!ios
|
||||||
|
|
||||||
package mobile
|
package mobile
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/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.21 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 GitHub Actions (see
|
# the system and within the PATH. This is ran currently by GitHub Actions (see
|
||||||
# the workflows in the repository).
|
# the workflows in the repository).
|
||||||
#
|
#
|
||||||
|
@ -16,7 +16,20 @@ then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Download the wix tools!
|
# Download the wix tools!
|
||||||
dotnet tool install --global wix --version 5.0.0
|
if [ ! -d wixbin ];
|
||||||
|
then
|
||||||
|
curl -LO https://wixtoolset.org/downloads/v3.14.0.3910/wix314-binaries.zip
|
||||||
|
if [ `md5sum wix314-binaries.zip | cut -f 1 -d " "` != "34f655cf108086838dd5a76d4318063b" ];
|
||||||
|
then
|
||||||
|
echo "wix package didn't match expected checksum"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p wixbin
|
||||||
|
unzip -o wix314-binaries.zip -d wixbin || (
|
||||||
|
echo "failed to unzip WiX"
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
# Build Yggdrasil!
|
# Build Yggdrasil!
|
||||||
[ "${PKGARCH}" == "x64" ] && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./build
|
[ "${PKGARCH}" == "x64" ] && GOOS=windows GOARCH=amd64 CGO_ENABLED=0 ./build
|
||||||
|
@ -48,11 +61,6 @@ PKGVERSIONMS=$(echo $PKGVERSION | tr - .)
|
||||||
if [ ! -d wintun ];
|
if [ ! -d wintun ];
|
||||||
then
|
then
|
||||||
curl -o wintun.zip https://www.wintun.net/builds/wintun-0.14.1.zip
|
curl -o wintun.zip https://www.wintun.net/builds/wintun-0.14.1.zip
|
||||||
if [ `sha256sum wintun.zip | cut -f 1 -d " "` != "07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51" ];
|
|
||||||
then
|
|
||||||
echo "wintun package didn't match expected checksum"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
unzip wintun.zip
|
unzip wintun.zip
|
||||||
fi
|
fi
|
||||||
if [ $PKGARCH = "x64" ]; then
|
if [ $PKGARCH = "x64" ]; then
|
||||||
|
@ -93,7 +101,7 @@ cat > wix.xml << EOF
|
||||||
Description="Yggdrasil Network Installer"
|
Description="Yggdrasil Network Installer"
|
||||||
Comments="Yggdrasil Network standalone router for Windows."
|
Comments="Yggdrasil Network standalone router for Windows."
|
||||||
Manufacturer="github.com/yggdrasil-network"
|
Manufacturer="github.com/yggdrasil-network"
|
||||||
InstallerVersion="500"
|
InstallerVersion="200"
|
||||||
InstallScope="perMachine"
|
InstallScope="perMachine"
|
||||||
Languages="1033"
|
Languages="1033"
|
||||||
Compressed="yes"
|
Compressed="yes"
|
||||||
|
@ -197,5 +205,5 @@ EOF
|
||||||
# Generate the MSI
|
# Generate the MSI
|
||||||
CANDLEFLAGS="-nologo"
|
CANDLEFLAGS="-nologo"
|
||||||
LIGHTFLAGS="-nologo -spdb -sice:ICE71 -sice:ICE61"
|
LIGHTFLAGS="-nologo -spdb -sice:ICE71 -sice:ICE61"
|
||||||
candle $CANDLEFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj -arch ${PKGARCH} wix.xml && \
|
wixbin/candle $CANDLEFLAGS -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj -arch ${PKGARCH} wix.xml && \
|
||||||
light $LIGHTFLAGS -ext WixUtilExtension.dll -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.msi ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj
|
wixbin/light $LIGHTFLAGS -ext WixUtilExtension.dll -out ${PKGNAME}-${PKGVERSION}-${PKGARCH}.msi ${PKGNAME}-${PKGVERSION}-${PKGARCH}.wixobj
|
||||||
|
|
|
@ -6,6 +6,7 @@ CONFFILE="/etc/yggdrasil.conf"
|
||||||
pidfile="/run/${RC_SVCNAME}.pid"
|
pidfile="/run/${RC_SVCNAME}.pid"
|
||||||
|
|
||||||
command="/usr/bin/yggdrasil"
|
command="/usr/bin/yggdrasil"
|
||||||
|
extra_started_commands="reload"
|
||||||
|
|
||||||
depend() {
|
depend() {
|
||||||
use net dns logger
|
use net dns logger
|
||||||
|
@ -41,6 +42,12 @@ start() {
|
||||||
eend $?
|
eend $?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reload() {
|
||||||
|
ebegin "Reloading ${RC_SVCNAME}"
|
||||||
|
start-stop-daemon --signal HUP --pidfile "${pidfile}"
|
||||||
|
eend $?
|
||||||
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
ebegin "Stopping ${RC_SVCNAME}"
|
ebegin "Stopping ${RC_SVCNAME}"
|
||||||
start-stop-daemon --stop --pidfile "${pidfile}" --exec "${command}"
|
start-stop-daemon --stop --pidfile "${pidfile}" --exec "${command}"
|
||||||
|
|
50
go.mod
50
go.mod
|
@ -1,49 +1,47 @@
|
||||||
module github.com/yggdrasil-network/yggdrasil-go
|
module github.com/yggdrasil-network/yggdrasil-go
|
||||||
|
|
||||||
go 1.22
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3
|
github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8
|
||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
|
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
|
||||||
github.com/cheggaaa/pb/v3 v3.1.5
|
github.com/cheggaaa/pb/v3 v3.1.4
|
||||||
github.com/coder/websocket v1.8.12
|
|
||||||
github.com/gologme/log v1.3.0
|
github.com/gologme/log v1.3.0
|
||||||
github.com/hashicorp/go-syslog v1.0.0
|
github.com/hashicorp/go-syslog v1.0.0
|
||||||
github.com/hjson/hjson-go/v4 v4.4.0
|
github.com/hjson/hjson-go/v4 v4.3.0
|
||||||
github.com/kardianos/minwinsvc v1.0.2
|
github.com/kardianos/minwinsvc v1.0.2
|
||||||
github.com/quic-go/quic-go v0.48.2
|
github.com/quic-go/quic-go v0.39.3
|
||||||
github.com/vishvananda/netlink v1.3.0
|
github.com/vishvananda/netlink v1.1.0
|
||||||
github.com/wlynxg/anet v0.0.5
|
golang.org/x/crypto v0.14.0
|
||||||
golang.org/x/crypto v0.33.0
|
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe
|
||||||
golang.org/x/net v0.35.0
|
golang.org/x/net v0.17.0
|
||||||
golang.org/x/sys v0.30.0
|
golang.org/x/sys v0.13.0
|
||||||
golang.org/x/text v0.22.0
|
golang.org/x/text v0.13.0
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
github.com/bits-and-blooms/bitset v1.5.0 // indirect
|
||||||
github.com/bits-and-blooms/bloom/v3 v3.7.0 // indirect
|
github.com/bits-and-blooms/bloom/v3 v3.3.1 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||||
|
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
go.uber.org/mock v0.5.0 // indirect
|
go.uber.org/mock v0.3.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||||
golang.org/x/mod v0.19.0 // indirect
|
golang.org/x/mod v0.13.0 // indirect
|
||||||
golang.org/x/sync v0.11.0 // indirect
|
golang.org/x/tools v0.14.0 // indirect
|
||||||
golang.org/x/tools v0.23.0 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.15.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/vishvananda/netns v0.0.5 // indirect
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
|
||||||
suah.dev/protect v1.2.4
|
|
||||||
)
|
)
|
||||||
|
|
156
go.sum
156
go.sum
|
@ -1,119 +1,137 @@
|
||||||
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 h1:d8N0z+udAnbU5PdjpLSNPTWlqeU/nnYsQ42B6+879aw=
|
github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8 h1:qyXiPZClVoe6QsbsiDP23g0Ze/MNXiHIAL/nVNfauH0=
|
||||||
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0=
|
github.com/Arceliar/ironwood v0.0.0-20231126105342-ad38416a77c8/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
|
||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
|
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
|
||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
|
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
|
||||||
github.com/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=
|
||||||
github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
github.com/bits-and-blooms/bitset v1.3.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
|
||||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||||
github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls=
|
github.com/bits-and-blooms/bloom/v3 v3.3.1 h1:K2+A19bXT8gJR5mU7y+1yW6hsKfNCjcP2uNfLFKncjQ=
|
||||||
github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg=
|
github.com/bits-and-blooms/bloom/v3 v3.3.1/go.mod h1:bhUUknWd5khVbTe4UgMCSiOOVJzr3tMoijSK3WwvW90=
|
||||||
github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk=
|
github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo=
|
||||||
github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI=
|
github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
|
||||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo=
|
github.com/gologme/log v1.3.0 h1:l781G4dE+pbigClDSDzSaaYKtiueHCILUa/qSDsmHAo=
|
||||||
github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4=
|
github.com/gologme/log v1.3.0/go.mod h1:yKT+DvIPdDdDoPtqFrFxheooyVmoqi0BAsw+erN3wA4=
|
||||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
|
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
github.com/hjson/hjson-go/v4 v4.4.0 h1:D/NPvqOCH6/eisTb5/ztuIS8GUvmpHaLOcNk1Bjr298=
|
github.com/hjson/hjson-go/v4 v4.3.0 h1:dyrzJdqqFGhHt+FSrs5n9s6b0fPM8oSJdWo+oS3YnJw=
|
||||||
github.com/hjson/hjson-go/v4 v4.4.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
|
github.com/hjson/hjson-go/v4 v4.3.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0=
|
github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0=
|
||||||
github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4=
|
github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
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/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
||||||
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||||
|
github.com/quic-go/quic-go v0.39.3 h1:o3YB6t2SR+HU/pgwF29kJ6g4jJIJEwEZ8CKia1h1TKg=
|
||||||
|
github.com/quic-go/quic-go v0.39.3/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
|
||||||
github.com/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=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=
|
github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=
|
||||||
github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||||
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||||
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe h1:lrXv4yHeD9FA8PSJATWowP1QvexpyAPWmPia+Kbzql8=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe/go.mod h1:BrnXpEObnFxpaT75Jo9hsCazwOWcp7nVIa8NNuH5cuA=
|
||||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||||
|
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||||
|
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt09xhCYQWtO2+3UVjlaq6/9hDZrjg2ZE6SyA=
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
|
|
||||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
|
|
||||||
suah.dev/protect v1.2.4 h1:iVZG/zQB63FKNpITDYM/cXoAeCTIjCiXHuFVByJFDzg=
|
|
||||||
suah.dev/protect v1.2.4/go.mod h1:vVrquYO3u1Ep9Ez2z8x+6N6/czm+TBmWKZfiXU2tb54=
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ func SubnetForKey(publicKey ed25519.PublicKey) *Subnet {
|
||||||
return &snet
|
return &snet
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKey returns the partial ed25519.PublicKey for the Address.
|
// GetKet returns the partial ed25519.PublicKey for the Address.
|
||||||
// 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
|
||||||
|
@ -141,7 +141,7 @@ func (a *Address) GetKey() ed25519.PublicKey {
|
||||||
return ed25519.PublicKey(key[:])
|
return ed25519.PublicKey(key[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKey returns the partial ed25519.PublicKey for the Subnet.
|
// GetKet returns the partial ed25519.PublicKey for the Subnet.
|
||||||
// This is used for key lookup.
|
// This is used for key lookup.
|
||||||
func (s *Subnet) GetKey() ed25519.PublicKey {
|
func (s *Subnet) GetKey() ed25519.PublicKey {
|
||||||
var addr Address
|
var addr Address
|
||||||
|
|
|
@ -12,7 +12,7 @@ type AddPeerRequest struct {
|
||||||
|
|
||||||
type AddPeerResponse struct{}
|
type AddPeerResponse struct{}
|
||||||
|
|
||||||
func (a *AdminSocket) addPeerHandler(req *AddPeerRequest, _ *AddPeerResponse) error {
|
func (a *AdminSocket) addPeerHandler(req *AddPeerRequest, res *AddPeerResponse) error {
|
||||||
u, err := url.Parse(req.Uri)
|
u, err := url.Parse(req.Uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse peering URI: %w", err)
|
return fmt.Errorf("unable to parse peering URI: %w", err)
|
||||||
|
|
|
@ -83,52 +83,6 @@ func New(c *core.Core, log core.Logger, opts ...SetupOption) (*AdminSocket, erro
|
||||||
if a.config.listenaddr == "none" || a.config.listenaddr == "" {
|
if a.config.listenaddr == "none" || a.config.listenaddr == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
listenaddr := string(a.config.listenaddr)
|
|
||||||
u, err := url.Parse(listenaddr)
|
|
||||||
if err == nil {
|
|
||||||
switch strings.ToLower(u.Scheme) {
|
|
||||||
case "unix":
|
|
||||||
if _, err := os.Stat(u.Path); err == nil {
|
|
||||||
a.log.Debugln("Admin socket", u.Path, "already exists, trying to clean up")
|
|
||||||
if _, err := net.DialTimeout("unix", u.Path, time.Second*2); err == nil || err.(net.Error).Timeout() {
|
|
||||||
a.log.Errorln("Admin socket", u.Path, "already exists and is in use by another process")
|
|
||||||
os.Exit(1)
|
|
||||||
} else {
|
|
||||||
if err := os.Remove(u.Path); err == nil {
|
|
||||||
a.log.Debugln(u.Path, "was cleaned up")
|
|
||||||
} else {
|
|
||||||
a.log.Errorln(u.Path, "already exists and was not cleaned up:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a.listener, err = net.Listen("unix", u.Path)
|
|
||||||
if err == nil {
|
|
||||||
switch u.Path[:1] {
|
|
||||||
case "@": // maybe abstract namespace
|
|
||||||
default:
|
|
||||||
if err := os.Chmod(u.Path, 0660); err != nil {
|
|
||||||
a.log.Warnln("WARNING:", u.Path, "may have unsafe permissions!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "tcp":
|
|
||||||
a.listener, err = net.Listen("tcp", u.Host)
|
|
||||||
default:
|
|
||||||
a.listener, err = net.Listen("tcp", listenaddr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
a.listener, err = net.Listen("tcp", listenaddr)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
a.log.Errorf("Admin socket failed to listen: %v", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
a.log.Infof("%s admin socket listening on %s",
|
|
||||||
strings.ToUpper(a.listener.Addr().Network()),
|
|
||||||
a.listener.Addr().String())
|
|
||||||
|
|
||||||
_ = a.AddHandler("list", "List available commands", []string{}, func(_ json.RawMessage) (interface{}, error) {
|
_ = a.AddHandler("list", "List available commands", []string{}, func(_ json.RawMessage) (interface{}, error) {
|
||||||
res := &ListResponse{}
|
res := &ListResponse{}
|
||||||
for name, handler := range a.handlers {
|
for name, handler := range a.handlers {
|
||||||
|
@ -247,6 +201,10 @@ func (a *AdminSocket) SetupAdminHandlers() {
|
||||||
return res, nil
|
return res, nil
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
//_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler)
|
||||||
|
//_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler)
|
||||||
|
//_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler)
|
||||||
|
//_ = a.AddHandler("debug_remoteGetDHT", []string{"key"}, t.proto.getDHTHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsStarted returns true if the module has been started.
|
// IsStarted returns true if the module has been started.
|
||||||
|
@ -279,6 +237,51 @@ 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() {
|
||||||
|
listenaddr := string(a.config.listenaddr)
|
||||||
|
u, err := url.Parse(listenaddr)
|
||||||
|
if err == nil {
|
||||||
|
switch strings.ToLower(u.Scheme) {
|
||||||
|
case "unix":
|
||||||
|
if _, err := os.Stat(listenaddr[7:]); err == nil {
|
||||||
|
a.log.Debugln("Admin socket", listenaddr[7:], "already exists, trying to clean up")
|
||||||
|
if _, err := net.DialTimeout("unix", listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() {
|
||||||
|
a.log.Errorln("Admin socket", listenaddr[7:], "already exists and is in use by another process")
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
if err := os.Remove(listenaddr[7:]); err == nil {
|
||||||
|
a.log.Debugln(listenaddr[7:], "was cleaned up")
|
||||||
|
} else {
|
||||||
|
a.log.Errorln(listenaddr[7:], "already exists and was not cleaned up:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.listener, err = net.Listen("unix", listenaddr[7:])
|
||||||
|
if err == nil {
|
||||||
|
switch listenaddr[7:8] {
|
||||||
|
case "@": // maybe abstract namespace
|
||||||
|
default:
|
||||||
|
if err := os.Chmod(listenaddr[7:], 0660); err != nil {
|
||||||
|
a.log.Warnln("WARNING:", listenaddr[:7], "may have unsafe permissions!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "tcp":
|
||||||
|
a.listener, err = net.Listen("tcp", u.Host)
|
||||||
|
default:
|
||||||
|
// err = errors.New(fmt.Sprint("protocol not supported: ", u.Scheme))
|
||||||
|
a.listener, err = net.Listen("tcp", listenaddr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
a.listener, err = net.Listen("tcp", listenaddr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
a.log.Errorf("Admin socket failed to listen: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
a.log.Infof("%s admin socket listening on %s",
|
||||||
|
strings.ToUpper(a.listener.Addr().Network()),
|
||||||
|
a.listener.Addr().String())
|
||||||
defer a.listener.Close()
|
defer a.listener.Close()
|
||||||
for {
|
for {
|
||||||
conn, err := a.listener.Accept()
|
conn, err := a.listener.Accept()
|
||||||
|
@ -306,6 +309,23 @@ func (a *AdminSocket) handleRequest(conn net.Conn) {
|
||||||
|
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
/*
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
if r != nil {
|
||||||
|
fmt.Println("ERROR:", r)
|
||||||
|
a.log.Debugln("Admin socket error:", r)
|
||||||
|
if err := encoder.Encode(&ErrorResponse{
|
||||||
|
Error: "Check your syntax and input types",
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println("ERROR 2:", err)
|
||||||
|
a.log.Debugln("Admin socket JSON encode error:", err)
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
*/
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var err error
|
var err error
|
||||||
var buf json.RawMessage
|
var buf json.RawMessage
|
||||||
|
@ -356,15 +376,13 @@ type DataUnit uint64
|
||||||
|
|
||||||
func (d DataUnit) String() string {
|
func (d DataUnit) String() string {
|
||||||
switch {
|
switch {
|
||||||
case d >= 1024*1024*1024*1024:
|
case d > 1024*1024*1024*1024:
|
||||||
return fmt.Sprintf("%2.1fTB", float64(d)/1024/1024/1024/1024)
|
return fmt.Sprintf("%2.ftb", float64(d)/1024/1024/1024/1024)
|
||||||
case d >= 1024*1024*1024:
|
case d > 1024*1024*1024:
|
||||||
return fmt.Sprintf("%2.1fGB", float64(d)/1024/1024/1024)
|
return fmt.Sprintf("%2.fgb", float64(d)/1024/1024/1024)
|
||||||
case d >= 1024*1024:
|
case d > 1024*1024:
|
||||||
return fmt.Sprintf("%2.1fMB", float64(d)/1024/1024)
|
return fmt.Sprintf("%2.fmb", float64(d)/1024/1024)
|
||||||
case d >= 100:
|
|
||||||
return fmt.Sprintf("%2.1fKB", float64(d)/1024)
|
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("%dB", d)
|
return fmt.Sprintf("%2.fkb", float64(d)/1024)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package admin
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
"slices"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
@ -23,7 +23,7 @@ type PathEntry struct {
|
||||||
Sequence uint64 `json:"sequence"`
|
Sequence uint64 `json:"sequence"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getPathsHandler(_ *GetPathsRequest, res *GetPathsResponse) error {
|
func (a *AdminSocket) getPathsHandler(req *GetPathsRequest, res *GetPathsResponse) error {
|
||||||
paths := a.core.GetPaths()
|
paths := a.core.GetPaths()
|
||||||
res.Paths = make([]PathEntry, 0, len(paths))
|
res.Paths = make([]PathEntry, 0, len(paths))
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
|
@ -35,8 +35,8 @@ func (a *AdminSocket) getPathsHandler(_ *GetPathsRequest, res *GetPathsResponse)
|
||||||
Sequence: p.Sequence,
|
Sequence: p.Sequence,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
slices.SortStableFunc(res.Paths, func(a, b PathEntry) int {
|
sort.SliceStable(res.Paths, func(i, j int) bool {
|
||||||
return strings.Compare(a.PublicKey, b.PublicKey)
|
return strings.Compare(res.Paths[i].PublicKey, res.Paths[j].PublicKey) < 0
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,7 @@ package admin
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
"slices"
|
"sort"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
@ -25,18 +24,14 @@ type PeerEntry struct {
|
||||||
PublicKey string `json:"key"`
|
PublicKey string `json:"key"`
|
||||||
Port uint64 `json:"port"`
|
Port uint64 `json:"port"`
|
||||||
Priority uint64 `json:"priority"`
|
Priority uint64 `json:"priority"`
|
||||||
Cost uint64 `json:"cost"`
|
|
||||||
RXBytes DataUnit `json:"bytes_recvd,omitempty"`
|
RXBytes DataUnit `json:"bytes_recvd,omitempty"`
|
||||||
TXBytes DataUnit `json:"bytes_sent,omitempty"`
|
TXBytes DataUnit `json:"bytes_sent,omitempty"`
|
||||||
RXRate DataUnit `json:"rate_recvd,omitempty"`
|
|
||||||
TXRate DataUnit `json:"rate_sent,omitempty"`
|
|
||||||
Uptime float64 `json:"uptime,omitempty"`
|
Uptime float64 `json:"uptime,omitempty"`
|
||||||
Latency time.Duration `json:"latency,omitempty"`
|
|
||||||
LastErrorTime time.Duration `json:"last_error_time,omitempty"`
|
|
||||||
LastError string `json:"last_error,omitempty"`
|
LastError string `json:"last_error,omitempty"`
|
||||||
|
LastErrorTime time.Duration `json:"last_error_time,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse) error {
|
func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersResponse) error {
|
||||||
peers := a.core.GetPeers()
|
peers := a.core.GetPeers()
|
||||||
res.Peers = make([]PeerEntry, 0, len(peers))
|
res.Peers = make([]PeerEntry, 0, len(peers))
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
|
@ -45,17 +40,11 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse)
|
||||||
Up: p.Up,
|
Up: p.Up,
|
||||||
Inbound: p.Inbound,
|
Inbound: p.Inbound,
|
||||||
Priority: uint64(p.Priority), // can't be uint8 thanks to gobind
|
Priority: uint64(p.Priority), // can't be uint8 thanks to gobind
|
||||||
Cost: p.Cost,
|
|
||||||
URI: p.URI,
|
URI: p.URI,
|
||||||
RXBytes: DataUnit(p.RXBytes),
|
RXBytes: DataUnit(p.RXBytes),
|
||||||
TXBytes: DataUnit(p.TXBytes),
|
TXBytes: DataUnit(p.TXBytes),
|
||||||
RXRate: DataUnit(p.RXRate),
|
|
||||||
TXRate: DataUnit(p.TXRate),
|
|
||||||
Uptime: p.Uptime.Seconds(),
|
Uptime: p.Uptime.Seconds(),
|
||||||
}
|
}
|
||||||
if p.Latency > 0 {
|
|
||||||
peer.Latency = p.Latency
|
|
||||||
}
|
|
||||||
if addr := address.AddrForKey(p.Key); addr != nil {
|
if addr := address.AddrForKey(p.Key); addr != nil {
|
||||||
peer.PublicKey = hex.EncodeToString(p.Key)
|
peer.PublicKey = hex.EncodeToString(p.Key)
|
||||||
peer.IPAddress = net.IP(addr[:]).String()
|
peer.IPAddress = net.IP(addr[:]).String()
|
||||||
|
@ -66,26 +55,11 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse)
|
||||||
}
|
}
|
||||||
res.Peers = append(res.Peers, peer)
|
res.Peers = append(res.Peers, peer)
|
||||||
}
|
}
|
||||||
slices.SortStableFunc(res.Peers, func(a, b PeerEntry) int {
|
sort.Slice(res.Peers, func(i, j int) bool {
|
||||||
if !a.Inbound && b.Inbound {
|
if res.Peers[i].Port == res.Peers[j].Port {
|
||||||
return -1
|
return res.Peers[i].Priority < res.Peers[j].Priority
|
||||||
}
|
}
|
||||||
if a.Inbound && !b.Inbound {
|
return res.Peers[i].Port < res.Peers[j].Port
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if d := strings.Compare(a.PublicKey, b.PublicKey); d != 0 {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
if d := a.Priority - b.Priority; d != 0 {
|
|
||||||
return int(d)
|
|
||||||
}
|
|
||||||
if d := a.Cost - b.Cost; d != 0 {
|
|
||||||
return int(d)
|
|
||||||
}
|
|
||||||
if d := a.Uptime - b.Uptime; d != 0 {
|
|
||||||
return int(d)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ type GetSelfResponse struct {
|
||||||
Subnet string `json:"subnet"`
|
Subnet string `json:"subnet"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getSelfHandler(_ *GetSelfRequest, res *GetSelfResponse) error {
|
func (a *AdminSocket) getSelfHandler(req *GetSelfRequest, res *GetSelfResponse) error {
|
||||||
self := a.core.GetSelf()
|
self := a.core.GetSelf()
|
||||||
snet := a.core.Subnet()
|
snet := a.core.Subnet()
|
||||||
res.BuildName = version.BuildName()
|
res.BuildName = version.BuildName()
|
||||||
|
|
|
@ -3,7 +3,7 @@ package admin
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
"slices"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
@ -23,7 +23,7 @@ type SessionEntry struct {
|
||||||
Uptime float64 `json:"uptime"`
|
Uptime float64 `json:"uptime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getSessionsHandler(_ *GetSessionsRequest, res *GetSessionsResponse) error {
|
func (a *AdminSocket) getSessionsHandler(req *GetSessionsRequest, res *GetSessionsResponse) error {
|
||||||
sessions := a.core.GetSessions()
|
sessions := a.core.GetSessions()
|
||||||
res.Sessions = make([]SessionEntry, 0, len(sessions))
|
res.Sessions = make([]SessionEntry, 0, len(sessions))
|
||||||
for _, s := range sessions {
|
for _, s := range sessions {
|
||||||
|
@ -36,8 +36,8 @@ func (a *AdminSocket) getSessionsHandler(_ *GetSessionsRequest, res *GetSessions
|
||||||
Uptime: s.Uptime.Seconds(),
|
Uptime: s.Uptime.Seconds(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
slices.SortStableFunc(res.Sessions, func(a, b SessionEntry) int {
|
sort.SliceStable(res.Sessions, func(i, j int) bool {
|
||||||
return strings.Compare(a.PublicKey, b.PublicKey)
|
return strings.Compare(res.Sessions[i].PublicKey, res.Sessions[j].PublicKey) < 0
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package admin
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"net"
|
"net"
|
||||||
"slices"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
@ -20,9 +20,11 @@ type TreeEntry struct {
|
||||||
PublicKey string `json:"key"`
|
PublicKey string `json:"key"`
|
||||||
Parent string `json:"parent"`
|
Parent string `json:"parent"`
|
||||||
Sequence uint64 `json:"sequence"`
|
Sequence uint64 `json:"sequence"`
|
||||||
|
//Port uint64 `json:"port"`
|
||||||
|
//Rest uint64 `json:"rest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AdminSocket) getTreeHandler(_ *GetTreeRequest, res *GetTreeResponse) error {
|
func (a *AdminSocket) getTreeHandler(req *GetTreeRequest, res *GetTreeResponse) error {
|
||||||
tree := a.core.GetTree()
|
tree := a.core.GetTree()
|
||||||
res.Tree = make([]TreeEntry, 0, len(tree))
|
res.Tree = make([]TreeEntry, 0, len(tree))
|
||||||
for _, d := range tree {
|
for _, d := range tree {
|
||||||
|
@ -32,10 +34,12 @@ func (a *AdminSocket) getTreeHandler(_ *GetTreeRequest, res *GetTreeResponse) er
|
||||||
PublicKey: hex.EncodeToString(d.Key[:]),
|
PublicKey: hex.EncodeToString(d.Key[:]),
|
||||||
Parent: hex.EncodeToString(d.Parent[:]),
|
Parent: hex.EncodeToString(d.Parent[:]),
|
||||||
Sequence: d.Sequence,
|
Sequence: d.Sequence,
|
||||||
|
//Port: d.Port,
|
||||||
|
//Rest: d.Rest,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
slices.SortStableFunc(res.Tree, func(a, b TreeEntry) int {
|
sort.SliceStable(res.Tree, func(i, j int) bool {
|
||||||
return strings.Compare(a.PublicKey, b.PublicKey)
|
return strings.Compare(res.Tree[i].PublicKey, res.Tree[j].PublicKey) < 0
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ type RemovePeerRequest struct {
|
||||||
|
|
||||||
type RemovePeerResponse struct{}
|
type RemovePeerResponse struct{}
|
||||||
|
|
||||||
func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, _ *RemovePeerResponse) error {
|
func (a *AdminSocket) removePeerHandler(req *RemovePeerRequest, res *RemovePeerResponse) error {
|
||||||
u, err := url.Parse(req.Uri)
|
u, err := url.Parse(req.Uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse peering URI: %w", err)
|
return fmt.Errorf("unable to parse peering URI: %w", err)
|
||||||
|
|
|
@ -43,25 +43,25 @@ type NodeConfig struct {
|
||||||
PrivateKey KeyBytes `json:",omitempty" comment:"Your private key. DO NOT share this with anyone!"`
|
PrivateKey KeyBytes `json:",omitempty" comment:"Your private key. DO NOT share this with anyone!"`
|
||||||
PrivateKeyPath string `json:",omitempty" comment:"The path to your private key file in PEM format."`
|
PrivateKeyPath string `json:",omitempty" comment:"The path to your private key file in PEM format."`
|
||||||
Certificate *tls.Certificate `json:"-"`
|
Certificate *tls.Certificate `json:"-"`
|
||||||
Peers []string `comment:"List of outbound peer connection strings (e.g. tls://a.b.c.d:e or\nsocks://a.b.c.d:e/f.g.h.i:j). Connection strings can contain options,\nsee https://yggdrasil-network.github.io/configurationref.html#peers.\nYggdrasil has no concept of bootstrap nodes - all network traffic\nwill transit peer connections. Therefore make sure to only peer with\nnearby nodes that have good connectivity and low latency. Avoid adding\npeers to this list from distant countries as this will worsen your\nnode's connectivity and performance considerably."`
|
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
|
||||||
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nYou should only use this option if your machine is multi-homed and you\nwant to establish outbound peer connections on different interfaces.\nOtherwise you should use \"Peers\"."`
|
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
|
||||||
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nThis is not required if you wish to establish outbound peerings only.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
|
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
|
||||||
AdminListen string `json:",omitempty" comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
|
AdminListen string `json:",omitempty" comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
|
||||||
MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Regex is a regular expression which is matched against an\ninterface name, and interfaces use the first configuration that they\nmatch against. Beacon controls whether or not your node advertises its\npresence to others, whereas Listen controls whether or not your node\nlistens out for and tries to connect to other advertising nodes. See\nhttps://yggdrasil-network.github.io/configurationref.html#multicastinterfaces\nfor more supported options."`
|
MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."`
|
||||||
AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast.\nWARNING: THIS IS NOT A FIREWALL and DOES NOT limit who can reach\nopen ports or services running on your machine!"`
|
AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."`
|
||||||
IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."`
|
IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."`
|
||||||
IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
|
IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
|
||||||
LogLookups bool `json:",omitempty"`
|
|
||||||
NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."`
|
NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."`
|
||||||
NodeInfo map[string]interface{} `comment:"Optional nodeinfo. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
|
NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
|
||||||
|
LogLookups bool `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MulticastInterfaceConfig struct {
|
type MulticastInterfaceConfig struct {
|
||||||
Regex string
|
Regex string
|
||||||
Beacon bool
|
Beacon bool
|
||||||
Listen bool
|
Listen bool
|
||||||
Port uint16 `json:",omitempty"`
|
Port uint16
|
||||||
Priority uint64 `json:",omitempty"` // really uint8, but gobind won't export it
|
Priority uint64 // really uint8, but gobind won't export it
|
||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ func getDefaults() platformDefaultParameters {
|
||||||
DefaultMulticastInterfaces: []MulticastInterfaceConfig{
|
DefaultMulticastInterfaces: []MulticastInterfaceConfig{
|
||||||
{Regex: "en.*", Beacon: true, Listen: true},
|
{Regex: "en.*", Beacon: true, Listen: true},
|
||||||
{Regex: "bridge.*", Beacon: true, Listen: true},
|
{Regex: "bridge.*", Beacon: true, Listen: true},
|
||||||
{Regex: "awdl0", Beacon: false, Listen: false},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// TUN
|
// TUN
|
||||||
|
|
|
@ -30,13 +30,9 @@ type PeerInfo struct {
|
||||||
Coords []uint64
|
Coords []uint64
|
||||||
Port uint64
|
Port uint64
|
||||||
Priority uint8
|
Priority uint8
|
||||||
Cost uint64
|
|
||||||
RXBytes uint64
|
RXBytes uint64
|
||||||
TXBytes uint64
|
TXBytes uint64
|
||||||
RXRate uint64
|
|
||||||
TXRate uint64
|
|
||||||
Uptime time.Duration
|
Uptime time.Duration
|
||||||
Latency time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TreeEntryInfo struct {
|
type TreeEntryInfo struct {
|
||||||
|
@ -89,8 +85,6 @@ func (c *Core) GetPeers() []PeerInfo {
|
||||||
peerinfo.Inbound = state.linkType == linkTypeIncoming
|
peerinfo.Inbound = state.linkType == linkTypeIncoming
|
||||||
peerinfo.RXBytes = atomic.LoadUint64(&c.rx)
|
peerinfo.RXBytes = atomic.LoadUint64(&c.rx)
|
||||||
peerinfo.TXBytes = atomic.LoadUint64(&c.tx)
|
peerinfo.TXBytes = atomic.LoadUint64(&c.tx)
|
||||||
peerinfo.RXRate = atomic.LoadUint64(&c.rxrate)
|
|
||||||
peerinfo.TXRate = atomic.LoadUint64(&c.txrate)
|
|
||||||
peerinfo.Uptime = time.Since(c.up)
|
peerinfo.Uptime = time.Since(c.up)
|
||||||
}
|
}
|
||||||
if p, ok := conns[conn]; ok {
|
if p, ok := conns[conn]; ok {
|
||||||
|
@ -98,8 +92,6 @@ func (c *Core) GetPeers() []PeerInfo {
|
||||||
peerinfo.Root = p.Root
|
peerinfo.Root = p.Root
|
||||||
peerinfo.Port = p.Port
|
peerinfo.Port = p.Port
|
||||||
peerinfo.Priority = p.Priority
|
peerinfo.Priority = p.Priority
|
||||||
peerinfo.Latency = p.Latency
|
|
||||||
peerinfo.Cost = p.Cost
|
|
||||||
}
|
}
|
||||||
peers = append(peers, peerinfo)
|
peers = append(peers, peerinfo)
|
||||||
}
|
}
|
||||||
|
@ -154,14 +146,7 @@ func (c *Core) GetSessions() []SessionInfo {
|
||||||
// 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) (*Listener, error) {
|
func (c *Core) Listen(u *url.URL, sintf string) (*Listener, error) {
|
||||||
return c.links.listen(u, sintf, false)
|
return c.links.listen(u, sintf)
|
||||||
}
|
|
||||||
|
|
||||||
// ListenLocal starts a listener, like the Listen function, but is used for
|
|
||||||
// more trustworthy situations where you want to ignore AllowedPublicKeys, i.e.
|
|
||||||
// with multicast listeners.
|
|
||||||
func (c *Core) ListenLocal(u *url.URL, sintf string) (*Listener, error) {
|
|
||||||
return c.links.listen(u, sintf, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -40,7 +40,6 @@ type Core struct {
|
||||||
tls *tls.Config // immutable after startup
|
tls *tls.Config // immutable after startup
|
||||||
//_peers map[Peer]*linkInfo // configurable after startup
|
//_peers map[Peer]*linkInfo // configurable after startup
|
||||||
_listeners map[ListenAddress]struct{} // configurable after startup
|
_listeners map[ListenAddress]struct{} // configurable after startup
|
||||||
peerFilter func(ip net.IP) bool // immutable after startup
|
|
||||||
nodeinfo NodeInfo // immutable after startup
|
nodeinfo NodeInfo // immutable after startup
|
||||||
nodeinfoPrivacy NodeInfoPrivacy // immutable after startup
|
nodeinfoPrivacy NodeInfoPrivacy // immutable after startup
|
||||||
_allowedPublicKeys map[[32]byte]struct{} // configurable after startup
|
_allowedPublicKeys map[[32]byte]struct{} // configurable after startup
|
||||||
|
@ -128,7 +127,7 @@ func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, erro
|
||||||
c.log.Errorf("Invalid listener URI %q specified, ignoring\n", listenaddr)
|
c.log.Errorf("Invalid listener URI %q specified, ignoring\n", listenaddr)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, err = c.links.listen(u, "", false); err != nil {
|
if _, err = c.links.listen(u, ""); err != nil {
|
||||||
c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err)
|
c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,7 +212,7 @@ func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) {
|
||||||
|
|
||||||
func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
buf := allocBytes(0)
|
buf := allocBytes(0)
|
||||||
defer func() { freeBytes(buf) }()
|
defer freeBytes(buf)
|
||||||
buf = append(buf, typeSessionTraffic)
|
buf = append(buf, typeSessionTraffic)
|
||||||
buf = append(buf, p...)
|
buf = append(buf, p...)
|
||||||
n, err = c.PacketConn.WriteTo(buf, addr)
|
n, err = c.PacketConn.WriteTo(buf, addr)
|
||||||
|
|
|
@ -25,27 +25,6 @@ func GetLoggerWithPrefix(prefix string, verbose bool) *log.Logger {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func require_NoError(t *testing.T, err error) {
|
|
||||||
t.Helper()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func require_Equal[T comparable](t *testing.T, a, b T) {
|
|
||||||
t.Helper()
|
|
||||||
if a != b {
|
|
||||||
t.Fatalf("%v != %v", a, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func require_True(t *testing.T, a bool) {
|
|
||||||
t.Helper()
|
|
||||||
if !a {
|
|
||||||
t.Fatal("expected true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -222,69 +201,3 @@ func BenchmarkCore_Start_Transfer(b *testing.B) {
|
||||||
}
|
}
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAllowedPublicKeys(t *testing.T) {
|
|
||||||
logger := GetLoggerWithPrefix("", false)
|
|
||||||
cfgA, cfgB := config.GenerateConfig(), config.GenerateConfig()
|
|
||||||
require_NoError(t, cfgA.GenerateSelfSignedCertificate())
|
|
||||||
require_NoError(t, cfgB.GenerateSelfSignedCertificate())
|
|
||||||
|
|
||||||
nodeA, err := New(cfgA.Certificate, logger, AllowedPublicKey("abcdef"))
|
|
||||||
require_NoError(t, err)
|
|
||||||
defer nodeA.Stop()
|
|
||||||
|
|
||||||
nodeB, err := New(cfgB.Certificate, logger)
|
|
||||||
require_NoError(t, err)
|
|
||||||
defer nodeB.Stop()
|
|
||||||
|
|
||||||
u, err := url.Parse("tcp://localhost:0")
|
|
||||||
require_NoError(t, err)
|
|
||||||
|
|
||||||
l, err := nodeA.Listen(u, "")
|
|
||||||
require_NoError(t, err)
|
|
||||||
|
|
||||||
u, err = url.Parse("tcp://" + l.Addr().String())
|
|
||||||
require_NoError(t, err)
|
|
||||||
|
|
||||||
require_NoError(t, nodeB.AddPeer(u, ""))
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
peers := nodeB.GetPeers()
|
|
||||||
require_Equal(t, len(peers), 1)
|
|
||||||
require_True(t, !peers[0].Up)
|
|
||||||
require_True(t, peers[0].LastError != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAllowedPublicKeysLocal(t *testing.T) {
|
|
||||||
logger := GetLoggerWithPrefix("", false)
|
|
||||||
cfgA, cfgB := config.GenerateConfig(), config.GenerateConfig()
|
|
||||||
require_NoError(t, cfgA.GenerateSelfSignedCertificate())
|
|
||||||
require_NoError(t, cfgB.GenerateSelfSignedCertificate())
|
|
||||||
|
|
||||||
nodeA, err := New(cfgA.Certificate, logger, AllowedPublicKey("abcdef"))
|
|
||||||
require_NoError(t, err)
|
|
||||||
defer nodeA.Stop()
|
|
||||||
|
|
||||||
nodeB, err := New(cfgB.Certificate, logger)
|
|
||||||
require_NoError(t, err)
|
|
||||||
defer nodeB.Stop()
|
|
||||||
|
|
||||||
u, err := url.Parse("tcp://localhost:0")
|
|
||||||
require_NoError(t, err)
|
|
||||||
|
|
||||||
l, err := nodeA.ListenLocal(u, "")
|
|
||||||
require_NoError(t, err)
|
|
||||||
|
|
||||||
u, err = url.Parse("tcp://" + l.Addr().String())
|
|
||||||
require_NoError(t, err)
|
|
||||||
|
|
||||||
require_NoError(t, nodeB.AddPeer(u, ""))
|
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
peers := nodeB.GetPeers()
|
|
||||||
require_Equal(t, len(peers), 1)
|
|
||||||
require_True(t, peers[0].Up)
|
|
||||||
require_True(t, peers[0].LastError == nil)
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,8 +12,6 @@ func init() {
|
||||||
envVarName := "PPROFLISTEN"
|
envVarName := "PPROFLISTEN"
|
||||||
if hostPort := os.Getenv(envVarName); hostPort != "" {
|
if hostPort := os.Getenv(envVarName); hostPort != "" {
|
||||||
fmt.Fprintf(os.Stderr, "DEBUG: Starting pprof on %s\n", hostPort)
|
fmt.Fprintf(os.Stderr, "DEBUG: Starting pprof on %s\n", hostPort)
|
||||||
go func() {
|
go fmt.Println(http.ListenAndServe(hostPort, nil))
|
||||||
fmt.Fprintf(os.Stderr, "DEBUG: %s", http.ListenAndServe(hostPort, nil))
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
272
src/core/link.go
272
src/core/link.go
|
@ -4,10 +4,11 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -27,9 +28,6 @@ const (
|
||||||
linkTypeIncoming // Incoming connection
|
linkTypeIncoming // Incoming connection
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultBackoffLimit = time.Second << 12 // 1h8m16s
|
|
||||||
const minimumBackoffLimit = time.Second * 30
|
|
||||||
|
|
||||||
type links struct {
|
type links struct {
|
||||||
phony.Inbox
|
phony.Inbox
|
||||||
core *Core
|
core *Core
|
||||||
|
@ -38,11 +36,8 @@ type links struct {
|
||||||
unix *linkUNIX // UNIX interface support
|
unix *linkUNIX // UNIX interface support
|
||||||
socks *linkSOCKS // SOCKS interface support
|
socks *linkSOCKS // SOCKS interface support
|
||||||
quic *linkQUIC // QUIC interface support
|
quic *linkQUIC // QUIC interface support
|
||||||
ws *linkWS // WS interface support
|
|
||||||
wss *linkWSS // WSS interface support
|
|
||||||
// _links can only be modified safely from within the links actor
|
// _links can only be modified safely from within the links actor
|
||||||
_links map[linkInfo]*link // *link is nil if connection in progress
|
_links map[linkInfo]*link // *link is nil if connection in progress
|
||||||
_listeners map[*Listener]context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type linkProtocol interface {
|
type linkProtocol interface {
|
||||||
|
@ -66,7 +61,7 @@ type link struct {
|
||||||
// The remaining fields can only be modified safely from within the links actor
|
// The remaining fields can only be modified safely from within the links actor
|
||||||
_conn *linkConn // Connected link, if any, nil if not connected
|
_conn *linkConn // Connected link, if any, nil if not connected
|
||||||
_err error // Last error on the connection, if any
|
_err error // Last error on the connection, if any
|
||||||
_errtime time.Time // Last time an error occurred
|
_errtime time.Time // Last time an error occured
|
||||||
}
|
}
|
||||||
|
|
||||||
type linkOptions struct {
|
type linkOptions struct {
|
||||||
|
@ -74,7 +69,6 @@ type linkOptions struct {
|
||||||
priority uint8
|
priority uint8
|
||||||
tlsSNI string
|
tlsSNI string
|
||||||
password []byte
|
password []byte
|
||||||
maxBackoff time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
|
@ -87,6 +81,13 @@ func (l *Listener) Addr() net.Addr {
|
||||||
return l.listener.Addr()
|
return l.listener.Addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
l.Cancel()
|
||||||
|
err := l.listener.Close()
|
||||||
|
<-l.ctx.Done()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (l *links) init(c *Core) error {
|
func (l *links) init(c *Core) error {
|
||||||
l.core = c
|
l.core = c
|
||||||
l.tcp = l.newLinkTCP()
|
l.tcp = l.newLinkTCP()
|
||||||
|
@ -94,50 +95,33 @@ func (l *links) init(c *Core) error {
|
||||||
l.unix = l.newLinkUNIX()
|
l.unix = l.newLinkUNIX()
|
||||||
l.socks = l.newLinkSOCKS()
|
l.socks = l.newLinkSOCKS()
|
||||||
l.quic = l.newLinkQUIC()
|
l.quic = l.newLinkQUIC()
|
||||||
l.ws = l.newLinkWS()
|
|
||||||
l.wss = l.newLinkWSS()
|
|
||||||
l._links = make(map[linkInfo]*link)
|
l._links = make(map[linkInfo]*link)
|
||||||
l._listeners = make(map[*Listener]context.CancelFunc)
|
|
||||||
|
|
||||||
l.Act(nil, l._updateAverages)
|
var listeners []ListenAddress
|
||||||
|
phony.Block(c, func() {
|
||||||
|
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) _updateAverages() {
|
|
||||||
select {
|
|
||||||
case <-l.core.ctx.Done():
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, l := range l._links {
|
|
||||||
if l._conn == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rx := atomic.LoadUint64(&l._conn.rx)
|
|
||||||
tx := atomic.LoadUint64(&l._conn.tx)
|
|
||||||
lastrx := atomic.LoadUint64(&l._conn.lastrx)
|
|
||||||
lasttx := atomic.LoadUint64(&l._conn.lasttx)
|
|
||||||
atomic.StoreUint64(&l._conn.rxrate, rx-lastrx)
|
|
||||||
atomic.StoreUint64(&l._conn.txrate, tx-lasttx)
|
|
||||||
atomic.StoreUint64(&l._conn.lastrx, rx)
|
|
||||||
atomic.StoreUint64(&l._conn.lasttx, tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.AfterFunc(time.Second, func() {
|
|
||||||
l.Act(nil, l._updateAverages)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *links) shutdown() {
|
func (l *links) shutdown() {
|
||||||
phony.Block(l, func() {
|
phony.Block(l.tcp, func() {
|
||||||
for _, cancel := range l._listeners {
|
for l := range l.tcp._listeners {
|
||||||
cancel()
|
_ = l.Close()
|
||||||
}
|
}
|
||||||
for _, link := range l._links {
|
})
|
||||||
if link._conn != nil {
|
phony.Block(l.tls, func() {
|
||||||
_ = link._conn.Close()
|
for l := range l.tls._listeners {
|
||||||
}
|
_ = l.Close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
phony.Block(l.unix, func() {
|
||||||
|
for l := range l.unix._listeners {
|
||||||
|
_ = l.Close()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -150,11 +134,8 @@ const ErrLinkAlreadyConfigured = linkError("peer is already configured")
|
||||||
const ErrLinkNotConfigured = linkError("peer is not configured")
|
const ErrLinkNotConfigured = linkError("peer is not configured")
|
||||||
const ErrLinkPriorityInvalid = linkError("priority value is invalid")
|
const ErrLinkPriorityInvalid = linkError("priority value is invalid")
|
||||||
const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid")
|
const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid")
|
||||||
const ErrLinkPasswordInvalid = linkError("invalid password supplied")
|
const ErrLinkPasswordInvalid = linkError("password is invalid")
|
||||||
const ErrLinkUnrecognisedSchema = linkError("link schema unknown")
|
const ErrLinkUnrecognisedSchema = linkError("link schema unknown")
|
||||||
const ErrLinkMaxBackoffInvalid = linkError("max backoff duration invalid")
|
|
||||||
const ErrLinkSNINotSupported = linkError("SNI not supported on this link type")
|
|
||||||
const ErrLinkNoSuitableIPs = linkError("peer has no suitable addresses")
|
|
||||||
|
|
||||||
func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||||
var retErr error
|
var retErr error
|
||||||
|
@ -169,9 +150,7 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||||
|
|
||||||
// Collect together the link options, these are global options
|
// Collect together the link options, these are global options
|
||||||
// that are not specific to any given protocol.
|
// that are not specific to any given protocol.
|
||||||
options := linkOptions{
|
var options linkOptions
|
||||||
maxBackoff: defaultBackoffLimit,
|
|
||||||
}
|
|
||||||
for _, pubkey := range u.Query()["key"] {
|
for _, pubkey := range u.Query()["key"] {
|
||||||
sigPub, err := hex.DecodeString(pubkey)
|
sigPub, err := hex.DecodeString(pubkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -200,14 +179,6 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||||
}
|
}
|
||||||
options.password = []byte(p)
|
options.password = []byte(p)
|
||||||
}
|
}
|
||||||
if p := u.Query().Get("maxbackoff"); p != "" {
|
|
||||||
d, err := time.ParseDuration(p)
|
|
||||||
if err != nil || d < minimumBackoffLimit {
|
|
||||||
retErr = ErrLinkMaxBackoffInvalid
|
|
||||||
return
|
|
||||||
}
|
|
||||||
options.maxBackoff = d
|
|
||||||
}
|
|
||||||
// SNI headers must contain hostnames and not IP addresses, so we must make sure
|
// 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
|
// 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
|
// the host-port combo from the query option and then seeing if it parses to an
|
||||||
|
@ -264,13 +235,10 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||||
// The caller should check the return value to decide whether
|
// The caller should check the return value to decide whether
|
||||||
// or not to give up trying.
|
// or not to give up trying.
|
||||||
backoffNow := func() bool {
|
backoffNow := func() bool {
|
||||||
if backoff < 32 {
|
if backoff < 14 { // Cap at roughly 4.5 hours maximum.
|
||||||
backoff++
|
backoff++
|
||||||
}
|
}
|
||||||
duration := time.Second << backoff
|
duration := time.Second * time.Duration(math.Exp2(float64(backoff)))
|
||||||
if duration > options.maxBackoff {
|
|
||||||
duration = options.maxBackoff
|
|
||||||
}
|
|
||||||
select {
|
select {
|
||||||
case <-state.kick:
|
case <-state.kick:
|
||||||
return true
|
return true
|
||||||
|
@ -365,12 +333,8 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||||
|
|
||||||
// Give the connection to the handler. The handler will block
|
// Give the connection to the handler. The handler will block
|
||||||
// for the lifetime of the connection.
|
// for the lifetime of the connection.
|
||||||
switch err = l.handler(linkType, options, lc, resetBackoff, false); {
|
if err = l.handler(linkType, options, lc, resetBackoff); err != nil && err != io.EOF {
|
||||||
case err == nil:
|
l.core.log.Debugf("Link %s error: %s\n", info.uri, err)
|
||||||
case errors.Is(err, io.EOF):
|
|
||||||
case errors.Is(err, net.ErrClosed):
|
|
||||||
default:
|
|
||||||
l.core.log.Debugf("Link %s error: %s\n", u.Host, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The handler has stopped running so the connection is dead,
|
// The handler has stopped running so the connection is dead,
|
||||||
|
@ -379,11 +343,9 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||||
_ = lc.Close()
|
_ = lc.Close()
|
||||||
phony.Block(l, func() {
|
phony.Block(l, func() {
|
||||||
state._conn = nil
|
state._conn = nil
|
||||||
if err == nil {
|
if state._err = err; state._err != nil {
|
||||||
err = fmt.Errorf("remote side closed the connection")
|
state._errtime = time.Now()
|
||||||
}
|
}
|
||||||
state._err = err
|
|
||||||
state._errtime = time.Now()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// If the link is persistently configured, back off if needed
|
// If the link is persistently configured, back off if needed
|
||||||
|
@ -392,16 +354,15 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
||||||
if backoffNow() {
|
if backoffNow() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Ephemeral or incoming connections don't reconnect.
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
return retErr
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *links) remove(u *url.URL, sintf string, _ linkType) error {
|
func (l *links) remove(u *url.URL, sintf string, linkType linkType) error {
|
||||||
var retErr error
|
var retErr error
|
||||||
phony.Block(l, func() {
|
phony.Block(l, func() {
|
||||||
// Generate the link info and see whether we think we already
|
// Generate the link info and see whether we think we already
|
||||||
|
@ -428,8 +389,8 @@ func (l *links) remove(u *url.URL, sintf string, _ linkType) error {
|
||||||
return retErr
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error) {
|
func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
||||||
ctx, ctxcancel := context.WithCancel(l.core.ctx)
|
ctx, cancel := context.WithCancel(l.core.ctx)
|
||||||
var protocol linkProtocol
|
var protocol linkProtocol
|
||||||
switch strings.ToLower(u.Scheme) {
|
switch strings.ToLower(u.Scheme) {
|
||||||
case "tcp":
|
case "tcp":
|
||||||
|
@ -440,26 +401,15 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error)
|
||||||
protocol = l.unix
|
protocol = l.unix
|
||||||
case "quic":
|
case "quic":
|
||||||
protocol = l.quic
|
protocol = l.quic
|
||||||
case "ws":
|
|
||||||
protocol = l.ws
|
|
||||||
case "wss":
|
|
||||||
protocol = l.wss
|
|
||||||
default:
|
default:
|
||||||
ctxcancel()
|
cancel()
|
||||||
return nil, ErrLinkUnrecognisedSchema
|
return nil, ErrLinkUnrecognisedSchema
|
||||||
}
|
}
|
||||||
listener, err := protocol.listen(ctx, u, sintf)
|
listener, err := protocol.listen(ctx, u, sintf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctxcancel()
|
cancel()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
addr := listener.Addr()
|
|
||||||
cancel := func() {
|
|
||||||
ctxcancel()
|
|
||||||
if err := listener.Close(); err != nil && !errors.Is(err, net.ErrClosed) {
|
|
||||||
l.core.log.Warnf("Error closing %s listener %s: %s", strings.ToUpper(u.Scheme), addr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
li := &Listener{
|
li := &Listener{
|
||||||
listener: listener,
|
listener: listener,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
@ -481,19 +431,11 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error)
|
||||||
options.password = []byte(p)
|
options.password = []byte(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
phony.Block(l, func() {
|
|
||||||
l._listeners[li] = cancel
|
|
||||||
})
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), addr)
|
l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr())
|
||||||
defer phony.Block(l, func() {
|
defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr())
|
||||||
cancel()
|
|
||||||
delete(l._listeners, li)
|
|
||||||
l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), addr)
|
|
||||||
})
|
|
||||||
for {
|
for {
|
||||||
conn, err := li.listener.Accept()
|
conn, err := listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -549,22 +491,13 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error)
|
||||||
// Store the state of the link so that it can be queried later.
|
// Store the state of the link so that it can be queried later.
|
||||||
l._links[info] = state
|
l._links[info] = state
|
||||||
})
|
})
|
||||||
defer phony.Block(l, func() {
|
|
||||||
if l._links[info] == state {
|
|
||||||
delete(l._links, info)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if lc == nil {
|
if lc == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give the connection to the handler. The handler will block
|
// Give the connection to the handler. The handler will block
|
||||||
// for the lifetime of the connection.
|
// for the lifetime of the connection.
|
||||||
switch err = l.handler(linkTypeIncoming, options, lc, nil, local); {
|
if err = l.handler(linkTypeIncoming, options, lc, nil); err != nil && err != io.EOF {
|
||||||
case err == nil:
|
|
||||||
case errors.Is(err, io.EOF):
|
|
||||||
case errors.Is(err, net.ErrClosed):
|
|
||||||
default:
|
|
||||||
l.core.log.Debugf("Link %s error: %s\n", u.Host, err)
|
l.core.log.Debugf("Link %s error: %s\n", u.Host, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,6 +505,11 @@ func (l *links) listen(u *url.URL, sintf string, local bool) (*Listener, error)
|
||||||
// try to close the underlying socket just in case and then
|
// try to close the underlying socket just in case and then
|
||||||
// drop the link state.
|
// drop the link state.
|
||||||
_ = lc.Close()
|
_ = lc.Close()
|
||||||
|
phony.Block(l, func() {
|
||||||
|
if l._links[info] == state {
|
||||||
|
delete(l._links, info)
|
||||||
|
}
|
||||||
|
})
|
||||||
}(conn)
|
}(conn)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -591,17 +529,13 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options
|
||||||
dialer = l.unix
|
dialer = l.unix
|
||||||
case "quic":
|
case "quic":
|
||||||
dialer = l.quic
|
dialer = l.quic
|
||||||
case "ws":
|
|
||||||
dialer = l.ws
|
|
||||||
case "wss":
|
|
||||||
dialer = l.wss
|
|
||||||
default:
|
default:
|
||||||
return nil, ErrLinkUnrecognisedSchema
|
return nil, ErrLinkUnrecognisedSchema
|
||||||
}
|
}
|
||||||
return dialer.dial(ctx, u, info, options)
|
return dialer.dial(ctx, u, info, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func(), local bool) error {
|
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func()) error {
|
||||||
meta := version_getBaseMetadata()
|
meta := version_getBaseMetadata()
|
||||||
meta.publicKey = l.core.public
|
meta.publicKey = l.core.public
|
||||||
meta.priority = options.priority
|
meta.priority = options.priority
|
||||||
|
@ -616,7 +550,7 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return fmt.Errorf("write handshake: %w", err)
|
return fmt.Errorf("write handshake: %w", err)
|
||||||
case n != len(metaBytes):
|
case err == nil && n != len(metaBytes):
|
||||||
return fmt.Errorf("incomplete handshake send")
|
return fmt.Errorf("incomplete handshake send")
|
||||||
}
|
}
|
||||||
meta = version_metadata{}
|
meta = version_metadata{}
|
||||||
|
@ -644,22 +578,20 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if we're authorized to connect to this key / IP
|
// Check if we're authorized to connect to this key / IP
|
||||||
if !local {
|
var allowed map[[32]byte]struct{}
|
||||||
var allowed map[[32]byte]struct{}
|
phony.Block(l.core, func() {
|
||||||
phony.Block(l.core, func() {
|
allowed = l.core.config._allowedPublicKeys
|
||||||
allowed = l.core.config._allowedPublicKeys
|
})
|
||||||
})
|
isallowed := len(allowed) == 0
|
||||||
isallowed := len(allowed) == 0
|
for k := range allowed {
|
||||||
for k := range allowed {
|
if bytes.Equal(k[:], meta.publicKey) {
|
||||||
if bytes.Equal(k[:], meta.publicKey) {
|
isallowed = true
|
||||||
isallowed = true
|
break
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if linkType == linkTypeIncoming && !isallowed {
|
|
||||||
return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if linkType == linkTypeIncoming && !isallowed {
|
||||||
|
return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.publicKey))
|
||||||
|
}
|
||||||
|
|
||||||
dir := "outbound"
|
dir := "outbound"
|
||||||
if linkType == linkTypeIncoming {
|
if linkType == linkTypeIncoming {
|
||||||
|
@ -687,70 +619,30 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
|
||||||
l.core.log.Infof("Disconnected %s: %s, source %s; error: %s",
|
l.core.log.Infof("Disconnected %s: %s, source %s; error: %s",
|
||||||
dir, remoteStr, localStr, err)
|
dir, remoteStr, localStr, err)
|
||||||
}
|
}
|
||||||
return err
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
func (l *links) findSuitableIP(url *url.URL, fn func(hostname string, ip net.IP, port int) (net.Conn, error)) (net.Conn, error) {
|
|
||||||
host, p, err := net.SplitHostPort(url.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
port, err := strconv.Atoi(p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resp, err := net.LookupIP(host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var _ips [64]net.IP
|
|
||||||
ips := _ips[:0]
|
|
||||||
for _, ip := range resp {
|
|
||||||
switch {
|
|
||||||
case ip.IsUnspecified():
|
|
||||||
continue
|
|
||||||
case ip.IsMulticast():
|
|
||||||
continue
|
|
||||||
case ip.IsLinkLocalMulticast():
|
|
||||||
continue
|
|
||||||
case ip.IsInterfaceLocalMulticast():
|
|
||||||
continue
|
|
||||||
case l.core.config.peerFilter != nil && !l.core.config.peerFilter(ip):
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ips = append(ips, ip)
|
|
||||||
}
|
|
||||||
if len(ips) == 0 {
|
|
||||||
return nil, ErrLinkNoSuitableIPs
|
|
||||||
}
|
|
||||||
for _, ip := range ips {
|
|
||||||
var conn net.Conn
|
|
||||||
if conn, err = fn(host, ip, port); err != nil {
|
|
||||||
url := *url
|
|
||||||
url.RawQuery = ""
|
|
||||||
l.core.log.Debugln("Dialling", url.Redacted(), "reported error:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func urlForLinkInfo(u url.URL) url.URL {
|
func urlForLinkInfo(u url.URL) url.URL {
|
||||||
u.RawQuery = ""
|
u.RawQuery = ""
|
||||||
|
if host, _, err := net.SplitHostPort(u.Host); err == nil {
|
||||||
|
if addr, err := netip.ParseAddr(host); err == nil {
|
||||||
|
// For peers that look like multicast peers (i.e.
|
||||||
|
// link-local addresses), we will ignore the port number,
|
||||||
|
// otherwise we might open multiple connections to them.
|
||||||
|
if addr.IsLinkLocalUnicast() {
|
||||||
|
u.Host = fmt.Sprintf("[%s]", addr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
type linkConn struct {
|
type linkConn struct {
|
||||||
// tx and rx are at the beginning of the struct to ensure 64-bit alignment
|
// 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
|
// on 32-bit platforms, see https://pkg.go.dev/sync/atomic#pkg-note-BUG
|
||||||
rx uint64
|
rx uint64
|
||||||
tx uint64
|
tx uint64
|
||||||
rxrate uint64
|
up time.Time
|
||||||
txrate uint64
|
|
||||||
lastrx uint64
|
|
||||||
lasttx uint64
|
|
||||||
up time.Time
|
|
||||||
net.Conn
|
net.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package core
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
@ -51,25 +50,18 @@ func (l *links) newLinkQUIC() *linkQUIC {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||||
tlsconfig := l.tlsconfig.Clone()
|
qc, err := quic.DialAddr(ctx, url.Host, l.tlsconfig, l.quicconfig)
|
||||||
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
|
if err != nil {
|
||||||
tlsconfig.ServerName = hostname
|
return nil, err
|
||||||
tlsconfig.MinVersion = tls.VersionTLS12
|
}
|
||||||
tlsconfig.MaxVersion = tls.VersionTLS13
|
qs, err := qc.OpenStreamSync(ctx)
|
||||||
hostport := net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port))
|
if err != nil {
|
||||||
qc, err := quic.DialAddr(ctx, hostport, l.tlsconfig, l.quicconfig)
|
return nil, err
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
return &linkQUICStream{
|
||||||
}
|
Connection: qc,
|
||||||
qs, err := qc.OpenStreamSync(ctx)
|
Stream: qs,
|
||||||
if err != nil {
|
}, nil
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &linkQUICStream{
|
|
||||||
Connection: qc,
|
|
||||||
Stream: qs,
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||||
|
@ -85,22 +77,18 @@ func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.List
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
qc, err := ql.Accept(ctx)
|
qc, err := ql.Accept(ctx)
|
||||||
switch err {
|
if err != nil {
|
||||||
case context.Canceled, context.DeadlineExceeded:
|
|
||||||
ql.Close()
|
ql.Close()
|
||||||
fallthrough
|
|
||||||
case quic.ErrServerClosed:
|
|
||||||
return
|
return
|
||||||
case nil:
|
}
|
||||||
qs, err := qc.AcceptStream(ctx)
|
qs, err := qc.AcceptStream(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = qc.CloseWithError(1, fmt.Sprintf("stream error: %s", err))
|
ql.Close()
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
ch <- &linkQUICStream{
|
ch <- &linkQUICStream{
|
||||||
Connection: qc,
|
Connection: qc,
|
||||||
Stream: qs,
|
Stream: qs,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -30,36 +30,21 @@ func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options
|
||||||
}
|
}
|
||||||
proxyAuth.Password, _ = url.User.Password()
|
proxyAuth.Password, _ = url.User.Password()
|
||||||
}
|
}
|
||||||
tlsconfig := l.tls.config.Clone()
|
dialer, err := proxy.SOCKS5("tcp", url.Host, proxyAuth, proxy.Direct)
|
||||||
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
|
if err != nil {
|
||||||
hostport := net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port))
|
return nil, fmt.Errorf("failed to configure proxy")
|
||||||
dialer, err := l.tcp.dialerFor(&net.TCPAddr{
|
}
|
||||||
IP: ip,
|
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
|
||||||
Port: port,
|
conn, err := dialer.Dial("tcp", pathtokens[0])
|
||||||
}, info.sintf)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("failed to dial: %w", err)
|
||||||
return nil, err
|
}
|
||||||
}
|
if url.Scheme == "sockstls" {
|
||||||
proxy, err := proxy.SOCKS5("tcp", hostport, proxyAuth, dialer)
|
tlsconfig := l.tls.config.Clone()
|
||||||
if err != nil {
|
tlsconfig.ServerName = options.tlsSNI
|
||||||
return nil, err
|
conn = tls.Client(conn, tlsconfig)
|
||||||
}
|
}
|
||||||
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
|
return conn, nil
|
||||||
conn, err := proxy.Dial("tcp", pathtokens[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if url.Scheme == "sockstls" {
|
|
||||||
tlsconfig.ServerName = hostname
|
|
||||||
tlsconfig.MinVersion = tls.VersionTLS12
|
|
||||||
tlsconfig.MaxVersion = tls.VersionTLS13
|
|
||||||
if sni := options.tlsSNI; sni != "" {
|
|
||||||
tlsconfig.ServerName = sni
|
|
||||||
}
|
|
||||||
conn = tls.Client(conn, tlsconfig)
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Arceliar/phony"
|
"github.com/Arceliar/phony"
|
||||||
|
@ -14,6 +15,7 @@ type linkTCP struct {
|
||||||
phony.Inbox
|
phony.Inbox
|
||||||
*links
|
*links
|
||||||
listenconfig *net.ListenConfig
|
listenconfig *net.ListenConfig
|
||||||
|
_listeners map[*Listener]context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *links) newLinkTCP() *linkTCP {
|
func (l *links) newLinkTCP() *linkTCP {
|
||||||
|
@ -22,23 +24,68 @@ func (l *links) newLinkTCP() *linkTCP {
|
||||||
listenconfig: &net.ListenConfig{
|
listenconfig: &net.ListenConfig{
|
||||||
KeepAlive: -1,
|
KeepAlive: -1,
|
||||||
},
|
},
|
||||||
|
_listeners: map[*Listener]context.CancelFunc{},
|
||||||
}
|
}
|
||||||
lt.listenconfig.Control = lt.tcpContext
|
lt.listenconfig.Control = lt.tcpContext
|
||||||
return lt
|
return lt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
type tcpDialer struct {
|
||||||
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
|
info linkInfo
|
||||||
|
dialer *net.Dialer
|
||||||
|
addr *net.TCPAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTCP) dialersFor(url *url.URL, info linkInfo) ([]*tcpDialer, error) {
|
||||||
|
host, p, err := net.SplitHostPort(url.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
port, err := strconv.Atoi(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ips, err := net.LookupIP(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dialers := make([]*tcpDialer, 0, len(ips))
|
||||||
|
for _, ip := range ips {
|
||||||
addr := &net.TCPAddr{
|
addr := &net.TCPAddr{
|
||||||
IP: ip,
|
IP: ip,
|
||||||
Port: port,
|
Port: port,
|
||||||
}
|
}
|
||||||
dialer, err := l.tcp.dialerFor(addr, info.sintf)
|
dialer, err := l.dialerFor(addr, info.sintf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
continue
|
||||||
}
|
}
|
||||||
return dialer.DialContext(ctx, "tcp", addr.String())
|
dialers = append(dialers, &tcpDialer{
|
||||||
})
|
info: info,
|
||||||
|
dialer: dialer,
|
||||||
|
addr: addr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return dialers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *linkTCP) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||||
|
dialers, err := l.dialersFor(url, info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(dialers) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
for _, d := range dialers {
|
||||||
|
var conn net.Conn
|
||||||
|
conn, err = d.dialer.DialContext(ctx, "tcp", d.addr.String())
|
||||||
|
if err != nil {
|
||||||
|
l.core.log.Warnf("Failed to connect to %s: %s", d.addr, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) {
|
func (l *linkTCP) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) {
|
||||||
|
|
|
@ -28,6 +28,6 @@ func (t *linkTCP) tcpContext(network, address string, c syscall.RawConn) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *linkTCP) getControl(_ 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,10 @@ import (
|
||||||
type linkTLS struct {
|
type linkTLS struct {
|
||||||
phony.Inbox
|
phony.Inbox
|
||||||
*links
|
*links
|
||||||
tcp *linkTCP
|
tcp *linkTCP
|
||||||
listener *net.ListenConfig
|
listener *net.ListenConfig
|
||||||
config *tls.Config
|
config *tls.Config
|
||||||
|
_listeners map[*Listener]context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
|
func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
|
||||||
|
@ -26,34 +27,35 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
|
||||||
Control: tcp.tcpContext,
|
Control: tcp.tcpContext,
|
||||||
KeepAlive: -1,
|
KeepAlive: -1,
|
||||||
},
|
},
|
||||||
config: l.core.config.tls.Clone(),
|
config: l.core.config.tls.Clone(),
|
||||||
|
_listeners: map[*Listener]context.CancelFunc{},
|
||||||
}
|
}
|
||||||
return lt
|
return lt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkTLS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
func (l *linkTLS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||||
tlsconfig := l.config.Clone()
|
dialers, err := l.tcp.dialersFor(url, info)
|
||||||
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
|
if err != nil {
|
||||||
tlsconfig.ServerName = hostname
|
return nil, err
|
||||||
tlsconfig.MinVersion = tls.VersionTLS12
|
}
|
||||||
tlsconfig.MaxVersion = tls.VersionTLS13
|
if len(dialers) == 0 {
|
||||||
if sni := options.tlsSNI; sni != "" {
|
return nil, nil
|
||||||
tlsconfig.ServerName = sni
|
}
|
||||||
}
|
for _, d := range dialers {
|
||||||
addr := &net.TCPAddr{
|
tlsconfig := l.config.Clone()
|
||||||
IP: ip,
|
tlsconfig.ServerName = options.tlsSNI
|
||||||
Port: port,
|
|
||||||
}
|
|
||||||
dialer, err := l.tcp.dialerFor(addr, info.sintf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tlsdialer := &tls.Dialer{
|
tlsdialer := &tls.Dialer{
|
||||||
NetDialer: dialer,
|
NetDialer: d.dialer,
|
||||||
Config: tlsconfig,
|
Config: tlsconfig,
|
||||||
}
|
}
|
||||||
return tlsdialer.DialContext(ctx, "tcp", addr.String())
|
var conn net.Conn
|
||||||
})
|
conn, err = tlsdialer.DialContext(ctx, "tcp", d.addr.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkTLS) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) {
|
func (l *linkTLS) listen(ctx context.Context, url *url.URL, sintf string) (net.Listener, error) {
|
||||||
|
|
|
@ -12,8 +12,9 @@ import (
|
||||||
type linkUNIX struct {
|
type linkUNIX struct {
|
||||||
phony.Inbox
|
phony.Inbox
|
||||||
*links
|
*links
|
||||||
dialer *net.Dialer
|
dialer *net.Dialer
|
||||||
listener *net.ListenConfig
|
listener *net.ListenConfig
|
||||||
|
_listeners map[*Listener]context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *links) newLinkUNIX() *linkUNIX {
|
func (l *links) newLinkUNIX() *linkUNIX {
|
||||||
|
@ -26,6 +27,7 @@ func (l *links) newLinkUNIX() *linkUNIX {
|
||||||
listener: &net.ListenConfig{
|
listener: &net.ListenConfig{
|
||||||
KeepAlive: -1,
|
KeepAlive: -1,
|
||||||
},
|
},
|
||||||
|
_listeners: map[*Listener]context.CancelFunc{},
|
||||||
}
|
}
|
||||||
return lt
|
return lt
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Arceliar/phony"
|
|
||||||
"github.com/coder/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
type linkWS struct {
|
|
||||||
phony.Inbox
|
|
||||||
*links
|
|
||||||
listenconfig *net.ListenConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
type linkWSConn struct {
|
|
||||||
net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
type linkWSListener struct {
|
|
||||||
ch chan *linkWSConn
|
|
||||||
ctx context.Context
|
|
||||||
httpServer *http.Server
|
|
||||||
listener net.Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
type wsServer struct {
|
|
||||||
ch chan *linkWSConn
|
|
||||||
ctx context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *linkWSListener) Accept() (net.Conn, error) {
|
|
||||||
qs := <-l.ch
|
|
||||||
if qs == nil {
|
|
||||||
return nil, context.Canceled
|
|
||||||
}
|
|
||||||
return qs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *linkWSListener) Addr() net.Addr {
|
|
||||||
return l.listener.Addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *linkWSListener) Close() error {
|
|
||||||
if err := l.httpServer.Shutdown(l.ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return l.listener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *wsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.URL.Path == "/health" || r.URL.Path == "/healthz" {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
_, _ = w.Write([]byte("OK"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
|
|
||||||
Subprotocols: []string{"ygg-ws"},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Subprotocol() != "ygg-ws" {
|
|
||||||
c.Close(websocket.StatusPolicyViolation, "client must speak the ygg-ws subprotocol")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.ch <- &linkWSConn{
|
|
||||||
Conn: websocket.NetConn(s.ctx, c, websocket.MessageBinary),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *links) newLinkWS() *linkWS {
|
|
||||||
lt := &linkWS{
|
|
||||||
links: l,
|
|
||||||
listenconfig: &net.ListenConfig{
|
|
||||||
KeepAlive: -1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return lt
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *linkWS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
|
||||||
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
|
|
||||||
u := *url
|
|
||||||
u.Host = net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port))
|
|
||||||
addr := &net.TCPAddr{
|
|
||||||
IP: ip,
|
|
||||||
Port: port,
|
|
||||||
}
|
|
||||||
dialer, err := l.tcp.dialerFor(addr, info.sintf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
wsconn, _, err := websocket.Dial(ctx, u.String(), &websocket.DialOptions{
|
|
||||||
HTTPClient: &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
Proxy: http.ProxyFromEnvironment,
|
|
||||||
Dial: dialer.Dial,
|
|
||||||
DialContext: dialer.DialContext,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Subprotocols: []string{"ygg-ws"},
|
|
||||||
Host: hostname,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &linkWSConn{
|
|
||||||
Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary),
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *linkWS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
|
||||||
nl, err := l.listenconfig.Listen(ctx, "tcp", url.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := make(chan *linkWSConn)
|
|
||||||
|
|
||||||
httpServer := &http.Server{
|
|
||||||
Handler: &wsServer{
|
|
||||||
ch: ch,
|
|
||||||
ctx: ctx,
|
|
||||||
},
|
|
||||||
BaseContext: func(_ net.Listener) context.Context { return ctx },
|
|
||||||
ReadTimeout: time.Second * 10,
|
|
||||||
WriteTimeout: time.Second * 10,
|
|
||||||
}
|
|
||||||
|
|
||||||
lwl := &linkWSListener{
|
|
||||||
ch: ch,
|
|
||||||
ctx: ctx,
|
|
||||||
httpServer: httpServer,
|
|
||||||
listener: nl,
|
|
||||||
}
|
|
||||||
go lwl.httpServer.Serve(nl) // nolint:errcheck
|
|
||||||
return lwl, nil
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/Arceliar/phony"
|
|
||||||
"github.com/coder/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
type linkWSS struct {
|
|
||||||
phony.Inbox
|
|
||||||
*links
|
|
||||||
tlsconfig *tls.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
type linkWSSConn struct {
|
|
||||||
net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *links) newLinkWSS() *linkWSS {
|
|
||||||
lwss := &linkWSS{
|
|
||||||
links: l,
|
|
||||||
tlsconfig: l.core.config.tls.Clone(),
|
|
||||||
}
|
|
||||||
return lwss
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *linkWSS) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
|
||||||
tlsconfig := l.tlsconfig.Clone()
|
|
||||||
return l.links.findSuitableIP(url, func(hostname string, ip net.IP, port int) (net.Conn, error) {
|
|
||||||
tlsconfig.ServerName = hostname
|
|
||||||
tlsconfig.MinVersion = tls.VersionTLS12
|
|
||||||
tlsconfig.MaxVersion = tls.VersionTLS13
|
|
||||||
u := *url
|
|
||||||
u.Host = net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port))
|
|
||||||
addr := &net.TCPAddr{
|
|
||||||
IP: ip,
|
|
||||||
Port: port,
|
|
||||||
}
|
|
||||||
dialer, err := l.tcp.dialerFor(addr, info.sintf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
wsconn, _, err := websocket.Dial(ctx, u.String(), &websocket.DialOptions{
|
|
||||||
HTTPClient: &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
Proxy: http.ProxyFromEnvironment,
|
|
||||||
Dial: dialer.Dial,
|
|
||||||
DialContext: dialer.DialContext,
|
|
||||||
TLSClientConfig: tlsconfig,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Subprotocols: []string{"ygg-ws"},
|
|
||||||
Host: hostname,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &linkWSSConn{
|
|
||||||
Conn: websocket.NetConn(ctx, wsconn, websocket.MessageBinary),
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *linkWSS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
|
||||||
return nil, fmt.Errorf("WSS listener not supported, use WS listener behind reverse proxy instead")
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ package core
|
||||||
import (
|
import (
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,8 +24,6 @@ func (c *Core) _applyOption(opt SetupOption) (err error) {
|
||||||
}
|
}
|
||||||
case ListenAddress:
|
case ListenAddress:
|
||||||
c.config._listeners[v] = struct{}{}
|
c.config._listeners[v] = struct{}{}
|
||||||
case PeerFilter:
|
|
||||||
c.config.peerFilter = v
|
|
||||||
case NodeInfo:
|
case NodeInfo:
|
||||||
c.config.nodeinfo = v
|
c.config.nodeinfo = v
|
||||||
case NodeInfoPrivacy:
|
case NodeInfoPrivacy:
|
||||||
|
@ -51,11 +48,9 @@ type Peer struct {
|
||||||
type NodeInfo map[string]interface{}
|
type NodeInfo map[string]interface{}
|
||||||
type NodeInfoPrivacy bool
|
type NodeInfoPrivacy bool
|
||||||
type AllowedPublicKey ed25519.PublicKey
|
type AllowedPublicKey ed25519.PublicKey
|
||||||
type PeerFilter func(net.IP) bool
|
|
||||||
|
|
||||||
func (a ListenAddress) isSetupOption() {}
|
func (a ListenAddress) isSetupOption() {}
|
||||||
func (a Peer) isSetupOption() {}
|
func (a Peer) isSetupOption() {}
|
||||||
func (a NodeInfo) isSetupOption() {}
|
func (a NodeInfo) isSetupOption() {}
|
||||||
func (a NodeInfoPrivacy) isSetupOption() {}
|
func (a NodeInfoPrivacy) isSetupOption() {}
|
||||||
func (a AllowedPublicKey) isSetupOption() {}
|
func (a AllowedPublicKey) isSetupOption() {}
|
||||||
func (a PeerFilter) isSetupOption() {}
|
|
||||||
|
|
|
@ -251,16 +251,15 @@ func (p *protoHandler) getSelfHandler(in json.RawMessage) (interface{}, error) {
|
||||||
if kbs, err = hex.DecodeString(req.Key); err != nil {
|
if kbs, err = hex.DecodeString(req.Key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(kbs) != ed25519.PublicKeySize {
|
|
||||||
return nil, fmt.Errorf("invalid public key length")
|
|
||||||
}
|
|
||||||
copy(key[:], kbs)
|
copy(key[:], kbs)
|
||||||
ch := make(chan []byte, 1)
|
ch := make(chan []byte, 1)
|
||||||
p.sendGetSelfRequest(key, func(info []byte) {
|
p.sendGetSelfRequest(key, func(info []byte) {
|
||||||
ch <- info
|
ch <- info
|
||||||
})
|
})
|
||||||
|
timer := time.NewTimer(6 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
select {
|
select {
|
||||||
case <-time.After(6 * time.Second):
|
case <-timer.C:
|
||||||
return nil, errors.New("timeout")
|
return nil, errors.New("timeout")
|
||||||
case info := <-ch:
|
case info := <-ch:
|
||||||
var msg json.RawMessage
|
var msg json.RawMessage
|
||||||
|
@ -292,16 +291,15 @@ func (p *protoHandler) getPeersHandler(in json.RawMessage) (interface{}, error)
|
||||||
if kbs, err = hex.DecodeString(req.Key); err != nil {
|
if kbs, err = hex.DecodeString(req.Key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(kbs) != ed25519.PublicKeySize {
|
|
||||||
return nil, fmt.Errorf("invalid public key length")
|
|
||||||
}
|
|
||||||
copy(key[:], kbs)
|
copy(key[:], kbs)
|
||||||
ch := make(chan []byte, 1)
|
ch := make(chan []byte, 1)
|
||||||
p.sendGetPeersRequest(key, func(info []byte) {
|
p.sendGetPeersRequest(key, func(info []byte) {
|
||||||
ch <- info
|
ch <- info
|
||||||
})
|
})
|
||||||
|
timer := time.NewTimer(6 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
select {
|
select {
|
||||||
case <-time.After(6 * time.Second):
|
case <-timer.C:
|
||||||
return nil, errors.New("timeout")
|
return nil, errors.New("timeout")
|
||||||
case info := <-ch:
|
case info := <-ch:
|
||||||
ks := make(map[string][]string)
|
ks := make(map[string][]string)
|
||||||
|
@ -343,16 +341,15 @@ func (p *protoHandler) getTreeHandler(in json.RawMessage) (interface{}, error) {
|
||||||
if kbs, err = hex.DecodeString(req.Key); err != nil {
|
if kbs, err = hex.DecodeString(req.Key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(kbs) != ed25519.PublicKeySize {
|
|
||||||
return nil, fmt.Errorf("invalid public key length")
|
|
||||||
}
|
|
||||||
copy(key[:], kbs)
|
copy(key[:], kbs)
|
||||||
ch := make(chan []byte, 1)
|
ch := make(chan []byte, 1)
|
||||||
p.sendGetTreeRequest(key, func(info []byte) {
|
p.sendGetTreeRequest(key, func(info []byte) {
|
||||||
ch <- info
|
ch <- info
|
||||||
})
|
})
|
||||||
|
timer := time.NewTimer(6 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
select {
|
select {
|
||||||
case <-time.After(6 * time.Second):
|
case <-timer.C:
|
||||||
return nil, errors.New("timeout")
|
return nil, errors.New("timeout")
|
||||||
case info := <-ch:
|
case info := <-ch:
|
||||||
ks := make(map[string][]string)
|
ks := make(map[string][]string)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"golang.org/x/crypto/blake2b"
|
"golang.org/x/crypto/blake2b"
|
||||||
|
@ -37,16 +38,6 @@ const (
|
||||||
metaPriority // uint8
|
metaPriority // uint8
|
||||||
)
|
)
|
||||||
|
|
||||||
type handshakeError string
|
|
||||||
|
|
||||||
func (e handshakeError) Error() string { return string(e) }
|
|
||||||
|
|
||||||
const ErrHandshakeInvalidPreamble = handshakeError("invalid handshake, remote side is not Yggdrasil")
|
|
||||||
const ErrHandshakeInvalidLength = handshakeError("invalid handshake length, possible version mismatch")
|
|
||||||
const ErrHandshakeInvalidPassword = handshakeError("invalid password supplied, check your config")
|
|
||||||
const ErrHandshakeHashFailure = handshakeError("invalid hash length")
|
|
||||||
const ErrHandshakeIncorrectPassword = handshakeError("password does not match remote side")
|
|
||||||
|
|
||||||
// Gets a base metadata with no keys set, but with the correct version numbers.
|
// Gets a base metadata with no keys set, but with the correct version numbers.
|
||||||
func version_getBaseMetadata() version_metadata {
|
func version_getBaseMetadata() version_metadata {
|
||||||
return version_metadata{
|
return version_metadata{
|
||||||
|
@ -86,7 +77,7 @@ func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if n != ed25519.PublicKeySize {
|
if n != ed25519.PublicKeySize {
|
||||||
return nil, ErrHandshakeHashFailure
|
return nil, fmt.Errorf("hash writer only wrote %d bytes", n)
|
||||||
}
|
}
|
||||||
hash := hasher.Sum(nil)
|
hash := hasher.Sum(nil)
|
||||||
bs = append(bs, ed25519.Sign(privateKey, hash)...)
|
bs = append(bs, ed25519.Sign(privateKey, hash)...)
|
||||||
|
@ -103,11 +94,11 @@ func (m *version_metadata) decode(r io.Reader, password []byte) error {
|
||||||
}
|
}
|
||||||
meta := [4]byte{'m', 'e', 't', 'a'}
|
meta := [4]byte{'m', 'e', 't', 'a'}
|
||||||
if !bytes.Equal(bh[:4], meta[:]) {
|
if !bytes.Equal(bh[:4], meta[:]) {
|
||||||
return ErrHandshakeInvalidPreamble
|
return fmt.Errorf("invalid handshake preamble")
|
||||||
}
|
}
|
||||||
hl := binary.BigEndian.Uint16(bh[4:6])
|
hl := binary.BigEndian.Uint16(bh[4:6])
|
||||||
if hl < ed25519.SignatureSize {
|
if hl < ed25519.SignatureSize {
|
||||||
return ErrHandshakeInvalidLength
|
return fmt.Errorf("invalid handshake length")
|
||||||
}
|
}
|
||||||
bs := make([]byte, hl)
|
bs := make([]byte, hl)
|
||||||
if _, err := io.ReadFull(r, bs); err != nil {
|
if _, err := io.ReadFull(r, bs); err != nil {
|
||||||
|
@ -141,15 +132,15 @@ func (m *version_metadata) decode(r io.Reader, password []byte) error {
|
||||||
|
|
||||||
hasher, err := blake2b.New512(password)
|
hasher, err := blake2b.New512(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrHandshakeInvalidPassword
|
return fmt.Errorf("invalid password supplied")
|
||||||
}
|
}
|
||||||
n, err := hasher.Write(m.publicKey)
|
n, err := hasher.Write(m.publicKey)
|
||||||
if err != nil || n != ed25519.PublicKeySize {
|
if err != nil || n != ed25519.PublicKeySize {
|
||||||
return ErrHandshakeHashFailure
|
return fmt.Errorf("failed to generate hash")
|
||||||
}
|
}
|
||||||
hash := hasher.Sum(nil)
|
hash := hasher.Sum(nil)
|
||||||
if !ed25519.Verify(m.publicKey, hash, sig) {
|
if !ed25519.Verify(m.publicKey, hash, sig) {
|
||||||
return ErrHandshakeIncorrectPassword
|
return fmt.Errorf("password is incorrect")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,47 +2,20 @@ package multicast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Arceliar/phony"
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
"github.com/yggdrasil-network/yggdrasil-go/src/admin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GetMulticastInterfacesRequest struct{}
|
type GetMulticastInterfacesRequest struct{}
|
||||||
type GetMulticastInterfacesResponse struct {
|
type GetMulticastInterfacesResponse struct {
|
||||||
Interfaces []MulticastInterfaceState `json:"multicast_interfaces"`
|
Interfaces []string `json:"multicast_interfaces"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MulticastInterfaceState struct {
|
func (m *Multicast) getMulticastInterfacesHandler(req *GetMulticastInterfacesRequest, res *GetMulticastInterfacesResponse) error {
|
||||||
Name string `json:"name"`
|
res.Interfaces = []string{}
|
||||||
Address string `json:"address"`
|
for _, v := range m.Interfaces() {
|
||||||
Beacon bool `json:"beacon"`
|
res.Interfaces = append(res.Interfaces, v.Name)
|
||||||
Listen bool `json:"listen"`
|
}
|
||||||
Password bool `json:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Multicast) getMulticastInterfacesHandler(_ *GetMulticastInterfacesRequest, res *GetMulticastInterfacesResponse) error {
|
|
||||||
res.Interfaces = []MulticastInterfaceState{}
|
|
||||||
phony.Block(m, func() {
|
|
||||||
for name, intf := range m._interfaces {
|
|
||||||
is := MulticastInterfaceState{
|
|
||||||
Name: intf.iface.Name,
|
|
||||||
Beacon: intf.beacon,
|
|
||||||
Listen: intf.listen,
|
|
||||||
Password: len(intf.password) > 0,
|
|
||||||
}
|
|
||||||
if li := m._listeners[name]; li != nil && li.listener != nil {
|
|
||||||
is.Address = li.listener.Addr().String()
|
|
||||||
} else {
|
|
||||||
is.Address = "-"
|
|
||||||
}
|
|
||||||
res.Interfaces = append(res.Interfaces, is)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
slices.SortStableFunc(res.Interfaces, func(a, b MulticastInterfaceState) int {
|
|
||||||
return strings.Compare(a.Name, b.Name)
|
|
||||||
})
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,10 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Arceliar/phony"
|
"github.com/Arceliar/phony"
|
||||||
"github.com/gologme/log"
|
"github.com/gologme/log"
|
||||||
"github.com/wlynxg/anet"
|
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||||
"golang.org/x/crypto/blake2b"
|
"golang.org/x/crypto/blake2b"
|
||||||
|
@ -30,7 +28,7 @@ type Multicast struct {
|
||||||
core *core.Core
|
core *core.Core
|
||||||
log *log.Logger
|
log *log.Logger
|
||||||
sock *ipv6.PacketConn
|
sock *ipv6.PacketConn
|
||||||
running atomic.Bool
|
_isOpen bool
|
||||||
_listeners map[string]*listenerInfo
|
_listeners map[string]*listenerInfo
|
||||||
_interfaces map[string]*interfaceInfo
|
_interfaces map[string]*interfaceInfo
|
||||||
_timer *time.Timer
|
_timer *time.Timer
|
||||||
|
@ -81,7 +79,7 @@ func New(core *core.Core, log *log.Logger, opts ...SetupOption) (*Multicast, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) _start() error {
|
func (m *Multicast) _start() error {
|
||||||
if !m.running.CompareAndSwap(false, true) {
|
if m._isOpen {
|
||||||
return fmt.Errorf("multicast module is already started")
|
return fmt.Errorf("multicast module is already started")
|
||||||
}
|
}
|
||||||
var anyEnabled bool
|
var anyEnabled bool
|
||||||
|
@ -89,14 +87,12 @@ func (m *Multicast) _start() error {
|
||||||
anyEnabled = anyEnabled || intf.Beacon || intf.Listen
|
anyEnabled = anyEnabled || intf.Beacon || intf.Listen
|
||||||
}
|
}
|
||||||
if !anyEnabled {
|
if !anyEnabled {
|
||||||
m.running.Store(false)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
m.log.Debugln("Starting multicast module")
|
m.log.Debugln("Starting multicast module")
|
||||||
defer m.log.Debugln("Started multicast module")
|
defer m.log.Debugln("Started multicast module")
|
||||||
addr, err := net.ResolveUDPAddr("udp", string(m.config._groupAddr))
|
addr, err := net.ResolveUDPAddr("udp", string(m.config._groupAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.running.Store(false)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
listenString := fmt.Sprintf("[::]:%v", addr.Port)
|
listenString := fmt.Sprintf("[::]:%v", addr.Port)
|
||||||
|
@ -105,7 +101,6 @@ func (m *Multicast) _start() error {
|
||||||
}
|
}
|
||||||
conn, err := lc.ListenPacket(context.Background(), "udp6", listenString)
|
conn, err := lc.ListenPacket(context.Background(), "udp6", listenString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.running.Store(false)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.sock = ipv6.NewPacketConn(conn)
|
m.sock = ipv6.NewPacketConn(conn)
|
||||||
|
@ -113,6 +108,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
|
||||||
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)
|
||||||
|
@ -122,7 +118,11 @@ func (m *Multicast) _start() error {
|
||||||
|
|
||||||
// IsStarted returns true if the module has been started.
|
// IsStarted returns true if the module has been started.
|
||||||
func (m *Multicast) IsStarted() bool {
|
func (m *Multicast) IsStarted() bool {
|
||||||
return m.running.Load()
|
var isOpen bool
|
||||||
|
phony.Block(m, func() {
|
||||||
|
isOpen = m._isOpen
|
||||||
|
})
|
||||||
|
return isOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the multicast module.
|
// Stop stops the multicast module.
|
||||||
|
@ -136,10 +136,8 @@ func (m *Multicast) Stop() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) _stop() error {
|
func (m *Multicast) _stop() error {
|
||||||
if !m.running.CompareAndSwap(true, false) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
m.log.Infoln("Stopping multicast module")
|
m.log.Infoln("Stopping multicast module")
|
||||||
|
m._isOpen = false
|
||||||
if m.sock != nil {
|
if m.sock != nil {
|
||||||
m.sock.Close()
|
m.sock.Close()
|
||||||
}
|
}
|
||||||
|
@ -149,22 +147,14 @@ 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 {
|
||||||
// 'anet' package is used here to avoid https://github.com/golang/go/issues/40569
|
addrs, err := info.iface.Addrs()
|
||||||
addrs, err := anet.InterfaceAddrsByInterface(&info.iface)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.log.Warnf("Failed up get addresses for interface %s: %s", name, err)
|
m.log.Warnf("Failed up get addresses for interface %s: %s", name, err)
|
||||||
delete(interfaces, name)
|
delete(interfaces, name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, addr := range addrs {
|
info.addrs = addrs
|
||||||
addrIP, _, err := net.ParseCIDR(addr.String())
|
|
||||||
if err != nil || addrIP.To4() != nil || !addrIP.IsLinkLocalUnicast() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
info.addrs = append(info.addrs, addr)
|
|
||||||
}
|
|
||||||
interfaces[name] = info
|
interfaces[name] = info
|
||||||
m.log.Debugf("Discovered addresses for interface %s: %s", name, addrs)
|
|
||||||
}
|
}
|
||||||
m._interfaces = interfaces
|
m._interfaces = interfaces
|
||||||
}
|
}
|
||||||
|
@ -183,11 +173,10 @@ func (m *Multicast) Interfaces() map[string]net.Interface {
|
||||||
func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
|
func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
|
||||||
interfaces := make(map[string]*interfaceInfo)
|
interfaces := make(map[string]*interfaceInfo)
|
||||||
// Ask the system for network interfaces
|
// Ask the system for network interfaces
|
||||||
// 'anet' package is used here to avoid https://github.com/golang/go/issues/40569
|
allifaces, err := net.Interfaces()
|
||||||
allifaces, err := anet.Interfaces()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't panic, since this may be from e.g. too many open files (from too much connection spam)
|
// Don't panic, since this may be from e.g. too many open files (from too much connection spam)
|
||||||
m.log.Debugf("Failed to get interfaces: %s", err)
|
// TODO? log something
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Work out which interfaces to announce on
|
// Work out which interfaces to announce on
|
||||||
|
@ -196,8 +185,6 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
|
||||||
switch {
|
switch {
|
||||||
case iface.Flags&net.FlagUp == 0:
|
case iface.Flags&net.FlagUp == 0:
|
||||||
continue // Ignore interfaces that are down
|
continue // Ignore interfaces that are down
|
||||||
case iface.Flags&net.FlagRunning == 0:
|
|
||||||
continue // Ignore interfaces that are not running
|
|
||||||
case iface.Flags&net.FlagMulticast == 0:
|
case iface.Flags&net.FlagMulticast == 0:
|
||||||
continue // Ignore non-multicast interfaces
|
continue // Ignore non-multicast interfaces
|
||||||
case iface.Flags&net.FlagPointToPoint != 0:
|
case iface.Flags&net.FlagPointToPoint != 0:
|
||||||
|
@ -246,7 +233,7 @@ func (m *Multicast) AnnounceNow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) _announce() {
|
func (m *Multicast) _announce() {
|
||||||
if !m.running.Load() {
|
if !m._isOpen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m._updateInterfaces()
|
m._updateInterfaces()
|
||||||
|
@ -263,7 +250,7 @@ func (m *Multicast) _announce() {
|
||||||
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.Cancel()
|
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)
|
||||||
}
|
}
|
||||||
|
@ -305,9 +292,13 @@ func (m *Multicast) _announce() {
|
||||||
for _, info := range m._interfaces {
|
for _, info := range m._interfaces {
|
||||||
iface := info.iface
|
iface := info.iface
|
||||||
for _, addr := range info.addrs {
|
for _, addr := range info.addrs {
|
||||||
addrIP, _, err := net.ParseCIDR(addr.String())
|
addrIP, _, _ := net.ParseCIDR(addr.String())
|
||||||
// Ignore IPv4 addresses or non-link-local addresses
|
// Ignore IPv4 addresses
|
||||||
if err != nil || addrIP.To4() != nil || !addrIP.IsLinkLocalUnicast() {
|
if addrIP.To4() != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Ignore non-link-local addresses
|
||||||
|
if !addrIP.IsLinkLocalUnicast() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if info.listen {
|
if info.listen {
|
||||||
|
@ -329,7 +320,7 @@ func (m *Multicast) _announce() {
|
||||||
Host: net.JoinHostPort(addrIP.String(), fmt.Sprintf("%d", info.port)),
|
Host: net.JoinHostPort(addrIP.String(), fmt.Sprintf("%d", info.port)),
|
||||||
RawQuery: v.Encode(),
|
RawQuery: v.Encode(),
|
||||||
}
|
}
|
||||||
if li, err := m.core.ListenLocal(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(), port: info.port}
|
linfo = &listenerInfo{listener: li, time: time.Now(), port: info.port}
|
||||||
|
@ -367,7 +358,6 @@ func (m *Multicast) _announce() {
|
||||||
if linfo.interval.Seconds() < 15 {
|
if linfo.interval.Seconds() < 15 {
|
||||||
linfo.interval += time.Second
|
linfo.interval += time.Second
|
||||||
}
|
}
|
||||||
linfo.time = time.Now()
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -385,9 +375,6 @@ func (m *Multicast) listen() {
|
||||||
bs := make([]byte, 2048)
|
bs := make([]byte, 2048)
|
||||||
hb := make([]byte, 0, blake2b.Size) // Reused to reduce hash allocations
|
hb := make([]byte, 0, blake2b.Size) // Reused to reduce hash allocations
|
||||||
for {
|
for {
|
||||||
if !m.running.Load() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n, rcm, fromAddr, err := m.sock.ReadFrom(bs)
|
n, rcm, fromAddr, err := m.sock.ReadFrom(bs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !m.IsStarted() {
|
if !m.IsStarted() {
|
||||||
|
|
|
@ -31,7 +31,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *Multicast) _multicastStarted() {
|
func (m *Multicast) _multicastStarted() {
|
||||||
if !m.running.Load() {
|
if !m._isOpen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
C.StopAWDLBrowsing()
|
C.StopAWDLBrowsing()
|
||||||
|
|
|
@ -1,70 +1,42 @@
|
||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
const TUN_OFFSET_BYTES = 4
|
||||||
"errors"
|
|
||||||
|
|
||||||
wgtun "golang.zx2c4.com/wireguard/tun"
|
|
||||||
)
|
|
||||||
|
|
||||||
const TUN_OFFSET_BYTES = 80 // sizeof(virtio_net_hdr)
|
|
||||||
|
|
||||||
func (tun *TunAdapter) read() {
|
func (tun *TunAdapter) read() {
|
||||||
vs := tun.iface.BatchSize()
|
var buf [TUN_OFFSET_BYTES + 65535]byte
|
||||||
bufs := make([][]byte, vs)
|
|
||||||
sizes := make([]int, vs)
|
|
||||||
for i := range bufs {
|
|
||||||
bufs[i] = make([]byte, TUN_OFFSET_BYTES+65535)
|
|
||||||
}
|
|
||||||
for {
|
for {
|
||||||
n, err := tun.iface.Read(bufs, sizes, TUN_OFFSET_BYTES)
|
n, err := tun.iface.Read(buf[:], TUN_OFFSET_BYTES)
|
||||||
if err != nil {
|
if n <= TUN_OFFSET_BYTES || err != nil {
|
||||||
if errors.Is(err, wgtun.ErrTooManySegments) {
|
|
||||||
tun.log.Debugln("TUN segments dropped: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tun.log.Errorln("Error reading TUN:", err)
|
tun.log.Errorln("Error reading TUN:", err)
|
||||||
return
|
ferr := tun.iface.Flush()
|
||||||
}
|
if ferr != nil {
|
||||||
for i, b := range bufs[:n] {
|
tun.log.Errorln("Unable to flush packets:", ferr)
|
||||||
if _, err := tun.rwc.Write(b[TUN_OFFSET_BYTES : TUN_OFFSET_BYTES+sizes[i]]); err != nil {
|
|
||||||
tun.log.Debugln("Unable to send packet:", err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tun *TunAdapter) queue() {
|
|
||||||
for {
|
|
||||||
p := bufPool.Get().([]byte)[:bufPoolSize]
|
|
||||||
n, err := tun.rwc.Read(p)
|
|
||||||
if err != nil {
|
|
||||||
tun.log.Errorln("Exiting TUN writer due to core read error:", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tun.ch <- p[:n]
|
begin := TUN_OFFSET_BYTES
|
||||||
|
end := begin + n
|
||||||
|
bs := buf[begin:end]
|
||||||
|
if _, err := tun.rwc.Write(bs); err != nil {
|
||||||
|
tun.log.Debugln("Unable to send packet:", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tun *TunAdapter) write() {
|
func (tun *TunAdapter) write() {
|
||||||
vs := cap(tun.ch)
|
var buf [TUN_OFFSET_BYTES + 65535]byte
|
||||||
bufs := make([][]byte, vs)
|
|
||||||
for i := range bufs {
|
|
||||||
bufs[i] = make([]byte, TUN_OFFSET_BYTES+65535)
|
|
||||||
}
|
|
||||||
for {
|
for {
|
||||||
n := len(tun.ch)
|
bs := buf[TUN_OFFSET_BYTES:]
|
||||||
if n == 0 {
|
n, err := tun.rwc.Read(bs)
|
||||||
n = 1 // Nothing queued up yet, wait for it instead
|
if err != nil {
|
||||||
}
|
tun.log.Errorln("Exiting TUN writer due to core read error:", err)
|
||||||
for i := 0; i < n; i++ {
|
return
|
||||||
msg := <-tun.ch
|
|
||||||
bufs[i] = append(bufs[i][:TUN_OFFSET_BYTES], msg...)
|
|
||||||
bufPool.Put(msg) // nolint:staticcheck
|
|
||||||
}
|
}
|
||||||
if !tun.isEnabled {
|
if !tun.isEnabled {
|
||||||
continue // Nothing to do, the tun isn't enabled
|
continue // Nothing to do, the tun isn't enabled
|
||||||
}
|
}
|
||||||
if _, err := tun.iface.Write(bufs[:n], TUN_OFFSET_BYTES); err != nil {
|
bs = buf[:TUN_OFFSET_BYTES+n]
|
||||||
|
if _, err = tun.iface.Write(bs, TUN_OFFSET_BYTES); err != nil {
|
||||||
tun.Act(nil, func() {
|
tun.Act(nil, func() {
|
||||||
if !tun.isOpen {
|
if !tun.isOpen {
|
||||||
tun.log.Errorln("TUN iface write error:", err)
|
tun.log.Errorln("TUN iface write error:", err)
|
||||||
|
|
|
@ -10,10 +10,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/Arceliar/phony"
|
"github.com/Arceliar/phony"
|
||||||
wgtun "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/config"
|
||||||
|
@ -40,7 +39,7 @@ type TunAdapter struct {
|
||||||
addr address.Address
|
addr address.Address
|
||||||
subnet address.Subnet
|
subnet address.Subnet
|
||||||
mtu uint64
|
mtu uint64
|
||||||
iface wgtun.Device
|
iface tun.Device
|
||||||
phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex below
|
phony.Inbox // Currently only used for _handlePacket from the reader, TODO: all the stuff that currently needs a mutex 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
|
||||||
|
@ -49,7 +48,6 @@ type TunAdapter struct {
|
||||||
name InterfaceName
|
name InterfaceName
|
||||||
mtu InterfaceMTU
|
mtu InterfaceMTU
|
||||||
}
|
}
|
||||||
ch chan []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -125,7 +123,6 @@ func (tun *TunAdapter) _start() error {
|
||||||
if tun.config.name == "none" || tun.config.name == "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.queue()
|
|
||||||
go tun.write()
|
go tun.write()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -148,8 +145,6 @@ func (tun *TunAdapter) _start() error {
|
||||||
tun.rwc.SetMTU(tun.MTU())
|
tun.rwc.SetMTU(tun.MTU())
|
||||||
tun.isOpen = true
|
tun.isOpen = true
|
||||||
tun.isEnabled = true
|
tun.isEnabled = true
|
||||||
tun.ch = make(chan []byte, tun.iface.BatchSize())
|
|
||||||
go tun.queue()
|
|
||||||
go tun.read()
|
go tun.read()
|
||||||
go tun.write()
|
go tun.write()
|
||||||
return nil
|
return nil
|
||||||
|
@ -183,12 +178,3 @@ func (tun *TunAdapter) _stop() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const bufPoolSize = TUN_OFFSET_BYTES + 65535
|
|
||||||
|
|
||||||
var bufPool = sync.Pool{
|
|
||||||
New: func() any {
|
|
||||||
b := [bufPoolSize]byte{}
|
|
||||||
return b[:]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build freebsd
|
//go:build openbsd || freebsd
|
||||||
// +build freebsd
|
// +build openbsd freebsd
|
||||||
|
|
||||||
package tun
|
package tun
|
||||||
|
|
||||||
|
@ -54,6 +54,11 @@ struct in6_ifreq {
|
||||||
290 };
|
290 };
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
type in6_ifreq_mtu struct {
|
||||||
|
ifr_name [syscall.IFNAMSIZ]byte
|
||||||
|
ifru_mtu int
|
||||||
|
}
|
||||||
|
|
||||||
type in6_ifreq_addr struct {
|
type in6_ifreq_addr struct {
|
||||||
ifr_name [syscall.IFNAMSIZ]byte
|
ifr_name [syscall.IFNAMSIZ]byte
|
||||||
ifru_addr sockaddr_in6
|
ifru_addr sockaddr_in6
|
||||||
|
@ -107,6 +112,26 @@ func (tun *TunAdapter) setupAddress(addr string) error {
|
||||||
tun.log.Infof("Interface IPv6: %s", addr)
|
tun.log.Infof("Interface IPv6: %s", addr)
|
||||||
tun.log.Infof("Interface MTU: %d", tun.mtu)
|
tun.log.Infof("Interface MTU: %d", tun.mtu)
|
||||||
|
|
||||||
|
// Create the MTU request
|
||||||
|
var ir in6_ifreq_mtu
|
||||||
|
copy(ir.ifr_name[:], tun.Name())
|
||||||
|
ir.ifru_mtu = int(tun.mtu)
|
||||||
|
|
||||||
|
// Set the MTU
|
||||||
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 {
|
||||||
|
err = errno
|
||||||
|
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||||
|
|
||||||
|
// Fall back to ifconfig to set the MTU
|
||||||
|
cmd := exec.Command("ifconfig", tun.Name(), "mtu", string(tun.mtu))
|
||||||
|
tun.log.Warnf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " "))
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
tun.log.Errorf("SIOCSIFMTU fallback failed: %v.", err)
|
||||||
|
tun.log.Traceln(string(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create the address request
|
// Create the address request
|
||||||
// FIXME: I don't work!
|
// FIXME: I don't work!
|
||||||
var ar in6_ifreq_addr
|
var ar in6_ifreq_addr
|
|
@ -1,122 +0,0 @@
|
||||||
//go:build openbsd
|
|
||||||
// +build openbsd
|
|
||||||
|
|
||||||
package tun
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
wgtun "golang.zx2c4.com/wireguard/tun"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
SIOCAIFADDR_IN6 = 0x8080691a
|
|
||||||
ND6_INFINITE_LIFETIME = 0xffffffff
|
|
||||||
)
|
|
||||||
|
|
||||||
type in6_addrlifetime struct {
|
|
||||||
ia6t_expire int64
|
|
||||||
ia6t_preferred int64
|
|
||||||
ia6t_vltime uint32
|
|
||||||
ia6t_pltime uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match types from the net package, effectively being [16]byte for IPv6 addresses.
|
|
||||||
type in6_addr [16]uint8
|
|
||||||
|
|
||||||
type sockaddr_in6 struct {
|
|
||||||
sin6_len uint8
|
|
||||||
sin6_family uint8
|
|
||||||
sin6_port uint16
|
|
||||||
sin6_flowinfo uint32
|
|
||||||
sin6_addr in6_addr
|
|
||||||
sin6_scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sa6 *sockaddr_in6) setSockaddr(addr [/*16*/]byte /* net.IP or net.IPMask */) {
|
|
||||||
sa6.sin6_len = uint8(unsafe.Sizeof(*sa6))
|
|
||||||
sa6.sin6_family = unix.AF_INET6
|
|
||||||
|
|
||||||
for i := range sa6.sin6_addr {
|
|
||||||
sa6.sin6_addr[i] = addr[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type in6_aliasreq struct {
|
|
||||||
ifra_name [syscall.IFNAMSIZ]byte
|
|
||||||
ifra_addr sockaddr_in6
|
|
||||||
ifra_dstaddr sockaddr_in6
|
|
||||||
ifra_prefixmask sockaddr_in6
|
|
||||||
ifra_flags int32
|
|
||||||
ifra_lifetime in6_addrlifetime
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configures the TUN adapter with the correct IPv6 address and MTU.
|
|
||||||
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
|
||||||
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create TUN: %w", err)
|
|
||||||
}
|
|
||||||
tun.iface = iface
|
|
||||||
if mtu, err := iface.MTU(); err == nil {
|
|
||||||
tun.mtu = getSupportedMTU(uint64(mtu))
|
|
||||||
} else {
|
|
||||||
tun.mtu = 0
|
|
||||||
}
|
|
||||||
if addr != "" {
|
|
||||||
return tun.setupAddress(addr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configures the "utun" adapter from an existing file descriptor.
|
|
||||||
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
|
||||||
return fmt.Errorf("setup via FD not supported on this platform")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
|
||||||
var sfd int
|
|
||||||
var err error
|
|
||||||
|
|
||||||
ip, prefix, err := net.ParseCIDR(addr)
|
|
||||||
if err != nil {
|
|
||||||
tun.log.Errorf("Error in ParseCIDR: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create system socket
|
|
||||||
if sfd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil {
|
|
||||||
tun.log.Printf("Create AF_INET6 socket failed: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Friendly output
|
|
||||||
tun.log.Infof("Interface name: %s", tun.Name())
|
|
||||||
tun.log.Infof("Interface IPv6: %s", addr)
|
|
||||||
tun.log.Infof("Interface MTU: %d", tun.mtu)
|
|
||||||
|
|
||||||
// Create the address request
|
|
||||||
var ar in6_aliasreq
|
|
||||||
copy(ar.ifra_name[:], tun.Name())
|
|
||||||
|
|
||||||
ar.ifra_addr.setSockaddr(ip)
|
|
||||||
|
|
||||||
prefixmask := net.CIDRMask(prefix.Mask.Size())
|
|
||||||
ar.ifra_prefixmask.setSockaddr(prefixmask)
|
|
||||||
|
|
||||||
ar.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME
|
|
||||||
ar.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME
|
|
||||||
|
|
||||||
// Set the interface address
|
|
||||||
if err = unix.IoctlSetInt(sfd, SIOCAIFADDR_IN6, int(uintptr(unsafe.Pointer(&ar)))); err != nil {
|
|
||||||
tun.log.Errorf("Error in SIOCAIFADDR_IN6: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -8,12 +8,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
"golang.zx2c4.com/wintun"
|
|
||||||
wgtun "golang.zx2c4.com/wireguard/tun"
|
wgtun "golang.zx2c4.com/wireguard/tun"
|
||||||
"golang.zx2c4.com/wireguard/windows/elevate"
|
"golang.zx2c4.com/wireguard/windows/elevate"
|
||||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||||
|
@ -33,23 +31,11 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||||
if guid, err = windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); err != nil {
|
if guid, err = windows.GUIDFromString("{8f59971a-7872-4aa6-b2eb-061fc4e9d0a7}"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu))
|
if iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu)); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
// Very rare condition, it will purge the old device and create new
|
|
||||||
tun.log.Printf("Error creating TUN: '%s'", err)
|
|
||||||
wintun.Uninstall()
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
tun.log.Printf("Trying again")
|
|
||||||
iface, err = wgtun.CreateTUNWithRequestedGUID(ifname, &guid, int(mtu))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tun.log.Printf("Waiting for TUN to come up")
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if addr != "" {
|
if addr != "" {
|
||||||
tun.log.Printf("Setting up address")
|
|
||||||
if err = tun.setupAddress(addr); err != nil {
|
if err = tun.setupAddress(addr); err != nil {
|
||||||
tun.log.Errorln("Failed to set up TUN address:", err)
|
tun.log.Errorln("Failed to set up TUN address:", err)
|
||||||
return err
|
return err
|
||||||
|
@ -62,7 +48,6 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||||
if mtu, err := iface.MTU(); err == nil {
|
if mtu, err := iface.MTU(); err == nil {
|
||||||
tun.mtu = uint64(mtu)
|
tun.mtu = uint64(mtu)
|
||||||
}
|
}
|
||||||
tun.log.Printf("TUN is set up successfully")
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue