mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
cmd/ctrld: allocate new ip instead of port
So the alternative listener address can still be used as system resolver.
This commit is contained in:
committed by
Cuong Manh Le
parent
82900eeca6
commit
cad71997aa
@@ -17,6 +17,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
@@ -87,7 +89,6 @@ func initCLI() {
|
|||||||
if daemon && runtime.GOOS == "windows" {
|
if daemon && runtime.GOOS == "windows" {
|
||||||
log.Fatal("Cannot run in daemon mode. Please install a Windows service.")
|
log.Fatal("Cannot run in daemon mode. Please install a Windows service.")
|
||||||
}
|
}
|
||||||
|
|
||||||
noConfigStart := isNoConfigStart(cmd)
|
noConfigStart := isNoConfigStart(cmd)
|
||||||
writeDefaultConfig := !noConfigStart && configBase64 == ""
|
writeDefaultConfig := !noConfigStart && configBase64 == ""
|
||||||
configs := []struct {
|
configs := []struct {
|
||||||
@@ -196,18 +197,23 @@ func initCLI() {
|
|||||||
}
|
}
|
||||||
setDependencies(sc)
|
setDependencies(sc)
|
||||||
sc.Arguments = append([]string{"run"}, osArgs...)
|
sc.Arguments = append([]string{"run"}, osArgs...)
|
||||||
|
|
||||||
|
// No config path, generating config in HOME directory.
|
||||||
|
noConfigStart := isNoConfigStart(cmd)
|
||||||
|
writeDefaultConfig := !noConfigStart && configBase64 == ""
|
||||||
|
if configPath != "" {
|
||||||
|
v.SetConfigFile(configPath)
|
||||||
|
}
|
||||||
if dir, err := os.UserHomeDir(); err == nil {
|
if dir, err := os.UserHomeDir(); err == nil {
|
||||||
setWorkingDirectory(sc, dir)
|
setWorkingDirectory(sc, dir)
|
||||||
// No config path, generating config in HOME directory.
|
|
||||||
noConfigStart := isNoConfigStart(cmd)
|
|
||||||
writeDefaultConfig := !noConfigStart && configBase64 == ""
|
|
||||||
if configPath == "" && writeDefaultConfig {
|
if configPath == "" && writeDefaultConfig {
|
||||||
defaultConfigFile = filepath.Join(dir, defaultConfigFile)
|
defaultConfigFile = filepath.Join(dir, defaultConfigFile)
|
||||||
readConfigFile(writeDefaultConfig && cdUID == "")
|
v.SetConfigFile(defaultConfigFile)
|
||||||
}
|
}
|
||||||
sc.Arguments = append(sc.Arguments, "--homedir="+dir)
|
sc.Arguments = append(sc.Arguments, "--homedir="+dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readConfigFile(writeDefaultConfig && cdUID == "")
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -480,7 +486,7 @@ func writeConfigFile() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
enc := toml.NewEncoder(f).SetIndentTables(true)
|
enc := toml.NewEncoder(f).SetIndentTables(true)
|
||||||
if err := enc.Encode(v.AllSettings()); err != nil {
|
if err := enc.Encode(&cfg); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := f.Close(); err != nil {
|
if err := f.Close(); err != nil {
|
||||||
@@ -495,6 +501,13 @@ func readConfigFile(writeDefaultConfig bool) bool {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Println("loading config file from:", v.ConfigFileUsed())
|
fmt.Println("loading config file from:", v.ConfigFileUsed())
|
||||||
defaultConfigFile = v.ConfigFileUsed()
|
defaultConfigFile = v.ConfigFileUsed()
|
||||||
|
v.OnConfigChange(func(in fsnotify.Event) {
|
||||||
|
if err := v.UnmarshalKey("listener", &cfg.Listener); err != nil {
|
||||||
|
log.Printf("failed to unmarshal listener config: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
v.WatchConfig()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -630,10 +643,6 @@ func processCDFlags() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
v = viper.NewWithOptions(viper.KeyDelimiter("::"))
|
|
||||||
v.Set("network", cfg.Network)
|
|
||||||
v.Set("upstream", cfg.Upstream)
|
|
||||||
v.Set("listener", cfg.Listener)
|
|
||||||
processLogAndCacheFlags()
|
processLogAndCacheFlags()
|
||||||
if err := writeConfigFile(); err != nil {
|
if err := writeConfigFile(); err != nil {
|
||||||
logger.Fatal().Err(err).Msg("failed to write config file")
|
logger.Fatal().Err(err).Msg("failed to write config file")
|
||||||
@@ -705,14 +714,14 @@ func defaultIfaceName() string {
|
|||||||
|
|
||||||
func selfCheckStatus(status service.Status) service.Status {
|
func selfCheckStatus(status service.Status) service.Status {
|
||||||
c := new(dns.Client)
|
c := new(dns.Client)
|
||||||
lc := cfg.Listener["0"]
|
|
||||||
bo := backoff.NewBackoff("self-check", logf, 10*time.Second)
|
bo := backoff.NewBackoff("self-check", logf, 10*time.Second)
|
||||||
bo.LogLongerThan = 500 * time.Millisecond
|
bo.LogLongerThan = 500 * time.Millisecond
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
err := errors.New("query failed")
|
err := errors.New("query failed")
|
||||||
maxAttempts := 10
|
maxAttempts := 20
|
||||||
mainLog.Debug().Msg("Performing self-check")
|
mainLog.Debug().Msg("Performing self-check")
|
||||||
for i := 0; i < maxAttempts; i++ {
|
for i := 0; i < maxAttempts; i++ {
|
||||||
|
lc := cfg.Listener["0"]
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetQuestion(selfCheckFQDN+".", dns.TypeA)
|
m.SetQuestion(selfCheckFQDN+".", dns.TypeA)
|
||||||
m.RecursionDesired = true
|
m.RecursionDesired = true
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import (
|
|||||||
// sudo ip a add 127.0.0.2/24 dev lo
|
// sudo ip a add 127.0.0.2/24 dev lo
|
||||||
func allocateIP(ip string) error {
|
func allocateIP(ip string) error {
|
||||||
cmd := exec.Command("ip", "a", "add", ip+"/24", "dev", "lo")
|
cmd := exec.Command("ip", "a", "add", ip+"/24", "dev", "lo")
|
||||||
if err := cmd.Run(); err != nil {
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
mainLog.Error().Err(err).Msg("allocateIP failed")
|
mainLog.Error().Err(err).Msgf("allocateIP failed: %s", string(out))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -120,7 +122,7 @@ func (p *prog) run() {
|
|||||||
addr := net.JoinHostPort(listenerConfig.IP, strconv.Itoa(listenerConfig.Port))
|
addr := net.JoinHostPort(listenerConfig.IP, strconv.Itoa(listenerConfig.Port))
|
||||||
mainLog.Info().Msgf("Starting DNS server on listener.%s: %s", listenerNum, addr)
|
mainLog.Info().Msgf("Starting DNS server on listener.%s: %s", listenerNum, addr)
|
||||||
err := p.serveUDP(listenerNum)
|
err := p.serveUDP(listenerNum)
|
||||||
if err != nil && !defaultConfigWritten {
|
if err != nil && !defaultConfigWritten && cdUID == "" {
|
||||||
mainLog.Fatal().Err(err).Msgf("Unable to start dns proxy on listener.%s", listenerNum)
|
mainLog.Fatal().Err(err).Msgf("Unable to start dns proxy on listener.%s", listenerNum)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -128,39 +130,21 @@ func (p *prog) run() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if opErr, ok := err.(*net.OpError); ok {
|
if opErr, ok := err.(*net.OpError); ok && listenerNum == "0" {
|
||||||
if sErr, ok := opErr.Err.(*os.SyscallError); ok && errors.Is(opErr.Err, syscall.EADDRINUSE) || errors.Is(sErr.Err, errWindowsAddrInUse) {
|
if sErr, ok := opErr.Err.(*os.SyscallError); ok && errors.Is(opErr.Err, syscall.EADDRINUSE) || errors.Is(sErr.Err, errWindowsAddrInUse) {
|
||||||
mainLog.Warn().Msgf("Address %s already in used, pick a random one", addr)
|
mainLog.Warn().Msgf("Address %s already in used, pick a random one", addr)
|
||||||
pc, err := net.ListenPacket("udp", net.JoinHostPort(listenerConfig.IP, "0"))
|
ip := randomLocalIP()
|
||||||
if err != nil {
|
listenerConfig.IP = ip
|
||||||
mainLog.Fatal().Err(err).Msg("failed to listen packet")
|
port := listenerConfig.Port
|
||||||
return
|
cfg.Upstream = map[string]*ctrld.UpstreamConfig{"0": cfg.Upstream["0"]}
|
||||||
}
|
|
||||||
_, portStr, _ := net.SplitHostPort(pc.LocalAddr().String())
|
|
||||||
port, err := strconv.Atoi(portStr)
|
|
||||||
if err != nil {
|
|
||||||
mainLog.Fatal().Err(err).Msg("malformed port")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
listenerConfig.Port = port
|
|
||||||
v.Set("listener", map[string]*ctrld.ListenerConfig{
|
|
||||||
"0": {
|
|
||||||
IP: "127.0.0.1",
|
|
||||||
Port: port,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err := writeConfigFile(); err != nil {
|
if err := writeConfigFile(); err != nil {
|
||||||
mainLog.Fatal().Err(err).Msg("failed to write config file")
|
mainLog.Fatal().Err(err).Msg("failed to write config file")
|
||||||
} else {
|
} else {
|
||||||
mainLog.Info().Msg("writing config file to: " + defaultConfigFile)
|
mainLog.Info().Msg("writing config file to: " + defaultConfigFile)
|
||||||
}
|
}
|
||||||
mainLog.Info().Msgf("Starting DNS server on listener.%s: %s", listenerNum, pc.LocalAddr())
|
p.cfg.Service.AllocateIP = true
|
||||||
// There can be a race between closing the listener and start our own UDP server, but it's
|
p.preRun()
|
||||||
// rare, and we only do this once, so let conservative here.
|
mainLog.Info().Msgf("Starting DNS server on listener.%s: %s", listenerNum, net.JoinHostPort(ip, strconv.Itoa(port)))
|
||||||
if err := pc.Close(); err != nil {
|
|
||||||
mainLog.Fatal().Err(err).Msg("failed to close packet conn")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := p.serveUDP(listenerNum); err != nil {
|
if err := p.serveUDP(listenerNum); err != nil {
|
||||||
mainLog.Fatal().Err(err).Msgf("Unable to start dns proxy on listener.%s", listenerNum)
|
mainLog.Fatal().Err(err).Msgf("Unable to start dns proxy on listener.%s", listenerNum)
|
||||||
return
|
return
|
||||||
@@ -254,3 +238,8 @@ func (p *prog) resetDNS() {
|
|||||||
}
|
}
|
||||||
logger.Debug().Msg("Restoring DNS successfully")
|
logger.Debug().Msg("Restoring DNS successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func randomLocalIP() string {
|
||||||
|
n := rand.Intn(254-2) + 2
|
||||||
|
return fmt.Sprintf("127.0.0.%d", n)
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ func InitConfig(v *viper.Viper, name string) {
|
|||||||
// Config represents ctrld supported configuration.
|
// Config represents ctrld supported configuration.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Service ServiceConfig `mapstructure:"service" toml:"service,omitempty"`
|
Service ServiceConfig `mapstructure:"service" toml:"service,omitempty"`
|
||||||
|
Listener map[string]*ListenerConfig `mapstructure:"listener" toml:"listener" validate:"min=1,dive"`
|
||||||
Network map[string]*NetworkConfig `mapstructure:"network" toml:"network" validate:"min=1,dive"`
|
Network map[string]*NetworkConfig `mapstructure:"network" toml:"network" validate:"min=1,dive"`
|
||||||
Upstream map[string]*UpstreamConfig `mapstructure:"upstream" toml:"upstream" validate:"min=1,dive"`
|
Upstream map[string]*UpstreamConfig `mapstructure:"upstream" toml:"upstream" validate:"min=1,dive"`
|
||||||
Listener map[string]*ListenerConfig `mapstructure:"listener" toml:"listener" validate:"min=1,dive"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceConfig specifies the general ctrld config.
|
// ServiceConfig specifies the general ctrld config.
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -5,6 +5,7 @@ go 1.19
|
|||||||
require (
|
require (
|
||||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534
|
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534
|
||||||
github.com/frankban/quicktest v1.14.3
|
github.com/frankban/quicktest v1.14.3
|
||||||
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/go-playground/validator/v10 v10.11.1
|
github.com/go-playground/validator/v10 v10.11.1
|
||||||
github.com/godbus/dbus/v5 v5.0.6
|
github.com/godbus/dbus/v5 v5.0.6
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.1
|
github.com/hashicorp/golang-lru/v2 v2.0.1
|
||||||
@@ -26,7 +27,6 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
|
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
|
||||||
github.com/go-playground/locales v0.14.0 // indirect
|
github.com/go-playground/locales v0.14.0 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
|
|||||||
Reference in New Issue
Block a user