mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-28 22:23:24 +00:00
Capitalize the first letter of all log messages throughout the codebase to improve readability and consistency in logging output. Key improvements: - All log messages now start with capital letters - Consistent formatting across all logging statements - Improved readability for debugging and monitoring - Enhanced user experience with better formatted messages Files updated: - CLI commands and service management - Internal client information discovery - Network operations and configuration - DNS resolver and proxy operations - Platform-specific implementations This completes the final phase of the logging improvement project, ensuring all log messages follow consistent capitalization standards for better readability and professional appearance.
210 lines
5.0 KiB
Go
210 lines
5.0 KiB
Go
package net
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"sync"
|
|
"sync/atomic"
|
|
"syscall"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
"tailscale.com/logtail/backoff"
|
|
)
|
|
|
|
const (
|
|
v4BootstrapDNS = "76.76.2.22:53"
|
|
v6BootstrapDNS = "[2606:1a40::22]:53"
|
|
v6BootstrapIP = "2606:1a40::22"
|
|
defaultHTTPSPort = "443"
|
|
defaultHTTPPort = "80"
|
|
defaultDNSPort = "53"
|
|
probeStackTimeout = 2 * time.Second
|
|
)
|
|
|
|
var commonIPv6Ports = []string{defaultHTTPSPort, defaultHTTPPort, defaultDNSPort}
|
|
|
|
var Dialer = &net.Dialer{
|
|
Resolver: &net.Resolver{
|
|
PreferGo: true,
|
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
d := ParallelDialer{}
|
|
d.Timeout = 10 * time.Second
|
|
l := zap.NewNop()
|
|
return d.DialContext(ctx, "udp", []string{v4BootstrapDNS, v6BootstrapDNS}, l)
|
|
},
|
|
},
|
|
}
|
|
|
|
var probeStackDialer = &net.Dialer{
|
|
Resolver: Dialer.Resolver,
|
|
Timeout: probeStackTimeout,
|
|
}
|
|
|
|
var (
|
|
stackOnce atomic.Pointer[sync.Once]
|
|
canListenIPv6Local bool
|
|
hasNetworkUp bool
|
|
)
|
|
|
|
func init() {
|
|
stackOnce.Store(new(sync.Once))
|
|
}
|
|
|
|
// supportIPv6 checks for IPv6 connectivity by attempting to connect to predefined ports
|
|
// on a specific IPv6 address.
|
|
// Returns a boolean indicating if IPv6 is supported and the port on which the connection succeeded.
|
|
// If no connection is successful, returns false and an empty string.
|
|
func supportIPv6(ctx context.Context) (supported bool, successPort string) {
|
|
for _, port := range commonIPv6Ports {
|
|
if canConnectToIPv6Port(ctx, port) {
|
|
return true, string(port)
|
|
}
|
|
}
|
|
return false, ""
|
|
}
|
|
|
|
// canConnectToIPv6Port attempts to establish a TCP connection to the specified port
|
|
// using IPv6. Returns true if the connection was successful.
|
|
func canConnectToIPv6Port(ctx context.Context, port string) bool {
|
|
address := net.JoinHostPort(v6BootstrapIP, port)
|
|
conn, err := probeStackDialer.DialContext(ctx, "tcp6", address)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
_ = conn.Close()
|
|
return true
|
|
}
|
|
|
|
func supportListenIPv6Local() bool {
|
|
if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil {
|
|
ln.Close()
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func probeStack() {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
go func() {
|
|
sigs := make(chan os.Signal, 1)
|
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
<-sigs
|
|
cancel()
|
|
}()
|
|
|
|
b := backoff.NewBackoff("probeStack", func(format string, args ...any) {}, 5*time.Second)
|
|
for {
|
|
if _, err := probeStackDialer.DialContext(ctx, "udp", v4BootstrapDNS); err == nil {
|
|
hasNetworkUp = true
|
|
break
|
|
}
|
|
if _, err := probeStackDialer.DialContext(ctx, "udp", v6BootstrapDNS); err == nil {
|
|
hasNetworkUp = true
|
|
break
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
}
|
|
b.BackOff(context.Background(), errors.New("network is down"))
|
|
}
|
|
canListenIPv6Local = supportListenIPv6Local()
|
|
}
|
|
|
|
func Up() bool {
|
|
stackOnce.Load().Do(probeStack)
|
|
return hasNetworkUp
|
|
}
|
|
|
|
func SupportsIPv6ListenLocal() bool {
|
|
stackOnce.Load().Do(probeStack)
|
|
return canListenIPv6Local
|
|
}
|
|
|
|
// IPv6Available is like SupportsIPv6, but always do the check without caching.
|
|
func IPv6Available(ctx context.Context) bool {
|
|
hasV6, _ := supportIPv6(ctx)
|
|
return hasV6
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// IsLinkLocalUnicastIPv6 checks if the provided IP is a link local unicast v6 address.
|
|
func IsLinkLocalUnicastIPv6(ip string) bool {
|
|
parsedIP := net.ParseIP(ip)
|
|
if parsedIP == nil || parsedIP.To4() != nil || parsedIP.To16() == nil {
|
|
return false
|
|
}
|
|
return parsedIP.To16().IsLinkLocalUnicast()
|
|
}
|
|
|
|
type parallelDialerResult struct {
|
|
conn net.Conn
|
|
err error
|
|
}
|
|
|
|
type ParallelDialer struct {
|
|
net.Dialer
|
|
}
|
|
|
|
func (d *ParallelDialer) DialContext(ctx context.Context, network string, addrs []string, logger *zap.Logger) (net.Conn, error) {
|
|
if len(addrs) == 0 {
|
|
return nil, errors.New("empty addresses")
|
|
}
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
defer cancel()
|
|
|
|
done := make(chan struct{})
|
|
defer close(done)
|
|
ch := make(chan *parallelDialerResult, len(addrs))
|
|
var wg sync.WaitGroup
|
|
wg.Add(len(addrs))
|
|
go func() {
|
|
wg.Wait()
|
|
close(ch)
|
|
}()
|
|
|
|
for _, addr := range addrs {
|
|
go func(addr string) {
|
|
defer wg.Done()
|
|
logger.Debug("Dialing to", zap.String("address", addr))
|
|
conn, err := d.Dialer.DialContext(ctx, network, addr)
|
|
if err != nil {
|
|
logger.Debug("Failed to dial", zap.String("address", addr), zap.Error(err))
|
|
}
|
|
select {
|
|
case ch <- ¶llelDialerResult{conn: conn, err: err}:
|
|
case <-done:
|
|
if conn != nil {
|
|
logger.Debug("Connection closed", zap.String("remote_address", conn.RemoteAddr().String()))
|
|
conn.Close()
|
|
}
|
|
}
|
|
}(addr)
|
|
}
|
|
|
|
errs := make([]error, 0, len(addrs))
|
|
for res := range ch {
|
|
if res.err == nil {
|
|
cancel()
|
|
logger.Debug("Connected to", zap.String("remote_address", res.conn.RemoteAddr().String()))
|
|
return res.conn, res.err
|
|
}
|
|
errs = append(errs, res.err)
|
|
}
|
|
return nil, errors.Join(errs...)
|
|
}
|