diff --git a/go.mod b/go.mod index 4b8157e40..72302adae 100644 --- a/go.mod +++ b/go.mod @@ -23,15 +23,15 @@ require ( github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 github.com/peterbourgon/ff/v2 v2.0.0 github.com/tailscale/depaware v0.0.0-20201003033024-5d95aab075be - github.com/tailscale/wireguard-go v0.0.0-20201204174856-4a319d8d5bfb + github.com/tailscale/wireguard-go v0.0.0-20201210001956-32a957fb6709 github.com/tcnksm/go-httpstat v0.2.0 github.com/toqueteos/webbrowser v1.2.0 go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 - golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 + golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 - golang.org/x/sys v0.0.0-20201202213521-69691e467435 + golang.org/x/sys v0.0.0-20201210223839-7e3030f88018 golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b golang.org/x/time v0.0.0-20191024005414-555d28b269f0 golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d diff --git a/go.sum b/go.sum index d0ffb3bba..f3260a82d 100644 --- a/go.sum +++ b/go.sum @@ -116,6 +116,9 @@ github.com/tailscale/wireguard-go v0.0.0-20201021041318-a6168fd06b3f h1:KMx58dbn github.com/tailscale/wireguard-go v0.0.0-20201021041318-a6168fd06b3f/go.mod h1:WXq+IkSOJGIgfF1XW+4z4oW+LX/TXzU9DcKlT5EZLi4= github.com/tailscale/wireguard-go v0.0.0-20201204174856-4a319d8d5bfb h1:lPrU+j3X85IpVF8saRAAnhER6/RmtpV9mSZFx/rQXJc= github.com/tailscale/wireguard-go v0.0.0-20201204174856-4a319d8d5bfb/go.mod h1:RMjerRBNxOIkYHMFnxMWLFYngXD6zexR/v5Dn4a5Zcc= +github.com/tailscale/wireguard-go v0.0.0-20201210001956-32a957fb6709 h1:cxiYxd+Kb+LuXBpv6rp2CpWGhhcVB5b07B6h+kA7LP4= +github.com/tailscale/wireguard-go v0.0.0-20201210001956-32a957fb6709/go.mod h1:9PbAnF5CAklkURoO0uQhm+YUjDmm9T9oCyTGlCHuTPQ= +github.com/tailscale/wireguard-go v0.0.20201118 h1:gtjCCz7el67M8M3BmOLlsaTBnWqVJ3uH7Z8+uO1fjeI= github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= @@ -141,6 +144,8 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc= +golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= @@ -182,6 +187,7 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -196,6 +202,9 @@ golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435 h1:25AvDqqB9PrNqj1FLf2/70I4W0L19qqoaFq3gjNwbKk= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201210223839-7e3030f88018 h1:XKi8B/gRBuTZN1vU9gFsLMm6zVz5FSCDzm8JYACnjy8= +golang.org/x/sys v0.0.0-20201210223839-7e3030f88018/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b h1:a0ErnNnPKmhDyIXQvdZr+Lq8dc8xpMeqkF8y5PgQU4Q= golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/wgengine/router/dns/manager_windows.go b/wgengine/router/dns/manager_windows.go index 685a471d4..ed5fdc620 100644 --- a/wgengine/router/dns/manager_windows.go +++ b/wgengine/router/dns/manager_windows.go @@ -7,12 +7,13 @@ import ( "fmt" "os/exec" + "runtime" "strings" "syscall" "time" "github.com/tailscale/wireguard-go/tun" - wgregistry "github.com/tailscale/wireguard-go/tun/wintun/registry" + "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" "tailscale.com/types/logger" ) @@ -35,14 +36,76 @@ func newManager(mconfig ManagerConfig) managerImpl { } } -// keyOpenTimeout is how long we wait for a registry key to -// appear. For some reason, registry keys tied to ephemeral interfaces -// can take a long while to appear after interface creation, and we -// can end up racing with that. -const keyOpenTimeout = time.Minute +const ( + // keyOpenTimeout is how long we wait for a registry key to + // appear. For some reason, registry keys tied to ephemeral interfaces + // can take a long while to appear after interface creation, and we + // can end up racing with that. + keyOpenTimeout = time.Minute + + // REG_NOTIFY_CHANGE_NAME notifies the caller if a subkey is added or deleted. + REG_NOTIFY_CHANGE_NAME uint32 = 0x00000001 +) + +func openKeyWait(k registry.Key, path string, access uint32, timeout time.Duration) (registry.Key, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + deadline := time.Now().Add(timeout) + pathSpl := strings.Split(path, "\\") + for i := 0; ; i++ { + keyName := pathSpl[i] + isLast := i+1 == len(pathSpl) + + event, err := windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return 0, fmt.Errorf("windows.CreateEvent: %v", err) + } + defer windows.CloseHandle(event) + + var key registry.Key + for { + err = windows.RegNotifyChangeKeyValue(windows.Handle(k), false, REG_NOTIFY_CHANGE_NAME, event, true) + if err != nil { + return 0, fmt.Errorf("windows.RegNotifyChangeKeyValue: %v", err) + } + + var accessFlags uint32 + if isLast { + accessFlags = access + } else { + accessFlags = registry.NOTIFY + } + key, err = registry.OpenKey(k, keyName, accessFlags) + if err == windows.ERROR_FILE_NOT_FOUND || err == windows.ERROR_PATH_NOT_FOUND { + timeout := time.Until(deadline) / time.Millisecond + if timeout < 0 { + timeout = 0 + } + s, err := windows.WaitForSingleObject(event, uint32(timeout)) + if err != nil { + return 0, fmt.Errorf("windows.WaitForSingleObject: %v", err) + } + if s == uint32(windows.WAIT_TIMEOUT) { // windows.WAIT_TIMEOUT status const is misclassified as error in golang.org/x/sys/windows + return 0, fmt.Errorf("timeout waiting for registry key") + } + } else if err != nil { + return 0, fmt.Errorf("registry.OpenKey(%v): %v", path, err) + } else { + if isLast { + return key, nil + } + defer key.Close() + break + } + } + + k = key + } +} func setRegistryString(path, name, value string) error { - key, err := wgregistry.OpenKeyWait(registry.LOCAL_MACHINE, path, registry.SET_VALUE, keyOpenTimeout) + key, err := openKeyWait(registry.LOCAL_MACHINE, path, registry.SET_VALUE, keyOpenTimeout) if err != nil { return fmt.Errorf("opening %s: %w", path, err) }