/*++ Copyright (C) Microsoft Corporation, 1997 - 1999 Module Name: sensapi.cxx Abstract: Implementation of the SENS Connectivity APIs. These are just stubs which call into SENS to do the actual work. Author: Gopal Parupudi [Notes:] optional-notes Revision History: GopalP 12/4/1997 Start. --*/ #include #include #include #include #include #include // // Globals // RPC_BINDING_HANDLE ghSens; CRITICAL_SECTION gSensapiLock; DWORD gdwCacheLastUpdatedTime; DWORD gdwCacheFirstReadTime; HANDLE ghSensFileMap; PSENS_CACHE gpSensCache; BOOL IsNetworkAlive( OUT LPDWORD lpdwFlags ) /*++ Routine Description: We try to find out if this machine has any network connectivity. Arguments: lpdwFlags - Receives information regarding the nature of the machine's network connectivity. It can be on of the following: o NETWORK_ALIVE_WAN o NETWORK_ALIVE_LAN Notes: a. This is available only for TCP/IP b. This API does not generate any Network traffic. Return Value: TRUE, if there is network connectivity FALSE, otherwise. Use GetLastError() to retrieve more error information. --*/ { BOOL bNetState; RPC_STATUS RpcStatus; DWORD fNetNature; DWORD dwLastError; // Basic parameter checks if (lpdwFlags == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // OUT parameter intialization. *lpdwFlags = 0x0; fNetNature = 0x0; dwLastError = ERROR_SUCCESS; bNetState = FALSE; if (TRUE == ReadConnectivityCache(lpdwFlags)) { return TRUE; } // // Need to contact SENS for information. // RpcStatus = DoRpcSetup(); // // Try to get the state information. // if (RPC_S_OK == RpcStatus) { RpcStatus = RPC_IsNetworkAlive( ghSens, &fNetNature, &bNetState, &dwLastError ); } if ( (RPC_S_OK != RpcStatus) || (RPC_S_SERVER_UNAVAILABLE == dwLastError)) { // // An RPC failure occurred. We treat this as a success and // set to return values to default values. // bNetState = TRUE; fNetNature = NETWORK_ALIVE_LAN; if (RPC_S_OK != RpcStatus) { dwLastError = RpcStatus; } } ASSERT((bNetState == TRUE) || (bNetState == FALSE)); ASSERT(fNetNature <= (NETWORK_ALIVE_LAN | NETWORK_ALIVE_WAN | NETWORK_ALIVE_AOL)); *lpdwFlags = fNetNature; SetLastError(dwLastError); // Since we retrieved information from SENS directly, reset the flag that // that indicates that we read from the cache. gdwCacheFirstReadTime = 0x0; return (bNetState); } #if !defined(SENS_CHICAGO) BOOL IsDestinationReachableA( LPCSTR lpszDestination, LPQOCINFO lpQOCInfo ) /*++ Routine Description: Given the name of a destination (IP Address, UNC, URL etc), we try to see if it is reachable. Arguments: lpszDestination - The destination (an ANSI string) whose rechability is of interest. lpQOCInfo - Pointer to a buffer that will receive Quality of Connection (QOC) Information. Can be NULL if QOC is not desired. Notes: a. This is available only for TCP/IP Return Value: TRUE, if the destination is reachable. FALSE, otherwise. Use GetLastError() to retrieve more error information. --*/ { BOOL bReachable; NTSTATUS NtStatus; ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; size_t uiLength; // Basic parameter checks if ( (lpszDestination == NULL) || ((uiLength = strlen(lpszDestination)) > MAX_DESTINATION_LENGTH) || (uiLength == 0)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // OUT parameter intialization. bReachable = FALSE; RtlInitAnsiString(&AnsiString, (PSZ)lpszDestination); NtStatus = RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, TRUE ); if (!NT_SUCCESS(NtStatus)) { SetLastError(ERROR_OUTOFMEMORY); // Only possible error. return bReachable; } // Call the Unicode version. bReachable = IsDestinationReachableW( UnicodeString.Buffer, lpQOCInfo ); ASSERT((bReachable == TRUE) || (bReachable == FALSE)); RtlFreeUnicodeString(&UnicodeString); return (bReachable); } BOOL IsDestinationReachableW( LPCWSTR lpszDestination, LPQOCINFO lpQOCInfo ) /*++ Routine Description: Given the name of a destination (IP Address, UNC, URL etc), we try to see if it is reachable. Arguments: lpszDestination - The destination (a UNICODE string) whose rechability is of interest. lpQOCInfo - Pointer to a buffer that will receive Quality of Connection (QOC) Information. Can be NULL if QOC is not desired. Notes: a. This is available only for TCP/IP Return Value: TRUE, if the destination is reachable. FALSE, otherwise. Use GetLastError() to retrieve more error information. --*/ { BOOL bReachable; RPC_STATUS RpcStatus; DWORD dwLastError; DWORD dwCallerQOCInfoSize; size_t uiLength; // Basic parameter checks if ( (lpszDestination == NULL) || ((uiLength = wcslen(lpszDestination)) > MAX_DESTINATION_LENGTH) || (uiLength == 0)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // OUT parameter intialization. dwLastError = ERROR_SUCCESS; bReachable = FALSE; if (lpQOCInfo != NULL) { dwCallerQOCInfoSize = lpQOCInfo->dwSize; memset(lpQOCInfo, 0, lpQOCInfo->dwSize); lpQOCInfo->dwSize = dwCallerQOCInfoSize; } RpcStatus = DoRpcSetup(); // // Try to get the state information. // if (RPC_S_OK == RpcStatus) { RpcStatus = RPC_IsDestinationReachableW( ghSens, (PSENS_CHAR) lpszDestination, lpQOCInfo, &bReachable, &dwLastError ); } if ( (RPC_S_OK != RpcStatus) || (RPC_S_SERVER_UNAVAILABLE == dwLastError)) { // // An RPC failure occurred. We treat this as a success and // set to return values to default values. // if (lpQOCInfo != NULL) { lpQOCInfo->dwFlags = NETWORK_ALIVE_LAN; lpQOCInfo->dwInSpeed = DEFAULT_LAN_BANDWIDTH; lpQOCInfo->dwOutSpeed = DEFAULT_LAN_BANDWIDTH; } bReachable = TRUE; if (RPC_S_OK != RpcStatus) { dwLastError = RpcStatus; } } ASSERT((bReachable == TRUE) || (bReachable == FALSE)); SetLastError(dwLastError); return (bReachable); } #else // SENS_CHICAGO BOOL IsDestinationReachableA( LPCSTR lpszDestination, LPQOCINFO lpQOCInfo ) /*++ Routine Description: Given the name of a destination (IP Address, UNC, URL etc), we try to see if it is reachable. Arguments: lpszDestination - The destination (a UNICODE string) whose rechability is of interest. lpQOCInfo - Pointer to a buffer that will receive Quality of Connection (QOC) Information. Can be NULL if QOC is not desired. Notes: a. This is available only for TCP/IP Return Value: TRUE, if the destination is reachable. FALSE, otherwise. Use GetLastError() to retrieve more error information. --*/ { BOOL bReachable; RPC_STATUS RpcStatus; DWORD dwLastError; DWORD dwCallerQOCInfoSize; size_t uiLength; // Basic parameter checks if ( (lpszDestination == NULL) || ((uiLength = strlen(lpszDestination)) > MAX_DESTINATION_LENGTH) || (uiLength == 0)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // OUT parameter intialization. dwLastError = ERROR_SUCCESS; bReachable = FALSE; if (lpQOCInfo != NULL) { dwCallerQOCInfoSize = lpQOCInfo->dwSize; memset(lpQOCInfo, 0, lpQOCInfo->dwSize); lpQOCInfo->dwSize = dwCallerQOCInfoSize; } RpcStatus = DoRpcSetup(); // // Try to get the state information. // if (RPC_S_OK == RpcStatus) { RpcStatus = RPC_IsDestinationReachableA( ghSens, (LPSTR) lpszDestination, lpQOCInfo, &bReachable, &dwLastError ); } if ( (RPC_S_OK != RpcStatus) || (RPC_S_SERVER_UNAVAILABLE == dwLastError)) { // // An RPC failure occurred. We treat this as a success and // set to return values to default values. // if (lpQOCInfo != NULL) { lpQOCInfo->dwFlags = NETWORK_ALIVE_LAN; lpQOCInfo->dwInSpeed = DEFAULT_LAN_BANDWIDTH; lpQOCInfo->dwOutSpeed = DEFAULT_LAN_BANDWIDTH; } bReachable = TRUE; if (RPC_S_OK != RpcStatus) { dwLastError = RpcStatus; } } ASSERT((bReachable == TRUE) || (bReachable == FALSE)); SetLastError(dwLastError); return (bReachable); } BOOL IsDestinationReachableW( LPCWSTR lpszDestination, LPQOCINFO lpQOCInfo ) /*++ Routine Description: This is just a stub on Win9x platforms. It returns FALSE always. This is provided for consistency between NT and Win9x platforms. Arguments: lpszDestination - The destination (a UNICODE string) whose rechability is of interest. lpQOCInfo - Pointer to a buffer that will receive Quality of Connection (QOC) Information. Can be NULL if QOC is not desired. Return Value: FALSE, always. Use GetLastError() to retrieve more error information. --*/ { SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } #endif // SENS_CHICAGO inline RPC_STATUS DoRpcSetup( void ) /*++ Routine Description: Do the miscellaneous work to talk to SENS via RPC. Arguments: None. Return Value: None. --*/ { RPC_STATUS status; SENS_CHAR * BindingString; RPC_BINDING_HANDLE hServer = NULL; status = RPC_S_OK; BindingString = NULL; if (ghSens != NULL) { return (status); } RequestLock(); if (ghSens != NULL) { ReleaseLock(); return (status); } status = RpcStringBindingCompose( NULL, // NULL ObjUuid SENS_PROTSEQ, NULL, // Local machine SENS_ENDPOINT, NULL, // No Options &BindingString ); if (BindingString) { status = RpcBindingFromStringBinding(BindingString, &hServer); } if (status == RPC_S_OK) { RPC_SECURITY_QOS RpcSecQos; RpcSecQos.Version= RPC_C_SECURITY_QOS_VERSION_1; RpcSecQos.ImpersonationType= RPC_C_IMP_LEVEL_IMPERSONATE; RpcSecQos.IdentityTracking= RPC_C_QOS_IDENTITY_DYNAMIC; RpcSecQos.Capabilities= RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; status= RpcBindingSetAuthInfoEx(hServer, L"NT Authority\\System", RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_WINNT, NULL, RPC_C_AUTHZ_NONE, (RPC_SECURITY_QOS *)&RpcSecQos); if (RPC_S_OK != status) { RpcBindingFree(&hServer); hServer = NULL; } } ghSens = hServer; ReleaseLock(); if (BindingString != NULL) { RpcStringFree(&BindingString); } return (status); } BOOL MapSensCacheView( void ) /*++ Routine Description: Prepare to read SENS information cache. Arguments: None. Notes: Should call it under a lock. Return Value: TRUE, if successful. FALSE, otherwise. --*/ { // // First, open the SENS cache file mapping object // ghSensFileMap = OpenFileMapping( FILE_MAP_READ, // Protection for mapping object FALSE, // Inherit flag SENS_CACHE_NAME // Name of the file mapping object ); if (NULL == ghSensFileMap) { goto Cleanup; } // // Map a view of SENS cache into the address space // gpSensCache = (PSENS_CACHE) MapViewOfFile( ghSensFileMap, // Map file object FILE_MAP_READ, // Access mode 0, // High-order 32 bits of file offset 0, // Low-order 32 bits of file offset 0 // Number of bytes to map ); if (NULL == gpSensCache) { goto Cleanup; } ASSERT(gpSensCache->dwCacheVer >= SENS_CACHE_VERSION); ASSERT(gpSensCache->dwCacheSize >= sizeof(SENS_CACHE)); return TRUE; Cleanup: // // Cleanup // if (ghSensFileMap != NULL) { CloseHandle(ghSensFileMap); } if (gpSensCache != NULL) { UnmapViewOfFile(gpSensCache); } ghSensFileMap = NULL; gpSensCache = NULL; return FALSE; } void UnmapSensCacheView( void ) /*++ Routine Description: Cleanup resources related to SENS information cache. Arguments: None. Notes: None. Return Value: None. --*/ { BOOL bStatus; // // Unmap the view of SENS cache from the address space // if (gpSensCache != NULL) { bStatus = UnmapViewOfFile(gpSensCache); ASSERT(bStatus); } // // Close File Mapping object // if (ghSensFileMap != NULL) { bStatus = CloseHandle(ghSensFileMap); ASSERT(bStatus); } } BOOL ReadConnectivityCache( OUT LPDWORD lpdwFlags ) /*++ Routine Description: Try to read SENS connectivity cache. Talk to SENS iff one of the following conditions is TRUE: o Failed to read the connectivity cache. o Read the cache but connectivity state is FALSE. o Read the cache and connectivity state is TRUE but stale. o Read the cache and there is updated information available. Arguments: lpdwFlags - OUT parameter that contains the connectivity state. Return Value: TRUE, successfully got cached information. FALSE, SENS needs to be contacted. --*/ { DWORD dwNow; dwNow = GetTickCount(); RequestLock(); // Failed to initialize/read Sens Cache if ( (NULL == gpSensCache) && (FALSE == MapSensCacheView())) { goto Cleanup; } // Cache has been updated since we last read. Note that dwLastUpdateTime // can wrap around. if (gpSensCache->dwLastUpdateTime != gdwCacheLastUpdatedTime) { gdwCacheLastUpdatedTime = gpSensCache->dwLastUpdateTime; goto Cleanup; } // It's been a while. if ( (gdwCacheFirstReadTime != 0x0) && (dwNow - gdwCacheFirstReadTime) > CACHE_VALID_INTERVAL) { goto Cleanup; } // Cached state is FALSE if (0x0 == gpSensCache->dwLastUpdateState) { goto Cleanup; } *lpdwFlags = gpSensCache->dwLastUpdateState; if (0 == gdwCacheFirstReadTime) { gdwCacheFirstReadTime = dwNow; } ASSERT(gdwCacheLastUpdatedTime == gpSensCache->dwLastUpdateTime); ReleaseLock(); SetLastError(ERROR_SUCCESS); return TRUE; Cleanup: // // Cleanup // ReleaseLock(); // Don't need to SetLastError() as we will go to SENS to retrieve it. return FALSE; } extern "C" int APIENTRY DllMain( IN HINSTANCE hInstance, IN DWORD dwReason, IN LPVOID lpvReserved ) /*++ Routine Description: This routine will get called either when a process attaches to this dll or when a process detaches from this dll. Return Value: TRUE - Initialization successfully occurred. FALSE - Insufficient memory is available for the process to attach to this dll. --*/ { BOOL bSuccess; RPC_STATUS RpcStatus; switch (dwReason) { case DLL_PROCESS_ATTACH: // Disable Thread attach/detach calls bSuccess = DisableThreadLibraryCalls(hInstance); ASSERT(bSuccess == TRUE); // Initialize the lock InitializeCriticalSection(&gSensapiLock); break; case DLL_PROCESS_DETACH: // Clean the lock DeleteCriticalSection(&gSensapiLock); // Cleanup cache related resources UnmapSensCacheView(); // Cleanup RPC Binding handle if (ghSens != NULL) { RpcStatus = RpcBindingFree(&ghSens); ASSERT(RPC_S_OK == RpcStatus); } break; } return(TRUE); }