📄 autodial.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
/*--
Module Name: autodial.c
Abstract: Autodialer functions
--*/
#include <autodprv.h>
#include <iphlpapi.h>
#include <Windev.h>
// Debug zones
#define ZONE_INIT DEBUGZONE(0)
#define ZONE_CONNECTION DEBUGZONE(11)
#define ZONE_IDLETIMEOUT DEBUGZONE(12)
#define ZONE_FUNCTION DEBUGZONE(13)
#define ZONE_WARN DEBUGZONE(14)
#define ZONE_ERROR DEBUGZONE(15)
#define STATE_OFF 0
#define STATE_ON 1
#define STATE_STARTING_UP 2
#define STATE_SHUTTING_DOWN 3
// FILETIME (100-ns intervals) to milleseonds (10 x 1000)
#define FILETIME_TO_MILLISECONDS 10000
#ifdef DEBUG
#define myleave(e) { ASSERT(dwRet != 0); err = e; goto done; }
#define myretleave(r,e) { dwRet = r; err = e; goto done; }
#else
#define myleave(e) { ASSERT(dwRet != 0); goto done;}
#define myretleave(r,e) { dwRet = r; goto done; }
#endif
typedef struct
{
HRASCONN m_hRasConn; // Global connection context
TCHAR szRasEntryName[RAS_MaxEntryName + 1];
// Used to determine when to end the connection.
__int64 m_ftIdleTimeout; // amount of time before we hang up, measure in filetime.
HANDLE m_hTimerThread; // Handle to created timer thread
HANDLE m_hTimerEvent; // Event to fire.
DWORD m_dwBytesTxRx; // Total # of bytes sent and received
__int64 m_ftLastActivity; // Treated as FILETIME, last time bytes were sent
DWORD m_dwRedialAttempts; // # of times we'll try to go throug redial cycle
DWORD m_dwRedialPauseTime; // Sleep this many ms between calls
BOOL m_fEnableOnPublicInterface;
// Used for FAIL_RETRY_WAIT monitoring
__int64 m_ftFailRetryWait; // Registry configured amount of time to wait on.
int m_iLastFailIndex;
__int64 g_ftLastFailTime; // Element treated as FILETIME
}
AUTODIAL_CONNECTION_INFO, *PAUTODIAL_CONNECTION_INFO;
// Global variables
AUTODIAL_CONNECTION_INFO g_AutoDialConn;
DWORD g_fState;
CRITICAL_SECTION g_GlobalCS;
BOOL g_fAutodialInitialized;
HINSTANCE g_hInstance;
// Function definitions
DWORD AutoDialConnect(RASDIALPARAMS *pRasDialParams);
DWORD AutoDialEndConnectionInternal(BOOL fTimedOut);
DWORD AutodialReadRegistrySettings(TCHAR *szDialRasEntryName, TCHAR *pszEntryName1, TCHAR *pszEntryName2, AUTODIAL_CONNECTION_INFO *pConnInfo);
BOOL GetConnectedStatus(TCHAR *szEntryName, RASCONNSTATUS *lpRasConn, HRASCONN *phRasConn);
DWORD CheckConnected(TCHAR *szEntryName);
DWORD HangUpIdleConnectionThread(LPVOID lpv);
void SetFailTime(void);
DWORD DetermineIfAutodialConnectionsActive(RASCONNSTATUS *lpRasConnStatus, TCHAR *szRasName, HRASCONN *phRasConn);
void SetAsPublicInterface(TCHAR *szEntryName);
BOOL CheckFailRetry(void);
#if defined(DEBUG) && defined (UNDER_CE)
DBGPARAM dpCurSettings = {
TEXT("Autodial"), {
TEXT("Init"),TEXT("Unused"),TEXT("Unused"),TEXT("Unused"),
TEXT("Unused"),TEXT("Unused"),TEXT("Unused"),
TEXT("Unused"),TEXT("Unused"),TEXT("Unused"),TEXT("Unused"),
TEXT("Connection"),TEXT("IdleTimeout"),TEXT("Function"),TEXT("Warning"),TEXT("Error") },
0x8801
};
#endif
//***********************************************************************
// AutoDial Functions
//***********************************************************************
#define MAX_HANGUP_RETRIES 50
void MyHangUp(LPHRASCONN phRasConn)
{
int i;
RASCONNSTATUS RasConnStatus;
RasConnStatus.dwSize = sizeof(RASCONNSTATUS);
RasHangUp(*phRasConn);
for (i = 0; i < MAX_HANGUP_RETRIES; i++)
{
if (ERROR_INVALID_HANDLE == RasGetConnectStatus(*phRasConn,&RasConnStatus))
break;
Sleep(1000);
}
ASSERT ( i != MAX_HANGUP_RETRIES);
*phRasConn = 0;
}
// This function is called when network services start up to initialize
// the Autodial members.
// Note: Calls to AutoDialInitializeModule and AutoDialCleanupModule
// must be syncronized by the function calling it. However,
// thread syncronization for AutodialStartConnection and
// AutoDialEndConnection are done by Autodial itself.
DWORD AutoDialInitializeModule(void)
{
DEBUGMSG(ZONE_FUNCTION,(TEXT("AutoDial:+AutoDialInitializeModule\r\n")));
if (!g_fAutodialInitialized)
{
memset(&g_AutoDialConn,0,sizeof(g_AutoDialConn));
g_fState = STATE_OFF;
g_AutoDialConn.m_iLastFailIndex = 0;
InitializeCriticalSection(&g_GlobalCS);
if (g_AutoDialConn.m_hTimerEvent = CreateEvent(NULL,FALSE,FALSE,NULL))
g_fAutodialInitialized = TRUE;
StatusInit(g_hInstance);
}
DEBUGMSG(ZONE_FUNCTION,(TEXT("AutoDial:-AutoDialInitializeModule\r\n")));
return g_fAutodialInitialized ? ERROR_SUCCESS : ERROR_RASAUTO_CANNOT_INITIALIZE;
}
// Called when shutting down AutoDial services. After calling this function
// it will be necessary to call AutoDialInitializeModule again to use AutoDial.
DWORD AutoDialCleanupModule(void)
{
DEBUGMSG(ZONE_FUNCTION,(TEXT("AutoDial:+AutoDialCleanupModule\r\n")));
g_fAutodialInitialized = FALSE;
if (g_AutoDialConn.m_hRasConn)
AutoDialEndConnectionInternal(FALSE);
DeleteCriticalSection(&g_GlobalCS);
CloseHandle(g_AutoDialConn.m_hTimerEvent);
StatusUninit(g_hInstance);
DEBUGMSG(ZONE_FUNCTION,(TEXT("AutoDial:-AutoDialCleanupModule\r\n")));
return ERROR_SUCCESS;
}
// NAT calls this routine when it is unable to deliver a packet
// The routine loads values from the registry to see who to dial and
// attempts to create a connection using RasDial if:
// 1) The registry key is valid
// 2) AutoDial has been enabled
// 3) We haven't failed 3 times in a row within a brief time span
// szDialRasEntryName is set to non-NULL if we wish to initiate a dial using
// the given RAS conn name and ignore the registry values.
DWORD AutoDialStartConnection(TCHAR *szDialRasEntryName)
{
#ifdef DEBUG
DWORD err = 0;
#endif
DWORD dwRet = 0;
TCHAR szEntryName1[RAS_MaxEntryName + 1];
TCHAR szEntryName2[RAS_MaxEntryName + 1];
RASDIALPARAMS RasDialParams1;
RASDIALPARAMS RasDialParams2;
RASDIALPARAMS *pRasDialParams;
DWORD i;
BOOL fExecute = FALSE;
DEBUGMSG(ZONE_FUNCTION,(TEXT("AutoDial:+AutoDialStartConnection\r\n")));
ASSERT(!szDialRasEntryName || _tcslen(szDialRasEntryName) < RAS_MaxEntryName);
if (!g_fAutodialInitialized)
{
DEBUGMSG(ZONE_ERROR,(TEXT("Autodial: AutoDialInitializeModule must be called before AutoDialStartConnection\r\n")));
ASSERT(0);
return ERROR_RASAUTO_CANNOT_INITIALIZE;
}
EnterCriticalSection(&g_GlobalCS);
if (STATE_OFF == g_fState)
{
g_fState = STATE_STARTING_UP;
fExecute = TRUE;
}
// It's possible we've lost the connection but are still in the STATE_ON
else if (STATE_ON == g_fState && g_AutoDialConn.m_hRasConn)
{
RASCONNSTATUS RasConnStatus;
RasConnStatus.dwSize = sizeof(RasConnStatus);
if (ERROR_SUCCESS == RasGetConnectStatus(g_AutoDialConn.m_hRasConn,&RasConnStatus))
{
if (RASCS_Disconnected == RasConnStatus.rasconnstate)
{
MyHangUp(&g_AutoDialConn.m_hRasConn);
g_fState = STATE_STARTING_UP;
fExecute = TRUE;
}
}
}
// Note: We leave the critical section here and rely on the g_fState so
// that if another thread tries to call this function it will immediatly
// receive an error (ERROR_DIAL_ALREADY_IN_PROGRESS) and won't be
// blocked on the CS potentially waiting for RasDial() to complete.
LeaveCriticalSection(&g_GlobalCS);
if (!fExecute)
{
// We only allow 1 autodial connection at the same time.
// Do not goto done, the cleanup code assumes we have fExecute = TRUE
DEBUGMSG(ZONE_ERROR,(TEXT("AutoDial:AutoDialStartConnection failed -- another connection is active\r\n")));
return ERROR_DIAL_ALREADY_IN_PROGRESS;
}
if (g_AutoDialConn.m_hTimerThread)
{
SetEvent(g_AutoDialConn.m_hTimerEvent);
WaitForSingleObject(g_AutoDialConn.m_hTimerThread,INFINITE);
CloseHandle(g_AutoDialConn.m_hTimerThread);
g_AutoDialConn.m_hTimerThread = 0;
}
ASSERT(g_fState == STATE_STARTING_UP);
ASSERT(g_AutoDialConn.m_hTimerThread == 0);
if (dwRet = AutodialReadRegistrySettings(szDialRasEntryName,szEntryName1,szEntryName2,&g_AutoDialConn))
myleave(10);
ASSERT( (szDialRasEntryName && !szEntryName1[0] && !szEntryName2[0]) ||
(!szDialRasEntryName && szEntryName1[0]));
if ( ! CheckFailRetry())
myretleave(ERROR_DISCONNECTION,3); // just set the best RAS err code we can
// It's possible that szEntryName has been connected through another application
// already, in which case we don't try to RasDial on it.
if (szDialRasEntryName && (dwRet = CheckConnected(szDialRasEntryName)))
myleave(20);
if (szEntryName1[0] && (dwRet = CheckConnected(szEntryName1)))
myleave(30);
if (szEntryName2[0] && (dwRet = CheckConnected(szEntryName2)))
myleave(40);
RasDialParams1.dwSize = sizeof(RASDIALPARAMS);
if (szDialRasEntryName)
_tcsncpy(RasDialParams1.szEntryName,szDialRasEntryName,RAS_MaxEntryName);
else
_tcsncpy(RasDialParams1.szEntryName,szEntryName1,RAS_MaxEntryName);
if (szEntryName2[0])
{
RasDialParams2.dwSize = sizeof(RASDIALPARAMS);
_tcsncpy(RasDialParams2.szEntryName,szEntryName2,RAS_MaxEntryName);
}
for (i = 0; i < g_AutoDialConn.m_dwRedialAttempts + 1; i++)
{
dwRet = AutoDialConnect(pRasDialParams = &RasDialParams1);
if (!dwRet)
break;
if (szEntryName2[0])
{
dwRet = AutoDialConnect(pRasDialParams = &RasDialParams2);
if (!dwRet)
break;
}
// If we're not on final redial attempt, and if pause time was set.
if (g_AutoDialConn.m_dwRedialPauseTime && i != g_AutoDialConn.m_dwRedialAttempts)
{
DEBUGMSG(ZONE_WARN,(TEXT("AutoDial: Connection attempt # %d failed, will sleep %d millisecs before attempting again\r\n"),
i,g_AutoDialConn.m_dwRedialPauseTime));
Sleep(g_AutoDialConn.m_dwRedialPauseTime);
}
}
// We only set fail time if our attempt to make a connection has failed,
// we don't set this for misconfigured registry settings.
if (i == g_AutoDialConn.m_dwRedialAttempts + 1)
{
DEBUGMSG(ZONE_ERROR,(TEXT("AutoDial: Unable to connect, RasError = %d\r\n"),dwRet));
SetFailTime();
myleave(50);
}
done:
ASSERT (g_fState == STATE_STARTING_UP);
#ifdef DEBUG
if (dwRet)
DEBUGMSG(ZONE_ERROR,(TEXT("AutoDial:StartConnection failed, returning = %d\r\n"),err,dwRet));
else
DEBUGMSG(ZONE_CONNECTION,(TEXT("AutoDial:StartConnection SUCCESS!\r\n")));
#endif // 0
// Don't need CS around this because our state is STATE_STARTING_UP, no other
// threads can modify g_fState when it's this value.
if (dwRet)
{
g_fState = STATE_OFF;
}
else
{
GetCurrentFT((FILETIME*) &g_AutoDialConn.m_ftLastActivity);
g_AutoDialConn.m_hTimerThread = CreateThread(NULL,0,HangUpIdleConnectionThread, NULL, 0, NULL);
_tcsncpy(g_AutoDialConn.szRasEntryName,pRasDialParams->szEntryName,RAS_MaxEntryName);
#if defined (DEBUG)
// If CreateThread fails just means we won't have an idle watch dog,
// all other parts of the connection will still run OK.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -