Skip to content

Conversation

@cuonglm
Copy link
Collaborator

@cuonglm cuonglm commented Oct 9, 2025

Major Release

This release contains new features, improvements and bug fixes.

Added

  • Rule Matching Engine: Implemented new modular rule matching engine infrastructure with support for configurable rule evaluation order (infrastructure ready, not yet exposed to users)
  • Modular CLI Architecture: Split monolithic CLI command structure into focused, maintainable command files
  • Context Support: Added context.Context support throughout configuration methods for better cancellation and timeout handling

Improvements

  • Logging System: Migrated from zerolog to uber zap logging package with improved performance, better structured logging, and enhanced extensibility
  • CLI Architecture: Refactored monolithic commands.go (1,397 lines) into 13 focused command files, improving maintainability and testability
  • DNS Proxy: Major refactoring of DNS proxy implementation with better code organization, improved error handling, and enhanced separation of concerns
  • Client Information System: Enhanced client discovery and information management with improved DHCP, mDNS, ARP, NDP, and hosts file parsing
  • Service Management: Improved service lifecycle management, enhanced reload functionality, and better status reporting across all platforms
  • Network/OS Abstractions: Improved network and OS abstraction layers with reduced code duplication and more consistent behavior across platforms
  • Configuration System: Added context support and improved logging throughout configuration initialization and bootstrap operations
  • Code Organization: Removed ~3,000 lines of deprecated router-specific code, focusing development on core DNS proxy functionality

Fixes

  • Improved error handling and recovery mechanisms throughout the codebase
  • Enhanced service state management and lifecycle handling

Breaking Changes

⚠️ Server and Router-Specific Integrations Removed: All server platforms and router-specific integration code has been removed, including support for:

  • DD-WRT, dnsmasq, EdgeOS, Firewalla, AsusWRT-Merlin, Netgear/Orbi/Voxel, OpenWRT, Synology, Tomato, and Ubiquiti UniFi OS routers
  • Windows Server

If you were using ctrld with any of these router platforms, you will need to use alternative deployment methods. See the migration guide for details.

Note: All other functionality remains backward compatible. Existing configuration files and CLI commands continue to work without changes.

cuonglm added 30 commits October 9, 2025 16:47
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 0e3f764.

Revert "Upgrade quic-go to v0.54.0"

This reverts commit e52402e.

Revert "docs: add known issues documentation for Darwin 15.5 upgrade issue"

This reverts commit 2133f31.

Revert "start mobile library with provision id and custom hostname."

This reverts commit a198a5c.

Revert "Add OPNsense new lease file"

This reverts commit 7af29cf.

Revert ".github/workflows: bump go version to 1.24.x"

This reverts commit ce1a165.

Revert "fix: ensure upstream health checks can handle large DNS responses"

This reverts commit fd48e6d.

Revert "refactor(prog): move network monitoring outside listener loop"

This reverts commit d71d134.

Revert "fix: correct Windows API constants to fix domain join detection"

This reverts commit 21855df.

Revert "refactor: move network monitoring to separate goroutine"

This reverts commit 66e2d3a.

Revert "refactor: extract empty string filtering to reusable function"

This reverts commit 36a7423.

Revert "cmd/cli: ignore empty positional argument for start command"

This reverts commit e616091.

Revert "Avoiding Windows runners file locking issue"

This reverts commit 0948161.

Revert "refactor: split selfUpgradeCheck into version check and upgrade execution"

This reverts commit ce29b5d.

Revert "internal/router: support Ubios 4.3+"

This reverts commit de24fa2.

Revert "internal/router: support Merlin Guest Network Pro VLAN"

This reverts commit 6663925.
So setting up logging for ctrld binary and ctrld packages could be done
more easily, decouple the required setup for interactive vs daemon
running.

This is the first step toward replacing rs/zerolog libary with a
different logging library.
By adding a logger field to "prog" struct, and use this field inside its
method instead of always accessing global mainLog variable. This at
least ensure more consistent usage of the logger during ctrld prog
runtime, and also help refactoring the code more easily in the future
(like replacing the logger library).
Make nameserver resolution functions more consistent and accessible:
- Rename currentNameserversFromResolvconf to CurrentNameserversFromResolvconf
- Move function to public API for better reusability
- Update all internal references to use the new public API
- Add comprehensive godoc comments for nameserver functions
- Improve code organization by centralizing DNS resolution logic

This change makes the nameserver resolution functionality more maintainable
and easier to use across different parts of the codebase.
- Add timeouts and proper cleanup in Test_osResolver_Singleflight:
  * Implement context timeout
  * Add proper PacketConn cleanup
  * Fix race conditions in error handling
  * Improve atomic value reporting

- Enhance Test_osResolver_HotCache:
  * Add proper timeout context
  * Implement more reliable cache verification
  * Fix potential resource leaks
  * Add deterministic polling intervals

- Add thread safety to Test_Edns0_CacheReply:
  * Implement proper timeout context
  * Add proper resource cleanup
  * Fix concurrent operations handling

The changes improve overall test suite reliability by addressing resource
management, timeout handling, and thread safety concerns across multiple DNS
resolver test cases.
Move client information related functions from client_info_*.go to desktop_*.go files
to better organize platform-specific code and separate desktop functionality from
shared code.

No functional changes.
Improve documentation for Test_prog_parseResolvConfNameservers to clarify that
the old implementation was removed as part of code deduplication effort. The code
for handling resolv.conf was unified into the resolvconffile package to provide
a consistent interface across the codebase.

This change provides better context for future developers about why the
refactoring was done and what benefits it brings.
Add context parameter to validInterfacesMap for better error handling and
logging. Move Windows-specific network adapter validation logic to the
ctrld package. Key changes include:

- Add context parameter to validInterfacesMap across all platforms
- Move Windows validInterfaces to ctrld.ValidInterfaces
- Improve error handling for virtual interface detection on Linux
- Update all callers to pass appropriate context

This change improves error reporting and makes the interface validation
code more maintainable across different platforms.
Move getDNS type definition from dns.go to os_linux.go where it is used.
Remove the now-empty dns.go file. This change improves code organization
by keeping platform-specific types with their implementations.
Break down the large DNS handling function into smaller, focused functions
with clear responsibilities:

- Extract handleDNSQuery from serveDNS handler function
- Create dedicated startListeners function for listener management
- Add standardQueryRequest struct to encapsulate query parameters
- Split special domain handling into separate function
- Add descriptive comments for each new function
- Improve variable names for better clarity (e.g., startTime vs t)

This refactoring improves code maintainability and readability without
changing the core DNS proxy functionality.
By looking for any additional dnsmasq configuration files under
/tmp/etc, and handling them like default one.
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.
Split the long proxy method into several smaller methods to improve maintainability
and testability. Each new method has a single responsibility:

- initializeUpstreams: handles upstream configuration setup
- tryCache: manages cache lookup logic
- tryUpstreams: coordinates upstream query attempts
- processUpstream: handles individual upstream query processing
- handleAllUpstreamsFailure: manages failure scenarios
- checkCache: performs cache checks and retrieval
- serveStaleResponse: handles stale cache responses
- shouldContinueWithNextUpstream: determines if failover is needed
- prepareSuccessResponse: formats successful responses

This refactoring:
- Reduces cognitive complexity
- Improves code testability
- Makes the DNS proxy logic flow clearer
- Isolates error handling and edge cases
- Maintains existing functionality

No behavioral changes were made.
Logging there should use Log function to include the request ID if
present. Changes were made unintentionally during the refactoring to
eliminate usage of global logger.

This commits message restores the correct/old behavior.
…tion

- Move version checking logic to shouldUpgrade for testability
- Move upgrade command execution to performUpgrade
- selfUpgradeCheck now composes these two for clarity
- Update and expand tests: focus on logic, not side effects
- Improves maintainability, testability, and separation of concerns
The validation was added during v1.4.0 release, but causing one-liner
install failed unexpectedly.
- Add filterEmptyStrings utility function for consistent string filtering
- Replace inline slices.DeleteFunc calls with filterEmptyStrings
- Apply filtering to osArgs in addition to command args
- Improves code readability and reduces duplication
- Uses slices.DeleteFunc internally for efficient filtering
- Split handleRecovery into focused helper methods for better maintainability:
  * shouldStartRecovery: handles recovery cancellation logic
  * createRecoveryContext: manages recovery context and cleanup
  * prepareForRecovery: removes DNS settings and initializes OS resolver
  * completeRecovery: resets upstream state and reapplies DNS settings
  * reinitializeOSResolver: reinitializes OS resolver with proper logging
  * Update handleRecovery documentation to reflect new orchestration role

- Improve tests:
  * Add newTestProg helper to reduce test setup duplication
  * Write comprehensive table-driven tests for all recovery methods

This refactoring improves code maintainability, testability, and reduces
complexity while maintaining the same recovery behavior. Each method now
has a single responsibility and can be tested independently.
- Add explicit foundDefaultRoute boolean variable to track default route discovery
- Initialize foundDefaultRoute to false and set to true only in success case
- Replace tautological condition `err == nil` with meaningful `foundDefaultRoute` check
- Fixes "tautological condition: nil == nil" linter error

The error occurred because err was being reused from net.Interfaces() call,
making the condition always true. Now we explicitly track whether a default
route was successfully found.
Replace github.com/rs/zerolog with go.uber.org/zap throughout the codebase
to improve performance and provide better structured logging capabilities.

Key changes:
- Replace zerolog imports with zap and zapcore
- Implement custom Logger wrapper in log.go to maintain zerolog-like API
- Add LogEvent struct with chained methods (Str, Int, Err, Bool, etc.)
- Update all logging calls to use the new zap-based wrapper
- Replace JSON encoders with Console encoders for better readability

Benefits:
- Better performance with zap's optimized logging
- Consistent structured logging across all components
- Maintained zerolog-like API for easy migration
- Proper field context preservation for debugging
- Multi-core logging architecture for better output control

All tests pass and build succeeds.
- Add condition to skip port 53 attempts when using zero IP address
- Improve error logging by using structured error field instead of string formatting
- Remove redundant error information from log message format

The changes prevent unnecessary port 53 binding attempts when using zero IP
addresses and improve log readability by using zap's structured error fields.
- Add NoticeLevel constant using zapcore.WarnLevel value (1)
- Implement custom level encoders (noticeLevelEncoder, noticeColorLevelEncoder)
- Update Notice() method to use custom level
- Add "notice" case to log level parsing in main.go
- Update encoder configurations to handle NOTICE level properly
- Add comprehensive test (TestNoticeLevel) to verify behavior

The NOTICE level provides visual distinction from INFO and ERROR levels,
with cyan color in development and proper level filtering. When log level
is set to NOTICE, it shows NOTICE and above (WARN, ERROR) while filtering
out DEBUG and INFO messages.

Note: NOTICE and WARN share the same numeric value (1) due to zap's
integer-based level system, so both display as "NOTICE" in logs for
visual consistency.

Usage:
- logger.Notice().Msg("message")
- log_level = "notice" in config
- Supports structured logging with fields
Add CommandRunner interface and ServiceManager types to support
dependency injection and better separation of concerns in command handling.
Create separate file for log command handling to improve code organization.
Add LogCommand struct with SendLogs and ViewLogs methods to handle
log-related operations with proper error handling and dependency injection.
Create separate file for service command handling to improve code organization.
Add ServiceCommand struct with Install, Uninstall, Start, Stop, and Status
methods to handle service operations with proper error handling and dependency
injection.
cuonglm added 14 commits October 9, 2025 19:12
Documents the self-upgrade issue on macOS Darwin 15.5 affecting
ctrld v1.4.2+ and provides workarounds for affected users.
… documentation

- Add newLogReader function with optional ANSI color code stripping
- Implement logReaderNoColor() and logReaderRaw() methods for different use cases
- Add comprehensive documentation for logReader struct and all related methods
- Add extensive test coverage with 16+ test cases covering edge cases

The new functionality allows consumers to choose between raw log data
(with ANSI color codes) or stripped content (without color codes),
making logs more suitable for different processing pipelines and
display environments.
Replace the legacy Unix socket log communication between `ctrld start` and
`ctrld run` with a modern HTTP-based system for better reliability and
maintainability.

Benefits:
- More reliable communication protocol using standard HTTP
- Better error handling and connection management
- Cleaner separation of concerns with dedicated endpoints
- Easier to test and debug with HTTP-based communication
- More maintainable code with proper abstraction layers

This change maintains backward compatibility while providing a more robust
foundation for inter-process communication between ctrld commands.
While at it, also updating CI flow to reflect new path.
Make RFC1918 listener spawning opt-in via --rfc1918 flag instead of automatic behavior.
This allows users to explicitly control when ctrld listens on private network addresses
to receive DNS queries from LAN clients, improving security and configurability.

Refactor network interface detection to better distinguish between physical and virtual
interfaces, ensuring only real hardware interfaces are used for RFC1918 address binding.
Move platform-specific network interface detection from cmd/cli/ to root package
as ValidInterfaces function. This eliminates code duplication and provides a
consistent interface for determining valid physical network interfaces across
all platforms.

- Remove duplicate validInterfacesMap functions from platform-specific files
- Add context parameter to virtualInterfaces for proper logging
- Update all callers to use ctrld.ValidInterfaces instead of local functions
- Improve error handling in virtual interface detection on Linux
Extract DNS policy rule matching logic from dns_proxy.go into a dedicated
internal/rulematcher package to improve code organization and maintainability.

The new package provides:
- RuleMatcher interface for extensible rule matching
- NetworkRuleMatcher for IP-based network rules
- MacRuleMatcher for MAC address-based rules
- DomainRuleMatcher for domain/wildcard rules
- Comprehensive unit tests for all matchers

This refactoring improves:
- Separation of concerns between DNS proxy and rule matching
- Testability with isolated rule matcher components
- Reusability of rule matching logic across the codebase
- Maintainability with focused, single-responsibility modules
Implement MatchingEngine in internal/rulematcher package to enable
configurable DNS policy rule evaluation order and behavior.

New components:
- MatchingConfig: Configuration for rule order and stop behavior
- MatchingEngine: Orchestrates rule matching with configurable order
- MatchingResult: Standardized result structure
- DefaultMatchingConfig(): Maintains backward compatibility

Key features:
- Configurable rule evaluation order (e.g., domain-first, MAC-first)
- StopOnFirstMatch configuration option
- Graceful handling of invalid rule types
- Comprehensive test coverage for all scenarios

The engine supports custom matching strategies while preserving
the default Networks → Macs → Domains order for backward compatibility.
This enables future configuration-driven rule matching without
breaking existing functionality.
Implement configurable DNS policy rule matching order and refactor
upstreamFor method for better maintainability.

New features:
- Add MatchingConfig to ListenerPolicyConfig for rule order configuration
- Support custom rule evaluation order (network, mac, domain)
- Add stop_on_first_match configuration option
- Hidden from config files (mapstructure:"-" toml:"-") for future release

Code improvements:
- Create upstreamForRequest struct to reduce method parameter count
- Refactor upstreamForWithConfig to use single struct parameter
- Improve code readability and maintainability
- Maintain full backward compatibility

Technical details:
- String-based configuration converted to RuleType enum internally
- Default behavior preserved (network → mac → domain order)
- Domain rules still override MAC/network rules regardless of order
- Comprehensive test coverage for configuration integration

The matching configuration is programmatically accessible but hidden
from user configuration files until ready for public release.
Remove StopOnFirstMatch field that was defined but never used in the
actual matching logic.

The current implementation always evaluates all rule types and applies
a fixed precedence (Domain > MAC > Network), making the StopOnFirstMatch
field unnecessary.

Changes:
- Remove StopOnFirstMatch from MatchingConfig structs
- Update DefaultMatchingConfig() function
- Update all test cases and references
- Simplify configuration to only include Order field

This cleanup removes dead code and simplifies the configuration API
without changing any functional behavior.
- Add detailed package documentation to engine.go explaining the rule matching
  system, supported rule types (Network, MAC, Domain), and priority ordering
- Include usage example demonstrating typical API usage patterns
- Remove unused Type() method from RuleMatcher interface and implementations
- Maintain backward compatibility while improving code documentation

The documentation explains the policy-based DNS routing system and how different
rule types interact with configurable priority ordering.
@cuonglm cuonglm force-pushed the release-branch-v2.0.0 branch from 05eb648 to f38c9ae Compare October 9, 2025 13:23
Discover while testing v2.0.0 Github MR.

See: golang/go#62614

While at it, also fix staticcheck linter on Windows.
@cuonglm cuonglm force-pushed the release-branch-v2.0.0 branch from f38c9ae to 90eddb8 Compare October 9, 2025 13:51
@cuonglm cuonglm force-pushed the release-branch-v2.0.0 branch from c9b186a to 1c74fc4 Compare November 12, 2025 08:16
While at it, also bump go version to 1.24
@cuonglm cuonglm force-pushed the release-branch-v2.0.0 branch from 1c74fc4 to f9d0263 Compare November 12, 2025 08:21
@cuonglm cuonglm changed the title [WIP] Release branch v2.0.0 Release branch v2.0.0 Nov 12, 2025
- Add comprehensive documentation for ctrld v2.0.0 breaking changes
- Document removal of automatic configuration for router/server platforms
- Provide step-by-step migration guide for affected users
- Include detailed dnsmasq and Windows Server configuration examples
- Update README.md to reflect v2.0.0 installer URLs and Go version requirements
- Remove references to automatic dnsmasq upstream configuration in README
@cuonglm cuonglm force-pushed the release-branch-v2.0.0 branch 3 times, most recently from 9547006 to 0d4b697 Compare December 17, 2025 08:26
Since WillAbides/setup-go-faster failed with macOS-latest.

See: WillAbides/setup-go-faster#37
@cuonglm cuonglm force-pushed the release-branch-v2.0.0 branch from 0d4b697 to d0e66b8 Compare December 17, 2025 08:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants