all: rework fetching/generating config in cd mode

Config fetching/generating in cd mode is currently weird, error prone,
and easy for user to break ctrld when using custom config.

This commit reworks the flow:

 - Fetching config from Control D API.
 - No custom config, use the current default config.
 - If custom config presents, but there's no listener, use 0.0.0.0:53.
 - Try listening on current ip+port config, if ok, ctrld could be a
   direct listener with current setup, moving on.
 - If failed, trying 127.0.0.1:53.
 - If failed, trying current ip + port 5354
 - If still failed, pick a random ip:port pair, retry until listening ok.

With this flow, thing is more predictable/stable, and help removing the
Config interface for router.
This commit is contained in:
Cuong Manh Le
2023-07-07 21:07:26 +07:00
committed by Cuong Manh Le
parent 3f3c1d6d78
commit 7af59ee589
23 changed files with 442 additions and 344 deletions
+1 -1
View File
@@ -267,5 +267,5 @@ func (m *nmManager) Close() error {
}
func (m *nmManager) Mode() string {
return "network-maanger"
return "network-manager"
}
-6
View File
@@ -57,12 +57,6 @@ func (d *Ddwrt) PreRun() error {
return ntp.Wait()
}
func (d *Ddwrt) Configure() error {
d.cfg.Listener["0"].IP = "127.0.0.1"
d.cfg.Listener["0"].Port = 5354
return nil
}
func (d *Ddwrt) Setup() error {
// Already setup.
if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" {
+10 -4
View File
@@ -1,6 +1,7 @@
package dnsmasq
import (
"errors"
"html/template"
"net"
"path/filepath"
@@ -60,15 +61,20 @@ type Upstream struct {
}
func ConfTmpl(tmplText string, cfg *ctrld.Config) (string, error) {
upstreams := make([]Upstream, 0, len(cfg.Listener))
for _, listener := range cfg.Listener {
upstreams = append(upstreams, Upstream{Ip: listener.IP, Port: listener.Port})
listener := cfg.FirstListener()
if listener == nil {
return "", errors.New("missing listener")
}
ip := listener.IP
if ip == "0.0.0.0" || ip == "::" || ip == "" {
ip = "127.0.0.1"
}
upstreams := []Upstream{{Ip: ip, Port: listener.Port}}
return confTmpl(tmplText, upstreams, cfg.HasUpstreamSendClientInfo())
}
func FirewallaConfTmpl(tmplText string, cfg *ctrld.Config) (string, error) {
if lc := cfg.Listener["0"]; lc != nil && lc.IP == "0.0.0.0" {
if lc := cfg.FirstListener(); lc != nil && (lc.IP == "0.0.0.0" || lc.IP == "") {
return confTmpl(tmplText, firewallaUpstreams(lc.Port), cfg.HasUpstreamSendClientInfo())
}
return ConfTmpl(tmplText, cfg)
-4
View File
@@ -4,10 +4,6 @@ import "github.com/kardianos/service"
type dummy struct{}
func NewDummyRouter() Router {
return &dummy{}
}
func (d *dummy) ConfigureService(_ *service.Config) error {
return nil
}
-6
View File
@@ -61,12 +61,6 @@ func (e *EdgeOS) PreRun() error {
return nil
}
func (e *EdgeOS) Configure() error {
e.cfg.Listener["0"].IP = "127.0.0.1"
e.cfg.Listener["0"].Port = 5354
return nil
}
func (e *EdgeOS) Setup() error {
if e.isUSG {
return e.setupUSG()
-6
View File
@@ -53,12 +53,6 @@ func (f *Firewalla) PreRun() error {
return nil
}
func (f *Firewalla) Configure() error {
f.cfg.Listener["0"].IP = "0.0.0.0"
f.cfg.Listener["0"].Port = 5354
return nil
}
func (f *Firewalla) Setup() error {
data, err := dnsmasq.FirewallaConfTmpl(dnsmasq.ConfigContentTmpl, f.cfg)
if err != nil {
-6
View File
@@ -48,12 +48,6 @@ func (m *Merlin) PreRun() error {
return ntp.Wait()
}
func (m *Merlin) Configure() error {
m.cfg.Listener["0"].IP = "127.0.0.1"
m.cfg.Listener["0"].Port = 5354
return nil
}
func (m *Merlin) Setup() error {
buf, err := os.ReadFile(dnsmasq.MerlinPostConfPath)
// Already setup.
-6
View File
@@ -48,12 +48,6 @@ func (o *Openwrt) PreRun() error {
return nil
}
func (o *Openwrt) Configure() error {
o.cfg.Listener["0"].IP = "127.0.0.1"
o.cfg.Listener["0"].Port = 5354
return nil
}
func (o *Openwrt) Setup() error {
// Delete dnsmasq port if set.
if _, err := uci("delete", "dhcp.@dnsmasq[0].port"); err != nil && !errors.Is(err, errUCIEntryNotFound) {
+13 -10
View File
@@ -54,6 +54,14 @@ func (p *Pfsense) ConfigureService(svc *service.Config) error {
}
func (p *Pfsense) Install(config *service.Config) error {
// pfsense need ".sh" extension for script to be run at boot.
// See: https://docs.netgate.com/pfsense/en/latest/development/boot-commands.html#shell-script-option
oldname := filepath.Join(rcPath, p.svcName)
newname := filepath.Join(rcPath, p.svcName+".sh")
_ = os.Remove(newname)
if err := os.Symlink(oldname, newname); err != nil {
return fmt.Errorf("os.Symlink: %w", err)
}
return nil
}
@@ -62,16 +70,7 @@ func (p *Pfsense) Uninstall(config *service.Config) error {
}
func (p *Pfsense) PreRun() error {
return nil
}
func (p *Pfsense) Configure() error {
p.cfg.Listener["0"].IP = "127.0.0.1"
p.cfg.Listener["0"].Port = 53
return nil
}
func (p *Pfsense) Setup() error {
// TODO: remove this hacky solution.
// If Pfsense is in DNS Resolver mode, ensure no unbound processes running.
_ = exec.Command("killall", "unbound").Run()
@@ -80,6 +79,10 @@ func (p *Pfsense) Setup() error {
return nil
}
func (p *Pfsense) Setup() error {
return nil
}
func (p *Pfsense) Cleanup() error {
if err := os.Remove(filepath.Join(rcPath, p.svcName+".sh")); err != nil {
return fmt.Errorf("os.Remove: %w", err)
+1 -39
View File
@@ -27,15 +27,9 @@ type Service interface {
Uninstall(*service.Config) error
}
// Config is the interface to manage ctrld config on router.
type Config interface {
Configure() error
}
// Router is the interface for managing ctrld running on router.
type Router interface {
Service
Config
PreRun() error
Setup() error
@@ -64,7 +58,7 @@ func New(cfg *ctrld.Config) Router {
case firewalla.Name:
return firewalla.New(cfg)
}
return NewDummyRouter()
return &dummy{}
}
// IsGLiNet reports whether the router is an GL.iNet router.
@@ -94,38 +88,6 @@ type router struct {
sendClientInfo bool
}
// IsSupported reports whether the given platform is supported by ctrld.
func IsSupported(platform string) bool {
switch platform {
case ddwrt.Name,
edgeos.Name,
firewalla.Name,
merlin.Name,
openwrt.Name,
pfsense.Name,
synology.Name,
tomato.Name,
ubios.Name:
return true
}
return false
}
// SupportedPlatforms return all platforms that can be configured to run with ctrld.
func SupportedPlatforms() []string {
return []string{
ddwrt.Name,
edgeos.Name,
firewalla.Name,
merlin.Name,
openwrt.Name,
pfsense.Name,
synology.Name,
tomato.Name,
ubios.Name,
}
}
// Name returns name of the router platform.
func Name() string {
if r := routerPlatform.Load(); r != nil {
-6
View File
@@ -43,12 +43,6 @@ func (s *Synology) PreRun() error {
return nil
}
func (s *Synology) Configure() error {
s.cfg.Listener["0"].IP = "127.0.0.1"
s.cfg.Listener["0"].Port = 5354
return nil
}
func (s *Synology) Setup() error {
data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, s.cfg)
if err != nil {
-6
View File
@@ -52,12 +52,6 @@ func (f *FreshTomato) PreRun() error {
return ntp.Wait()
}
func (f *FreshTomato) Configure() error {
f.cfg.Listener["0"].IP = "127.0.0.1"
f.cfg.Listener["0"].Port = 5354
return nil
}
func (f *FreshTomato) Setup() error {
// Already setup.
if val, _ := nvram.Run("get", nvram.CtrldSetupKey); val == "1" {
-6
View File
@@ -46,12 +46,6 @@ func (u *Ubios) PreRun() error {
return nil
}
func (u *Ubios) Configure() error {
u.cfg.Listener["0"].IP = "127.0.0.1"
u.cfg.Listener["0"].Port = 5354
return nil
}
func (u *Ubios) Setup() error {
data, err := dnsmasq.ConfTmpl(dnsmasq.ConfigContentTmpl, u.cfg)
if err != nil {