Multicast only finds peers on the same LAN. Reach off-LAN boxes by probing them directly over unicast (works over any routable address; Tailscale is the easy, secure choice): - discovery.Probe: unicast POST /register (https->http fallback) with a two-way handshake, so send and receive both work; offline peers age out. - internal/tailscale: Peers() shells `tailscale status --json` for online peers. - config.KnownPeers: persisted manual remotes. - main.go: watchRemotes goroutine probes knownPeers ∪ tailscale peers every 10s, in both the normal TUI path and quick-send. - TUI: `+` on Devices opens an add-remote modal (host/IP/Tailscale name). - install.sh: interactive local/remote install prompt; remote mode locks port 53317 to the Tailscale interface (ufw), with container/userspace-networking detection. README documents remote devices. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
46 lines
1.1 KiB
Go
46 lines
1.1 KiB
Go
package tailscale
|
|
|
|
import (
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
)
|
|
|
|
func TestParsePeersOnlineIPv4Only(t *testing.T) {
|
|
data := []byte(`{
|
|
"Peer": {
|
|
"key1": {"TailscaleIPs": ["100.64.0.1", "fd7a:115c::1"], "Online": true},
|
|
"key2": {"TailscaleIPs": ["100.64.0.2"], "Online": false},
|
|
"key3": {"TailscaleIPs": ["fd7a:115c::3"], "Online": true},
|
|
"key4": {"TailscaleIPs": ["100.64.0.4"], "Online": true}
|
|
}
|
|
}`)
|
|
got := parsePeers(data)
|
|
sort.Strings(got)
|
|
want := []string{"100.64.0.1", "100.64.0.4"} // online + has IPv4; offline and v6-only excluded
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("parsePeers = %v, want %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestParsePeersBadJSON(t *testing.T) {
|
|
if got := parsePeers([]byte("not json")); got != nil {
|
|
t.Errorf("bad JSON should yield nil, got %v", got)
|
|
}
|
|
}
|
|
|
|
func TestIsIPv4(t *testing.T) {
|
|
cases := map[string]bool{
|
|
"100.64.0.1": true,
|
|
"1.2.3.4": true,
|
|
"fd7a:115c::1": false,
|
|
"1.2.3": false,
|
|
"": false,
|
|
"abc": false,
|
|
}
|
|
for in, want := range cases {
|
|
if got := isIPv4(in); got != want {
|
|
t.Errorf("isIPv4(%q) = %v, want %v", in, got, want)
|
|
}
|
|
}
|
|
}
|