/*++ Copyright (c) 2000-2001 Microsoft Corporation Module Name: registry.c Abstract: Domain Name System (DNS) API Registry management routines. Author: Jim Gilroy (jamesg) March, 2000 Revision History: --*/ #include "local.h" #include "registry.h" // // Globals // // DWORD globals blob // // g_IsRegReady protects needs init and protects (requires) // global init. // // See registry.h for discussion of how these globals are // exposed both internal to the DLL and external. // DNS_GLOBALS_BLOB DnsGlobals; BOOL g_IsRegReady = FALSE; PWSTR g_pwsRemoteResolver = NULL; // // Property table // // // WARNING: table must be in sync with DNS_REGID definitions // // For simplicity I did not provide a separate index field and // a lookup function (or alternatively a function that returned // all the properties or a property pointer). // // The DNS_REGID values ARE the INDEXES! // Hence the table MUST be in sync or the whole deal blows up // If you make a change to either -- you must change the other! // REG_PROPERTY RegPropertyTable[] = { // Basic HOST_NAME , 0 , // Default FALSE 0 , // No policy 1 , // Client 1 , // TcpIp 0 , // No Cache DOMAIN_NAME , 0 , // Default FALSE 1 , // Policy 0 , // No Client 1 , // TcpIp 0 , // No Cache DHCP_DOMAIN_NAME , 0 , // Default FALSE 1 , // Policy 0 , // No Client 1 , // TcpIp 0 , // No Cache ADAPTER_DOMAIN_NAME , 0 , // Default FALSE 1 , // Policy 1 , // Client 0 , // No TcpIp 0 , // No Cache PRIMARY_DOMAIN_NAME , 0 , // Default FALSE 1 , // Policy 1 , // Client 0 , // No TcpIp 0 , // No Cache PRIMARY_SUFFIX , 0 , // Default FALSE 1 , // Policy 1 , // Client 1 , // TcpIp 0 , // No Cache ALTERNATE_NAMES , 0 , // Default NULL 0 , // No Policy 0 , // No Client 0 , // No TcpIp 1 , // Cache DNS_SERVERS , 0 , // Default FALSE 1 , // Policy 0 , // Client 0 , // TcpIp 0 , // No Cache SEARCH_LIST_KEY , 0 , // Default FALSE 1 , // Policy 1 , // Client 1 , // TcpIp 0 , // No Cache UPDATE_ZONE_EXCLUSIONS , 0 , // Default 1 , // Policy 1 , // Client 0 , // No TcpIp 0 , // No Cache // Query QUERY_ADAPTER_NAME , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache USE_DOMAIN_NAME_DEVOLUTION , 1 , // Default TRUE 1 , // Policy 1 , // Client 1 , // TcpIp 1 , // Cache PRIORITIZE_RECORD_DATA , 1 , // Default TRUE 1 , // Policy 1 , // Client 1 , // TcpIp 1 , // Cache ALLOW_UNQUALIFIED_QUERY , 0 , // Default FALSE 1 , // Policy 1 , // Client 1 , // TcpIp 1 , // Cache APPEND_TO_MULTI_LABEL_NAME , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache SCREEN_BAD_TLDS , DNS_TLD_SCREEN_DEFAULT , // Default 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache SCREEN_UNREACHABLE_SERVERS , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache FILTER_CLUSTER_IP , 0 , // Default FALSE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache WAIT_FOR_NAME_ERROR_ON_ALL , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache USE_EDNS , //REG_EDNS_TRY , // Default TRY EDNS 0 , // Default FALSE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache // Update REGISTRATION_ENABLED , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache REGISTER_PRIMARY_NAME , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache REGISTER_ADAPTER_NAME , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache REGISTER_REVERSE_LOOKUP , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache REGISTER_WAN_ADAPTERS , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache REGISTRATION_OVERWRITES_IN_CONFLICT , 1 , // Default TRUE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache REGISTRATION_TTL , REGDEF_REGISTRATION_TTL , 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache REGISTRATION_REFRESH_INTERVAL , REGDEF_REGISTRATION_REFRESH_INTERVAL , 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache REGISTRATION_MAX_ADDRESS_COUNT , 1 , // Default register 1 address 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache UPDATE_SECURITY_LEVEL , DNS_UPDATE_SECURITY_USE_DEFAULT , 1 , // Policy 1 , // Client 1 , // TcpIp 1 , // No Cache UPDATE_ZONE_EXCLUDE_FILE , 1 , // Default ON 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache UPDATE_TOP_LEVEL_DOMAINS , 0 , // Default OFF 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // No Cache // // Backcompat // // DCR: once policy fixed, policy should be OFF on all backcompat // DISABLE_ADAPTER_DOMAIN_NAME , 0 , // Default FALSE 0 , // Policy 0 , // Client 1 , // TcpIp 0 , // No Cache DISABLE_DYNAMIC_UPDATE , 0 , // Default FALSE 0 , // Policy 0 , // Client 1 , // TcpIp 0 , // No Cache ENABLE_ADAPTER_DOMAIN_NAME_REGISTRATION , 0 , // Default TRUE 0 , // Policy 0 , // Client 1 , // TcpIp 0 , // No Cache DISABLE_REVERSE_ADDRESS_REGISTRATIONS , 0 , // Default FALSE 0 , // Policy 0 , // Client 1 , // TcpIp 0 , // No Cache DISABLE_WAN_DYNAMIC_UPDATE , 0 , // Default FALSE 0 , // Policy OFF 0 , // Client 1 , // TcpIp 0 , // No Cache ENABLE_WAN_UPDATE_EVENT_LOG , 0 , // Default FALSE 0 , // Policy OFF 0 , // Client 1 , // TcpIp 0 , // No Cache DISABLE_REPLACE_ADDRESSES_IN_CONFLICTS , 0 , // Default FALSE 0 , // Policy 0 , // Client 1 , // TcpIp 0 , // No Cache DEFAULT_REGISTRATION_TTL , REGDEF_REGISTRATION_TTL , 0 , // Policy OFF 0 , // Client 1 , // TcpIp 0 , // No Cache DEFAULT_REGISTRATION_REFRESH_INTERVAL , REGDEF_REGISTRATION_REFRESH_INTERVAL , 0 , // Policy 0 , // Client 1 , // TcpIp 0 , // No Cache MAX_NUMBER_OF_ADDRESSES_TO_REGISTER , 1 , // Default register 1 address 0 , // Policy 0 , // Client 1 , // TcpIp 0 , // No Cache // Micellaneous NT_SETUP_MODE , 0 , // Default FALSE 0 , // No policy 0 , // Client 0 , // No TcpIp 0 , // No Cache DNS_TEST_MODE , 0 , // Default FALSE 0 , // No policy 0 , // No Client 0 , // No TcpIp 1 , // In Cache REMOTE_DNS_RESOLVER , 0 , // Default FALSE 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // In Cache // Resolver MAX_CACHE_SIZE , 1000 , // Default 1000 record sets 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache MAX_CACHE_TTL , 86400 , // Default 1 day 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache MAX_NEGATIVE_CACHE_TTL , 900 , // Default 15 minutes 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache ADAPTER_TIMEOUT_LIMIT , 600 , // Default 10 minutes 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache SERVER_PRIORITY_TIME_LIMIT , //3600 , // Default 1 hour // DCR: change once registry change notify // while no change-notify, this is essentially registry // check time so use 15 minutes 900 , // Default 15 minutes 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache MAX_CACHED_SOCKETS , 10 , // Default 10 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache // Multicast USE_MULTICAST , 0 , // Default OFF //1 , // Default ON 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache MULTICAST_ON_NAME_ERROR , 0 , // Default OFF //1 , // Default ON 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache USE_DOT_LOCAL_DOMAIN , 0 , // Default OFF //1 , // Default ON 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache LISTEN_ON_MULTICAST , 0 , // Default OFF //1 , // Default ON 1 , // Policy 1 , // Client 0 , // No TcpIp 1 , // Cache // Termination NULL, 0, 0, 0, 0, 0 }; // // Backward compatibility list // // Maps new reg id to old reg id. // Flag fReverse indicates need to reverse (!) the value. // #define NO_BACK_VALUE ((DWORD)(0xffffffff)) typedef struct _Backpat { DWORD NewId; DWORD OldId; BOOL fReverse; } BACKPAT; BACKPAT BackpatArray[] = { RegIdQueryAdapterName, RegIdDisableAdapterDomainName, TRUE, RegIdRegistrationEnabled, RegIdDisableDynamicUpdate, TRUE, RegIdRegisterAdapterName, RegIdEnableAdapterDomainNameRegistration, FALSE, RegIdRegisterReverseLookup, RegIdDisableReverseAddressRegistrations, TRUE, RegIdRegisterWanAdapters, RegIdDisableWanDynamicUpdate, TRUE, RegIdRegistrationOverwritesInConflict, RegIdDisableReplaceAddressesInConflicts, TRUE, RegIdRegistrationTtl, RegIdDefaultRegistrationTTL, FALSE, RegIdRegistrationRefreshInterval, RegIdDefaultRegistrationRefreshInterval, FALSE, RegIdRegistrationMaxAddressCount, RegIdMaxNumberOfAddressesToRegister, FALSE, NO_BACK_VALUE, 0, 0 }; VOID Reg_Init( VOID ) /*++ Routine Description: Init DNS registry stuff. Essentially this means get system version info. Arguments: None. Globals: Sets the system info globals above: g_IsWin9X g_IsWin2000 g_IsNT4 g_IsWorkstation g_IsServer g_IsDomainController g_IsRegReady Return Value: None. --*/ { OSVERSIONINFOEX osvi; BOOL bversionInfoEx; // // do this just once // if ( g_IsRegReady ) { return; } // // code validity check // property table should have entry for every reg value plus an // extra one for the terminator // #if DBG DNS_ASSERT( (RegIdValueCount+1)*sizeof(REG_PROPERTY) == sizeof(RegPropertyTable) ); #endif // // clear globals blob // // DCR: warning clearing DnsGlobals but don't read them all // this is protected by read-once deal but still kind of // RtlZeroMemory( & DnsGlobals, sizeof(DnsGlobals) ); // // get version info // - Win2000 supports OSVERSIONINFOEX // try first with it, then fail back to standard // ZeroMemory( &osvi, sizeof(OSVERSIONINFOEX) ); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); bversionInfoEx = GetVersionEx( (OSVERSIONINFO*) &osvi ); if ( !bversionInfoEx) { osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if ( ! GetVersionEx( (OSVERSIONINFO *) &osvi ) ) { DNS_ASSERT( FALSE ); return; } } #if DNSBUILDOLD // // suck out system info // // if Win9x -- that's it done // if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) { g_IsWin9X = TRUE; goto Done; } // // assume anything else is NT, this gets around WIN32 tag // leaving in Win64 versions // // DCR: WinCE issue here? // DCR: Win64 issue here? // DNS_ASSERT( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT ); if ( osvi.dwMajorVersion >= 5 ) { g_IsWin2000 = TRUE; } else { g_IsNT4 = TRUE; } #else // set Win2K flag // - easier to keep this flag then to #ifdef the remaining uses g_IsWin2000 = TRUE; #endif // // get system type -- workstation, server, DC // if ( bversionInfoEx ) { if ( osvi.wProductType == VER_NT_WORKSTATION ) { g_IsWorkstation = TRUE; } else if ( osvi.wProductType == VER_NT_SERVER ) { g_IsServer = TRUE; } else if ( osvi.wProductType == VER_NT_DOMAIN_CONTROLLER ) { g_IsServer = TRUE; g_IsDomainController = TRUE; } ELSE_ASSERT( FALSE ); } #if DNSNT4 // // no osviEX (NT4), must suck product from registry // else { HKEY hkey = NULL; CHAR productType[MAX_PATH] = "0"; DWORD bufLength = MAX_PATH; RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", 0, KEY_QUERY_VALUE, & hkey ); if ( !hkey ) { DNS_ASSERT( FALSE ); goto Done; } RegQueryValueExA( hkey, "ProductType", NULL, NULL, (LPBYTE) productType, & bufLength ); RegCloseKey( hkey ); if ( lstrcmpi( "WINNT", productType) == 0 ) { g_IsWorkstation = TRUE; } else if ( lstrcmpi( "SERVERNT", productType) == 0 ) { g_IsServer = TRUE; } else if ( lstrcmpi( "LANMANNT", productType) == 0 ) { g_IsDomainController = TRUE; } } Done: #endif g_IsRegReady = TRUE; #if DNSBUILDOLD DNSDBG( REGISTRY, ( "DNS API registry init:\n" "\tWin9X = %d\n" "\tNT4 = %d\n" "\tWin2000 = %d\n" "\tWorksta = %d\n" "\tServer = %d\n" "\tDC = %d\n", g_IsWin9X, g_IsNT4, g_IsWin2000, g_IsWorkstation, g_IsServer, g_IsDomainController )); #endif DNSDBG( REGISTRY, ( "DNS registry init:\n" "\tWin2000 = %d\n" "\tWorksta = %d\n" "\tServer = %d\n" "\tDC = %d\n", g_IsWin2000, g_IsWorkstation, g_IsServer, g_IsDomainController )); } // // Registry table routines // PSTR regValueNameForId( IN DWORD RegId ) /*++ Routine Description: Return registry value name for reg ID Arguments: RegId -- ID for value Return Value: Ptr to reg value name. NULL on error. --*/ { DNSDBG( REGISTRY, ( "regValueNameForId( id=%d )\n", RegId )); // // validate ID // if ( RegId > RegIdValueMax ) { return( NULL ); } // // index into table // return( (PSTR)REGPROP_NAME(RegId) ); } DWORD checkBackCompat( IN DWORD NewId, OUT PBOOL pfReverse ) /*++ Routine Description: Check if have backward compatible regkey. Arguments: NewId -- id to check for old backward compatible id pfReverse -- addr to receive reverse flag Return Value: Reg Id of old backward compatible value. NO_BACK_VALUE if no old value. --*/ { DWORD i = 0; DWORD id; // // loop through backcompat list looking for value // while ( 1 ) { id = BackpatArray[i].NewId; if ( id == NO_BACK_VALUE ) { return( NO_BACK_VALUE ); } if ( id != NewId ) { i++; continue; } // found value in backcompat array break; } *pfReverse = BackpatArray[i].fReverse; return BackpatArray[i].OldId; } // // Registry session handle // DNS_STATUS WINAPI Reg_OpenSession( OUT PREG_SESSION pRegSession, IN DWORD Level, IN DWORD RegId ) /*++ Routine Description: Open registry for DNS client info. Arguments: pRegSession -- ptr to unitialize reg session blob Level -- level of access to get RegId -- ID of value we're interested in Return Value: None. --*/ { DWORD status = NO_ERROR; HKEY hkey = NULL; DWORD disposition; // auto init Reg_Init(); // // clear handles // RtlZeroMemory( pRegSession, sizeof( REG_SESSION ) ); // // DCR: handle multiple access levels // // For know assume that if getting access to "standard" // section we'll need both policy and regular. // // // Win95 // - TCP/IP location slightly different than NT // // Devnote: we could collapse these using all _A calls // (all key names are ANSI) but we gain some perf // by calling NT wide; and since we currently have // to do that, it seems worth it // #if DNSWIN9X if ( g_IsWin9X ) { status = RegCreateKeyExA( HKEY_LOCAL_MACHINE, WIN95_TCPIP_KEY_A, 0, "Class", REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkey, &disposition ); } else #endif // // NT // - Win2000 // - open TCPIP // note, always open TCPIP as may not be any policy // for some or all of our desired reg values, even // if policy key is available // - open policy (only if standard successful) // // - NT4 // - open TCPIP (no policy) // { status = RegCreateKeyExW( HKEY_LOCAL_MACHINE, TCPIP_PARAMETERS_KEY, 0, L"Class", REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkey, &disposition ); if ( !g_IsWin2000 || status != ERROR_SUCCESS ) { goto Done; } #ifdef DNSCLIENTKEY // open DNS client key // // DCR: currently no DNSClient regkey RegOpenKeyExW( HKEY_LOCAL_MACHINE, DNS_CLIENT_KEY, 0, KEY_READ, & pRegSession->hClient ); #endif // open DNS cache key RegOpenKeyExW( HKEY_LOCAL_MACHINE, DNS_CACHE_KEY, 0, KEY_READ, & pRegSession->hCache ); // open DNS policy key RegOpenKeyExW( HKEY_LOCAL_MACHINE, DNS_POLICY_KEY, 0, KEY_READ, & pRegSession->hPolicy ); } Done: // // all OS versions return TCP/IP key // if ( status == ERROR_SUCCESS ) { pRegSession->hTcpip = hkey; } else { Reg_CloseSession( pRegSession ); } DNSDBG( TRACE, ( "Leave: Reg_OpenSession( s=%d, t=%p, p=%p, c=%p )\n", status, pRegSession->hTcpip, pRegSession->hPolicy, pRegSession->hClient )); return( status ); } VOID WINAPI Reg_CloseSession( IN OUT PREG_SESSION pRegSession ) /*++ Routine Description: Close registry session handle. This means close underlying regkeys. Arguments: pSessionHandle -- ptr to registry session handle Return Value: None. --*/ { // // allow sloppy cleanup // if ( !pRegSession ) { return; } // // close any non-NULL handles // if ( pRegSession->hPolicy ) { RegCloseKey( pRegSession->hPolicy ); } if ( pRegSession->hTcpip ) { RegCloseKey( pRegSession->hTcpip ); } #ifdef DNSCLIENTKEY if ( pRegSession->hClient ) { RegCloseKey( pRegSession->hClient ); } #endif if ( pRegSession->hCache ) { RegCloseKey( pRegSession->hCache ); } // // clear handles (just for safety) // RtlZeroMemory( pRegSession, sizeof(REG_SESSION) ); } // // Registry reading routines // DNS_STATUS Reg_GetDword( IN PREG_SESSION pRegSession, OPTIONAL IN HKEY hRegKey, OPTIONAL IN PWSTR pwsKeyName, OPTIONAL IN DWORD RegId, OUT PDWORD pResult //OUT PDWORD pfRead ) /*++ Routine Description: Read REG_DWORD value from registry. // // DCR: do we need to expose location result? // (explicit, policy, defaulted) // Arguments: pRegSession -- ptr to reg session already opened (OPTIONAL) hRegKey -- explicit regkey pwsKeyName -- key name OR dummy key RegId -- ID for value pResult -- addr of DWORD to recv result pfRead -- addr to recv result of how value read 0 -- defaulted 1 -- read Currently just use ERROR_SUCCESS to mean read rather than defaulted. Return Value: ERROR_SUCCESS on success. ErrorCode on failure -- value is then defaulted. --*/ { DNS_STATUS status; REG_SESSION session; PREG_SESSION psession = pRegSession; PBYTE pname; DWORD regType = REG_DWORD; DWORD dataLength = sizeof(DWORD); HKEY hkey; HKEY hlocalKey = NULL; DNSDBG( REGISTRY, ( "Reg_GetDword( s=%p, k=%p, a=%p, id=%d )\n", pRegSession, hRegKey, pwsKeyName, RegId )); // auto init Reg_Init(); // // clear result for error case // *pResult = 0; // // get proper regval name // - wide for NT // - narrow for 9X // pname = regValueNameForId( RegId ); if ( !pname ) { DNS_ASSERT( FALSE ); return( ERROR_INVALID_PARAMETER ); } // // DCR: can use function pointers for wide narrow // // // three paradigms // // 1) specific key (adapter or something else) // => use it // // 2) specific key name (adapter or dummy key location) // => open key // => use it // => close // // 3) session -- passed in or created (default) // => use pRegSession or open new // => try policy first then TCPIP parameters // => close if open // if ( hRegKey ) { hkey = hRegKey; } else if ( pwsKeyName ) { hkey = Reg_CreateKey( pwsKeyName, FALSE // read access ); if ( !hkey ) { status = GetLastError(); goto Done; } hlocalKey = hkey; } else { // open reg handle if not open if ( !psession ) { status = Reg_OpenSession( &session, 0, // standard level RegId // target key ); if ( status != ERROR_SUCCESS ) { goto Done; } psession = &session; } // try policy section -- if available hkey = psession->hPolicy; if ( hkey && REGPROP_POLICY(RegId) ) { //status = DnsShimRegQueryValueExW( status = RegQueryValueExW( hkey, (PWSTR) pname, 0, & regType, (PBYTE) pResult, & dataLength ); if ( status == ERROR_SUCCESS ) { goto DoneSuccess; } } // unsuccessful -- try DnsClient #ifdef DNSCLIENTKEY hkey = psession->hClient; if ( hkey && REGPROP_CLIENT(RegId) ) { //status = DnsShimRegQueryValueExW( status = RegQueryValueExW( hkey, (PWSTR) pname, 0, & regType, (PBYTE) pResult, & dataLength ); if ( status == ERROR_SUCCESS ) { goto DoneSuccess; } } #endif // unsuccessful -- try DnsCache hkey = psession->hCache; if ( hkey && REGPROP_CACHE(RegId) ) { //status = DnsShimRegQueryValueExW( status = RegQueryValueExW( hkey, (PWSTR) pname, 0, & regType, (PBYTE) pResult, & dataLength ); if ( status == ERROR_SUCCESS ) { goto DoneSuccess; } } // unsuccessful -- try TCPIP key // - if have open session it MUST include TCPIP key hkey = psession->hTcpip; if ( hkey && REGPROP_TCPIP(RegId) ) { //status = DnsShimRegQueryValueExW( status = RegQueryValueExW( hkey, (PWSTR) pname, 0, & regType, (PBYTE) pResult, & dataLength ); if ( status == ERROR_SUCCESS ) { goto DoneSuccess; } } status = ERROR_FILE_NOT_FOUND; goto Done; } // // explict key (passed in or from name) // if ( hkey ) { //status = DnsShimRegQueryValueExW( status = RegQueryValueExW( hkey, (PWSTR) pname, 0, & regType, (PBYTE) pResult, & dataLength ); } ELSE_ASSERT_FALSE; Done: // // if value not found, check for backward compatibility value // if ( status != ERROR_SUCCESS ) { DWORD oldId; BOOL freverse; oldId = checkBackCompat( RegId, &freverse ); if ( oldId != NO_BACK_VALUE ) { DWORD backValue; status = Reg_GetDword( psession, ( psession ) ? NULL : hkey, ( psession ) ? NULL : pwsKeyName, oldId, & backValue ); if ( status == ERROR_SUCCESS ) { if ( freverse ) { backValue = !backValue; } *pResult = backValue; } } } // default the value if read failed if ( status != ERROR_SUCCESS ) { *pResult = REGPROP_DEFAULT( RegId ); } DoneSuccess: // cleanup any regkey's opened if ( psession == &session ) { Reg_CloseSession( psession ); } else if ( hlocalKey ) { RegCloseKey( hlocalKey ); } return( status ); } // // DCR_CLEANUP: cleanup Reg_ReadValue() // this is a slightly altered version of Glenn's // routine to "do it all" -- stripped of the // unnecessary Win9x flag; // it does not do policy or use the RegHandle yet // // DCR_PERF: it also makes a bunch of unnecessary // heap allocations // DNS_STATUS privateRegReadValue( IN HKEY hKey, IN DWORD RegId, IN DWORD Flag, OUT PBYTE * ppBuffer, OUT PDWORD pBufferLength ) /*++ Routine Description: Arguments: hKey -- handle of the key whose value field is retrieved. RegId -- reg value ID, assumed to be validated (in table) ppBuffer -- ptr to address to receive buffer ptr pBufferLength -- addr to receive buffer length Return Value: ERROR_SUCCESS if successful. ErrorCode on failure. --*/ { DWORD status; PSTR pname; DWORD valueType = 0; // prefix DWORD valueSize = 0; // prefix PBYTE pdataBuffer; PBYTE pallocBuffer = NULL; // // query for buffer size // pname = (PSTR) REGPROP_NAME( RegId ); //status = DnsShimRegQueryValueExW( status = RegQueryValueExW( hKey, (PWSTR) pname, 0, &valueType, NULL, &valueSize ); if ( status != ERROR_SUCCESS ) { return( status ); } // // setup result buffer // switch( valueType ) { case REG_DWORD: pdataBuffer = (PBYTE) ppBuffer; break; case REG_SZ: case REG_MULTI_SZ: case REG_EXPAND_SZ: case REG_BINARY: // if size is zero, still allocate empty string // - min alloc DWORD // - can't possibly alloc smaller // - good clean init to zero includes MULTISZ zero // - need at least WCHAR string zero init // and much catch small regbinary (1,2,3) if ( valueSize <= sizeof(DWORD) ) { valueSize = sizeof(DWORD); } pallocBuffer = pdataBuffer = ALLOCATE_HEAP( valueSize ); if ( !pdataBuffer ) { return( DNS_ERROR_NO_MEMORY ); } *(PDWORD)pdataBuffer = 0; break; default: return( ERROR_INVALID_PARAMETER ); } // // query for data // //status = DnsShimRegQueryValueExW( status = RegQueryValueExW( hKey, (PWSTR) pname, 0, &valueType, pdataBuffer, &valueSize ); // // setup return buffer // switch( valueType ) { case REG_DWORD: case REG_BINARY: break; case REG_SZ: case REG_EXPAND_SZ: case REG_MULTI_SZ: // // dump empty strings? // // note: we always allocate at least a DWORD and // set it NULL, so rather than a complex test for // different reg types and char sets, can just test // if that DWORD is still NULL // // DCR: do we want to screen whitespace-empty strings // - example blank string // if ( Flag & DNSREG_FLAG_DUMP_EMPTY ) { if ( valueSize==0 || *(PDWORD)pdataBuffer == 0 ) { status = ERROR_INVALID_DATA; goto Cleanup; } } // // by default we return strings as UTF8 // // if flagged, return in unicode // if ( Flag & DNSREG_FLAG_GET_UNICODE ) { // no-op, keep unicode string } // // do default convert to UTF8 // else { PBYTE putf8Buffer = ALLOCATE_HEAP( valueSize * 2 ); if ( !putf8Buffer ) { status = DNS_ERROR_NO_MEMORY; goto Cleanup; } if ( !Dns_UnicodeToUtf8( (PWSTR) pdataBuffer, valueSize / sizeof(WCHAR), putf8Buffer, valueSize * 2 ) ) { FREE_HEAP( putf8Buffer ); status = ERROR_INVALID_DATA; goto Cleanup; } FREE_HEAP( pallocBuffer ); pallocBuffer = NULL; pdataBuffer = putf8Buffer; } break; default: break; } Cleanup: // // set return // - REG_DWORD writes DWORD to ppBuffer directly // - otherwise ppBuffer set to allocated buffer ptr // or cleanup // - on failure dump allocated buffer // if ( status == ERROR_SUCCESS ) { if ( valueType != REG_DWORD ) { *ppBuffer = pdataBuffer; } *pBufferLength = valueSize; } else { *ppBuffer = NULL; *pBufferLength = 0; FREE_HEAP( pallocBuffer ); } return( status ); } DNS_STATUS Reg_GetValueEx( IN PREG_SESSION pRegSession, OPTIONAL IN HKEY hRegKey, OPTIONAL IN LPSTR pwsAdapter, OPTIONAL IN DWORD RegId, IN DWORD ValueType, IN DWORD Flag, OUT PBYTE * ppBuffer ) /*++ Routine Description: Arguments: pRegSession -- ptr to registry session, OPTIONAL hRegKey -- handle to open regkey OPTIONAL pwsAdapter -- name of adapter to query under OPTIONAL RegId -- value ID ValueType -- reg type of value Flag -- flags with tweaks to lookup ppBuffer -- addr to receive buffer ptr Note, for REG_DWORD, DWORD data is written directly to this location instead of a buffer being allocated and it's ptr being written. Return Value: ERROR_SUCCESS if successful. Registry error code on failure. --*/ { DNS_STATUS status = ERROR_FILE_NOT_FOUND; REG_SESSION session; PREG_SESSION psession = pRegSession; PBYTE pname; DWORD regType = REG_DWORD; DWORD dataLength; HKEY hkey; HKEY hadapterKey = NULL; DNSDBG( REGISTRY, ( "Reg_GetValueEx( s=%p, k=%p, id=%d )\n", pRegSession, hRegKey, RegId )); ASSERT( !pwsAdapter ); // auto init Reg_Init(); // // get proper regval name // - wide for NT // - narrow for 9X // pname = regValueNameForId( RegId ); if ( !pname ) { DNS_ASSERT( FALSE ); status = ERROR_INVALID_PARAMETER; goto FailedDone; } // // DCR: can use fucntion pointers for wide narrow // // // two paradigms // // 1) specific key (adapter or something else) // => use it // => open adapter subkey if necessary // // 2) standard // => try policy first, then DNSCache, then TCPIP // => use pRegSession or open it // if ( hRegKey ) { hkey = hRegKey; // need to open adapter subkey if ( pwsAdapter ) { status = RegOpenKeyExA( hkey, (PCSTR) pwsAdapter, 0, KEY_QUERY_VALUE, & hadapterKey ); if ( status != ERROR_SUCCESS ) { goto FailedDone; } } } else { // open reg handle if not open if ( !pRegSession ) { status = Reg_OpenSession( &session, 0, // standard level RegId // target key ); if ( status != ERROR_SUCCESS ) { goto FailedDone; } psession = &session; } // try policy section -- if available hkey = psession->hPolicy; if ( hkey && REGPROP_POLICY(RegId) ) { status = privateRegReadValue( hkey, RegId, Flag, ppBuffer, & dataLength ); if ( status == ERROR_SUCCESS ) { goto Done; } } // try DNS cache -- if available hkey = psession->hCache; if ( hkey && REGPROP_CACHE(RegId) ) { status = privateRegReadValue( hkey, RegId, Flag, ppBuffer, & dataLength ); if ( status == ERROR_SUCCESS ) { goto Done; } } // unsuccessful -- use TCPIP key hkey = psession->hTcpip; if ( !hkey ) { goto Done; } } // // explict key OR standard key case // status = privateRegReadValue( hkey, RegId, Flag, ppBuffer, & dataLength ); if ( status == ERROR_SUCCESS ) { goto Done; } FailedDone: // // if failed // - for REG_DWORD, default the value // - for strings, ensure NULL return buffer // this takes care of cases where privateRegReadValue() // never got called // if ( status != ERROR_SUCCESS ) { if ( ValueType == REG_DWORD ) { *(PDWORD) ppBuffer = REGPROP_DEFAULT( RegId ); } else { *ppBuffer = NULL; } } Done: // cleanup any regkey's opened if ( psession == &session ) { Reg_CloseSession( psession ); } if ( hadapterKey ) { RegCloseKey( hadapterKey ); } return( status ); } DNS_STATUS Reg_GetIpArray( IN PREG_SESSION pRegSession, OPTIONAL IN HKEY hRegKey, OPTIONAL IN LPSTR pwsAdapter, OPTIONAL IN DWORD RegId, IN DWORD ValueType, OUT PIP_ARRAY * ppIpArray ) /*++ Routine Description: Arguments: pRegSession -- ptr to registry session, OPTIONAL hRegKey -- handle to open regkey OPTIONAL pwsAdapter -- name of adapter to query under OPTIONAL RegId -- value ID ValueType -- currently ignored, but could later use to distinguish REG_SZ from REG_MULTI_SZ processing ppIpArray -- addr to receive IP array ptr - array is allocated with Dns_Alloc(), caller must free with Dns_Free() Return Value: ERROR_SUCCESS if successful. Registry error code on failure. --*/ { DNS_STATUS status; PSTR pstring = NULL; DNSDBG( REGISTRY, ( "Reg_GetIpArray( s=%p, k=%p, id=%d )\n", pRegSession, hRegKey, RegId )); // // make call to get IP array as string // status = Reg_GetValueEx( pRegSession, hRegKey, pwsAdapter, RegId, REG_SZ, // only supported type is REG_SZ 0, // no flag & pstring ); if ( status != ERROR_SUCCESS ) { ASSERT( pstring == NULL ); return( status ); } // // convert from string to IP array // // note: this call is limited to a parsing limit // but it is a large number suitable for stuff // like DNS server lists // // DCR: use IP array builder for local IP address // then need Dns_CreateIpArrayFromMultiIpString() // to use count\alloc method when buffer overflows // status = Dns_CreateIpArrayFromMultiIpString( pstring, ppIpArray ); // cleanup if ( pstring ) { FREE_HEAP( pstring ); } return( status ); } // // Registry writing routines // HKEY WINAPI Reg_CreateKey( IN PWSTR pwsKeyName, IN BOOL bWrite ) /*++ Routine Description: Open registry key. The purpose of this routine is simply to functionalize opening with\without an adapter name. So caller can pass through adapter name argument instead of building key name or doing two opens for adapter present\absent. This is NT only. Arguments: pwsKeyName -- key "name" this is one of the REGKEY_X from registry.h OR adapter name bWrite -- TRUE for write access, FALSE for read Return Value: New opened key. --*/ { HKEY hkey = NULL; DWORD disposition; DWORD status; PWSTR pnameKey; WCHAR nameBuffer[ MAX_PATH ]; // // don't bother for Win9x // // DCR_QUESTION: any need for use with Win9x // #if DNSWIN9X if ( g_IsWin9X ) { ASSERT( FALSE ); SetLastError( ERROR_INVALID_PARAMETER ); return( NULL ); } #endif // // determine key name // // this is either DNSKEY_X dummy pointer from registry.h // OR // is an adapter name; // // - if adapter given, open under it // adapters are under TCPIP\Interfaces // - any other specific key // - default is TCPIP params key // // note: if if grows too big, turn into table // if ( pwsKeyName <= REGKEY_DNS_MAX ) { if ( pwsKeyName == REGKEY_TCPIP_PARAMETERS ) { pnameKey = TCPIP_PARAMETERS_KEY; } else if ( pwsKeyName == REGKEY_DNS_CACHE ) { pnameKey = DNS_CACHE_KEY; } else if ( pwsKeyName == REGKEY_DNS_POLICY ) { pnameKey = DNS_POLICY_KEY; } else if ( pwsKeyName == REGKEY_SETUP_MODE_LOCATION ) { pnameKey = NT_SETUP_MODE_KEY; } else { pnameKey = TCPIP_PARAMETERS_KEY; } } else // adapter name { wcscpy( nameBuffer, TCPIP_INTERFACES_KEY ); wcscat( nameBuffer, pwsKeyName ); pnameKey = nameBuffer; } // // create\open key // if ( bWrite ) { status = RegCreateKeyExW( HKEY_LOCAL_MACHINE, pnameKey, 0, L"Class", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, & hkey, & disposition ); } else { status = RegOpenKeyExW( HKEY_LOCAL_MACHINE, pnameKey, 0, KEY_QUERY_VALUE, & hkey ); } if ( status != ERROR_SUCCESS ) { SetLastError( status ); } ELSE_ASSERT( hkey != NULL ); return( hkey ); } DNS_STATUS WINAPI Reg_SetDwordValueByName( IN PVOID pReserved, IN HKEY hRegKey, IN PWSTR pwsNameKey, OPTIONAL IN PWSTR pwsNameValue, OPTIONAL IN DWORD dwValue ) /*++ Routine Description: Set DWORD regkey. Arguments: pReserved -- reserved (may become session) hRegKey -- existing key to set under OPTIONAL pwsNameKey -- name of key or adapter to set under pwsNameValue -- name of reg value to set dwValue -- value to set Return Value: ERROR_SUCCESS if successful. ErrorCode on failure. --*/ { HKEY hkey; DNS_STATUS status; // // don't bother for Win9x // #if DNSWIN9X if ( g_IsWin9X ) { ASSERT( FALSE ); return( ERROR_INVALID_PARAMETER ); } #endif // // open key, if not provided // - if adapter given, open under it // - otherwise TCPIP params key // hkey = hRegKey; if ( !hkey ) { hkey = Reg_CreateKey( pwsNameKey, TRUE // open for write ); if ( !hkey ) { return( GetLastError() ); } } // // write back value // status = RegSetValueExW( hkey, pwsNameValue, 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD) ); if ( !hRegKey ) { RegCloseKey( hkey ); } return status; } DNS_STATUS WINAPI Reg_SetDwordValue( IN PVOID pReserved, IN HKEY hRegKey, IN PWSTR pwsNameKey, OPTIONAL IN DWORD RegId, IN DWORD dwValue ) /*++ Routine Description: Set DWORD regkey. Arguments: pReserved -- reserved (may become session) hRegKey -- existing key to set under OPTIONAL pwsNameKey -- name of key or adapter to set under RegId -- id of value to set dwValue -- value to set Return Value: ERROR_SUCCESS if successful. ErrorCode on failure. --*/ { // // write back value using name of id // return Reg_SetDwordValueByName( pReserved, hRegKey, pwsNameKey, REGPROP_NAME( RegId ), dwValue ); } #if DNSWIN9X // // Removed with Win95 support // Further Win95 support is doubtful -- but this works if needed. // // // Registry shims // Win9x in not providing wide reg calls. // PCHAR standardWideToAnsiConvert( IN PWSTR pWide, OUT PCHAR pAnsiBuf ) /*++ Routine Description: Standard conversion of wide to narron for shimming registry calls. Arguments: pWide -- parameter in wide call pAnsiBuf -- buffer to hold ANSI param; assumed to be max path Return Value: Ptr to converted buffer. NULL if pWide NULL or error in conversion. --*/ { DWORD length; DWORD result; // no wide param, no ANSI param if ( !pWide ) { return NULL; } // copy\convert to ANSI buf length = MAX_PATH; result = Dns_StringCopy( pAnsiBuf, &length, (PSTR) pWide, 0, // calc length DnsCharSetUnicode, DnsCharSetAnsi ); if ( result == 0 ) { return( NULL ); } else { return( pAnsiBuf ); } } //WINADVAPI LONG WINAPI DnsShimRegCreateKeyExW( IN HKEY hKey, IN LPCWSTR lpSubKey, IN DWORD Reserved, IN LPWSTR lpClass, IN DWORD dwOptions, IN REGSAM samDesired, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes, OUT PHKEY phkResult, OUT LPDWORD lpdwDisposition ) /*++ Routine Description: Shim RegCreateKeyExW() Arguments: See SDK doc. Return Value: ERROR_SUCCESS if successful. Error code on failure. --*/ { // for NT it's a dumb stub if ( !g_IsWin9X ) { return RegCreateKeyExW( hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition ); } // for Win9x, convert to ANSI and call else { PCHAR pclass; PCHAR psubKey; CHAR class[ MAX_PATH ]; CHAR subKey[ MAX_PATH ]; psubKey = standardWideToAnsiConvert( (PWSTR) lpSubKey, subKey ); if ( !psubKey ) { return ERROR_INVALID_PARAMETER; } pclass = standardWideToAnsiConvert( (PWSTR) lpClass, class ); return RegCreateKeyExA( hKey, (LPCSTR) psubKey, Reserved, pclass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition ); } } //WINADVAPI LONG WINAPI DnsShimRegOpenKeyExW( IN HKEY hKey, IN LPCWSTR lpSubKey, IN DWORD dwOptions, IN REGSAM samDesired, OUT PHKEY phkResult ) /*++ Routine Description: Shim RegOpenKeyExW() Arguments: See SDK doc. Return Value: ERROR_SUCCESS if successful. Error code on failure. --*/ { // for NT it's a dumb stub if ( !g_IsWin9X ) { return RegOpenKeyExW( hKey, lpSubKey, dwOptions, samDesired, phkResult ); } // for Win9x, convert to ANSI and call else { PCHAR psubKey; CHAR subKey[ MAX_PATH ]; psubKey = standardWideToAnsiConvert( (PWSTR) lpSubKey, subKey ); return RegOpenKeyExA( hKey, (LPCSTR) psubKey, dwOptions, samDesired, phkResult ); } } LONG WINAPI DnsShimRegQueryValueExW( IN HKEY hKey, IN LPCWSTR lpValueName, IN LPDWORD lpReserved, IN LPDWORD lpType, IN LPBYTE lpData, IN LPDWORD lpcbData ) /*++ Routine Description: Shim RegQueryValueExW() Arguments: See SDK doc. Return Value: ERROR_SUCCESS if successful. Error code on failure. --*/ { // for NT it's a dumb stub if ( !g_IsWin9X ) { return RegQueryValueExW( hKey, lpValueName, lpReserved, lpType, lpData, lpcbData ); } // for Win9x, convert to ANSI and call else { LONG status; DWORD type = 0; DWORD length = 0; PDWORD plength = &length; PCHAR pvalueName; CHAR valueName[ MAX_PATH ]; // convert value name pvalueName = standardWideToAnsiConvert( (PWSTR) lpValueName, valueName ); // use start length if ( lpcbData ) { length = *lpcbData; plength = NULL; } // make ANSI call status = RegQueryValueExA( hKey, pvalueName, lpReserved, & type, lpData, plength ); // return type if ( lpType ) { *lpType = type; } // // setup length\data return // - no length call => just status // - no data return or non-string data => set length // - string data => convert // if ( !lpcbData ) { // no-op, this kind of call just gets status } // // no data conversion -- set length // - unicode length required at twice ANSI length // - DWORD and binary length unchanged else if ( !lpData || status != ERROR_SUCCESS || type == REG_DWORD || type == REG_BINARY ) { if ( type == REG_DWORD || type == REG_BINARY ) { *lpcbData = length; } else { *lpcbData = length * 2; } } // // convert ANSI data to unicode // else { PBYTE pdataUnicode; DWORD unicodeLength; pdataUnicode = (PBYTE) ALLOCATE_HEAP( *lpcbData ); if ( !pdataUnicode ) { return( DNS_ERROR_NO_MEMORY ); } unicodeLength = Dns_StringCopy( pdataUnicode, lpcbData, lpData, // ANSI data length, // ANSI length DnsCharSetAnsi, DnsCharSetUnicode ); if ( unicodeLength == 0 ) { status = GetLastError(); ASSERT( status != ERROR_SUCCESS ); unicodeLength = length * 2; } *lpcbData = unicodeLength; FREE_HEAP( pdataUnicode ); } return( status ); } } #endif // Win95 registry shims // // End registry.c //