mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
At startup, ctrld gathers bootstrap IP information and use this bootstrap IP for connecting to upstream. However, in case the network stack changed, for example, dues to VPN connection, ctrld will still use this old (maybe invalid) bootstrap IP for the current network stack. This commit rework the discovering process, and re-initializing the bootstrap IP if connecting to upstream failed.
96 lines
1.9 KiB
Go
96 lines
1.9 KiB
Go
package net
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"tailscale.com/logtail/backoff"
|
|
)
|
|
|
|
const (
|
|
controldIPv6Test = "ipv6.controld.io"
|
|
controldIPv4Test = "ipv4.controld.io"
|
|
bootstrapDNS = "76.76.2.0:53"
|
|
)
|
|
|
|
var Dialer = &net.Dialer{
|
|
Resolver: &net.Resolver{
|
|
PreferGo: true,
|
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
d := net.Dialer{
|
|
Timeout: 10 * time.Second,
|
|
}
|
|
return d.DialContext(ctx, "udp", bootstrapDNS)
|
|
},
|
|
},
|
|
}
|
|
|
|
var (
|
|
stackOnce atomic.Pointer[sync.Once]
|
|
ipv4Enabled bool
|
|
ipv6Enabled bool
|
|
canListenIPv6Local bool
|
|
hasNetworkUp bool
|
|
)
|
|
|
|
func init() {
|
|
stackOnce.Store(new(sync.Once))
|
|
}
|
|
|
|
func probeStack() {
|
|
b := backoff.NewBackoff("probeStack", func(format string, args ...any) {}, time.Minute)
|
|
for {
|
|
if _, err := Dialer.Dial("udp", bootstrapDNS); err == nil {
|
|
hasNetworkUp = true
|
|
break
|
|
} else {
|
|
b.BackOff(context.Background(), err)
|
|
}
|
|
}
|
|
if _, err := Dialer.Dial("tcp4", net.JoinHostPort(controldIPv4Test, "80")); err == nil {
|
|
ipv4Enabled = true
|
|
}
|
|
if _, err := Dialer.Dial("tcp6", net.JoinHostPort(controldIPv6Test, "80")); err == nil {
|
|
ipv6Enabled = true
|
|
}
|
|
if ln, err := net.Listen("tcp6", "[::1]:53"); err == nil {
|
|
ln.Close()
|
|
canListenIPv6Local = true
|
|
}
|
|
}
|
|
|
|
func Reset() {
|
|
stackOnce.Store(new(sync.Once))
|
|
}
|
|
|
|
func Up() bool {
|
|
stackOnce.Load().Do(probeStack)
|
|
return hasNetworkUp
|
|
}
|
|
|
|
func SupportsIPv4() bool {
|
|
stackOnce.Load().Do(probeStack)
|
|
return ipv4Enabled
|
|
}
|
|
|
|
func SupportsIPv6() bool {
|
|
stackOnce.Load().Do(probeStack)
|
|
return ipv6Enabled
|
|
}
|
|
|
|
func SupportsIPv6ListenLocal() bool {
|
|
stackOnce.Load().Do(probeStack)
|
|
return canListenIPv6Local
|
|
}
|
|
|
|
// IsIPv6 checks if the provided IP is v6.
|
|
//
|
|
//lint:ignore U1000 use in os_windows.go
|
|
func IsIPv6(ip string) bool {
|
|
parsedIP := net.ParseIP(ip)
|
|
return parsedIP != nil && parsedIP.To4() == nil && parsedIP.To16() != nil
|
|
}
|