668 lines
15 KiB
C++
668 lines
15 KiB
C++
/*++
|
|
|
|
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 <winsvc.h>
|
|
#include <iphlpapi.h>
|
|
|
|
// 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;
|
|
}
|
|
|
|
|