diff --git a/cmd/cli/cli.go b/cmd/cli/cli.go index 6ac3123..42e20b6 100644 --- a/cmd/cli/cli.go +++ b/cmd/cli/cli.go @@ -48,6 +48,7 @@ import ( // selfCheckInternalTestDomain is used for testing ctrld self response to clients. const selfCheckInternalTestDomain = "ctrld" + loopTestDomain +const windowsForwardersFilename = ".forwarders.txt" var ( version = "dev" @@ -610,8 +611,22 @@ NOTE: Uninstalling will set DNS to values provided by DHCP.`, files = append(files, filepath.Join(dir, ctrldControlUnixSock)) files = append(files, filepath.Join(dir, ctrldLogUnixSock)) } + // Static DNS settings files. + withEachPhysicalInterfaces("", "", func(i *net.Interface) error { + file := savedStaticDnsSettingsFilePath(i) + if _, err := os.Stat(file); err == nil { + files = append(files, file) + } + return nil + }) + // Windows forwarders file. + if windowsHasLocalDnsServerRunning() { + files = append(files, absHomeDir(windowsForwardersFilename)) + } // Binary itself. - if bin, _ := os.Executable(); bin != "" { + + bin, _ := os.Executable() + if bin != "" && supportedSelfDelete { files = append(files, bin) } for _, file := range files { @@ -627,6 +642,13 @@ NOTE: Uninstalling will set DNS to values provided by DHCP.`, mainLog.Load().Debug().Msgf("file removed: %s", file) } } + if err := selfDeleteExe(); err != nil { + mainLog.Load().Warn().Err(err).Msg("failed to remove file") + } else { + if !supportedSelfDelete { + mainLog.Load().Debug().Msgf("file removed: %s", bin) + } + } } }, } diff --git a/cmd/cli/os_windows.go b/cmd/cli/os_windows.go index 6441e05..e5ac1d2 100644 --- a/cmd/cli/os_windows.go +++ b/cmd/cli/os_windows.go @@ -16,7 +16,6 @@ import ( ) const ( - forwardersFilename = ".forwarders.txt" v4InterfaceKeyPathFormat = `HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\` v6InterfaceKeyPathFormat = `HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\` ) @@ -40,7 +39,7 @@ func setDNS(iface *net.Interface, nameservers []string) error { // If there's a Dns server running, that means we are on AD with Dns feature enabled. // Configuring the Dns server to forward queries to ctrld instead. if windowsHasLocalDnsServerRunning() { - file := absHomeDir(forwardersFilename) + file := absHomeDir(windowsForwardersFilename) oldForwardersContent, _ := os.ReadFile(file) if err := os.WriteFile(file, []byte(strings.Join(nameservers, ",")), 0600); err != nil { mainLog.Load().Warn().Err(err).Msg("could not save forwarders settings") @@ -72,7 +71,7 @@ func resetDNS(iface *net.Interface) error { resetDNSOnce.Do(func() { // See corresponding comment in setDNS. if windowsHasLocalDnsServerRunning() { - file := absHomeDir(forwardersFilename) + file := absHomeDir(windowsForwardersFilename) content, err := os.ReadFile(file) if err != nil { mainLog.Load().Error().Err(err).Msg("could not read forwarders settings") diff --git a/cmd/cli/prog.go b/cmd/cli/prog.go index 34cdc45..c745d79 100644 --- a/cmd/cli/prog.go +++ b/cmd/cli/prog.go @@ -797,7 +797,9 @@ func withEachPhysicalInterfaces(excludeIfaceName, context string, f func(i *net. } // TODO: investigate whether we should report this error? if err := f(netIface); err == nil { - mainLog.Load().Debug().Msgf("%s for interface %q successfully", context, i.Name) + if context != "" { + mainLog.Load().Debug().Msgf("%s for interface %q successfully", context, i.Name) + } } else if !errors.Is(err, errSaveCurrentStaticDNSNotSupported) { mainLog.Load().Err(err).Msgf("%s for interface %q failed", context, i.Name) } diff --git a/cmd/cli/self_delete_others.go b/cmd/cli/self_delete_others.go new file mode 100644 index 0000000..02ae977 --- /dev/null +++ b/cmd/cli/self_delete_others.go @@ -0,0 +1,7 @@ +//go:build !windows + +package cli + +var supportedSelfDelete = true + +func selfDeleteExe() error { return nil } diff --git a/cmd/cli/self_delete_windows.go b/cmd/cli/self_delete_windows.go new file mode 100644 index 0000000..c2f2719 --- /dev/null +++ b/cmd/cli/self_delete_windows.go @@ -0,0 +1,134 @@ +// Copied from https://github.com/secur30nly/go-self-delete +// with modification to suitable for ctrld usage. + +/* + License: MIT Licence + + References: + - https://github.com/LloydLabs/delete-self-poc + - https://twitter.com/jonasLyk/status/1350401461985955840 +*/ + +package cli + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +var supportedSelfDelete = false + +type FILE_RENAME_INFO struct { + Union struct { + ReplaceIfExists bool + Flags uint32 + } + RootDirectory windows.Handle + FileNameLength uint32 + FileName [1]uint16 +} + +type FILE_DISPOSITION_INFO struct { + DeleteFile bool +} + +func dsOpenHandle(pwPath *uint16) (windows.Handle, error) { + handle, err := windows.CreateFile( + pwPath, + windows.DELETE, + 0, + nil, + windows.OPEN_EXISTING, + windows.FILE_ATTRIBUTE_NORMAL, + 0, + ) + + if err != nil { + return 0, err + } + + return handle, nil +} + +func dsRenameHandle(hHandle windows.Handle) error { + var fRename FILE_RENAME_INFO + DS_STREAM_RENAME, err := windows.UTF16FromString(":deadbeef") + + if err != nil { + return err + } + + lpwStream := &DS_STREAM_RENAME[0] + fRename.FileNameLength = uint32(unsafe.Sizeof(lpwStream)) + + windows.NewLazyDLL("kernel32.dll").NewProc("RtlCopyMemory").Call( + uintptr(unsafe.Pointer(&fRename.FileName[0])), + uintptr(unsafe.Pointer(lpwStream)), + unsafe.Sizeof(lpwStream), + ) + + err = windows.SetFileInformationByHandle( + hHandle, + windows.FileRenameInfo, + (*byte)(unsafe.Pointer(&fRename)), + uint32(unsafe.Sizeof(fRename)+unsafe.Sizeof(lpwStream)), + ) + + if err != nil { + return err + } + + return nil +} + +func dsDepositeHandle(hHandle windows.Handle) error { + var fDelete FILE_DISPOSITION_INFO + fDelete.DeleteFile = true + + err := windows.SetFileInformationByHandle( + hHandle, + windows.FileDispositionInfo, + (*byte)(unsafe.Pointer(&fDelete)), + uint32(unsafe.Sizeof(fDelete)), + ) + + if err != nil { + return err + } + + return nil +} + +func selfDeleteExe() error { + var wcPath [windows.MAX_PATH + 1]uint16 + var hCurrent windows.Handle + + _, err := windows.GetModuleFileName(0, &wcPath[0], windows.MAX_PATH) + if err != nil { + return err + } + + hCurrent, err = dsOpenHandle(&wcPath[0]) + if err != nil || hCurrent == windows.InvalidHandle { + return err + } + + if err := dsRenameHandle(hCurrent); err != nil { + _ = windows.CloseHandle(hCurrent) + return err + } + _ = windows.CloseHandle(hCurrent) + + hCurrent, err = dsOpenHandle(&wcPath[0]) + if err != nil || hCurrent == windows.InvalidHandle { + return err + } + + if err := dsDepositeHandle(hCurrent); err != nil { + _ = windows.CloseHandle(hCurrent) + return err + } + + return windows.CloseHandle(hCurrent) +}