mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-04-20 00:36:37 +02:00
de24fa293e
This change improves compatibility with newer UniFi OS versions while maintaining backward compatibility with UniFi OS 4.2 and earlier. The refactoring also reduces code duplication and improves maintainability by centralizing dnsmasq configuration path logic.
191 lines
5.8 KiB
Go
191 lines
5.8 KiB
Go
package dnsmasq
|
|
|
|
import (
|
|
"errors"
|
|
"html/template"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/Control-D-Inc/ctrld"
|
|
)
|
|
|
|
const CtrldMarker = `# GENERATED BY ctrld - DO NOT MODIFY`
|
|
|
|
const ConfigContentTmpl = `# GENERATED BY ctrld - DO NOT MODIFY
|
|
no-resolv
|
|
{{- range .Upstreams}}
|
|
server={{ .IP }}#{{ .Port }}
|
|
{{- end}}
|
|
add-mac
|
|
add-subnet=32,128
|
|
{{- if .CacheDisabled}}
|
|
cache-size=0
|
|
{{- else}}
|
|
max-cache-ttl=0
|
|
{{- end}}
|
|
`
|
|
|
|
const (
|
|
MerlinConfPath = "/tmp/etc/dnsmasq.conf"
|
|
MerlinJffsConfDir = "/jffs/configs"
|
|
MerlinJffsConfPath = "/jffs/configs/dnsmasq.conf"
|
|
MerlinPostConfPath = "/jffs/scripts/dnsmasq.postconf"
|
|
)
|
|
|
|
const MerlinPostConfMarker = `# GENERATED BY ctrld - EOF`
|
|
const MerlinPostConfTmpl = `# GENERATED BY ctrld - DO NOT MODIFY
|
|
|
|
#!/bin/sh
|
|
|
|
config_file="$1"
|
|
. /usr/sbin/helper.sh
|
|
|
|
pid=$(cat /tmp/ctrld.pid 2>/dev/null)
|
|
if [ -n "$pid" ] && [ -f "/proc/${pid}/cmdline" ]; then
|
|
pc_delete "servers-file" "$config_file" # no WAN DNS settings
|
|
pc_append "no-resolv" "$config_file" # do not read /etc/resolv.conf
|
|
# use ctrld as upstream
|
|
pc_delete "server=" "$config_file"
|
|
{{- range .Upstreams}}
|
|
pc_append "server={{ .IP }}#{{ .Port }}" "$config_file"
|
|
{{- end}}
|
|
pc_delete "add-mac" "$config_file"
|
|
pc_delete "add-subnet" "$config_file"
|
|
pc_append "add-mac" "$config_file" # add client mac
|
|
pc_append "add-subnet=32,128" "$config_file" # add client ip
|
|
pc_delete "dnssec" "$config_file" # disable DNSSEC
|
|
pc_delete "trust-anchor=" "$config_file" # disable DNSSEC
|
|
pc_delete "cache-size=" "$config_file"
|
|
pc_append "cache-size=0" "$config_file" # disable cache
|
|
|
|
# For John fork
|
|
pc_delete "resolv-file" "$config_file" # no WAN DNS settings
|
|
|
|
# Change /etc/resolv.conf, which may be changed by WAN DNS setup
|
|
pc_delete "nameserver" /etc/resolv.conf
|
|
pc_append "nameserver 127.0.0.1" /etc/resolv.conf
|
|
|
|
exit 0
|
|
fi
|
|
`
|
|
|
|
type Upstream struct {
|
|
IP string
|
|
Port int
|
|
}
|
|
|
|
// ConfTmpl generates dnsmasq configuration from ctrld config.
|
|
func ConfTmpl(tmplText string, cfg *ctrld.Config) (string, error) {
|
|
return ConfTmplWithCacheDisabled(tmplText, cfg, true)
|
|
}
|
|
|
|
// ConfTmplWithCacheDisabled is like ConfTmpl, but the caller can control whether
|
|
// dnsmasq cache is disabled using cacheDisabled parameter.
|
|
//
|
|
// Generally, the caller should use ConfTmpl, but on some routers which dnsmasq config may be changed
|
|
// after ctrld started (like EdgeOS/Ubios, Firewalla ...), dnsmasq cache should not be disabled because
|
|
// the cache-size=0 generated by ctrld will conflict with router's generated config.
|
|
func ConfTmplWithCacheDisabled(tmplText string, cfg *ctrld.Config, cacheDisabled bool) (string, error) {
|
|
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, cacheDisabled)
|
|
}
|
|
|
|
// FirewallaConfTmpl generates dnsmasq config for Firewalla routers.
|
|
func FirewallaConfTmpl(tmplText string, cfg *ctrld.Config) (string, error) {
|
|
// If ctrld listen on all interfaces, generating config for all of them.
|
|
if lc := cfg.FirstListener(); lc != nil && (lc.IP == "0.0.0.0" || lc.IP == "") {
|
|
return confTmpl(tmplText, firewallaUpstreams(lc.Port), false)
|
|
}
|
|
// Otherwise, generating config for the specific listener from ctrld's config.
|
|
return ConfTmplWithCacheDisabled(tmplText, cfg, false)
|
|
}
|
|
|
|
func confTmpl(tmplText string, upstreams []Upstream, cacheDisabled bool) (string, error) {
|
|
tmpl := template.Must(template.New("").Parse(tmplText))
|
|
var to = &struct {
|
|
Upstreams []Upstream
|
|
CacheDisabled bool
|
|
}{
|
|
Upstreams: upstreams,
|
|
CacheDisabled: cacheDisabled,
|
|
}
|
|
var sb strings.Builder
|
|
if err := tmpl.Execute(&sb, to); err != nil {
|
|
return "", err
|
|
}
|
|
return sb.String(), nil
|
|
}
|
|
|
|
func firewallaUpstreams(port int) []Upstream {
|
|
ifaces := FirewallaSelfInterfaces()
|
|
upstreams := make([]Upstream, 0, len(ifaces))
|
|
for _, netIface := range ifaces {
|
|
addrs, _ := netIface.Addrs()
|
|
for _, addr := range addrs {
|
|
if netIP, ok := addr.(*net.IPNet); ok && netIP.IP.To4() != nil {
|
|
upstreams = append(upstreams, Upstream{
|
|
IP: netIP.IP.To4().String(),
|
|
Port: port,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return upstreams
|
|
}
|
|
|
|
// firewallaDnsmasqConfFiles returns dnsmasq config files of all firewalla interfaces.
|
|
func firewallaDnsmasqConfFiles() ([]string, error) {
|
|
return filepath.Glob("/home/pi/firerouter/etc/dnsmasq.dns.*.conf")
|
|
}
|
|
|
|
// FirewallaSelfInterfaces returns list of interfaces that will be configured with default dnsmasq setup on Firewalla.
|
|
func FirewallaSelfInterfaces() []*net.Interface {
|
|
matches, err := firewallaDnsmasqConfFiles()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
ifaces := make([]*net.Interface, 0, len(matches))
|
|
for _, match := range matches {
|
|
// Trim prefix and suffix to get the iface name only.
|
|
ifaceName := strings.TrimSuffix(strings.TrimPrefix(match, "/home/pi/firerouter/etc/dnsmasq.dns."), ".conf")
|
|
if netIface, _ := net.InterfaceByName(ifaceName); netIface != nil {
|
|
ifaces = append(ifaces, netIface)
|
|
}
|
|
}
|
|
return ifaces
|
|
}
|
|
|
|
const (
|
|
ubios43ConfPath = "/run/dnsmasq.dhcp.conf.d"
|
|
ubios42ConfPath = "/run/dnsmasq.conf.d"
|
|
ubios43PidFile = "/run/dnsmasq-main.pid"
|
|
ubios42PidFile = "/run/dnsmasq.pid"
|
|
UbiosConfName = "zzzctrld.conf"
|
|
)
|
|
|
|
// UbiosConfPath returns the appropriate configuration path based on the system's directory structure.
|
|
func UbiosConfPath() string {
|
|
if st, _ := os.Stat(ubios43ConfPath); st != nil && st.IsDir() {
|
|
return ubios43ConfPath
|
|
}
|
|
return ubios42ConfPath
|
|
}
|
|
|
|
// UbiosPidFile returns the appropriate dnsmasq pid file based on the system's directory structure.
|
|
func UbiosPidFile() string {
|
|
if st, _ := os.Stat(ubios43PidFile); st != nil && !st.IsDir() {
|
|
return ubios43PidFile
|
|
}
|
|
return ubios42PidFile
|
|
}
|