mirror of
https://github.com/fosrl/olm.git
synced 2026-05-12 19:29:55 +00:00
enhance(dns): expose stale cleanup functionality
When the tunnel is forced close an integration may want to manually call cleanup function to fix stale issues without having the knowledge of which configuration to cleanup
This commit is contained in:
@@ -13,4 +13,9 @@ func SetupDNSOverride(interfaceName string, proxyIp netip.Addr) error {
|
||||
// RestoreDNSOverride is a no-op on Android
|
||||
func RestoreDNSOverride() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupStaleState is a no-op on Android as DNS configuration is handled by the VpnService API
|
||||
func CleanupStaleState() error {
|
||||
return nil
|
||||
}
|
||||
@@ -61,3 +61,19 @@ func RestoreDNSOverride() error {
|
||||
logger.Info("DNS configuration restored successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupStaleState removes any stale DNS configuration left over from a previous
|
||||
// unclean shutdown (e.g., system crash, power loss while tunnel was active).
|
||||
// This function should be called early during startup, before any network operations,
|
||||
// to ensure DNS is working properly.
|
||||
//
|
||||
// On macOS, this cleans up any scutil DNS keys that were created but not removed.
|
||||
func CleanupStaleState() error {
|
||||
if err := platform.CleanupStaleDarwinDNS(); err != nil {
|
||||
logger.Warn("Failed to cleanup stale Darwin DNS config: %v", err)
|
||||
return fmt.Errorf("Darwin DNS cleanup: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("Stale DNS state cleanup completed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,4 +12,9 @@ func SetupDNSOverride(interfaceName string, proxyIp netip.Addr) error {
|
||||
// RestoreDNSOverride is a no-op on iOS as DNS configuration is handled by the system
|
||||
func RestoreDNSOverride() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupStaleState is a no-op on iOS as DNS configuration is handled by the system
|
||||
func CleanupStaleState() error {
|
||||
return nil
|
||||
}
|
||||
@@ -98,3 +98,49 @@ func RestoreDNSOverride() error {
|
||||
logger.Info("DNS configuration restored successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupStaleState removes any stale DNS configuration left over from a previous
|
||||
// unclean shutdown (e.g., system crash, power loss while tunnel was active).
|
||||
// This function should be called early during startup, before any network operations,
|
||||
// to ensure DNS is working properly.
|
||||
//
|
||||
// It checks and cleans up stale state from all supported DNS managers:
|
||||
// - NetworkManager: removes /etc/NetworkManager/conf.d/olm-dns.conf
|
||||
// - resolvconf: removes entry for the "olm" interface
|
||||
// - File-based: restores /etc/resolv.conf from backup if it exists
|
||||
//
|
||||
// This is safe to call even if no stale state exists.
|
||||
func CleanupStaleState() error {
|
||||
var errs []error
|
||||
|
||||
// Clean up NetworkManager stale config
|
||||
if err := platform.CleanupStaleNetworkManagerDNS(); err != nil {
|
||||
logger.Warn("Failed to cleanup stale NetworkManager DNS config: %v", err)
|
||||
errs = append(errs, fmt.Errorf("NetworkManager cleanup: %w", err))
|
||||
} else {
|
||||
logger.Debug("NetworkManager DNS cleanup completed")
|
||||
}
|
||||
|
||||
// Clean up resolvconf stale entries (use default interface name "olm")
|
||||
if err := platform.CleanupStaleResolvconfDNS("olm"); err != nil {
|
||||
logger.Warn("Failed to cleanup stale resolvconf DNS config: %v", err)
|
||||
errs = append(errs, fmt.Errorf("resolvconf cleanup: %w", err))
|
||||
} else {
|
||||
logger.Debug("resolvconf DNS cleanup completed")
|
||||
}
|
||||
|
||||
// Clean up file-based stale backup
|
||||
if err := platform.CleanupStaleFileDNS(); err != nil {
|
||||
logger.Warn("Failed to cleanup stale file-based DNS config: %v", err)
|
||||
errs = append(errs, fmt.Errorf("file DNS cleanup: %w", err))
|
||||
} else {
|
||||
logger.Debug("File-based DNS cleanup completed")
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("some DNS cleanup operations failed: %v", errs)
|
||||
}
|
||||
|
||||
logger.Info("Stale DNS state cleanup completed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -61,3 +61,18 @@ func RestoreDNSOverride() error {
|
||||
logger.Info("DNS configuration restored successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupStaleState removes any stale DNS configuration left over from a previous
|
||||
// unclean shutdown (e.g., system crash, power loss while tunnel was active).
|
||||
// This function should be called early during startup, before any network operations,
|
||||
// to ensure DNS is working properly.
|
||||
//
|
||||
// On Windows, DNS configuration is tied to the interface GUID. When the WireGuard
|
||||
// interface is recreated, it gets a new GUID, so there's no stale state to clean up.
|
||||
func CleanupStaleState() error {
|
||||
// Windows DNS configuration via registry is interface-specific.
|
||||
// When the WireGuard interface is recreated, it gets a new GUID,
|
||||
// so there's no leftover state to clean up from previous sessions.
|
||||
logger.Debug("Windows DNS cleanup: no stale state to clean (interface-specific)")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -417,3 +417,59 @@ func (d *DarwinDNSConfigurator) clearState() error {
|
||||
logger.Debug("Cleared DNS state file")
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupStaleDarwinDNS removes any stale DNS configuration left by the Darwin
|
||||
// configurator from a previous unclean shutdown. This is a static function that can be
|
||||
// called without creating a configurator instance, useful for cleanup before network operations.
|
||||
func CleanupStaleDarwinDNS() error {
|
||||
stateFilePath := getDNSStateFilePath()
|
||||
|
||||
// Check if state file exists
|
||||
data, err := os.ReadFile(stateFilePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// No state file, nothing to clean up
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("read state file: %w", err)
|
||||
}
|
||||
|
||||
var state DNSPersistentState
|
||||
if err := json.Unmarshal(data, &state); err != nil {
|
||||
// Invalid state file, remove it
|
||||
os.Remove(stateFilePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(state.CreatedKeys) == 0 {
|
||||
// No keys to clean up
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Info("Found DNS state from previous session, cleaning up %d keys", len(state.CreatedKeys))
|
||||
|
||||
// Remove all keys from previous session using scutil directly
|
||||
for _, key := range state.CreatedKeys {
|
||||
logger.Debug("Removing leftover DNS key: %s", key)
|
||||
cmd := fmt.Sprintf("open\nremove %s\nquit\n", key)
|
||||
scutilCmd := exec.Command(scutilPath)
|
||||
scutilCmd.Stdin = strings.NewReader(cmd)
|
||||
if err := scutilCmd.Run(); err != nil {
|
||||
logger.Warn("Failed to remove DNS key %s: %v", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear state file
|
||||
if err := os.Remove(stateFilePath); err != nil && !os.IsNotExist(err) {
|
||||
logger.Warn("Failed to clear DNS state file: %v", err)
|
||||
}
|
||||
|
||||
// Flush DNS cache after cleanup
|
||||
cacheCmd := exec.Command(dscacheutilPath, "-flushcache")
|
||||
_ = cacheCmd.Run()
|
||||
|
||||
killCmd := exec.Command("killall", "-HUP", "mDNSResponder")
|
||||
_ = killCmd.Run()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -218,3 +218,27 @@ func copyFile(src, dst string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupStaleFileDNS removes any stale DNS configuration left by the file-based
|
||||
// configurator from a previous unclean shutdown. This is a static function that can be
|
||||
// called without creating a configurator instance, useful for cleanup before network operations.
|
||||
func CleanupStaleFileDNS() error {
|
||||
// Check if backup file exists from a previous session
|
||||
if _, err := os.Stat(resolvConfBackupPath); os.IsNotExist(err) {
|
||||
// No backup file, nothing to clean up
|
||||
return nil
|
||||
}
|
||||
|
||||
// A backup exists, which means we crashed while DNS was configured
|
||||
// Restore the original resolv.conf
|
||||
if err := copyFile(resolvConfBackupPath, resolvConfPath); err != nil {
|
||||
return fmt.Errorf("restore from backup during cleanup: %w", err)
|
||||
}
|
||||
|
||||
// Remove backup file
|
||||
if err := os.Remove(resolvConfBackupPath); err != nil {
|
||||
return fmt.Errorf("remove backup file during cleanup: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -323,3 +323,41 @@ func GetNetworkManagerVersion() (string, error) {
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// CleanupStaleNetworkManagerDNS removes any stale DNS configuration left by NetworkManager
|
||||
// configurator from a previous unclean shutdown. This is a static function that can be called
|
||||
// without creating a configurator instance, useful for cleanup before network operations.
|
||||
func CleanupStaleNetworkManagerDNS() error {
|
||||
confPath := networkManagerConfDir + "/" + networkManagerDNSConfFile
|
||||
|
||||
// Check if our config file exists from a previous session
|
||||
if _, err := os.Stat(confPath); os.IsNotExist(err) {
|
||||
// No config file, nothing to clean up
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove the stale configuration file
|
||||
if err := os.Remove(confPath); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("remove stale DNS config file: %w", err)
|
||||
}
|
||||
|
||||
// Try to reload NetworkManager if it's available
|
||||
if IsNetworkManagerAvailable() {
|
||||
conn, err := dbus.SystemBus()
|
||||
if err != nil {
|
||||
return fmt.Errorf("connect to system bus for reload: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
obj := conn.Object(networkManagerDest, networkManagerDbusObjectNode)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := obj.CallWithContext(ctx, networkManagerDest+".Reload", 0, uint32(0)).Store(); err != nil {
|
||||
return fmt.Errorf("reload NetworkManager after cleanup: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -219,3 +219,37 @@ func IsResolvconfAvailable() bool {
|
||||
cmd := exec.Command(resolvconfCommand, "--version")
|
||||
return cmd.Run() == nil
|
||||
}
|
||||
|
||||
// CleanupStaleResolvconfDNS removes any stale DNS configuration left by the resolvconf
|
||||
// configurator from a previous unclean shutdown. This is a static function that can be
|
||||
// called without creating a configurator instance, useful for cleanup before network operations.
|
||||
// The interfaceName parameter specifies which interface entry to clean up (typically "olm").
|
||||
func CleanupStaleResolvconfDNS(interfaceName string) error {
|
||||
if !IsResolvconfAvailable() {
|
||||
// resolvconf not available, nothing to clean up
|
||||
return nil
|
||||
}
|
||||
|
||||
// Detect resolvconf implementation type
|
||||
implType, err := detectResolvconfType()
|
||||
if err != nil {
|
||||
// Can't detect type, try default
|
||||
implType = "resolvconf"
|
||||
}
|
||||
|
||||
// Try to delete any existing entry for this interface
|
||||
// This is idempotent - if no entry exists, resolvconf will just return success
|
||||
var cmd *exec.Cmd
|
||||
|
||||
switch implType {
|
||||
case "openresolv":
|
||||
cmd = exec.Command(resolvconfCommand, "-f", "-d", interfaceName)
|
||||
default:
|
||||
cmd = exec.Command(resolvconfCommand, "-d", interfaceName)
|
||||
}
|
||||
|
||||
// Ignore errors - the entry may not exist, which is fine
|
||||
_ = cmd.Run()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user