/*++ Copyright (c) 1997 Microsoft Corporation Module Name: autodial.cxx Abstract: Contains the implementation of autodial Contents: Author: Darren Mitchell (darrenmi) 22-Apr-1997 Environment: Win32(s) user-mode DLL Revision History: 14-Jan-2002 ssulzer Ported small subset to WinHttp 22-Apr-1997 darrenmi Created --*/ #include "wininetp.h" #include "autodial.h" #include "rashelp.h" #include #include // Globals. DWORD g_dwLastTickCount = 0; // serialize access to RAS HANDLE g_hRasMutex = INVALID_HANDLE_VALUE; // don't check RNA state more than once every 10 seconds #define MIN_RNA_BUSY_CHECK_INTERVAL 10000 // // Current ras connections - used so we don't poll ras every time we're // interested - only poll every 10 seconds (const. above) // RasEnumConnHelp * g_RasCon; DWORD g_dwConnections = 0; BOOL g_fRasInstalled = FALSE; DWORD g_dwLastDialupTicks = 0; // // Control of autodial initialization // BOOL g_fAutodialInitialized = FALSE; /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // // RAS dynaload code // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// static HINSTANCE g_hRasLib = NULL; static _RASENUMENTRIESW pfnRasEnumEntriesW = NULL; static _RASGETCONNECTSTATUSW pfnRasGetConnectStatusW = NULL; static _RASENUMCONNECTIONSW pfnRasEnumConnectionsW = NULL; static _RASGETENTRYPROPERTIESW pfnRasGetEntryPropertiesW = NULL; typedef struct _tagAPIMAPENTRY { FARPROC* pfn; LPSTR pszProc; } APIMAPENTRY; APIMAPENTRY rgRasApiMapW[] = { { (FARPROC*) &pfnRasEnumEntriesW, "RasEnumEntriesW" }, { (FARPROC*) &pfnRasGetConnectStatusW, "RasGetConnectStatusW" }, { (FARPROC*) &pfnRasEnumConnectionsW, "RasEnumConnectionsW" }, { (FARPROC*) &pfnRasGetEntryPropertiesW, "RasGetEntryPropertiesW"}, { NULL, NULL }, }; #define RASFCN(_fn, _part, _par, _dbge, _dbgl) \ DWORD _##_fn _part \ { \ DEBUG_ENTER(_dbge); \ \ DWORD dwRet; \ if(NULL == pfn##_fn) \ { \ _dbgl(ERROR_INVALID_FUNCTION); \ return ERROR_INVALID_FUNCTION; \ } \ \ dwRet = (* pfn##_fn) _par; \ \ _dbgl(dwRet); \ return dwRet; \ } RASFCN(RasEnumEntriesW, (LPWSTR lpszReserved, LPWSTR lpszPhonebook, LPRASENTRYNAMEW lprasentryname, LPDWORD lpcb, LPDWORD lpcEntries), (lpszReserved, lpszPhonebook, lprasentryname, lpcb, lpcEntries), (DBG_DIALUP, Dword, "RasEnumEntriesW", "%#x (%Q), %#x (%Q), %#x, %#x %#x", lpszReserved, lpszReserved, lpszPhonebook, lpszPhonebook, lprasentryname, lpcb, lpcEntries), DEBUG_LEAVE ); RASFCN(RasGetConnectStatusW, (HRASCONN hrasconn, LPRASCONNSTATUSW lprasconnstatus), (hrasconn, lprasconnstatus), (DBG_DIALUP, Dword, "RasGetConnectStatusW", "%#x, %#x", hrasconn, lprasconnstatus), DEBUG_LEAVE ); RASFCN(RasGetEntryPropertiesW, (LPWSTR lpszPhonebook, LPWSTR lpszEntry, LPRASENTRYW lpRasEntry, LPDWORD lpdwEntryInfoSize, LPBYTE lpbDeviceInfo, LPDWORD lpdwDeviceInfoSize), (lpszPhonebook, lpszEntry, lpRasEntry, lpdwEntryInfoSize, lpbDeviceInfo, lpdwDeviceInfoSize), (DBG_DIALUP, Dword, "RasGetEntryPropertiesW", "%#x (%Q), %#x (%Q), %#x, %#x, %#x %#x", lpszPhonebook, lpszPhonebook, lpszEntry, lpszEntry, lpRasEntry, lpdwEntryInfoSize, lpbDeviceInfo, lpdwDeviceInfoSize), DEBUG_LEAVE ); RASFCN(RasEnumConnectionsW, (LPRASCONNW lpRasConn, LPDWORD lpdwSize, LPDWORD lpdwConn), (lpRasConn, lpdwSize, lpdwConn), (DBG_DIALUP, Dword, "RasEnumConnectionsW", "%#x, %#x, %#x", lpRasConn, lpdwSize, lpdwConn), DEBUG_LEAVE ); /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// BOOL EnsureRasLoaded( VOID ) /*++ Routine Description: Dynaload ras apis Arguments: pfInstalled - return installed state of ras Return Value: BOOL TRUE - Ras loaded FALSE - Ras not loaded --*/ { DEBUG_ENTER((DBG_DIALUP, Bool, "EnsureRasLoaded", NULL )); // // Looks like RAS is installed - try and load it up! // if(NULL == g_hRasLib) { g_hRasLib = LoadLibrary("RASAPI32.DLL"); if(NULL == g_hRasLib) { DEBUG_LEAVE(FALSE); return FALSE; } APIMAPENTRY *prgRasApiMap = rgRasApiMapW; int nIndex = 0; while ((prgRasApiMap+nIndex)->pszProc != NULL) { // Some functions are only present on some platforms. Don't // assume this succeeds for all functions. *(prgRasApiMap+nIndex)->pfn = GetProcAddress(g_hRasLib, (prgRasApiMap+nIndex)->pszProc); nIndex++; } } if(g_hRasLib) { DEBUG_LEAVE(TRUE); return TRUE; } DEBUG_LEAVE(FALSE); return FALSE; } BOOL IsRasInstalled( VOID ) /*++ Routine Description: Determines whether ras is installed on this machine Arguments: none Return Value: BOOL TRUE - Ras is installed FALSE - Ras is not installed --*/ { DEBUG_ENTER_API((DBG_DIALUP, Bool, "IsRasInstalled", NULL )); static fChecked = FALSE; // // If RAS is already loaded, don't bother doing any work. // if(g_hRasLib) { DEBUG_LEAVE_API(TRUE); return TRUE; } // // if we've already done the check, don't do it again // if(fChecked) { DEBUG_LEAVE_API(g_fRasInstalled); return g_fRasInstalled; } OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); if (osvi.dwMajorVersion < 5) { // WinHttp does not support the use of RAS on NT4 g_fRasInstalled = FALSE; } else { // NT5 and presumably beyond, ras is always installed g_fRasInstalled = TRUE; } fChecked = TRUE; DEBUG_LEAVE_API(g_fRasInstalled); return g_fRasInstalled; } BOOL DoConnectoidsExist( VOID ) /*++ Routine Description: Determines whether any ras connectoids exist Arguments: none Return Value: BOOL TRUE - Connectoids exist FALSE - No connectoids exist --*/ { DEBUG_ENTER_API((DBG_DIALUP, Bool, "DoConnectoidsExist", NULL )); static BOOL fExist = FALSE; // // If we found connectoids before, don't bother looking again // if(fExist) { DEBUG_LEAVE_API(TRUE); return TRUE; } // // If RAS is already loaded, ask it // if(g_hRasLib) { DWORD dwRet, dwEntries; RasEnumHelp *pRasEnum = new RasEnumHelp; if (pRasEnum) { dwRet = pRasEnum->GetError(); dwEntries = pRasEnum->GetEntryCount(); delete pRasEnum; } else { dwRet = ERROR_NOT_ENOUGH_MEMORY; dwEntries = 0; } // If ras tells us there are none, return none if(ERROR_SUCCESS == dwRet && 0 == dwEntries) { DEBUG_LEAVE_API(FALSE); return FALSE; } // couldn't determine that there aren't any so assume there are. fExist = TRUE; DEBUG_LEAVE_API(TRUE); return TRUE; } // // if ras isn't installed, say no connectoids // if(FALSE == IsRasInstalled()) { DEBUG_LEAVE_API(FALSE); return FALSE; } OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); // assume connectoids exist fExist = TRUE; if (osvi.dwMajorVersion < 5) { fExist = FALSE; } DEBUG_LEAVE_API(fExist); return fExist; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // // Initialization // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// BOOL InitAutodialModule() /*++ Routine Description: Initialize autodial code Arguments: None. Return Value: None. --*/ { DEBUG_ENTER((DBG_DIALUP, Bool, "InitAutodialModule", NULL )); // Assert that WinHttp's global data has already been initialized INET_ASSERT(GlobalDataInitialized); // only do this once... if(g_fAutodialInitialized) { DEBUG_LEAVE(g_fAutodialInitialized); return g_fAutodialInitialized; } if (GlobalDataInitCritSec.Lock()) { if (!g_fAutodialInitialized) { // create mutex to serialize access to RAS (per process) g_hRasMutex = CreateMutex(NULL, FALSE, NULL); if (g_hRasMutex != INVALID_HANDLE_VALUE) { g_RasCon = new RasEnumConnHelp(); if (g_RasCon != NULL) { g_fAutodialInitialized = TRUE; } else { CloseHandle(g_hRasMutex); g_hRasMutex = INVALID_HANDLE_VALUE; } } } GlobalDataInitCritSec.Unlock(); } DEBUG_LEAVE(g_fAutodialInitialized); return g_fAutodialInitialized; } VOID ExitAutodialModule( VOID ) /*++ Routine Description: Clean up autodial code Arguments: None. Return Value: None. --*/ { DEBUG_ENTER((DBG_DIALUP, None, "ExitAutodialModule", NULL )); // don't do anything if not initialized if(FALSE == g_fAutodialInitialized) { DEBUG_LEAVE(0); return; } if (g_RasCon) { delete g_RasCon; g_RasCon = NULL; } if(INVALID_HANDLE_VALUE != g_hRasMutex) { CloseHandle(g_hRasMutex); g_hRasMutex = INVALID_HANDLE_VALUE; } if (g_hRasLib) { FreeLibrary(g_hRasLib); g_hRasLib = NULL; } g_fAutodialInitialized = FALSE; DEBUG_LEAVE(0); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // // Connection management code // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// LPSTR GetActiveConnectionName() /*++ Routine Description: Figure out the current connection and fix proxy settings for it. Basically a cheap, return-no-info version of GetConnectedStateEx used by the winsock callback. Arguments: none Return Value: BOOL TRUE - connected FALSE - not connected --*/ { DEBUG_ENTER((DBG_DIALUP, String, "GetActiveConnectionName", NULL )); static BOOL fRasLoaded = FALSE; DWORD dwNewTickCount, dwElapsed; DWORD dwConnection = 0; LPSTR lpstrConnection = NULL; // // Make sure everything's initialized // if (!InitAutodialModule()) goto quit; // // serialize // WaitForSingleObject(g_hRasMutex, INFINITE); // // Check out how recently we polled ras // dwNewTickCount = GetTickCountWrap(); dwElapsed = dwNewTickCount - g_dwLastDialupTicks; // // Only refresh if more than MIN... ticks has passed // if(dwElapsed >= MIN_RNA_BUSY_CHECK_INTERVAL) { g_dwLastDialupTicks = dwNewTickCount; if(DoConnectoidsExist()) { if(FALSE == fRasLoaded) fRasLoaded = EnsureRasLoaded(); if(fRasLoaded) { g_RasCon->Enum(); if(g_RasCon->GetError() == 0) g_dwConnections = g_RasCon->GetConnectionsCount(); else g_dwConnections = 0; } } else { g_dwConnections = 0; } } DEBUG_PRINT(DIALUP, INFO, ("Found %d connections\n", g_dwConnections)); if(g_dwConnections > 1) { // // We have more than one connection and caller wants to know which one // is the interesting one. Try to find a VPN connectoid. // RasEntryPropHelp *pRasProp = new RasEntryPropHelp; if (pRasProp) { for(DWORD dwConNum = 0; dwConNum < g_dwConnections; dwConNum++) { if(0 == pRasProp->GetW(g_RasCon->GetEntryW(dwConNum))) { if(0 == lstrcmpiA(pRasProp->GetDeviceTypeA(), RASDT_Vpn)) { DEBUG_PRINT(DIALUP, INFO, ("Found VPN entry: %ws\n", g_RasCon->GetEntryW(dwConNum))); dwConnection = dwConNum; break; } } } delete pRasProp; } } // // verify status of connection we're interested in is RASCS_Connected. // if(g_dwConnections != 0) { RasGetConnectStatusHelp RasGetConnectStatus(g_RasCon->GetHandle(dwConnection)); DWORD dwRes = RasGetConnectStatus.GetError(); if (!dwRes && (RasGetConnectStatus.ConnState() == RASCS_Connected)) { WideCharToAscii_UsingGlobalAlloc(g_RasCon->GetEntryW(dwConnection), &lpstrConnection); } DEBUG_PRINT(DIALUP, INFO, ("Connect Status: dwRet=%x, connstate=%x\n", dwRes, RasGetConnectStatus.ConnState())); } ReleaseMutex(g_hRasMutex); quit: DEBUG_LEAVE(lpstrConnection); return lpstrConnection; }