2023-01-27 13:37:20 -08:00
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
2022-05-25 15:51:54 -06:00
package dns
import (
2022-08-19 10:19:50 -07:00
"bytes"
2022-06-29 15:02:23 -06:00
"context"
2022-06-01 14:41:11 -06:00
"fmt"
2022-05-25 15:51:54 -06:00
"math/rand"
2022-07-25 20:55:44 -07:00
"net/netip"
2022-05-25 15:51:54 -06:00
"strings"
"testing"
"time"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
2025-01-13 13:47:56 -07:00
"tailscale.com/types/logger"
2022-05-25 15:51:54 -06:00
"tailscale.com/util/dnsname"
"tailscale.com/util/winutil"
2024-06-30 17:04:10 -05:00
"tailscale.com/util/winutil/gp"
2022-05-25 15:51:54 -06:00
)
2022-06-01 14:41:11 -06:00
const testGPRuleID = "{7B1B6151-84E6-41A3-8967-62F7F7B45687}"
2022-08-19 10:19:50 -07:00
func TestHostFileNewLines ( t * testing . T ) {
in := [ ] byte ( "#foo\r\n#bar\n#baz\n" )
2025-01-13 13:47:56 -07:00
want := [ ] byte ( "#foo\r\n#bar\r\n#baz\r\n# TailscaleHostsSectionStart\r\n# This section contains MagicDNS entries for Tailscale.\r\n# Do not edit this section manually.\r\n\r\n192.168.1.1 aaron\r\n\r\n# TailscaleHostsSectionEnd\r\n" )
2022-08-19 10:19:50 -07:00
2025-01-13 13:47:56 -07:00
he := [ ] * HostEntry {
& HostEntry {
Addr : netip . MustParseAddr ( "192.168.1.1" ) ,
Hosts : [ ] string { "aaron" } ,
} ,
}
got , err := setTailscaleHosts ( logger . Discard , in , he )
if err != nil {
t . Fatal ( err )
}
if ! bytes . Equal ( got , want ) {
t . Errorf ( "got %q, want %q\n" , got , want )
}
}
func TestHostFileUnchanged ( t * testing . T ) {
in := [ ] byte ( "#foo\r\n#bar\r\n#baz\r\n# TailscaleHostsSectionStart\r\n# This section contains MagicDNS entries for Tailscale.\r\n# Do not edit this section manually.\r\n\r\n192.168.1.1 aaron\r\n\r\n# TailscaleHostsSectionEnd\r\n" )
he := [ ] * HostEntry {
& HostEntry {
Addr : netip . MustParseAddr ( "192.168.1.1" ) ,
Hosts : [ ] string { "aaron" } ,
} ,
}
got , err := setTailscaleHosts ( logger . Discard , in , he )
if err != nil {
t . Fatal ( err )
}
if got != nil {
t . Errorf ( "got %q, want nil\n" , got )
}
}
func TestHostFileChanged ( t * testing . T ) {
in := [ ] byte ( "#foo\r\n#bar\r\n#baz\r\n# TailscaleHostsSectionStart\r\n# This section contains MagicDNS entries for Tailscale.\r\n# Do not edit this section manually.\r\n\r\n192.168.1.1 aaron1\r\n\r\n# TailscaleHostsSectionEnd\r\n" )
want := [ ] byte ( "#foo\r\n#bar\r\n#baz\r\n# TailscaleHostsSectionStart\r\n# This section contains MagicDNS entries for Tailscale.\r\n# Do not edit this section manually.\r\n\r\n192.168.1.1 aaron1\r\n192.168.1.2 aaron2\r\n\r\n# TailscaleHostsSectionEnd\r\n" )
he := [ ] * HostEntry {
& HostEntry {
Addr : netip . MustParseAddr ( "192.168.1.1" ) ,
Hosts : [ ] string { "aaron1" } ,
} ,
& HostEntry {
Addr : netip . MustParseAddr ( "192.168.1.2" ) ,
Hosts : [ ] string { "aaron2" } ,
} ,
}
got , err := setTailscaleHosts ( logger . Discard , in , he )
2022-08-19 10:19:50 -07:00
if err != nil {
t . Fatal ( err )
}
if ! bytes . Equal ( got , want ) {
t . Errorf ( "got %q, want %q\n" , got , want )
}
}
2022-06-01 14:41:11 -06:00
func TestManagerWindowsLocal ( t * testing . T ) {
if ! isWindows10OrBetter ( ) || ! winutil . IsCurrentProcessElevated ( ) {
t . Skipf ( "test requires running as elevated user on Windows 10+" )
}
runTest ( t , true )
}
func TestManagerWindowsGP ( t * testing . T ) {
if ! isWindows10OrBetter ( ) || ! winutil . IsCurrentProcessElevated ( ) {
t . Skipf ( "test requires running as elevated user on Windows 10+" )
}
checkGPNotificationsWork ( t )
// Make sure group policy is refreshed before this test exits but after we've
// cleaned everything else up.
2024-06-30 17:04:10 -05:00
defer gp . RefreshMachinePolicy ( true )
2022-06-01 14:41:11 -06:00
err := createFakeGPKey ( )
if err != nil {
t . Fatalf ( "Creating fake GP key: %v\n" , err )
2022-05-25 15:51:54 -06:00
}
2022-06-01 14:41:11 -06:00
defer deleteFakeGPKey ( t )
2022-05-25 15:51:54 -06:00
2022-06-01 14:41:11 -06:00
runTest ( t , false )
}
2024-04-10 14:31:40 -06:00
func TestManagerWindowsGPCopy ( t * testing . T ) {
2022-06-29 15:02:23 -06:00
if ! isWindows10OrBetter ( ) || ! winutil . IsCurrentProcessElevated ( ) {
t . Skipf ( "test requires running as elevated user on Windows 10+" )
}
checkGPNotificationsWork ( t )
logf := func ( format string , args ... any ) {
t . Logf ( format , args ... )
}
fakeInterface , err := windows . GenerateGUID ( )
if err != nil {
t . Fatalf ( "windows.GenerateGUID: %v\n" , err )
}
delIfKey , err := createFakeInterfaceKey ( t , fakeInterface )
if err != nil {
t . Fatalf ( "createFakeInterfaceKey: %v\n" , err )
}
defer delIfKey ( )
2024-06-10 22:05:15 -05:00
cfg , err := NewOSConfigurator ( logf , nil , nil , fakeInterface . String ( ) )
2022-06-29 15:02:23 -06:00
if err != nil {
t . Fatalf ( "NewOSConfigurator: %v\n" , err )
}
2022-08-03 16:18:19 -06:00
mgr := cfg . ( * windowsManager )
2022-06-29 15:02:23 -06:00
defer mgr . Close ( )
usingGP := mgr . nrptDB . writeAsGP
if usingGP {
t . Fatalf ( "usingGP %v, want %v\n" , usingGP , false )
}
regWatcher , err := newRegKeyWatcher ( )
if err != nil {
t . Fatalf ( "newRegKeyWatcher error %v\n" , err )
}
// Upon initialization of cfg, we should not have any NRPT rules
ensureNoRules ( t )
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-25 21:14:09 -07:00
resolvers := [ ] netip . Addr { netip . MustParseAddr ( "1.1.1.1" ) }
2022-06-29 15:02:23 -06:00
domains := genRandomSubdomains ( t , 1 )
// 1. Populate local NRPT
err = mgr . setSplitDNS ( resolvers , domains )
if err != nil {
t . Fatalf ( "setSplitDNS: %v\n" , err )
}
t . Logf ( "Validating that local NRPT is populated...\n" )
validateRegistry ( t , nrptBaseLocal , domains )
ensureNoRulesInSubkey ( t , nrptBaseGP )
// 2. Create fake GP key and refresh
t . Logf ( "Creating fake group policy key and refreshing...\n" )
err = createFakeGPKey ( )
if err != nil {
t . Fatalf ( "createFakeGPKey: %v\n" , err )
}
err = regWatcher . watch ( )
if err != nil {
t . Fatalf ( "regWatcher.watch: %v\n" , err )
}
2024-06-30 17:04:10 -05:00
err = gp . RefreshMachinePolicy ( true )
2022-06-29 15:02:23 -06:00
if err != nil {
t . Fatalf ( "testDoRefresh: %v\n" , err )
}
err = regWatcher . wait ( )
if err != nil {
t . Fatalf ( "regWatcher.wait: %v\n" , err )
}
2024-04-10 14:31:40 -06:00
// 3. Check that both local NRPT and GP NRPT are populated
2022-06-29 15:02:23 -06:00
t . Logf ( "Validating that group policy NRPT is populated...\n" )
2024-04-10 14:31:40 -06:00
validateRegistry ( t , nrptBaseLocal , domains )
2022-06-29 15:02:23 -06:00
validateRegistry ( t , nrptBaseGP , domains )
// 4. Delete fake GP key and refresh
t . Logf ( "Deleting fake group policy key and refreshing...\n" )
deleteFakeGPKey ( t )
err = regWatcher . watch ( )
if err != nil {
t . Fatalf ( "regWatcher.watch: %v\n" , err )
}
2024-06-30 17:04:10 -05:00
err = gp . RefreshMachinePolicy ( true )
2022-06-29 15:02:23 -06:00
if err != nil {
t . Fatalf ( "testDoRefresh: %v\n" , err )
}
err = regWatcher . wait ( )
if err != nil {
t . Fatalf ( "regWatcher.wait: %v\n" , err )
}
// 5. Check that local NRPT is populated and GP is empty
t . Logf ( "Validating that local NRPT is populated...\n" )
validateRegistry ( t , nrptBaseLocal , domains )
ensureNoRulesInSubkey ( t , nrptBaseGP )
// 6. Cleanup
t . Logf ( "Cleaning up...\n" )
err = mgr . setSplitDNS ( nil , domains )
if err != nil {
t . Fatalf ( "setSplitDNS: %v\n" , err )
}
ensureNoRules ( t )
}
2022-06-01 14:41:11 -06:00
func checkGPNotificationsWork ( t * testing . T ) {
// Test to ensure that RegisterGPNotification work on this machine,
// otherwise this test will fail.
trk , err := newGPNotificationTracker ( )
if err != nil {
t . Skipf ( "newGPNotificationTracker error: %v\n" , err )
}
defer trk . Close ( )
2024-06-30 17:04:10 -05:00
err = gp . RefreshMachinePolicy ( true )
if err != nil {
2022-06-01 14:41:11 -06:00
t . Fatalf ( "RefreshPolicyEx error: %v\n" , err )
}
timeout := uint32 ( 10000 ) // Milliseconds
if ! trk . DidRefreshTimeout ( timeout ) {
t . Skipf ( "GP notifications are not working on this machine\n" )
}
}
func runTest ( t * testing . T , isLocal bool ) {
2022-05-25 15:51:54 -06:00
logf := func ( format string , args ... any ) {
t . Logf ( format , args ... )
}
fakeInterface , err := windows . GenerateGUID ( )
if err != nil {
t . Fatalf ( "windows.GenerateGUID: %v\n" , err )
}
2022-06-29 15:02:23 -06:00
delIfKey , err := createFakeInterfaceKey ( t , fakeInterface )
if err != nil {
t . Fatalf ( "createFakeInterfaceKey: %v\n" , err )
}
defer delIfKey ( )
2024-06-10 22:05:15 -05:00
cfg , err := NewOSConfigurator ( logf , nil , nil , fakeInterface . String ( ) )
2022-05-25 15:51:54 -06:00
if err != nil {
t . Fatalf ( "NewOSConfigurator: %v\n" , err )
}
2022-08-03 16:18:19 -06:00
mgr := cfg . ( * windowsManager )
2022-06-29 15:02:23 -06:00
defer mgr . Close ( )
2022-05-25 15:51:54 -06:00
2022-06-01 14:41:11 -06:00
usingGP := mgr . nrptDB . writeAsGP
if isLocal == usingGP {
t . Fatalf ( "usingGP %v, want %v\n" , usingGP , ! usingGP )
}
2022-05-25 15:51:54 -06:00
// Upon initialization of cfg, we should not have any NRPT rules
ensureNoRules ( t )
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-25 21:14:09 -07:00
resolvers := [ ] netip . Addr { netip . MustParseAddr ( "1.1.1.1" ) }
2022-05-25 15:51:54 -06:00
2022-06-29 15:02:23 -06:00
domains := genRandomSubdomains ( t , 2 * nrptMaxDomainsPerRule + 1 )
2022-05-25 15:51:54 -06:00
cases := [ ] int {
1 ,
50 ,
51 ,
100 ,
101 ,
100 ,
50 ,
1 ,
51 ,
}
2022-06-01 14:41:11 -06:00
var regBaseValidate string
var regBaseEnsure string
if isLocal {
regBaseValidate = nrptBaseLocal
regBaseEnsure = nrptBaseGP
} else {
regBaseValidate = nrptBaseGP
regBaseEnsure = nrptBaseLocal
}
var trk * gpNotificationTracker
if isLocal {
// (dblohm7) When isLocal == true, we keep trk active through the entire
// sequence of test cases, and then we verify that no policy notifications
// occurred. Because policy notifications are scoped to the entire computer,
// this check could potentially fail if another process concurrently modifies
// group policies while this test is running. I don't expect this to be an
// issue on any computer on which we run this test, but something to keep in
// mind if we start seeing flakiness around these GP notifications.
trk , err = newGPNotificationTracker ( )
if err != nil {
t . Fatalf ( "newGPNotificationTracker: %v\n" , err )
}
defer trk . Close ( )
}
runCase := func ( n int ) {
2022-05-25 15:51:54 -06:00
t . Logf ( "Test case: %d domains\n" , n )
2022-06-01 14:41:11 -06:00
if ! isLocal {
2022-09-25 14:29:55 -04:00
// When !isLocal, we want to check that a GP notification occurred for
2022-06-01 14:41:11 -06:00
// every single test case.
trk , err = newGPNotificationTracker ( )
if err != nil {
t . Fatalf ( "newGPNotificationTracker: %v\n" , err )
}
defer trk . Close ( )
}
2022-05-25 15:51:54 -06:00
caseDomains := domains [ : n ]
2022-06-01 14:41:11 -06:00
err = mgr . setSplitDNS ( resolvers , caseDomains )
2022-05-25 15:51:54 -06:00
if err != nil {
t . Fatalf ( "setSplitDNS: %v\n" , err )
}
2022-06-01 14:41:11 -06:00
validateRegistry ( t , regBaseValidate , caseDomains )
ensureNoRulesInSubkey ( t , regBaseEnsure )
if ! isLocal && ! trk . DidRefresh ( true ) {
t . Fatalf ( "DidRefresh false, want true\n" )
}
}
for _ , n := range cases {
runCase ( n )
}
if isLocal && trk . DidRefresh ( false ) {
t . Errorf ( "DidRefresh true, want false\n" )
2022-05-25 15:51:54 -06:00
}
t . Logf ( "Test case: nil resolver\n" )
err = mgr . setSplitDNS ( nil , domains )
if err != nil {
t . Fatalf ( "setSplitDNS: %v\n" , err )
}
ensureNoRules ( t )
}
2022-06-01 14:41:11 -06:00
func createFakeGPKey ( ) error {
keyStr := nrptBaseGP + ` \ ` + testGPRuleID
key , _ , err := registry . CreateKey ( registry . LOCAL_MACHINE , keyStr , registry . SET_VALUE )
if err != nil {
return fmt . Errorf ( "opening %s: %w" , keyStr , err )
}
defer key . Close ( )
if err := key . SetDWordValue ( "Version" , 1 ) ; err != nil {
return err
}
if err := key . SetStringsValue ( "Name" , [ ] string { "._setbygp_.example.com" } ) ; err != nil {
return err
}
if err := key . SetStringValue ( "GenericDNSServers" , "1.1.1.1" ) ; err != nil {
return err
}
if err := key . SetDWordValue ( "ConfigOptions" , nrptOverrideDNS ) ; err != nil {
return err
}
return nil
}
func deleteFakeGPKey ( t * testing . T ) {
keyName := nrptBaseGP + ` \ ` + testGPRuleID
if err := registry . DeleteKey ( registry . LOCAL_MACHINE , keyName ) ; err != nil && err != registry . ErrNotExist {
t . Fatalf ( "Error deleting NRPT rule key %q: %v\n" , keyName , err )
}
isEmpty , err := isPolicyConfigSubkeyEmpty ( )
if err != nil {
t . Fatalf ( "isPolicyConfigSubkeyEmpty: %v" , err )
}
if ! isEmpty {
return
}
if err := registry . DeleteKey ( registry . LOCAL_MACHINE , nrptBaseGP ) ; err != nil {
t . Fatalf ( "Deleting DnsPolicyKey Subkey: %v" , err )
}
}
2022-06-29 15:02:23 -06:00
func createFakeInterfaceKey ( t * testing . T , guid windows . GUID ) ( func ( ) , error ) {
2022-08-12 14:55:01 -07:00
basePaths := [ ] winutil . RegistryPathPrefix { winutil . IPv4TCPIPInterfacePrefix , winutil . IPv6TCPIPInterfacePrefix }
2022-06-29 15:02:23 -06:00
keyPaths := make ( [ ] string , 0 , len ( basePaths ) )
2022-08-12 14:55:01 -07:00
guidStr := guid . String ( )
2022-06-29 15:02:23 -06:00
for _ , basePath := range basePaths {
2022-08-12 14:55:01 -07:00
keyPath := string ( basePath . WithSuffix ( guidStr ) )
2022-06-29 15:02:23 -06:00
key , _ , err := registry . CreateKey ( registry . LOCAL_MACHINE , keyPath , registry . SET_VALUE )
if err != nil {
return nil , err
}
key . Close ( )
keyPaths = append ( keyPaths , keyPath )
}
result := func ( ) {
for _ , keyPath := range keyPaths {
if err := registry . DeleteKey ( registry . LOCAL_MACHINE , keyPath ) ; err != nil {
t . Fatalf ( "deleting fake interface key \"%s\": %v\n" , keyPath , err )
}
}
}
return result , nil
}
2022-05-25 15:51:54 -06:00
func ensureNoRules ( t * testing . T ) {
ruleIDs := winutil . GetRegStrings ( nrptRuleIDValueName , nil )
if ruleIDs != nil {
t . Errorf ( "%s: %v, want nil\n" , nrptRuleIDValueName , ruleIDs )
}
2022-06-01 14:41:11 -06:00
for _ , base := range [ ] string { nrptBaseLocal , nrptBaseGP } {
ensureNoSingleRule ( t , base )
}
}
func ensureNoRulesInSubkey ( t * testing . T , base string ) {
ruleIDs := winutil . GetRegStrings ( nrptRuleIDValueName , nil )
if ruleIDs == nil {
for _ , base := range [ ] string { nrptBaseLocal , nrptBaseGP } {
ensureNoSingleRule ( t , base )
}
return
}
for _ , ruleID := range ruleIDs {
keyName := base + ` \ ` + ruleID
key , err := registry . OpenKey ( registry . LOCAL_MACHINE , keyName , registry . READ )
if err == nil {
key . Close ( )
2022-06-29 15:02:23 -06:00
} else if err != registry . ErrNotExist {
2022-06-01 14:41:11 -06:00
t . Fatalf ( "%s: %q, want %q\n" , keyName , err , registry . ErrNotExist )
}
}
2022-06-29 15:02:23 -06:00
if base == nrptBaseGP {
// When dealing with the group policy subkey, we want the base key to
// also be absent.
key , err := registry . OpenKey ( registry . LOCAL_MACHINE , base , registry . READ )
if err == nil {
key . Close ( )
isEmpty , err := isPolicyConfigSubkeyEmpty ( )
if err != nil {
t . Fatalf ( "isPolicyConfigSubkeyEmpty: %v" , err )
}
if isEmpty {
t . Errorf ( "Unexpectedly found group policy key\n" )
}
} else if err != registry . ErrNotExist {
t . Errorf ( "Group policy key error: %q, want %q\n" , err , registry . ErrNotExist )
}
}
2022-06-01 14:41:11 -06:00
}
func ensureNoSingleRule ( t * testing . T , base string ) {
singleKeyPath := base + ` \ ` + nrptSingleRuleID
key , err := registry . OpenKey ( registry . LOCAL_MACHINE , singleKeyPath , registry . READ )
2022-05-25 15:51:54 -06:00
if err == nil {
key . Close ( )
}
if err != registry . ErrNotExist {
2022-06-01 14:41:11 -06:00
t . Fatalf ( "%s: %q, want %q\n" , singleKeyPath , err , registry . ErrNotExist )
2022-05-25 15:51:54 -06:00
}
}
2022-06-01 14:41:11 -06:00
func validateRegistry ( t * testing . T , nrptBase string , domains [ ] dnsname . FQDN ) {
2022-05-25 15:51:54 -06:00
q := len ( domains ) / nrptMaxDomainsPerRule
r := len ( domains ) % nrptMaxDomainsPerRule
numRules := q
if r > 0 {
numRules ++
}
ruleIDs := winutil . GetRegStrings ( nrptRuleIDValueName , nil )
if ruleIDs == nil {
ruleIDs = [ ] string { nrptSingleRuleID }
} else if len ( ruleIDs ) != numRules {
t . Errorf ( "%s for %d domains: %d, want %d\n" , nrptRuleIDValueName , len ( domains ) , len ( ruleIDs ) , numRules )
}
for i , ruleID := range ruleIDs {
2022-06-01 14:41:11 -06:00
savedDomains , err := getSavedDomainsForRule ( nrptBase , ruleID )
2022-05-25 15:51:54 -06:00
if err != nil {
2022-06-01 14:41:11 -06:00
t . Fatalf ( "getSavedDomainsForRule(%q, %q): %v\n" , nrptBase , ruleID , err )
2022-05-25 15:51:54 -06:00
}
start := i * nrptMaxDomainsPerRule
end := start + nrptMaxDomainsPerRule
if i == len ( ruleIDs ) - 1 && r > 0 {
end = start + r
}
checkDomains := domains [ start : end ]
if len ( checkDomains ) != len ( savedDomains ) {
t . Errorf ( "len(checkDomains) != len(savedDomains): %d, want %d\n" , len ( savedDomains ) , len ( checkDomains ) )
}
for j , cd := range checkDomains {
sd := strings . TrimPrefix ( savedDomains [ j ] , "." )
if string ( cd . WithoutTrailingDot ( ) ) != sd {
t . Errorf ( "checkDomain differs savedDomain: %s, want %s\n" , sd , cd . WithoutTrailingDot ( ) )
}
}
}
}
2022-06-01 14:41:11 -06:00
func getSavedDomainsForRule ( base , ruleID string ) ( [ ] string , error ) {
keyPath := base + ` \ ` + ruleID
2022-05-25 15:51:54 -06:00
key , err := registry . OpenKey ( registry . LOCAL_MACHINE , keyPath , registry . READ )
if err != nil {
return nil , err
}
defer key . Close ( )
result , _ , err := key . GetStringsValue ( "Name" )
return result , err
}
2022-06-01 14:41:11 -06:00
2022-06-29 15:02:23 -06:00
func genRandomSubdomains ( t * testing . T , n int ) [ ] dnsname . FQDN {
domains := make ( [ ] dnsname . FQDN , 0 , n )
seed := time . Now ( ) . UnixNano ( )
t . Logf ( "genRandomSubdomains(%d) seed: %v\n" , n , seed )
r := rand . New ( rand . NewSource ( seed ) )
const charset = "abcdefghijklmnopqrstuvwxyz"
for len ( domains ) < cap ( domains ) {
l := r . Intn ( 19 ) + 1
b := make ( [ ] byte , l )
2023-04-17 18:38:24 -04:00
for i := range b {
2022-06-29 15:02:23 -06:00
b [ i ] = charset [ r . Intn ( len ( charset ) ) ]
}
d := string ( b ) + ".example.com"
fqdn , err := dnsname . ToFQDN ( d )
if err != nil {
t . Fatalf ( "dnsname.ToFQDN: %v\n" , err )
}
domains = append ( domains , fqdn )
}
return domains
}
2024-06-30 17:04:10 -05:00
var (
libUserenv = windows . NewLazySystemDLL ( "userenv.dll" )
procRegisterGPNotification = libUserenv . NewProc ( "RegisterGPNotification" )
procUnregisterGPNotification = libUserenv . NewProc ( "UnregisterGPNotification" )
)
2022-06-29 15:02:23 -06:00
2022-06-01 14:41:11 -06:00
// gpNotificationTracker registers with the Windows policy engine and receives
// notifications when policy refreshes occur.
type gpNotificationTracker struct {
event windows . Handle
}
func newGPNotificationTracker ( ) ( * gpNotificationTracker , error ) {
var err error
evt , err := windows . CreateEvent ( nil , 0 , 0 , nil )
if err != nil {
return nil , err
}
defer func ( ) {
if err != nil {
windows . CloseHandle ( evt )
}
} ( )
ok , _ , e := procRegisterGPNotification . Call (
uintptr ( evt ) ,
uintptr ( 1 ) , // We want computer policy changes, not user policy changes.
)
if ok == 0 {
err = e
return nil , err
}
return & gpNotificationTracker { evt } , nil
}
func ( trk * gpNotificationTracker ) DidRefresh ( isExpected bool ) bool {
// If we're not expecting a refresh event, then we need to use a timeout.
timeout := uint32 ( 1000 ) // 1 second (in milliseconds)
if isExpected {
// Otherwise, since it is imperative that we see an event, we wait infinitely.
timeout = windows . INFINITE
}
return trk . DidRefreshTimeout ( timeout )
}
func ( trk * gpNotificationTracker ) DidRefreshTimeout ( timeout uint32 ) bool {
waitCode , _ := windows . WaitForSingleObject ( trk . event , timeout )
return waitCode == windows . WAIT_OBJECT_0
}
func ( trk * gpNotificationTracker ) Close ( ) error {
procUnregisterGPNotification . Call ( uintptr ( trk . event ) )
windows . CloseHandle ( trk . event )
trk . event = 0
return nil
}
2022-06-29 15:02:23 -06:00
type regKeyWatcher struct {
2024-04-10 14:31:40 -06:00
keyGP registry . Key
evtGP windows . Handle
2022-06-29 15:02:23 -06:00
}
2024-04-10 14:31:40 -06:00
func newRegKeyWatcher ( ) ( result * regKeyWatcher , err error ) {
2022-06-29 15:02:23 -06:00
// Monitor dnsBaseGP instead of nrptBaseGP, since the latter will be
// repeatedly created and destroyed throughout the course of the test.
keyGP , _ , err := registry . CreateKey ( registry . LOCAL_MACHINE , dnsBaseGP , registry . READ )
if err != nil {
return nil , err
}
defer func ( ) {
if err != nil {
keyGP . Close ( )
}
} ( )
evtGP , err := windows . CreateEvent ( nil , 0 , 0 , nil )
if err != nil {
return nil , err
}
2024-04-10 14:31:40 -06:00
return & regKeyWatcher {
keyGP : keyGP ,
evtGP : evtGP ,
} , nil
2022-06-29 15:02:23 -06:00
}
func ( rw * regKeyWatcher ) watch ( ) error {
// We can make these waits thread-agnostic because the tests that use this code must already run on Windows 10+
return windows . RegNotifyChangeKeyValue ( windows . Handle ( rw . keyGP ) , true ,
windows . REG_NOTIFY_CHANGE_NAME | windows . REG_NOTIFY_THREAD_AGNOSTIC , rw . evtGP , true )
}
func ( rw * regKeyWatcher ) wait ( ) error {
2024-04-10 14:31:40 -06:00
waitCode , err := windows . WaitForSingleObject (
2022-06-29 15:02:23 -06:00
rw . evtGP ,
10000 , // 10 seconds (as milliseconds)
)
switch waitCode {
2024-04-10 14:31:40 -06:00
case uint32 ( windows . WAIT_TIMEOUT ) :
2022-06-29 15:02:23 -06:00
return context . DeadlineExceeded
case windows . WAIT_FAILED :
return err
default :
return nil
}
}
func ( rw * regKeyWatcher ) Close ( ) error {
rw . keyGP . Close ( )
windows . CloseHandle ( rw . evtGP )
return nil
}