From e016eaf41009a4898fecf24a68d146af5874a795 Mon Sep 17 00:00:00 2001 From: Aaron Klotz Date: Tue, 28 Sep 2021 16:33:08 -0600 Subject: [PATCH] cmd/tailscaled: conditionally flush Windows DNS cache on SessionChange For the service, all we need to do is handle the `svc.SessionChange` command. Upon receipt of a `windows.WTS_SESSION_UNLOCK` event, we fire off a goroutine to flush the DNS cache. (Windows expects responses to service requests to be quick, so we don't want to do that synchronously.) This is gated on an integral registry value named `FlushDNSOnSessionUnlock`, whose value we obtain during service initialization. (See [this link](https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nc-winsvc-lphandler_function_ex) for information re: handling `SERVICE_CONTROL_SESSIONCHANGE`.) Fixes #2956 Signed-off-by: Aaron Klotz --- cmd/tailscaled/tailscaled_windows.go | 22 +++++++++++++++++++ .../tailscaled_deps_test_windows.go | 1 + 2 files changed, 23 insertions(+) diff --git a/cmd/tailscaled/tailscaled_windows.go b/cmd/tailscaled/tailscaled_windows.go index 447ee67f3..5b94eb6c7 100644 --- a/cmd/tailscaled/tailscaled_windows.go +++ b/cmd/tailscaled/tailscaled_windows.go @@ -34,6 +34,7 @@ import ( "tailscale.com/net/dns" "tailscale.com/net/tstun" "tailscale.com/types/logger" + "tailscale.com/util/winutil" "tailscale.com/version" "tailscale.com/wf" "tailscale.com/wgengine" @@ -43,6 +44,8 @@ import ( const serviceName = "Tailscale" +var flushDNSOnSessionUnlock bool + func isWindowsService() bool { v, err := svc.IsWindowsService() if err != nil { @@ -63,6 +66,7 @@ type ipnService struct { func (service *ipnService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) { changes <- svc.Status{State: svc.StartPending} + flushDNSOnSessionUnlock = winutil.GetRegInteger("FlushDNSOnSessionUnlock", 0) != 0 ctx, cancel := context.WithCancel(context.Background()) doneCh := make(chan struct{}) go func() { @@ -82,6 +86,9 @@ func (service *ipnService) Execute(args []string, r <-chan svc.ChangeRequest, ch cancel() case svc.Interrogate: changes <- cmd.CurrentStatus + case svc.SessionChange: + handleSessionChange(cmd) + changes <- cmd.CurrentStatus } } } @@ -264,6 +271,21 @@ func startIPNServer(ctx context.Context, logid string) error { return err } +func handleSessionChange(chgRequest svc.ChangeRequest) { + if chgRequest.Cmd != svc.SessionChange || chgRequest.EventType != windows.WTS_SESSION_UNLOCK || + !flushDNSOnSessionUnlock { + return + } + + log.Printf("Received WTS_SESSION_UNLOCK event, initiating DNS flush.") + go func() { + err := dns.Flush() + if err != nil { + log.Printf("Error flushing DNS on session unlock: %v", err) + } + }() +} + var ( kernel32 = windows.NewLazySystemDLL("kernel32.dll") getTickCount64Proc = kernel32.NewProc("GetTickCount64") diff --git a/tstest/integration/tailscaled_deps_test_windows.go b/tstest/integration/tailscaled_deps_test_windows.go index 8dd2c4bdc..a1b3dd41f 100644 --- a/tstest/integration/tailscaled_deps_test_windows.go +++ b/tstest/integration/tailscaled_deps_test_windows.go @@ -57,6 +57,7 @@ import ( _ "tailscale.com/types/key" _ "tailscale.com/types/logger" _ "tailscale.com/util/osshare" + _ "tailscale.com/util/winutil" _ "tailscale.com/version" _ "tailscale.com/version/distro" _ "tailscale.com/wf"