From 51e58b64a55ca32650cdc6aa2adaed35db936da9 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 9 Oct 2025 16:47:12 +0700 Subject: [PATCH] Preparing for v2.0.0 branch merge This commit reverts changes from v1.4.5 to v1.4.7, to prepare for v2.0.0 branch codes. Changes includes in these releases have been included in v2.0.0 branch already. Details: Revert "feat: add --rfc1918 flag for explicit LAN client support" This reverts commit 0e3f76429901d6353ac3938f0c8d09ca67c1b4a4. Revert "Upgrade quic-go to v0.54.0" This reverts commit e52402eb0c771ea767c8fab8ff1e7bc01f6a40d5. Revert "docs: add known issues documentation for Darwin 15.5 upgrade issue" This reverts commit 2133f318544e0c8a57360ee886e1f340a1792485. Revert "start mobile library with provision id and custom hostname." This reverts commit a198a5cd65eddf09bf604d1c53673d5a6132add9. Revert "Add OPNsense new lease file" This reverts commit 7af29cfbc0ce0c070e327286feb2f14522e5b86b. Revert ".github/workflows: bump go version to 1.24.x" This reverts commit ce1a16534899fd991f21935cf6d1065fdd33182e. Revert "fix: ensure upstream health checks can handle large DNS responses" This reverts commit fd48e6d795133df4b45d4641121aa6a989a0a7fe. Revert "refactor(prog): move network monitoring outside listener loop" This reverts commit d71d1341b61b3cd183f9d894100985637f10a79a. Revert "fix: correct Windows API constants to fix domain join detection" This reverts commit 21855df4afafbcf1b95a81e47f54d4da6781dbe8. Revert "refactor: move network monitoring to separate goroutine" This reverts commit 66e2d3a40a661e3b03a93cfd681bcece33777c59. Revert "refactor: extract empty string filtering to reusable function" This reverts commit 36a7423634bccd0a894e6a36b0e8ff01891c6773. Revert "cmd/cli: ignore empty positional argument for start command" This reverts commit e6160912497036d58c9e52debb4f694d3c39f78c. Revert "Avoiding Windows runners file locking issue" This reverts commit 0948161529505bd52c82f6a913bbc546bb21bd7f. Revert "refactor: split selfUpgradeCheck into version check and upgrade execution" This reverts commit ce29b5d217624192c975ddc09a073827b180deb6. Revert "internal/router: support Ubios 4.3+" This reverts commit de24fa293ec6b5dc93b85095b676494eae1fd7c4. Revert "internal/router: support Merlin Guest Network Pro VLAN" This reverts commit 6663925c4d576109df349d98b3907714f9afa0ce. --- .github/workflows/ci.yml | 4 +- cmd/cli/cli.go | 10 +- cmd/cli/commands.go | 14 -- cmd/cli/dns_proxy.go | 20 +- cmd/cli/library.go | 12 +- cmd/cli/main.go | 1 - .../cli/net_darwin_test.go | 2 +- cmd/cli/prog.go | 69 +----- cmd/cli/prog_test.go | 218 +----------------- cmd/ctrld_library/main.go | 14 +- config.go | 9 - config_quic.go | 6 +- docs/known-issues.md | 42 ---- doq_test.go | 4 +- go.mod | 7 +- go.sum | 20 +- internal/clientinfo/dhcp_lease_files.go | 1 - internal/clientinfo/mdns.go | 1 + internal/clientinfo/ptr_lookup.go | 4 +- internal/router/dnsmasq/conf.go | 60 ----- internal/router/dnsmasq/conf_test.go | 47 ---- internal/router/dnsmasq/dnsmasq.go | 35 +-- internal/router/edgeos/edgeos.go | 3 +- internal/router/merlin/merlin.go | 123 +++------- internal/router/service_ubios.go | 7 +- internal/router/ubios/ubios.go | 21 +- nameservers_linux.go | 25 -- nameservers_windows.go | 54 ++--- net_darwin.go | 35 --- net_others.go | 15 -- resolver.go | 7 +- resolver_test.go | 91 -------- 32 files changed, 150 insertions(+), 831 deletions(-) rename net_darwin_test.go => cmd/cli/net_darwin_test.go (99%) delete mode 100644 docs/known-issues.md delete mode 100644 net_darwin.go delete mode 100644 net_others.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bd4d27..b4b44d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: fail-fast: false matrix: os: ["windows-latest", "ubuntu-latest", "macOS-latest"] - go: ["1.24.x"] + go: ["1.23.x"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 @@ -21,6 +21,6 @@ jobs: - run: "go test -race ./..." - uses: dominikh/staticcheck-action@v1.3.1 with: - version: "2025.1" + version: "2024.1.1" install-go: false cache-key: ${{ matrix.go }} diff --git a/cmd/cli/cli.go b/cmd/cli/cli.go index 1984f70..b99c48f 100644 --- a/cmd/cli/cli.go +++ b/cmd/cli/cli.go @@ -178,15 +178,7 @@ func RunMobile(appConfig *AppConfig, appCallback *AppCallback, stopCh chan struc noConfigStart = false homedir = appConfig.HomeDir verbose = appConfig.Verbose - if appConfig.ProvisionID != "" { - cdOrg = appConfig.ProvisionID - } - if appConfig.CustomHostname != "" { - customHostname = appConfig.CustomHostname - } - if appConfig.CdUID != "" { - cdUID = appConfig.CdUID - } + cdUID = appConfig.CdUID cdUpstreamProto = appConfig.UpstreamProto logPath = appConfig.LogPath run(appCallback, stopCh) diff --git a/cmd/cli/commands.go b/cmd/cli/commands.go index a1074f2..048212a 100644 --- a/cmd/cli/commands.go +++ b/cmd/cli/commands.go @@ -13,7 +13,6 @@ import ( "os/exec" "path/filepath" "runtime" - "slices" "sort" "strconv" "strings" @@ -189,7 +188,6 @@ func initRunCmd() *cobra.Command { runCmd.Flags().StringVarP(&iface, "iface", "", "", `Update DNS setting for iface, "auto" means the default interface gateway`) _ = runCmd.Flags().MarkHidden("iface") runCmd.Flags().StringVarP(&cdUpstreamProto, "proto", "", ctrld.ResolverTypeDOH, `Control D upstream type, either "doh" or "doh3"`) - runCmd.Flags().BoolVarP(&rfc1918, "rfc1918", "", false, "Listen on RFC1918 addresses when 127.0.0.1 is the only listener") runCmd.FParseErrWhitelist = cobra.FParseErrWhitelist{UnknownFlags: true} rootCmd.AddCommand(runCmd) @@ -208,7 +206,6 @@ func initStartCmd() *cobra.Command { NOTE: running "ctrld start" without any arguments will start already installed ctrld service.`, Args: func(cmd *cobra.Command, args []string) error { - args = filterEmptyStrings(args) if len(args) > 0 { return fmt.Errorf("'ctrld start' doesn't accept positional arguments\n" + "Use flags instead (e.g. --cd, --iface) or see 'ctrld start --help' for all options") @@ -222,7 +219,6 @@ NOTE: running "ctrld start" without any arguments will start already installed c sc := &service.Config{} *sc = *svcConfig osArgs := os.Args[2:] - osArgs = filterEmptyStrings(osArgs) if os.Args[1] == "service" { osArgs = os.Args[3:] } @@ -532,7 +528,6 @@ NOTE: running "ctrld start" without any arguments will start already installed c startCmd.Flags().BoolVarP(&skipSelfChecks, "skip_self_checks", "", false, `Skip self checks after installing ctrld service`) startCmd.Flags().BoolVarP(&startOnly, "start_only", "", false, "Do not install new service") _ = startCmd.Flags().MarkHidden("start_only") - startCmd.Flags().BoolVarP(&rfc1918, "rfc1918", "", false, "Listen on RFC1918 addresses when 127.0.0.1 is the only listener") routerCmd := &cobra.Command{ Use: "setup", @@ -571,7 +566,6 @@ NOTE: running "ctrld start" without any arguments will start already installed c NOTE: running "ctrld start" without any arguments will start already installed ctrld service.`, Args: func(cmd *cobra.Command, args []string) error { - args = filterEmptyStrings(args) if len(args) > 0 { return fmt.Errorf("'ctrld start' doesn't accept positional arguments\n" + "Use flags instead (e.g. --cd, --iface) or see 'ctrld start --help' for all options") @@ -1387,11 +1381,3 @@ func initServicesCmd(commands ...*cobra.Command) *cobra.Command { return serviceCmd } - -// filterEmptyStrings removes empty strings from a slice of strings. -// It returns a new slice containing only non-empty strings. -func filterEmptyStrings(slice []string) []string { - return slices.DeleteFunc(slice, func(s string) bool { - return s == "" - }) -} diff --git a/cmd/cli/dns_proxy.go b/cmd/cli/dns_proxy.go index 994741b..33012fa 100644 --- a/cmd/cli/dns_proxy.go +++ b/cmd/cli/dns_proxy.go @@ -84,7 +84,13 @@ type upstreamForResult struct { srcAddr string } -func (p *prog) serveDNS(listenerNum string) error { +func (p *prog) serveDNS(mainCtx context.Context, listenerNum string) error { + // Start network monitoring + if err := p.monitorNetworkChanges(mainCtx); err != nil { + mainLog.Load().Error().Err(err).Msg("Failed to start network monitoring") + // Don't return here as we still want DNS service to run + } + listenerConfig := p.cfg.Listener[listenerNum] // make sure ip is allocated if allocErr := p.allocateIP(listenerConfig.IP); allocErr != nil { @@ -207,8 +213,8 @@ func (p *prog) serveDNS(listenerNum string) error { return nil }) } - // When we spawn a listener on 127.0.0.1, also spawn listeners on the RFC1918 addresses of the machine - // if explicitly set via setting rfc1918 flag, so ctrld could receive queries from LAN clients. + // When we spawn a listener on 127.0.0.1, also spawn listeners on the RFC1918 + // addresses of the machine. So ctrld could receive queries from LAN clients. if needRFC1918Listeners(listenerConfig) { g.Go(func() error { for _, addr := range ctrld.Rfc1918Addresses() { @@ -1039,7 +1045,7 @@ func (p *prog) queryFromSelf(ip string) bool { // needRFC1918Listeners reports whether ctrld need to spawn listener for RFC 1918 addresses. // This is helpful for non-desktop platforms to receive queries from LAN clients. func needRFC1918Listeners(lc *ctrld.ListenerConfig) bool { - return rfc1918 && lc.IP == "127.0.0.1" && lc.Port == 53 + return lc.IP == "127.0.0.1" && lc.Port == 53 && !ctrld.IsDesktopPlatform() } // ipFromARPA parses a FQDN arpa domain and return the IP address if valid. @@ -1181,7 +1187,7 @@ func FlushDNSCache() error { } // monitorNetworkChanges starts monitoring for network interface changes -func (p *prog) monitorNetworkChanges() error { +func (p *prog) monitorNetworkChanges(ctx context.Context) error { mon, err := netmon.New(func(format string, args ...any) { // Always fetch the latest logger (and inject the prefix) mainLog.Load().Printf("netmon: "+format, args...) @@ -1400,6 +1406,9 @@ func (p *prog) checkUpstreamOnce(upstream string, uc *ctrld.UpstreamConfig) erro return err } + msg := new(dns.Msg) + msg.SetQuestion(".", dns.TypeNS) + timeout := 1000 * time.Millisecond if uc.Timeout > 0 { timeout = time.Millisecond * time.Duration(uc.Timeout) @@ -1413,7 +1422,6 @@ func (p *prog) checkUpstreamOnce(upstream string, uc *ctrld.UpstreamConfig) erro mainLog.Load().Debug().Msgf("Rebootstrapping resolver for upstream: %s", upstream) start := time.Now() - msg := uc.VerifyMsg() _, err = resolver.Resolve(ctx, msg) duration := time.Since(start) diff --git a/cmd/cli/library.go b/cmd/cli/library.go index 7847dd7..3c1db1b 100644 --- a/cmd/cli/library.go +++ b/cmd/cli/library.go @@ -18,13 +18,11 @@ type AppCallback struct { // AppConfig allows overwriting ctrld cli flags from mobile platforms. type AppConfig struct { - CdUID string - ProvisionID string - CustomHostname string - HomeDir string - UpstreamProto string - Verbose int - LogPath string + CdUID string + HomeDir string + UpstreamProto string + Verbose int + LogPath string } const ( diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 0783975..6a8cb62 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -39,7 +39,6 @@ var ( skipSelfChecks bool cleanup bool startOnly bool - rfc1918 bool mainLog atomic.Pointer[zerolog.Logger] consoleWriter zerolog.ConsoleWriter diff --git a/net_darwin_test.go b/cmd/cli/net_darwin_test.go similarity index 99% rename from net_darwin_test.go rename to cmd/cli/net_darwin_test.go index 8f9734f..9ef1906 100644 --- a/net_darwin_test.go +++ b/cmd/cli/net_darwin_test.go @@ -1,4 +1,4 @@ -package ctrld +package cli import ( "maps" diff --git a/cmd/cli/prog.go b/cmd/cli/prog.go index 76f7c36..dd8de9f 100644 --- a/cmd/cli/prog.go +++ b/cmd/cli/prog.go @@ -35,7 +35,6 @@ import ( "github.com/Control-D-Inc/ctrld/internal/controld" "github.com/Control-D-Inc/ctrld/internal/dnscache" "github.com/Control-D-Inc/ctrld/internal/router" - "github.com/Control-D-Inc/ctrld/internal/router/dnsmasq" ) const ( @@ -329,7 +328,7 @@ func (p *prog) apiConfigReload() { // Performing self-upgrade check for production version. if isStable { - _ = selfUpgradeCheck(resolverConfig.Ctrld.VersionTarget, curVer, &logger) + selfUpgradeCheck(resolverConfig.Ctrld.VersionTarget, curVer, &logger) } if resolverConfig.DeactivationPin != nil { @@ -530,15 +529,6 @@ func (p *prog) run(reload bool, reloadCh chan struct{}) { go p.watchLinkState(ctx) } - if !reload { - go func() { - // Start network monitoring - if err := p.monitorNetworkChanges(); err != nil { - mainLog.Load().Error().Err(err).Msg("Failed to start network monitoring") - } - }() - } - for listenerNum := range p.cfg.Listener { p.cfg.Listener[listenerNum].Init() if !reload { @@ -550,7 +540,7 @@ func (p *prog) run(reload bool, reloadCh chan struct{}) { } addr := net.JoinHostPort(listenerConfig.IP, strconv.Itoa(listenerConfig.Port)) mainLog.Load().Info().Msgf("starting DNS server on listener.%s: %s", listenerNum, addr) - if err := p.serveDNS(listenerNum); err != nil { + if err := p.serveDNS(ctx, listenerNum); err != nil { mainLog.Load().Fatal().Err(err).Msgf("unable to start dns proxy on listener.%s", listenerNum) } mainLog.Load().Debug().Msgf("end of serveDNS listener.%s: %s", listenerNum, addr) @@ -617,12 +607,6 @@ func (p *prog) setupClientInfoDiscover(selfIP string) { format := ctrld.LeaseFileFormat(p.cfg.Service.DHCPLeaseFileFormat) p.ciTable.AddLeaseFile(leaseFile, format) } - if leaseFiles := dnsmasq.AdditionalLeaseFiles(); len(leaseFiles) > 0 { - mainLog.Load().Debug().Msgf("watching additional lease files: %v", leaseFiles) - for _, leaseFile := range leaseFiles { - p.ciTable.AddLeaseFile(leaseFile, ctrld.Dnsmasq) - } - } } // runClientInfoDiscover runs the client info discover. @@ -1483,15 +1467,14 @@ func selfUninstallCheck(uninstallErr error, p *prog, logger zerolog.Logger) { } } -// shouldUpgrade checks if the version target vt is greater than the current one cv. -// Major version upgrades are not allowed to prevent breaking changes. +// selfUpgradeCheck checks if the version target vt is greater +// than the current one cv, perform self-upgrade then. // // The callers must ensure curVer and logger are non-nil. -// Returns true if upgrade is allowed, false otherwise. -func shouldUpgrade(vt string, cv *semver.Version, logger *zerolog.Logger) bool { +func selfUpgradeCheck(vt string, cv *semver.Version, logger *zerolog.Logger) { if vt == "" { logger.Debug().Msg("no version target set, skipped checking self-upgrade") - return false + return } vts := vt if !strings.HasPrefix(vts, "v") { @@ -1500,58 +1483,28 @@ func shouldUpgrade(vt string, cv *semver.Version, logger *zerolog.Logger) bool { targetVer, err := semver.NewVersion(vts) if err != nil { logger.Warn().Err(err).Msgf("invalid target version, skipped self-upgrade: %s", vt) - return false + return } - - // Prevent major version upgrades to avoid breaking changes - if targetVer.Major() != cv.Major() { - logger.Warn(). - Str("target", vt). - Str("current", cv.String()). - Msgf("major version upgrade not allowed (target: %d, current: %d), skipped self-upgrade", targetVer.Major(), cv.Major()) - return false - } - if !targetVer.GreaterThan(cv) { logger.Debug(). Str("target", vt). Str("current", cv.String()). Msgf("target version is not greater than current one, skipped self-upgrade") - return false + return } - return true -} - -// performUpgrade executes the self-upgrade command. -// Returns true if upgrade was initiated successfully, false otherwise. -func performUpgrade(vt string) bool { exe, err := os.Executable() if err != nil { mainLog.Load().Error().Err(err).Msg("failed to get executable path, skipped self-upgrade") - return false + return } cmd := exec.Command(exe, "upgrade", "prod", "-vv") cmd.SysProcAttr = sysProcAttrForDetachedChildProcess() if err := cmd.Start(); err != nil { mainLog.Load().Error().Err(err).Msg("failed to start self-upgrade") - return false + return } - mainLog.Load().Debug().Msgf("self-upgrade triggered, version target: %s", vt) - return true -} - -// selfUpgradeCheck checks if the version target vt is greater -// than the current one cv, perform self-upgrade then. -// Major version upgrades are not allowed to prevent breaking changes. -// -// The callers must ensure curVer and logger are non-nil. -// Returns true if upgrade is allowed and should proceed, false otherwise. -func selfUpgradeCheck(vt string, cv *semver.Version, logger *zerolog.Logger) bool { - if shouldUpgrade(vt, cv, logger) { - return performUpgrade(vt) - } - return false + mainLog.Load().Debug().Msgf("self-upgrade triggered, version target: %s", vts) } // leakOnUpstreamFailure reports whether ctrld should initiate a recovery flow diff --git a/cmd/cli/prog_test.go b/cmd/cli/prog_test.go index c4ef5c3..5f2f8e1 100644 --- a/cmd/cli/prog_test.go +++ b/cmd/cli/prog_test.go @@ -1,15 +1,11 @@ package cli import ( - "runtime" "testing" "time" - "github.com/Masterminds/semver/v3" - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/Control-D-Inc/ctrld" + "github.com/stretchr/testify/assert" ) func Test_prog_dnsWatchdogEnabled(t *testing.T) { @@ -59,215 +55,3 @@ func Test_prog_dnsWatchdogInterval(t *testing.T) { }) } } - -func Test_shouldUpgrade(t *testing.T) { - // Helper function to create a version - makeVersion := func(v string) *semver.Version { - ver, err := semver.NewVersion(v) - if err != nil { - t.Fatalf("failed to create version %s: %v", v, err) - } - return ver - } - - tests := []struct { - name string - versionTarget string - currentVersion *semver.Version - shouldUpgrade bool - description string - }{ - { - name: "empty version target", - versionTarget: "", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: false, - description: "should skip upgrade when version target is empty", - }, - { - name: "invalid version target", - versionTarget: "invalid-version", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: false, - description: "should skip upgrade when version target is invalid", - }, - { - name: "same version", - versionTarget: "v1.0.0", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: false, - description: "should skip upgrade when target version equals current version", - }, - { - name: "older version", - versionTarget: "v1.0.0", - currentVersion: makeVersion("v1.1.0"), - shouldUpgrade: false, - description: "should skip upgrade when target version is older than current version", - }, - { - name: "patch upgrade allowed", - versionTarget: "v1.0.1", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: true, - description: "should allow patch version upgrade within same major version", - }, - { - name: "minor upgrade allowed", - versionTarget: "v1.1.0", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: true, - description: "should allow minor version upgrade within same major version", - }, - { - name: "major upgrade blocked", - versionTarget: "v2.0.0", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: false, - description: "should block major version upgrade", - }, - { - name: "major downgrade blocked", - versionTarget: "v1.0.0", - currentVersion: makeVersion("v2.0.0"), - shouldUpgrade: false, - description: "should block major version downgrade", - }, - { - name: "version without v prefix", - versionTarget: "1.0.1", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: true, - description: "should handle version target without v prefix", - }, - { - name: "complex version upgrade allowed", - versionTarget: "v1.5.3", - currentVersion: makeVersion("v1.4.2"), - shouldUpgrade: true, - description: "should allow complex version upgrade within same major version", - }, - { - name: "complex major upgrade blocked", - versionTarget: "v3.1.0", - currentVersion: makeVersion("v2.5.3"), - shouldUpgrade: false, - description: "should block complex major version upgrade", - }, - { - name: "pre-release version upgrade allowed", - versionTarget: "v1.0.1-beta.1", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: true, - description: "should allow pre-release version upgrade within same major version", - }, - { - name: "pre-release major upgrade blocked", - versionTarget: "v2.0.0-alpha.1", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: false, - description: "should block pre-release major version upgrade", - }, - } - - for _, tc := range tests { - tc := tc - t.Run(tc.name, func(t *testing.T) { - // Create test logger - testLogger := zerolog.New(zerolog.NewTestWriter(t)).With().Logger() - - // Call the function and capture the result - result := shouldUpgrade(tc.versionTarget, tc.currentVersion, &testLogger) - - // Assert the expected result - assert.Equal(t, tc.shouldUpgrade, result, tc.description) - }) - } -} - -func Test_selfUpgradeCheck(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("skipped due to Windows file locking issue on Github Action runners") - } - - // Helper function to create a version - makeVersion := func(v string) *semver.Version { - ver, err := semver.NewVersion(v) - if err != nil { - t.Fatalf("failed to create version %s: %v", v, err) - } - return ver - } - - tests := []struct { - name string - versionTarget string - currentVersion *semver.Version - shouldUpgrade bool - description string - }{ - { - name: "upgrade allowed", - versionTarget: "v1.0.1", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: true, - description: "should allow upgrade and attempt to perform it", - }, - { - name: "upgrade blocked", - versionTarget: "v2.0.0", - currentVersion: makeVersion("v1.0.0"), - shouldUpgrade: false, - description: "should block upgrade and not attempt to perform it", - }, - } - - for _, tc := range tests { - tc := tc - t.Run(tc.name, func(t *testing.T) { - // Create test logger - testLogger := zerolog.New(zerolog.NewTestWriter(t)).With().Logger() - - // Call the function and capture the result - result := selfUpgradeCheck(tc.versionTarget, tc.currentVersion, &testLogger) - - // Assert the expected result - assert.Equal(t, tc.shouldUpgrade, result, tc.description) - }) - } -} - -func Test_performUpgrade(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("skipped due to Windows file locking issue on Github Action runners") - } - - tests := []struct { - name string - versionTarget string - expectedResult bool - description string - }{ - { - name: "valid version target", - versionTarget: "v1.0.1", - expectedResult: true, - description: "should attempt to perform upgrade with valid version target", - }, - { - name: "empty version target", - versionTarget: "", - expectedResult: true, - description: "should attempt to perform upgrade even with empty version target", - }, - } - - for _, tc := range tests { - tc := tc - t.Run(tc.name, func(t *testing.T) { - // Call the function and capture the result - result := performUpgrade(tc.versionTarget) - assert.Equal(t, tc.expectedResult, result, tc.description) - }) - } -} diff --git a/cmd/ctrld_library/main.go b/cmd/ctrld_library/main.go index b2e643d..49f5b26 100644 --- a/cmd/ctrld_library/main.go +++ b/cmd/ctrld_library/main.go @@ -28,17 +28,15 @@ type AppCallback interface { // Start configures utility with config.toml from provided directory. // This function will block until Stop is called // Check port availability prior to calling it. -func (c *Controller) Start(CdUID string, ProvisionID string, CustomHostname string, HomeDir string, UpstreamProto string, logLevel int, logPath string) { +func (c *Controller) Start(CdUID string, HomeDir string, UpstreamProto string, logLevel int, logPath string) { if c.stopCh == nil { c.stopCh = make(chan struct{}) c.Config = cli.AppConfig{ - CdUID: CdUID, - ProvisionID: ProvisionID, - CustomHostname: CustomHostname, - HomeDir: HomeDir, - UpstreamProto: UpstreamProto, - Verbose: logLevel, - LogPath: logPath, + CdUID: CdUID, + HomeDir: HomeDir, + UpstreamProto: UpstreamProto, + Verbose: logLevel, + LogPath: logPath, } appCallback := mapCallback(c.AppCallback) cli.RunMobile(&c.Config, &appCallback, c.stopCh) diff --git a/config.go b/config.go index 73484d7..96f6686 100644 --- a/config.go +++ b/config.go @@ -358,15 +358,6 @@ func (uc *UpstreamConfig) Init() { } } -// VerifyMsg creates and returns a new DNS message could be used for testing upstream health. -func (uc *UpstreamConfig) VerifyMsg() *dns.Msg { - msg := new(dns.Msg) - msg.RecursionDesired = true - msg.SetQuestion(".", dns.TypeNS) - msg.SetEdns0(4096, false) // ensure handling of large DNS response - return msg -} - // VerifyDomain returns the domain name that could be resolved by the upstream endpoint. // It returns empty for non-ControlD upstream endpoint. func (uc *UpstreamConfig) VerifyDomain() string { diff --git a/config_quic.go b/config_quic.go index 33f56b9..cadcb6b 100644 --- a/config_quic.go +++ b/config_quic.go @@ -36,7 +36,7 @@ func (uc *UpstreamConfig) setupDOH3Transport() { func (uc *UpstreamConfig) newDOH3Transport(addrs []string) http.RoundTripper { rt := &http3.Transport{} rt.TLSClientConfig = &tls.Config{RootCAs: uc.certPool} - rt.Dial = func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) { + rt.Dial = func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { _, port, _ := net.SplitHostPort(addr) // if we have a bootstrap ip set, use it to avoid DNS lookup if uc.BootstrapIP != "" { @@ -96,14 +96,14 @@ func (uc *UpstreamConfig) doh3Transport(dnsType uint16) http.RoundTripper { // - quic dialer is different with net.Dialer // - simplification for quic free version type parallelDialerResult struct { - conn *quic.Conn + conn quic.EarlyConnection err error } type quicParallelDialer struct{} // Dial performs parallel dialing to the given address list. -func (d *quicParallelDialer) Dial(ctx context.Context, addrs []string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) { +func (d *quicParallelDialer) Dial(ctx context.Context, addrs []string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { if len(addrs) == 0 { return nil, errors.New("empty addresses") } diff --git a/docs/known-issues.md b/docs/known-issues.md deleted file mode 100644 index 0d13bcc..0000000 --- a/docs/known-issues.md +++ /dev/null @@ -1,42 +0,0 @@ -# Known Issues - -This document outlines known issues with ctrld and their current status, workarounds, and recommendations. - -## macOS (Darwin) Issues - -### Self-Upgrade Issue on Darwin 15.5 - -**Issue**: ctrld self-upgrading functionality may not work on macOS Darwin 15.5. - -**Status**: Under investigation - -**Description**: Users on macOS Darwin 15.5 may experience issues when ctrld attempts to perform automatic self-upgrades. The upgrade process would be triggered, but ctrld won't be upgraded. - -**Workarounds**: -1. **Recommended**: Upgrade your macOS system to Darwin 15.6 or later, which has been tested and verified to work correctly with ctrld self-upgrade functionality. -2. **Alternative**: Run `ctrld upgrade prod` directly to manually upgrade ctrld to the latest version on Darwin 15.5. - -**Affected Versions**: ctrld v1.4.2 and later on macOS Darwin 15.5 - -**Last Updated**: 05/09/2025 - ---- - -## Contributing to Known Issues - -If you encounter an issue not listed here, please: - -1. Check the [GitHub Issues](https://github.com/Control-D-Inc/ctrld/issues) to see if it's already reported -2. If not reported, create a new issue with: - - Detailed description of the problem - - Steps to reproduce - - Expected vs actual behavior - - System information (OS, version, architecture) - - ctrld version - -## Issue Status Legend - -- **Under investigation**: Issue is confirmed and being analyzed -- **Workaround available**: Temporary solution exists while permanent fix is developed -- **Fixed**: Issue has been resolved in a specific version -- **Won't fix**: Issue is acknowledged but will not be addressed due to technical limitations or design decisions diff --git a/doq_test.go b/doq_test.go index 14055dd..430a22a 100644 --- a/doq_test.go +++ b/doq_test.go @@ -142,7 +142,7 @@ func (s *testQUICServer) serve(t *testing.T) { } // handleConnection manages an individual QUIC connection by accepting and handling incoming streams in separate goroutines. -func (s *testQUICServer) handleConnection(t *testing.T, conn *quic.Conn) { +func (s *testQUICServer) handleConnection(t *testing.T, conn quic.Connection) { for { stream, err := conn.AcceptStream(context.Background()) if err != nil { @@ -154,7 +154,7 @@ func (s *testQUICServer) handleConnection(t *testing.T, conn *quic.Conn) { } // handleStream processes a single QUIC stream, reads DNS messages, generates a response, and sends it back to the client. -func (s *testQUICServer) handleStream(t *testing.T, stream *quic.Stream) { +func (s *testQUICServer) handleStream(t *testing.T, stream quic.Stream) { defer stream.Close() // Read length (2 bytes) diff --git a/go.mod b/go.mod index 2280eb6..1d94a07 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.5.0 github.com/prometheus/prom2json v1.3.3 - github.com/quic-go/quic-go v0.54.0 + github.com/quic-go/quic-go v0.48.2 github.com/rs/zerolog v1.28.0 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 @@ -54,8 +54,10 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -72,6 +74,7 @@ require ( github.com/mdlayher/packet v1.1.2 // indirect github.com/mdlayher/socket v0.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -86,7 +89,7 @@ require ( github.com/subosito/gotenv v1.4.2 // indirect github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect github.com/vishvananda/netns v0.0.4 // indirect - go.uber.org/mock v0.5.0 // indirect + go.uber.org/mock v0.4.0 // indirect go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/crypto v0.36.0 // indirect diff --git a/go.sum b/go.sum index 56a71e1..25af133 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= @@ -101,6 +103,8 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg= github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU= @@ -158,6 +162,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -236,6 +242,10 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= @@ -261,8 +271,8 @@ github.com/prometheus/prom2json v1.3.3 h1:IYfSMiZ7sSOfliBoo89PcufjWO4eAR0gznGcET github.com/prometheus/prom2json v1.3.3/go.mod h1:Pv4yIPktEkK7btWsrUTWDDDrnpUrAELaOCj+oFwlgmc= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= -github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= +github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -320,8 +330,8 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= @@ -495,6 +505,8 @@ golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/internal/clientinfo/dhcp_lease_files.go b/internal/clientinfo/dhcp_lease_files.go index 34aabf3..1b5d829 100644 --- a/internal/clientinfo/dhcp_lease_files.go +++ b/internal/clientinfo/dhcp_lease_files.go @@ -16,5 +16,4 @@ var clientInfoFiles = map[string]ctrld.LeaseFileFormat{ "/var/dhcpd/var/db/dhcpd.leases": ctrld.IscDhcpd, // Pfsense "/home/pi/.router/run/dhcp/dnsmasq.leases": ctrld.Dnsmasq, // Firewalla "/var/lib/kea/dhcp4.leases": ctrld.KeaDHCP4, // Pfsense - "/var/db/dnsmasq.leases": ctrld.Dnsmasq, // OPNsense } diff --git a/internal/clientinfo/mdns.go b/internal/clientinfo/mdns.go index a09d729..e009e01 100644 --- a/internal/clientinfo/mdns.go +++ b/internal/clientinfo/mdns.go @@ -74,6 +74,7 @@ func (m *mdns) lookupIPByHostname(name string, v6 bool) string { if value == name { if addr, err := netip.ParseAddr(key.(string)); err == nil && addr.Is6() == v6 { ip = addr.String() + //lint:ignore S1008 This is used for readable. if addr.IsLoopback() { // Continue searching if this is loopback address. return true } diff --git a/internal/clientinfo/ptr_lookup.go b/internal/clientinfo/ptr_lookup.go index 9a1d10c..8e6b3f7 100644 --- a/internal/clientinfo/ptr_lookup.go +++ b/internal/clientinfo/ptr_lookup.go @@ -104,6 +104,7 @@ func (p *ptrDiscover) lookupIPByHostname(name string, v6 bool) string { if value == name { if addr, err := netip.ParseAddr(key.(string)); err == nil && addr.Is6() == v6 { ip = addr.String() + //lint:ignore S1008 This is used for readable. if addr.IsLoopback() { // Continue searching if this is loopback address. return true } @@ -119,7 +120,8 @@ func (p *ptrDiscover) lookupIPByHostname(name string, v6 bool) string { // is reachable, set p.serverDown to false, so p.lookupHostname can continue working. func (p *ptrDiscover) checkServer() { bo := backoff.NewBackoff("ptrDiscover", func(format string, args ...any) {}, time.Minute*5) - m := (&ctrld.UpstreamConfig{}).VerifyMsg() + m := new(dns.Msg) + m.SetQuestion(".", dns.TypeNS) ping := func() error { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() diff --git a/internal/router/dnsmasq/conf.go b/internal/router/dnsmasq/conf.go index bb81d60..b168042 100644 --- a/internal/router/dnsmasq/conf.go +++ b/internal/router/dnsmasq/conf.go @@ -6,7 +6,6 @@ import ( "errors" "io" "os" - "path/filepath" "strings" ) @@ -29,62 +28,3 @@ func interfaceNameFromReader(r io.Reader) (string, error) { } return "", errors.New("not found") } - -// AdditionalConfigFiles returns a list of Dnsmasq configuration files found in the "/tmp/etc" directory. -func AdditionalConfigFiles() []string { - if paths, err := filepath.Glob("/tmp/etc/dnsmasq-*.conf"); err == nil { - return paths - } - return nil -} - -// AdditionalLeaseFiles returns a list of lease file paths corresponding to the Dnsmasq configuration files. -func AdditionalLeaseFiles() []string { - cfgFiles := AdditionalConfigFiles() - if len(cfgFiles) == 0 { - return nil - } - leaseFiles := make([]string, 0, len(cfgFiles)) - for _, cfgFile := range cfgFiles { - if leaseFile := leaseFileFromConfigFileName(cfgFile); leaseFile != "" { - leaseFiles = append(leaseFiles, leaseFile) - - } else { - leaseFiles = append(leaseFiles, defaultLeaseFileFromConfigPath(cfgFile)) - } - } - return leaseFiles -} - -// leaseFileFromConfigFileName retrieves the DHCP lease file path by reading and parsing the provided configuration file. -func leaseFileFromConfigFileName(cfgFile string) string { - if f, err := os.Open(cfgFile); err == nil { - return leaseFileFromReader(f) - } - return "" -} - -// leaseFileFromReader parses the given io.Reader for the "dhcp-leasefile" configuration and returns its value as a string. -func leaseFileFromReader(r io.Reader) string { - scanner := bufio.NewScanner(r) - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, "#") { - continue - } - before, after, found := strings.Cut(line, "=") - if !found { - continue - } - if before == "dhcp-leasefile" { - return after - } - } - return "" -} - -// defaultLeaseFileFromConfigPath generates the default lease file path based on the provided configuration file path. -func defaultLeaseFileFromConfigPath(path string) string { - name := filepath.Base(path) - return filepath.Join("/var/lib/misc", strings.TrimSuffix(name, ".conf")+".leases") -} diff --git a/internal/router/dnsmasq/conf_test.go b/internal/router/dnsmasq/conf_test.go index 9ca672b..99a0710 100644 --- a/internal/router/dnsmasq/conf_test.go +++ b/internal/router/dnsmasq/conf_test.go @@ -1,7 +1,6 @@ package dnsmasq import ( - "io" "strings" "testing" ) @@ -45,49 +44,3 @@ interface=eth0 }) } } - -func Test_leaseFileFromReader(t *testing.T) { - tests := []struct { - name string - in io.Reader - expected string - }{ - { - "default", - strings.NewReader(` -dhcp-script=/sbin/dhcpc_lease -dhcp-leasefile=/var/lib/misc/dnsmasq-1.leases -script-arp -`), - "/var/lib/misc/dnsmasq-1.leases", - }, - { - "non-default", - strings.NewReader(` -dhcp-script=/sbin/dhcpc_lease -dhcp-leasefile=/tmp/var/lib/misc/dnsmasq-1.leases -script-arp -`), - "/tmp/var/lib/misc/dnsmasq-1.leases", - }, - { - "missing", - strings.NewReader(` -dhcp-script=/sbin/dhcpc_lease -script-arp -`), - "", - }, - } - - for _, tc := range tests { - tc := tc - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - if got := leaseFileFromReader(tc.in); got != tc.expected { - t.Errorf("leaseFileFromReader() = %v, want %v", got, tc.expected) - } - }) - } - -} diff --git a/internal/router/dnsmasq/dnsmasq.go b/internal/router/dnsmasq/dnsmasq.go index 058b0b5..819bd59 100644 --- a/internal/router/dnsmasq/dnsmasq.go +++ b/internal/router/dnsmasq/dnsmasq.go @@ -4,7 +4,6 @@ import ( "errors" "html/template" "net" - "os" "path/filepath" "strings" @@ -27,13 +26,9 @@ 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 MerlinConfPath = "/tmp/etc/dnsmasq.conf" +const MerlinJffsConfPath = "/jffs/configs/dnsmasq.conf" +const MerlinPostConfPath = "/jffs/scripts/dnsmasq.postconf" const MerlinPostConfMarker = `# GENERATED BY ctrld - EOF` const MerlinPostConfTmpl = `# GENERATED BY ctrld - DO NOT MODIFY @@ -164,27 +159,3 @@ func FirewallaSelfInterfaces() []*net.Interface { } 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 -} diff --git a/internal/router/edgeos/edgeos.go b/internal/router/edgeos/edgeos.go index 7364ac1..2e229ac 100644 --- a/internal/router/edgeos/edgeos.go +++ b/internal/router/edgeos/edgeos.go @@ -6,7 +6,6 @@ import ( "fmt" "os" "os/exec" - "path/filepath" "strings" "github.com/kardianos/service" @@ -182,7 +181,7 @@ func ContentFilteringEnabled() bool { // DnsShieldEnabled reports whether DNS Shield is enabled. // See: https://community.ui.com/releases/UniFi-OS-Dream-Machines-3-2-7/251dfc1e-f4dd-4264-a080-3be9d8b9e02b func DnsShieldEnabled() bool { - buf, err := os.ReadFile(filepath.Join(dnsmasq.UbiosConfPath(), "dns.conf")) + buf, err := os.ReadFile("/var/run/dnsmasq.conf.d/dns.conf") if err != nil { return false } diff --git a/internal/router/merlin/merlin.go b/internal/router/merlin/merlin.go index c1c6821..cacc508 100644 --- a/internal/router/merlin/merlin.go +++ b/internal/router/merlin/merlin.go @@ -6,7 +6,6 @@ import ( "io" "os" "os/exec" - "path/filepath" "strings" "time" "unicode" @@ -21,18 +20,10 @@ import ( const Name = "merlin" -// nvramKvMap is a map of NVRAM key-value pairs used to configure and manage Merlin-specific settings. var nvramKvMap = map[string]string{ "dnspriv_enable": "0", // Ensure Merlin native DoT disabled. } -// dnsmasqConfig represents configuration paths for dnsmasq operations in Merlin firmware. -type dnsmasqConfig struct { - confPath string - jffsConfPath string -} - -// Merlin represents a configuration handler for setting up and managing ctrld on Merlin routers. type Merlin struct { cfg *ctrld.Config } @@ -42,22 +33,18 @@ func New(cfg *ctrld.Config) *Merlin { return &Merlin{cfg: cfg} } -// ConfigureService configures the service based on the provided configuration. It returns an error if the configuration fails. func (m *Merlin) ConfigureService(config *service.Config) error { return nil } -// Install sets up the necessary configurations and services required for the Merlin instance to function properly. func (m *Merlin) Install(_ *service.Config) error { return nil } -// Uninstall removes the ctrld-related configurations and services from the Merlin router and reverts to the original state. func (m *Merlin) Uninstall(_ *service.Config) error { return nil } -// PreRun prepares the Merlin instance for operation by waiting for essential services and directories to become available. func (m *Merlin) PreRun() error { // Wait NTP ready. _ = m.Cleanup() @@ -79,7 +66,6 @@ func (m *Merlin) PreRun() error { return nil } -// Setup initializes and configures the Merlin instance for use, including setting up dnsmasq and necessary nvram settings. func (m *Merlin) Setup() error { if m.cfg.FirstListener().IsDirectDnsListener() { return nil @@ -93,10 +79,35 @@ func (m *Merlin) Setup() error { return err } - for _, cfg := range getDnsmasqConfigs() { - if err := m.setupDnsmasq(cfg); err != nil { - return fmt.Errorf("failed to setup dnsmasq: config: %s, error: %w", cfg.confPath, err) - } + // Copy current dnsmasq config to /jffs/configs/dnsmasq.conf, + // Then we will run postconf script on this file. + // + // Normally, adding postconf script is enough. However, we see + // reports on some Merlin devices that postconf scripts does not + // work, but manipulating the config directly via /jffs/configs does. + src, err := os.Open(dnsmasq.MerlinConfPath) + if err != nil { + return fmt.Errorf("failed to open dnsmasq config: %w", err) + } + defer src.Close() + + dst, err := os.Create(dnsmasq.MerlinJffsConfPath) + if err != nil { + return fmt.Errorf("failed to create %s: %w", dnsmasq.MerlinJffsConfPath, err) + } + defer dst.Close() + + if _, err := io.Copy(dst, src); err != nil { + return fmt.Errorf("failed to copy current dnsmasq config: %w", err) + } + if err := dst.Close(); err != nil { + return fmt.Errorf("failed to save %s: %w", dnsmasq.MerlinJffsConfPath, err) + } + + // Run postconf script on /jffs/configs/dnsmasq.conf directly. + cmd := exec.Command("/bin/sh", dnsmasq.MerlinPostConfPath, dnsmasq.MerlinJffsConfPath) + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("failed to run post conf: %s: %w", string(out), err) } // Restart dnsmasq service. @@ -111,7 +122,6 @@ func (m *Merlin) Setup() error { return nil } -// Cleanup restores the original dnsmasq and nvram configurations and restarts dnsmasq if necessary. func (m *Merlin) Cleanup() error { if m.cfg.FirstListener().IsDirectDnsListener() { return nil @@ -133,11 +143,9 @@ func (m *Merlin) Cleanup() error { if err := os.WriteFile(dnsmasq.MerlinPostConfPath, merlinParsePostConf(buf), 0750); err != nil { return err } - - for _, cfg := range getDnsmasqConfigs() { - if err := m.cleanupDnsmasqJffs(cfg); err != nil { - return fmt.Errorf("failed to cleanup jffs dnsmasq: config: %s, error: %w", cfg.confPath, err) - } + // Remove /jffs/configs/dnsmasq.conf file. + if err := os.Remove(dnsmasq.MerlinJffsConfPath); err != nil && !os.IsNotExist(err) { + return err } // Restart dnsmasq service. if err := restartDNSMasq(); err != nil { @@ -146,54 +154,6 @@ func (m *Merlin) Cleanup() error { return nil } -// setupDnsmasq sets up dnsmasq configuration by writing postconf, copying configuration, and running a postconf script. -func (m *Merlin) setupDnsmasq(cfg *dnsmasqConfig) error { - src, err := os.Open(cfg.confPath) - if os.IsNotExist(err) { - return nil // nothing to do if conf file does not exist. - } - if err != nil { - return fmt.Errorf("failed to open dnsmasq config: %w", err) - } - defer src.Close() - - // Copy current dnsmasq config to cfg.jffsConfPath, - // Then we will run postconf script on this file. - // - // Normally, adding postconf script is enough. However, we see - // reports on some Merlin devices that postconf scripts does not - // work, but manipulating the config directly via /jffs/configs does. - dst, err := os.Create(cfg.jffsConfPath) - if err != nil { - return fmt.Errorf("failed to create %s: %w", cfg.jffsConfPath, err) - } - defer dst.Close() - - if _, err := io.Copy(dst, src); err != nil { - return fmt.Errorf("failed to copy current dnsmasq config: %w", err) - } - if err := dst.Close(); err != nil { - return fmt.Errorf("failed to save %s: %w", cfg.jffsConfPath, err) - } - - // Run postconf script on cfg.jffsConfPath directly. - cmd := exec.Command("/bin/sh", dnsmasq.MerlinPostConfPath, cfg.jffsConfPath) - if out, err := cmd.CombinedOutput(); err != nil { - return fmt.Errorf("failed to run post conf: %s: %w", string(out), err) - } - return nil -} - -// cleanupDnsmasqJffs removes the JFFS configuration file specified in the given dnsmasqConfig, if it exists. -func (m *Merlin) cleanupDnsmasqJffs(cfg *dnsmasqConfig) error { - // Remove cfg.jffsConfPath file. - if err := os.Remove(cfg.jffsConfPath); err != nil && !os.IsNotExist(err) { - return err - } - return nil -} - -// writeDnsmasqPostconf writes the requireddnsmasqConfigs post-configuration for dnsmasq to enable custom DNS settings with ctrld. func (m *Merlin) writeDnsmasqPostconf() error { buf, err := os.ReadFile(dnsmasq.MerlinPostConfPath) // Already setup. @@ -219,8 +179,6 @@ func (m *Merlin) writeDnsmasqPostconf() error { return os.WriteFile(dnsmasq.MerlinPostConfPath, []byte(data), 0750) } -// restartDNSMasq restarts the dnsmasq service by executing the appropriate system command using "service". -// Returns an error if the command fails or if there is an issue processing the command output. func restartDNSMasq() error { if out, err := exec.Command("service", "restart_dnsmasq").CombinedOutput(); err != nil { return fmt.Errorf("restart_dnsmasq: %s, %w", string(out), err) @@ -228,22 +186,6 @@ func restartDNSMasq() error { return nil } -// getDnsmasqConfigs retrieves a list of dnsmasqConfig containing configuration and JFFS paths for dnsmasq operations. -func getDnsmasqConfigs() []*dnsmasqConfig { - cfgs := []*dnsmasqConfig{ - {dnsmasq.MerlinConfPath, dnsmasq.MerlinJffsConfPath}, - } - for _, path := range dnsmasq.AdditionalConfigFiles() { - jffsConfPath := filepath.Join(dnsmasq.MerlinJffsConfDir, filepath.Base(path)) - cfgs = append(cfgs, &dnsmasqConfig{path, jffsConfPath}) - } - - return cfgs -} - -// merlinParsePostConf parses the dnsmasq post configuration by removing content after the MerlinPostConfMarker, if present. -// If no marker is found, the original buffer is returned unmodified. -// Returns nil if the input buffer is empty. func merlinParsePostConf(buf []byte) []byte { if len(buf) == 0 { return nil @@ -255,7 +197,6 @@ func merlinParsePostConf(buf []byte) []byte { return buf } -// waitDirExists waits until the specified directory exists, polling its existence every second. func waitDirExists(dir string) { for { if _, err := os.Stat(dir); !os.IsNotExist(err) { diff --git a/internal/router/service_ubios.go b/internal/router/service_ubios.go index 9ad971d..8077c07 100644 --- a/internal/router/service_ubios.go +++ b/internal/router/service_ubios.go @@ -13,13 +13,14 @@ import ( "time" "github.com/kardianos/service" - - "github.com/Control-D-Inc/ctrld/internal/router/dnsmasq" ) // This is a copy of https://github.com/kardianos/service/blob/v1.2.1/service_sysv_linux.go, // with modification for supporting ubios v1 init system. +// Keep in sync with ubios.ubiosDNSMasqConfigPath +const ubiosDNSMasqConfigPath = "/run/dnsmasq.conf.d/zzzctrld.conf" + type ubiosSvc struct { i service.Interface platform string @@ -85,7 +86,7 @@ func (s *ubiosSvc) Install() error { }{ s.Config, path, - filepath.Join(dnsmasq.UbiosConfPath(), dnsmasq.UbiosConfName), + ubiosDNSMasqConfigPath, } if err := s.template().Execute(f, to); err != nil { diff --git a/internal/router/ubios/ubios.go b/internal/router/ubios/ubios.go index cba6842..a1f0b6c 100644 --- a/internal/router/ubios/ubios.go +++ b/internal/router/ubios/ubios.go @@ -3,7 +3,6 @@ package ubios import ( "bytes" "os" - "path/filepath" "strconv" "github.com/kardianos/service" @@ -13,19 +12,19 @@ import ( "github.com/Control-D-Inc/ctrld/internal/router/edgeos" ) -const Name = "ubios" +const ( + Name = "ubios" + ubiosDNSMasqConfigPath = "/run/dnsmasq.conf.d/zzzctrld.conf" + ubiosDNSMasqDnsConfigPath = "/run/dnsmasq.conf.d/dns.conf" +) type Ubios struct { - cfg *ctrld.Config - dnsmasqConfPath string + cfg *ctrld.Config } // New returns a router.Router for configuring/setup/run ctrld on Ubios routers. func New(cfg *ctrld.Config) *Ubios { - return &Ubios{ - cfg: cfg, - dnsmasqConfPath: filepath.Join(dnsmasq.UbiosConfPath(), dnsmasq.UbiosConfName), - } + return &Ubios{cfg: cfg} } func (u *Ubios) ConfigureService(config *service.Config) error { @@ -60,7 +59,7 @@ func (u *Ubios) Setup() error { if err != nil { return err } - if err := os.WriteFile(u.dnsmasqConfPath, []byte(data), 0600); err != nil { + if err := os.WriteFile(ubiosDNSMasqConfigPath, []byte(data), 0600); err != nil { return err } // Restart dnsmasq service. @@ -75,7 +74,7 @@ func (u *Ubios) Cleanup() error { return nil } // Remove the custom dnsmasq config - if err := os.Remove(u.dnsmasqConfPath); err != nil { + if err := os.Remove(ubiosDNSMasqConfigPath); err != nil { return err } // Restart dnsmasq service. @@ -86,7 +85,7 @@ func (u *Ubios) Cleanup() error { } func restartDNSMasq() error { - buf, err := os.ReadFile(dnsmasq.UbiosPidFile()) + buf, err := os.ReadFile("/run/dnsmasq.pid") if err != nil { return err } diff --git a/nameservers_linux.go b/nameservers_linux.go index 37a9ed2..13a5507 100644 --- a/nameservers_linux.go +++ b/nameservers_linux.go @@ -5,12 +5,9 @@ import ( "bytes" "encoding/hex" "net" - "net/netip" "os" "strings" - "tailscale.com/net/netmon" - "github.com/Control-D-Inc/ctrld/internal/dns/resolvconffile" ) @@ -131,25 +128,3 @@ func virtualInterfaces() set { } return s } - -// validInterfacesMap returns a set containing non virtual interfaces. -// TODO: deduplicated with cmd/cli/net_linux.go in v2. -func validInterfaces() set { - m := make(map[string]struct{}) - vis := virtualInterfaces() - netmon.ForeachInterface(func(i netmon.Interface, prefixes []netip.Prefix) { - if _, existed := vis[i.Name]; existed { - return - } - m[i.Name] = struct{}{} - }) - // Fallback to default route interface if found nothing. - if len(m) == 0 { - defaultRoute, err := netmon.DefaultRoute() - if err != nil { - return m - } - m[defaultRoute.InterfaceName] = struct{}{} - } - return m -} diff --git a/nameservers_windows.go b/nameservers_windows.go index 7b16e8e..eb4f2b5 100644 --- a/nameservers_windows.go +++ b/nameservers_windows.go @@ -23,17 +23,20 @@ import ( ) const ( - maxDNSAdapterRetries = 5 - retryDelayDNSAdapter = 1 * time.Second - defaultDNSAdapterTimeout = 10 * time.Second - minDNSServers = 1 // Minimum number of DNS servers we want to find - - DS_FORCE_REDISCOVERY = 0x00000001 - DS_DIRECTORY_SERVICE_REQUIRED = 0x00000010 - DS_BACKGROUND_ONLY = 0x00000100 - DS_IP_REQUIRED = 0x00000200 - DS_IS_DNS_NAME = 0x00020000 - DS_RETURN_DNS_NAME = 0x40000000 + maxDNSAdapterRetries = 5 + retryDelayDNSAdapter = 1 * time.Second + defaultDNSAdapterTimeout = 10 * time.Second + minDNSServers = 1 // Minimum number of DNS servers we want to find + NetSetupUnknown uint32 = 0 + NetSetupWorkgroup uint32 = 1 + NetSetupDomain uint32 = 2 + NetSetupCloudDomain uint32 = 3 + DS_FORCE_REDISCOVERY = 0x00000001 + DS_DIRECTORY_SERVICE_REQUIRED = 0x00000010 + DS_BACKGROUND_ONLY = 0x00000100 + DS_IP_REQUIRED = 0x00000200 + DS_IS_DNS_NAME = 0x00020000 + DS_RETURN_DNS_NAME = 0x40000000 ) type DomainControllerInfo struct { @@ -155,7 +158,7 @@ func getDNSServers(ctx context.Context) ([]string, error) { 0, // DomainGuid - not needed 0, // SiteName - not needed uintptr(flags), // Flags - uintptr(unsafe.Pointer(&info))) // DomainControllerInfo - output + uintptr(unsafe.Pointer(&info))) // DomainControllerInfo - output if ret != 0 { switch ret { @@ -340,28 +343,27 @@ func checkDomainJoined() bool { var domain *uint16 var status uint32 - if err := windows.NetGetJoinInformation(nil, &domain, &status); err != nil { - Log(context.Background(), logger.Debug(), "Failed to get domain join status: %v", err) + err := windows.NetGetJoinInformation(nil, &domain, &status) + if err != nil { + Log(context.Background(), logger.Debug(), + "Failed to get domain join status: %v", err) return false } defer windows.NetApiBufferFree((*byte)(unsafe.Pointer(domain))) - // NETSETUP_JOIN_STATUS constants from Microsoft Windows API - // See: https://learn.microsoft.com/en-us/windows/win32/api/lmjoin/ne-lmjoin-netsetup_join_status - // - // NetSetupUnknownStatus uint32 = 0 // The status is unknown - // NetSetupUnjoined uint32 = 1 // The computer is not joined to a domain or workgroup - // NetSetupWorkgroupName uint32 = 2 // The computer is joined to a workgroup - // NetSetupDomainName uint32 = 3 // The computer is joined to a domain - // - // We only care about NetSetupDomainName. domainName := windows.UTF16PtrToString(domain) Log(context.Background(), logger.Debug(), - "Domain join status: domain=%s status=%d (UnknownStatus=0, Unjoined=1, WorkgroupName=2, DomainName=3)", + "Domain join status: domain=%s status=%d (Unknown=0, Workgroup=1, Domain=2, CloudDomain=3)", domainName, status) - isDomain := status == syscall.NetSetupDomainName - Log(context.Background(), logger.Debug(), "Is domain joined? status=%d, result=%v", status, isDomain) + // Consider domain or cloud domain as domain-joined + isDomain := status == NetSetupDomain || status == NetSetupCloudDomain + Log(context.Background(), logger.Debug(), + "Is domain joined? status=%d, traditional=%v, cloud=%v, result=%v", + status, + status == NetSetupDomain, + status == NetSetupCloudDomain, + isDomain) return isDomain } diff --git a/net_darwin.go b/net_darwin.go deleted file mode 100644 index 5b01e9f..0000000 --- a/net_darwin.go +++ /dev/null @@ -1,35 +0,0 @@ -package ctrld - -import ( - "bufio" - "bytes" - "io" - "os/exec" - "strings" -) - -// validInterfaces returns a set of all valid hardware ports. -// TODO: deduplicated with cmd/cli/net_darwin.go in v2. -func validInterfaces() map[string]struct{} { - b, err := exec.Command("networksetup", "-listallhardwareports").Output() - if err != nil { - return nil - } - return parseListAllHardwarePorts(bytes.NewReader(b)) -} - -// parseListAllHardwarePorts parses output of "networksetup -listallhardwareports" -// and returns map presents all hardware ports. -func parseListAllHardwarePorts(r io.Reader) map[string]struct{} { - m := make(map[string]struct{}) - scanner := bufio.NewScanner(r) - for scanner.Scan() { - line := scanner.Text() - after, ok := strings.CutPrefix(line, "Device: ") - if !ok { - continue - } - m[after] = struct{}{} - } - return m -} diff --git a/net_others.go b/net_others.go deleted file mode 100644 index ae7ab8e..0000000 --- a/net_others.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build !darwin && !windows && !linux - -package ctrld - -import "tailscale.com/net/netmon" - -// validInterfaces returns a set containing only default route interfaces. -// TODO: deuplicated with cmd/cli/net_others.go in v2. -func validInterfaces() map[string]struct{} { - defaultRoute, err := netmon.DefaultRoute() - if err != nil { - return nil - } - return map[string]struct{}{defaultRoute.InterfaceName: {}} -} diff --git a/resolver.go b/resolver.go index 3aeddd0..27c0108 100644 --- a/resolver.go +++ b/resolver.go @@ -729,15 +729,10 @@ func newResolverWithNameserver(nameservers []string) *osResolver { return r } -// Rfc1918Addresses returns the list of local physical interfaces private IP addresses +// Rfc1918Addresses returns the list of local interfaces private IP addresses func Rfc1918Addresses() []string { - vis := validInterfaces() var res []string netmon.ForeachInterface(func(i netmon.Interface, prefixes []netip.Prefix) { - // Skip virtual interfaces. - if _, existed := vis[i.Name]; !existed { - return - } addrs, _ := i.Addrs() for _, addr := range addrs { ipNet, ok := addr.(*net.IPNet) diff --git a/resolver_test.go b/resolver_test.go index f030739..ebcad16 100644 --- a/resolver_test.go +++ b/resolver_test.go @@ -282,35 +282,6 @@ func Test_Edns0_CacheReply(t *testing.T) { } } -// https://github.com/Control-D-Inc/ctrld/issues/255 -func Test_legacyResolverWithBigExtraSection(t *testing.T) { - lanPC, err := net.ListenPacket("udp", "127.0.0.1:0") // 127.0.0.1 is considered LAN (loopback) - if err != nil { - t.Fatalf("failed to listen on LAN address: %v", err) - } - lanServer, lanAddr, err := runLocalPacketConnTestServer(t, lanPC, bigExtraSectionHandler()) - if err != nil { - t.Fatalf("failed to run LAN test server: %v", err) - } - defer lanServer.Shutdown() - - uc := &UpstreamConfig{ - Name: "Legacy", - Type: ResolverTypeLegacy, - Endpoint: lanAddr, - } - uc.Init() - r, err := NewResolver(uc) - if err != nil { - t.Fatal(err) - } - - _, err = r.Resolve(context.Background(), uc.VerifyMsg()) - if err != nil { - t.Fatal(err) - } -} - func Test_upstreamTypeFromEndpoint(t *testing.T) { tests := []struct { name string @@ -399,68 +370,6 @@ func countHandler(call *atomic.Int64) dns.HandlerFunc { } } -func mustRR(s string) dns.RR { - r, err := dns.NewRR(s) - if err != nil { - panic(err) - } - return r -} - -func bigExtraSectionHandler() dns.HandlerFunc { - return func(w dns.ResponseWriter, msg *dns.Msg) { - m := &dns.Msg{ - Answer: []dns.RR{ - mustRR(". 7149 IN NS m.root-servers.net."), - mustRR(". 7149 IN NS c.root-servers.net."), - mustRR(". 7149 IN NS e.root-servers.net."), - mustRR(". 7149 IN NS j.root-servers.net."), - mustRR(". 7149 IN NS g.root-servers.net."), - mustRR(". 7149 IN NS k.root-servers.net."), - mustRR(". 7149 IN NS l.root-servers.net."), - mustRR(". 7149 IN NS d.root-servers.net."), - mustRR(". 7149 IN NS h.root-servers.net."), - mustRR(". 7149 IN NS b.root-servers.net."), - mustRR(". 7149 IN NS a.root-servers.net."), - mustRR(". 7149 IN NS f.root-servers.net."), - mustRR(". 7149 IN NS i.root-servers.net."), - }, - Extra: []dns.RR{ - mustRR("m.root-servers.net. 656 IN A 202.12.27.33"), - mustRR("m.root-servers.net. 656 IN AAAA 2001:dc3::35"), - mustRR("c.root-servers.net. 656 IN A 192.33.4.12"), - mustRR("c.root-servers.net. 656 IN AAAA 2001:500:2::c"), - mustRR("e.root-servers.net. 656 IN A 192.203.230.10"), - mustRR("e.root-servers.net. 656 IN AAAA 2001:500:a8::e"), - mustRR("j.root-servers.net. 656 IN A 192.58.128.30"), - mustRR("j.root-servers.net. 656 IN AAAA 2001:503:c27::2:30"), - mustRR("g.root-servers.net. 656 IN A 192.112.36.4"), - mustRR("g.root-servers.net. 656 IN AAAA 2001:500:12::d0d"), - mustRR("k.root-servers.net. 656 IN A 193.0.14.129"), - mustRR("k.root-servers.net. 656 IN AAAA 2001:7fd::1"), - mustRR("l.root-servers.net. 656 IN A 199.7.83.42"), - mustRR("l.root-servers.net. 656 IN AAAA 2001:500:9f::42"), - mustRR("d.root-servers.net. 656 IN A 199.7.91.13"), - mustRR("d.root-servers.net. 656 IN AAAA 2001:500:2d::d"), - mustRR("h.root-servers.net. 656 IN A 198.97.190.53"), - mustRR("h.root-servers.net. 656 IN AAAA 2001:500:1::53"), - mustRR("b.root-servers.net. 656 IN A 170.247.170.2"), - mustRR("b.root-servers.net. 656 IN AAAA 2801:1b8:10::b"), - mustRR("a.root-servers.net. 656 IN A 198.41.0.4"), - mustRR("a.root-servers.net. 656 IN AAAA 2001:503:ba3e::2:30"), - mustRR("f.root-servers.net. 656 IN A 192.5.5.241"), - mustRR("f.root-servers.net. 656 IN AAAA 2001:500:2f::f"), - mustRR("i.root-servers.net. 656 IN A 192.36.148.17"), - mustRR("i.root-servers.net. 656 IN AAAA 2001:7fe::53"), - }, - } - - m.Compress = true - m.SetReply(msg) - w.WriteMsg(m) - } -} - func generateEdns0ClientCookie() string { cookie := make([]byte, 8) if _, err := rand.Read(cookie); err != nil {