mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-07-04 01:07:49 +02:00
all: wait for network up before running
If ctrld setup the interface correctly, the interface DNS is set to ctrld listener address. At boot time, the ctrld is not up yet, so it would break the processing Control D config fetching. Fixing this by waiting for network up before doing the query.
This commit is contained in:
committed by
Cuong Manh Le
parent
cd37d93b06
commit
837563dcd5
@@ -103,6 +103,10 @@ func initCLI() {
|
|||||||
if err := v.Unmarshal(&cfg); err != nil {
|
if err := v.Unmarshal(&cfg); err != nil {
|
||||||
log.Fatalf("failed to unmarshal config: %v", err)
|
log.Fatalf("failed to unmarshal config: %v", err)
|
||||||
}
|
}
|
||||||
|
// Wait for network up.
|
||||||
|
if !netUp() {
|
||||||
|
log.Fatal("network is not up yet")
|
||||||
|
}
|
||||||
processCDFlags()
|
processCDFlags()
|
||||||
if err := ctrld.ValidateConfig(validator.New(), &cfg); err != nil {
|
if err := ctrld.ValidateConfig(validator.New(), &cfg); err != nil {
|
||||||
log.Fatalf("invalid config: %v", err)
|
log.Fatalf("invalid config: %v", err)
|
||||||
|
|||||||
+28
-2
@@ -1,20 +1,41 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/logtail/backoff"
|
||||||
)
|
)
|
||||||
|
|
||||||
const controldIPv6Test = "ipv6.controld.io"
|
const (
|
||||||
|
controldIPv6Test = "ipv6.controld.io"
|
||||||
|
controldIPv4Test = "ipv4.controld.io"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
stackOnce sync.Once
|
stackOnce sync.Once
|
||||||
ipv6Enabled bool
|
ipv6Enabled bool
|
||||||
canListenIPv6Local bool
|
canListenIPv6Local bool
|
||||||
|
hasNetworkUp bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func probeStack() {
|
func probeStack() {
|
||||||
if _, err := net.Dial("tcp6", controldIPv6Test); err == nil {
|
logf := func(format string, args ...any) {
|
||||||
|
fmt.Printf(format, args...)
|
||||||
|
}
|
||||||
|
b := backoff.NewBackoff("probeStack", logf, time.Minute)
|
||||||
|
for {
|
||||||
|
if _, err := net.Dial("tcp", net.JoinHostPort(controldIPv4Test, "80")); err == nil {
|
||||||
|
hasNetworkUp = true
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
b.BackOff(context.Background(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := net.Dial("tcp6", net.JoinHostPort(controldIPv6Test, "80")); err == nil {
|
||||||
ipv6Enabled = true
|
ipv6Enabled = true
|
||||||
}
|
}
|
||||||
if ln, err := net.Listen("tcp6", "[::1]:53"); err == nil {
|
if ln, err := net.Listen("tcp6", "[::1]:53"); err == nil {
|
||||||
@@ -23,6 +44,11 @@ func probeStack() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func netUp() bool {
|
||||||
|
stackOnce.Do(probeStack)
|
||||||
|
return hasNetworkUp
|
||||||
|
}
|
||||||
|
|
||||||
func supportsIPv6() bool {
|
func supportsIPv6() bool {
|
||||||
stackOnce.Do(probeStack)
|
stackOnce.Do(probeStack)
|
||||||
return ipv6Enabled
|
return ipv6Enabled
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package controld
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -13,6 +15,20 @@ const (
|
|||||||
InvalidConfigCode = 40401
|
InvalidConfigCode = 40401
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const 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)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// ResolverConfig represents Control D resolver data.
|
// ResolverConfig represents Control D resolver data.
|
||||||
type ResolverConfig struct {
|
type ResolverConfig struct {
|
||||||
DOH string `json:"doh"`
|
DOH string `json:"doh"`
|
||||||
@@ -52,7 +68,14 @@ func FetchResolverConfig(uid string) (*ResolverConfig, error) {
|
|||||||
q.Set("platform", "ctrld")
|
q.Set("platform", "ctrld")
|
||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
client := http.Client{Timeout: 5 * time.Second}
|
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
return dialer.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
|
client := http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
Transport: transport,
|
||||||
|
}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("client.Do: %w", err)
|
return nil, fmt.Errorf("client.Do: %w", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user