|
|
/*++
Copyright(c) 1995 Microsoft Corporation
MODULE NAME connect.c
ABSTRACT Connection routines for the automatic connection service.
AUTHOR Anthony Discolo (adiscolo) 23-Feb-1995
REVISION HISTORY Original version from Gurdeep
--*/
#define UNICODE
#define _UNICODE
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <npapi.h>
#include <ras.h>
#include <rasman.h>
#include <raserror.h>
#include <rasuip.h>
#include <acd.h>
#include <debug.h>
#include <nouiutil.h>
#include <pbk.h>
#include "table.h"
#include "addrmap.h"
#include "netmap.h"
#include "rasprocs.h"
#include "reg.h"
#include "misc.h"
#include "imperson.h"
#include "init.h"
#include "process.h"
extern LONG g_lRasAutoRunning;
//
// A request from the driver.
//
typedef struct _REQUEST_ENTRY { LIST_ENTRY listEntry; // link to other requests
ACD_NOTIFICATION notif; // the driver request
} REQUEST_ENTRY, *PREQUEST_ENTRY;
//
// The list of requests from the driver.
//
typedef struct _REQUEST_LIST { CRITICAL_SECTION csLock; // list lock
HANDLE hEvent; // non-empty transistion event
LIST_ENTRY listHead; // list head
} REQUEST_LIST, *PREQUEST_LIST;
//
// Arguments we pass to AcsCreateConnectionThread().
//
typedef struct _CREATION_ARGS { HANDLE hProcess; // process handle to impersonate
ACD_ADDR addr; // original type/address from driver
LPTSTR pszAddress; // canonicalized address
DWORD dwTimeout; // RASADP_FailedConnectionTimeout
} CREATION_ARGS, *PCREATION_ARGS;
//
// Arguments we pass to AcsProcessLearnedAddressThread().
//
typedef struct _PROCESS_ADDR_ARGS { ACD_ADDR_TYPE fType; // address type
LPTSTR pszAddress; // canonicalized address
ACD_ADAPTER adapter; // adapter structure
} PROCESS_ADDR_ARGS, *PPROCESS_ADDR_ARGS;
//
// Information we need to pass to ResetEntryName()
// to reset an invalid address map entry name.
//
typedef struct _RESET_ENTRY_INFO { LPTSTR pszOldEntryName; LPTSTR pszNewEntryName; } RESET_ENTRY_INFO, *PRESET_ENTRY_INFO;
//
// Arguments we pass to AcsRedialOnLinkFailureThread().
//
typedef struct _REDIAL_ARGS { LPTSTR pszPhonebook; // the phonebook
LPTSTR pszEntry; // the phonebook entry
} REDIAL_ARGS, *PREDIAL_ARGS;
//
// Global variables
//
HANDLE hAcdG; REQUEST_LIST RequestListG;
//
// External variables
//
extern HANDLE hTerminatingG; extern HANDLE hSharedConnectionG; extern PHASH_TABLE pDisabledAddressesG; extern FARPROC lpfnRasDialG; extern FARPROC lpfnRasQuerySharedAutoDialG; extern FARPROC lpfnRasQuerySharedConnectionG; extern FARPROC lpfnRasQueryRedialOnLinkFailureG; extern FARPROC lpfnRasGetCredentialsG; extern FARPROC lpfnRasHangUpG; extern FARPROC lpfnRasGetEntryPropertiesG;
//
// Forward declarations
//
BOOLEAN CreateConnection( IN HANDLE hToken, IN PACD_ADDR pAddr, IN LPTSTR lpRemoteName, IN DWORD dwTimeout );
DWORD AcsRedialOnLinkFailureThread( LPVOID lpArg );
VOID AcsRedialOnLinkFailure( IN LPSTR lpszPhonebook, IN LPSTR lpszEntry );
VOID AcsDialSharedConnection( HANDLE *phProcess );
DWORD WINAPI AcsDialSharedConnectionNoUser( PVOID Parameter );
DWORD AcsRequestWorkerThread( LPVOID pArgs ) { HANDLE hProcess = NULL, hEvents[3]; NTSTATUS status; PLIST_ENTRY pEntry; PREQUEST_ENTRY pRequest = NULL; LPTSTR pszAddress = NULL; IO_STATUS_BLOCK ioStatusBlock;
hEvents[0] = hTerminatingG; hEvents[1] = RequestListG.hEvent; hEvents[2] = hSharedConnectionG; for (;;) { //
// Unload any user-based resources before
// a potentially long-term wait.
//
// PrepareForLongWait();
//
// Wait for something to do.
//
RASAUTO_TRACE("AcsRequestWorkerThread: waiting..."); status = WaitForMultipleObjects(3, hEvents, FALSE, INFINITE); if (status == WAIT_OBJECT_0 || status == WAIT_FAILED) { RASAUTO_TRACE1("AcsRequestWorkerThread: status=%d: shutting down", status); break; } if (status == WAIT_OBJECT_0 + 2) { //
// Check to see if connections are disabled
// for this dialing location.
//
BOOL fEnabled; if ((*lpfnRasQuerySharedAutoDialG)(&fEnabled) || !fEnabled) { RASAUTO_TRACE("AcsRequestWorkerThread: shared-autodial disabled!"); continue; } //
// Dial the shared connection
//
if ((hProcess = RefreshImpersonation(hProcess)) == NULL) { RASAUTO_TRACE("AcsRequestWorkerThread: no currently logged-on user!"); QueueUserWorkItem(AcsDialSharedConnectionNoUser, NULL, 0); continue; } AcsDialSharedConnection(&hProcess); continue; } //
// RASAUTO_TRACE() who we think the current user is.
//
TraceCurrentUser(); //
// Process all requests in the list.
//
for (;;) { //
// Make sure we aren't shutting down
// before processing the next request.
//
if (WaitForSingleObject(hTerminatingG, 0) != WAIT_TIMEOUT) { RASAUTO_TRACE("AcsRequestWorkerThread: shutting down"); return 0; } //
// Get the next request.
//
EnterCriticalSection(&RequestListG.csLock); if (IsListEmpty(&RequestListG.listHead)) { LeaveCriticalSection(&RequestListG.csLock); break; } pEntry = RemoveHeadList(&RequestListG.listHead); LeaveCriticalSection(&RequestListG.csLock); pRequest = CONTAINING_RECORD(pEntry, REQUEST_ENTRY, listEntry); //
// Make sure the current thread is impersonating
// the currently logged-on user.
//
if ((hProcess = RefreshImpersonation(hProcess)) == NULL) { RASAUTO_TRACE("AcsRequestWorkerThread: no currently logged-on user!"); goto done; } //
// Handle the request.
//
pszAddress = AddressToUnicodeString(&pRequest->notif.addr); if (pszAddress == NULL) { RASAUTO_TRACE("AcsRequestWorkerThread: AddressToUnicodeString failed"); goto done; } RASAUTO_TRACE2( "AcsRequestWorkerThread: pszAddress=%S, ulFlags=0x%x", pszAddress, pRequest->notif.ulFlags); if (pRequest->notif.ulFlags & ACD_NOTIFICATION_SUCCESS) { //
// Process a learned address.
//
ProcessLearnedAddress( pRequest->notif.addr.fType, pszAddress, &pRequest->notif.adapter); } else { ACD_STATUS connStatus; DWORD dwTimeout;
//
// Get the connection timeout value.
//
dwTimeout = GetAutodialParam(RASADP_FailedConnectionTimeout); //
// Create the new connection.
//
connStatus.fSuccess = CreateConnection( hProcess, &pRequest->notif.addr, pszAddress, dwTimeout); RASAUTO_TRACE1( "AcsRequestWorkerThread: CreateConnection returned %d", connStatus.fSuccess); //
// Complete the connection by issuing
// the completion ioctl to the driver.
//
RtlCopyMemory( &connStatus.addr, &pRequest->notif.addr, sizeof (ACD_ADDR)); status = NtDeviceIoControlFile( hAcdG, NULL, NULL, NULL, &ioStatusBlock, IOCTL_ACD_COMPLETION, &connStatus, sizeof (connStatus), NULL, 0); if (status != STATUS_SUCCESS) { RASAUTO_TRACE1( "AcsRequestWorkerThread: NtDeviceIoControlFile(IOCTL_ACD_COMPLETION) failed (status=0x%x)", status); } } done: if (pszAddress != NULL) { LocalFree(pszAddress); pszAddress = NULL; } if (pRequest != NULL) { LocalFree(pRequest); pRequest = NULL; } } }
return 0; } // AcsRequestWorkerThread
BOOL fProcessDisabled(HANDLE hPid) { PSYSTEM_PROCESS_INFORMATION pProcessInfo; ULONG ulTotalOffset = 0; PUCHAR pLargeBuffer = NULL; BOOL fProcessDisabled = FALSE; pProcessInfo = GetSystemProcessInfo();
if(NULL == pProcessInfo) { goto done; }
pLargeBuffer = (PUCHAR)pProcessInfo;
//
// Look in the process list for svchost.exe and services.exe
//
for (;;) { if ( (pProcessInfo->ImageName.Buffer != NULL) && (hPid == pProcessInfo->UniqueProcessId)) { if( (0 == _wcsicmp( pProcessInfo->ImageName.Buffer, L"svchost.exe")) || (0 == _wcsicmp( pProcessInfo->ImageName.Buffer, L"services.exe")) || (0 == _wcsicmp( pProcessInfo->ImageName.Buffer, L"llssrv.exe"))) { fProcessDisabled = TRUE; }
break; }
//
// Increment offset to next process information block.
//
if (!pProcessInfo->NextEntryOffset) { break; } ulTotalOffset += pProcessInfo->NextEntryOffset; pProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&pLargeBuffer[ulTotalOffset]; }
done:
if(NULL != pLargeBuffer) { FreeSystemProcessInfo((PSYSTEM_PROCESS_INFORMATION)pLargeBuffer); }
return fProcessDisabled; }
VOID AcsDoService() { HANDLE hProcess = NULL, hNotif, hObjects[2]; HANDLE hWorkerThread; PWCHAR pszAddr; LONG cbAddr; NTSTATUS status; BOOLEAN fDisabled, fStatus, fEnabled; BOOLEAN fAsynchronousRequest; IO_STATUS_BLOCK ioStatusBlock; PREQUEST_ENTRY pRequest; ACD_NOTIFICATION connInfo; DWORD dwErr, dwThreadId, dwfDisableLoginSession; ULONG ulAttributes;
{ LONG l; l = InterlockedIncrement(&g_lRasAutoRunning);
// DbgPrint("RASAUTO: AcsDoService: lrasautorunning=%d\n",
// l);
} //
// Initialize the request list.
//
InitializeCriticalSection(&RequestListG.csLock); RequestListG.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (RequestListG.hEvent == NULL) { RASAUTO_TRACE1( "AcsDoService: CreateEvent failed (error=0x%x)", GetLastError()); DeleteCriticalSection(&RequestListG.csLock); return; } InitializeListHead(&RequestListG.listHead); //
// Start the asynchronous request worker
// thread.
//
hWorkerThread = CreateThread( NULL, 10000L, (LPTHREAD_START_ROUTINE)AcsRequestWorkerThread, NULL, 0, &dwThreadId); if (hWorkerThread == NULL) { RASAUTO_TRACE1( "AcsDoService: CreateThread failed (error=0x%x)", GetLastError()); goto done; } //
// Create an event to wait for
// the ioctl completion.
//
hNotif = CreateEvent(NULL, FALSE, FALSE, NULL); if (hNotif == NULL) { RASAUTO_TRACE1( "AcsDoService: CreateEvent failed (error=0x%x)", GetLastError());
DeleteCriticalSection(&RequestListG.csLock); return; } //
// Initialize the array of events
// we need to wait for with WaitForMultipleObjects()
// below.
//
hObjects[0] = hNotif; hObjects[1] = hTerminatingG; for (;;) { //
// Unload any user-based resources before
// a potentially long-term wait.
//
// PrepareForLongWait();
//
// Initialize the connection information.
//
pszAddr = NULL; RtlZeroMemory(&connInfo, sizeof (connInfo)); //
// Wait for a connection notification.
//
status = NtDeviceIoControlFile( hAcdG, hNotif, NULL, NULL, &ioStatusBlock, IOCTL_ACD_NOTIFICATION, NULL, 0, &connInfo, sizeof (connInfo)); if (status == STATUS_PENDING) { RASAUTO_TRACE("AcsDoService: waiting for notification"); status = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); RASAUTO_TRACE1( "AcsDoService: WaitForMultipleObjects returned 0x%x", status); if (status == WAIT_OBJECT_0 + 1) break; status = ioStatusBlock.Status; } if (status != STATUS_SUCCESS) { RASAUTO_TRACE1( "AcsDoService: NtDeviceIoControlFile(IOCTL_ACD_NOTIFICATION) failed (status=0x%x)", status); return; } //
// Initialize the flag that notes whether
// the request is added to the list of
// asynchronous requests.
//
fAsynchronousRequest = FALSE; //
// RASAUTO_TRACE() who we think the currently
// impersonated user is.
//
TraceCurrentUser(); //
// Convert the address structure to a Unicode string.
//
pszAddr = AddressToUnicodeString(&connInfo.addr); if (pszAddr == NULL) { RASAUTO_TRACE("AcsDoService: AddressToUnicodeString failed"); continue; } //
// If we get a bogus address from
// the driver, ignore it.
//
if (!wcslen(pszAddr)) { RASAUTO_TRACE("AcsDoService: ignoring null address"); LocalFree(pszAddr); continue; } RASAUTO_TRACE2( "AcsDoService: got notification: address: %S, ulFlags=0x%x", pszAddr, connInfo.ulFlags); //
// Make sure the current thread is impersonating
// the currently logged-on user. We need this
// so the RAS utilities run with the user's credentials.
//
if ((hProcess = RefreshImpersonation(hProcess)) == NULL) { RASAUTO_TRACE("AcsDoService: no currently logged-on user!"); goto done; } //
// Check to see if this address is in the list
// of disabled addresses.
//
LockDisabledAddresses(); if (GetTableEntry(pDisabledAddressesG, pszAddr, NULL)) { RASAUTO_TRACE1("AcsDoService: %S: is disabled", pszAddr); UnlockDisabledAddresses(); goto done; } UnlockDisabledAddresses(); //
// Check to see if connections are disabled
// for this login session.
//
dwfDisableLoginSession = GetAutodialParam(RASADP_LoginSessionDisable); if (dwfDisableLoginSession) { RASAUTO_TRACE("AcsDoService: connections disabled for this login session"); goto done; } //
// Check to see if connections are disabled
// for this dialing location.
//
dwErr = AutoDialEnabled(&fEnabled); if (!dwErr && !fEnabled) { RASAUTO_TRACE("AcsDoService: connections disabled for this dialing location"); goto done; } //
// If the address we're trying to connect
// to is on the disabled list, then fail
// this connection attempt.
//
LockAddressMap(); GetAddressDisabled(pszAddr, &fDisabled); UnlockAddressMap(); if (fDisabled) { RASAUTO_TRACE1("AcsDoService: %S: address disabled", RASAUTO_TRACESTRW(pszAddr)); goto done; }
RASAUTO_TRACE1("AcsDoService: notif.ulFlags=0x%x", connInfo.ulFlags);
//
// If autodial is disabled for this pid, don't start autodial and bail
//
if( (0 == (connInfo.ulFlags & ACD_NOTIFICATION_SUCCESS)) && fProcessDisabled(connInfo.Pid)) { RASAUTO_TRACE1("AcsDoService: Autodial is disabled for process 0x%lx", connInfo.Pid);
goto done; } else { RASAUTO_TRACE1("AcsDoService: process 0x%lx is not disabled", connInfo.Pid); } //
// We need to process this request
// asynchronously. Create and initialize
// a request entry.
//
pRequest = LocalAlloc(LPTR, sizeof (REQUEST_ENTRY)); if (pRequest == NULL) { RASAUTO_TRACE("AcsDoService: LocalAlloc failed"); goto done; } RtlCopyMemory(&pRequest->notif, &connInfo, sizeof (ACD_NOTIFICATION)); //
// Add this request to the list of
// requests to be processed asynchronously.
//
EnterCriticalSection(&RequestListG.csLock); InsertTailList(&RequestListG.listHead, &pRequest->listEntry); SetEvent(RequestListG.hEvent); LeaveCriticalSection(&RequestListG.csLock); fAsynchronousRequest = TRUE;
done: if (pszAddr != NULL) LocalFree(pszAddr); //
// If we aren't going to process this request
// asynchronously, then we need to signal the
// (unsuccessful) completion of the connection
// attempt. Only signal completion of
// non-ACD_NOTIFICATION_SUCCESS requests.
//
if (!fAsynchronousRequest) { if (!(connInfo.ulFlags & ACD_NOTIFICATION_SUCCESS)) { ACD_STATUS connStatus;
connStatus.fSuccess = FALSE; RtlCopyMemory(&connStatus.addr, &connInfo.addr, sizeof (ACD_ADDR)); status = NtDeviceIoControlFile( hAcdG, NULL, NULL, NULL, &ioStatusBlock, IOCTL_ACD_COMPLETION, &connStatus, sizeof (connStatus), NULL, 0); if (status != STATUS_SUCCESS) { RASAUTO_TRACE1( "AcsDoService: NtDeviceIoControlFile(IOCTL_ACD_COMPLETION) failed (status=0x%x)", status); } } } } //
// Clean up the worker thread.
//
RASAUTO_TRACE("AcsDoService: signaling worker thread to shutdown"); WaitForSingleObject(hWorkerThread, INFINITE); if(RequestListG.hEvent != NULL) { CloseHandle(RequestListG.hEvent); RequestListG.hEvent = NULL; } DeleteCriticalSection(&RequestListG.csLock); CloseHandle(hWorkerThread); RASAUTO_TRACE("AcsDoService: worker thread shutdown done"); //
// Clean up all resources associated
// with the service.
//
CloseHandle(hNotif); AcsCleanup(); RASAUTO_TRACE("AcsDoService: exiting"); } // AcsDoService
VOID AcsDialSharedConnection( HANDLE *phProcess )
/*++
DESCRIPTION Looks for a shared connection and initiates a connection for it.
ARGUMENTS phProcess: pointer to the handle to the process token that we inherit the security attributes from when we exec the dialer
RETURN VALUE none
--*/
{ DWORD dwErr; BOOLEAN fEntryInvalid; BOOLEAN fRasLoaded; RASSHARECONN rsc; TCHAR* pszEntryName; TCHAR szEntryName[RAS_MaxEntryName + 1]; RASAUTO_TRACE("AcsDialSharedConnection"); //
// Load RAS entrypoints
//
fRasLoaded = LoadRasDlls(); if (!fRasLoaded) { RASAUTO_TRACE("AcsDialSharedConnection: Could not load RAS DLLs."); return; } //
// A guest isn't able to dial a RAS connection, so if we're currently
// impersonating a guest we need to perform a no-user autodial
//
if (ImpersonatingGuest()) { QueueUserWorkItem(AcsDialSharedConnectionNoUser, NULL, 0); return; } //
// Get the shared connection, if any. We can't do this in an impersonated
// context, as the user we're impersonating may not have sufficient access
// to retrieve the current shared connection.
//
RevertImpersonation(); *phProcess = NULL; dwErr = (DWORD)(*lpfnRasQuerySharedConnectionG)(&rsc); if ((*phProcess = RefreshImpersonation(NULL)) == NULL) { RASAUTO_TRACE("AcsDialSharedConnection: unable to refresh impersonation!"); if (NO_ERROR == dwErr && !rsc.fIsLanConnection) { //
// Attempt to do no-user autodial
//
QueueUserWorkItem(AcsDialSharedConnectionNoUser, NULL, 0); return; } } if (dwErr) { RASAUTO_TRACE1("AcsDialSharedConnection: RasQuerySharedConnection=%d", dwErr); return; } else if (rsc.fIsLanConnection) { RASAUTO_TRACE("AcsDialSharedConnection: shared connection is LAN adapter"); return; } #ifdef UNICODE
pszEntryName = rsc.name.szEntryName; #else
//
// Convert to ANSI
//
pszEntryName = szEntryName; wcstombs(pszEntryName, rsc.name.szEntryName, RAS_MaxEntryName); #endif
//
// Initiate a dial-attempt
//
StartAutoDialer( *phProcess, NULL, pszEntryName, pszEntryName, TRUE, &fEntryInvalid); }
DWORD WINAPI AcsDialSharedConnectionNoUser( PVOID Parameter )
/*++
DESCRIPTION Looks for a shared connection and initiates a connection for it using RasDial and the cached credentials for the connection.
ARGUMENTS none
RETURN VALUE none
--*/
{ DWORD dwErr; BOOLEAN fRasLoaded; HRASCONN hrasconn; RASCREDENTIALSW rc; RASDIALEXTENSIONS rde; RASDIALPARAMSW rdp; RASSHARECONN rsc; RASAUTO_TRACE("AcsDialSharedConnectionNoUser"); //
// Load RAS entrypoints
//
fRasLoaded = LoadRasDlls(); if (!fRasLoaded) { RASAUTO_TRACE("AcsDialSharedConnectionNoUser: Could not load RAS DLLs."); return NO_ERROR; } //
// Get the shared connection, if any
//
dwErr = (DWORD)(*lpfnRasQuerySharedConnectionG)(&rsc); if (dwErr) { RASAUTO_TRACE1("AcsDialSharedConnectionNoUser: RasQuerySharedConnection=%d", dwErr); return NO_ERROR; } else if (rsc.fIsLanConnection) { RASAUTO_TRACE("AcsDialSharedConnectionNoUser: shared connection is LAN"); return NO_ERROR; } //
// Retrieve the credentials for the shared connection.
//
rc.dwSize = sizeof(rc); rc.dwMask = RASCM_UserName | RASCM_Password | RASCM_Domain | RASCM_DefaultCreds; dwErr = (DWORD)(*lpfnRasGetCredentialsG)( rsc.name.szPhonebookPath, rsc.name.szEntryName, &rc ); if (dwErr) { RASAUTO_TRACE1("AcsDialSharedConnectionNoUser: " "RasGetCredentials=%d", dwErr); return NO_ERROR; } //
// Prepare to initiate the connection, setting up the dial-extensions
// and the dial-parameters.
//
ZeroMemory(&rde, sizeof(rde)); rde.dwSize = sizeof(rde); rde.dwfOptions = RDEOPT_NoUser;
ZeroMemory(&rdp, sizeof(rdp)); rdp.dwSize = sizeof(rdp); lstrcpyW(rdp.szEntryName, rsc.name.szEntryName); lstrcpyW(rdp.szUserName, rc.szUserName); lstrcpyW(rdp.szDomain, rc.szDomain); lstrcpyW(rdp.szPassword, rc.szPassword); //
// Clear the credentials from memory, and dial the connection.
//
RASAUTO_TRACE("AcsDialSharedConnectionNoUser: RasDial"); hrasconn = NULL; ZeroMemory(&rc, sizeof(rc)); dwErr = (DWORD)(*lpfnRasDialG)( &rde, rsc.name.szPhonebookPath, &rdp, 0, NULL, &hrasconn ); ZeroMemory(&rdp, sizeof(rdp)); RASAUTO_TRACE1("AcsDialSharedConnectionNoUser: RasDial=%d", dwErr);
if (E_NOTIMPL == dwErr) { //
// This is possibly a Connection Manager connection since it's returning E_NOTIMPL,
// we should check the phonebook entry for the type and then call the RasDialDlg
// with the RASDDFLAG_NoPrompt flag.
//
RASDIALDLG info; BOOL fRetVal = FALSE; HINSTANCE hRasDlgDll = NULL; FARPROC lpfnRasDialDlg = NULL; RASENTRY re; DWORD dwRasEntrySize; DWORD dwIgnore; typedef BOOL (*lpfnRasDialDlgFunc)(LPWSTR, LPWSTR, LPWSTR, LPRASDIALDLG);
ZeroMemory(&info, sizeof(info)); info.dwSize = sizeof(info);
ZeroMemory(&re, sizeof(re)); dwRasEntrySize = sizeof(re); re.dwSize = dwRasEntrySize;
dwErr = (DWORD)(*lpfnRasGetEntryPropertiesG)( rsc.name.szPhonebookPath, rsc.name.szEntryName, &re, &dwRasEntrySize, NULL, &dwIgnore);
if (ERROR_SUCCESS == dwErr) { dwErr = ERROR_NOT_SUPPORTED; //
// Check if this is a Connection Manager entry
//
if (RASET_Internet == re.dwType) { //
// Prevent the DialerDialog
//
info.dwFlags |= RASDDFLAG_NoPrompt;
hRasDlgDll = LoadLibrary(L"RASDLG.DLL"); if (hRasDlgDll) { lpfnRasDialDlgFunc lpfnRasDialDlg = (lpfnRasDialDlgFunc)GetProcAddress(hRasDlgDll, "RasDialDlgW");
if (lpfnRasDialDlg) { fRetVal = (BOOL)(lpfnRasDialDlg)(rsc.name.szPhonebookPath, rsc.name.szEntryName, NULL, &info ); RASAUTO_TRACE1("AcsDialSharedConnectionNoUser: lpfnRasDialDlg returns %d", (DWORD)fRetVal); if (fRetVal) { dwErr = ERROR_SUCCESS; } } else { RASAUTO_TRACE("AcsDialSharedConnectionNoUser: Failed to get procaddress for RasDialDlgW");
} FreeLibrary(hRasDlgDll); hRasDlgDll = NULL; } else { RASAUTO_TRACE("AcsDialSharedConnectionNoUser: Failed to load RASDLG.dll"); } } else { RASAUTO_TRACE1("AcsDialSharedConnectionNoUser: Wrong type. RASENTRY.dwType=%d", re.dwType); } } else { RASAUTO_TRACE1("AcsDialSharedConnectionNoUser: lpfnRasGetEntryPropertiesG=%d", dwErr); } }
//
// If RasDial returned an error and passed back a valid connection
// handle we need to call RasHangUp on that handle.
//
if (ERROR_SUCCESS != dwErr && NULL != hrasconn) { dwErr = (DWORD)(*lpfnRasHangUpG)(hrasconn); RASAUTO_TRACE1("AcsDialSharedConnectionNoUser: RasHangUp=%d", dwErr); } return NO_ERROR; }
BOOLEAN ResetEntryName( IN PVOID pArg, IN LPTSTR pszAddress, IN PVOID pData )
/*++
DESCRIPTION A table enumerator procedure to reset all address map entries referencing an old RAS phonebook entry to a new one.
ARGUMENTS pArg: a pointer to a RESET_ENTRY_INFO structure
pszAddress: a pointer to the address string
pData: ignored
RETURN VALUE Always TRUE to continue the enumeration.
--*/
{ PRESET_ENTRY_INFO pResetEntryInfo = (PRESET_ENTRY_INFO)pArg; LPTSTR pszEntryName;
if (GetAddressDialingLocationEntry(pszAddress, &pszEntryName)) { if (!_wcsicmp(pszEntryName, pResetEntryInfo->pszOldEntryName)) { if (!SetAddressDialingLocationEntry( pszAddress, pResetEntryInfo->pszNewEntryName)) { RASAUTO_TRACE("ResetEntryName: SetAddressEntryName failed"); } } LocalFree(pszEntryName); }
return TRUE; } // ResetEntryName
BOOL fRequestToSelf(LPTSTR lpRemoteName) { BOOL fRet = FALSE;
TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1]; DWORD dwSize;
RASAUTO_TRACE1("fRequestToSelf. lpRemoteName=%S", lpRemoteName);
dwSize = MAX_COMPUTERNAME_LENGTH; if(GetComputerName(szComputerName, &dwSize)) { if(0 == lstrcmpi(lpRemoteName, szComputerName)) { fRet = TRUE; } }
return fRet; }
BOOLEAN CreateConnection( IN HANDLE hProcess, IN PACD_ADDR pAddr, IN LPTSTR lpRemoteName, IN DWORD dwTimeout )
/*++
DESCRIPTION Take a notification and figure out what to do with it.
ARGUMENTS hToken: the handle to the process token that we inherit the security attributes from when we exec the dialer
pAddr: a pointer to the original address from the driver
lpRemoteName: a pointer to the address of the connection attempt
dwTimeout: number of seconds to disable the address between failed connections
RETURN VALUE Returns TRUE if the net attempt should be retried, FALSE otherwise.
--*/
{ DWORD dwStatus = WN_SUCCESS; RASENTRYNAME entry; DWORD dwErr, dwSize, dwEntries; DWORD dwPreConnections, dwPostConnections, i; DWORD dwTicks; BOOLEAN fRasLoaded; BOOLEAN fMappingExists, fRasConnectSuccess = FALSE; BOOLEAN fStatus, fEntryInvalid; BOOLEAN fFailedConnection = FALSE; LPTSTR lpEntryName = NULL; LPTSTR *lpPreActiveEntries = NULL, *lpPostActiveEntries = NULL; LPTSTR lpNewConnection, lpNetworkName = NULL; BOOL fDefault = FALSE;
RASAUTO_TRACE1("CreateConnection: lpRemoteName=%S", RASAUTO_TRACESTRW(lpRemoteName)); //
// Load the RAS DLLs.
//
fRasLoaded = LoadRasDlls(); if (!fRasLoaded) { RASAUTO_TRACE("CreateConnection: Could not load RAS DLLs."); goto done; }
//
// Check to see if the request is for the same machine. Bail if so.
// we don't want autodial to kick in if the connection request is
// to the same machine.
//
if(fRequestToSelf(lpRemoteName)) { RASAUTO_TRACE("CreateConnetion: Request to self. Bailing."); goto done; } //
// Get a list of the active RAS connections before
// we attempt to create a new one.
//
dwPreConnections = ActiveConnections(TRUE, &lpPreActiveEntries, NULL); RASAUTO_TRACE1("CreateConnection: dwPreConnections=%d", dwPreConnections); //
// If we reach this point, we have an unsuccessful
// network connection without any active RAS
// connections. Try to start the implicit connection
// machinery. See if there already exists a mapping
// for the address.
//
LockAddressMap(); //
// Make sure we have the current information
// about this address from the registry.
//
ResetAddressMapAddress(lpRemoteName); fMappingExists = GetAddressDialingLocationEntry(lpRemoteName, &lpEntryName); //
// If the entry doesn't exist, and this is a
// Internet hostname, then see if we can find
// an address with the same organization name.
//
if (!fMappingExists && pAddr->fType == ACD_ADDR_INET) fMappingExists = GetSimilarDialingLocationEntry(lpRemoteName, &lpEntryName); fFailedConnection = GetAddressLastFailedConnectTime( lpRemoteName, &dwTicks); UnlockAddressMap(); RASAUTO_TRACE2( "CreateConnection: lookup of %S returned %S", RASAUTO_TRACESTRW(lpRemoteName), RASAUTO_TRACESTRW(lpEntryName)); //
// If we know nothing about the address, and
// we are connected to some network, then ignore
// the request.
//
if (!fMappingExists && IsNetworkConnected()) { RASAUTO_TRACE1( "CreateConnection: no mapping for lpRemoteName=%S and connected to a network", lpRemoteName); goto done; }
//
// If no mapping exists and not connected to network,
// check to see if theres a default internet connection.
//
if(!fMappingExists && !IsNetworkConnected()) { RASAUTO_TRACE1( "CreateConnection: no mapping for lpRemoteName=%S and" " not connected to a network", lpRemoteName);
dwErr = DwGetDefaultEntryName(&lpEntryName);
RASAUTO_TRACE1( "CreateConnection: found default entry %S", (NULL == lpEntryName)?TEXT("NULL"):lpEntryName);
if(NULL != lpEntryName) { fMappingExists = TRUE; fDefault = TRUE; } } //
// If there is a mapping, but the phonebook
// entry is missing from the mapping, then
// ignore the request. Also check to make
// sure the phonebook entry isn't already
// connected.
//
//
// Perform various checks on the mapping.
//
if (fMappingExists) { BOOLEAN bStatus, bConnected = FALSE;
//
// Make sure it's not NULL.
//
if (!wcslen(lpEntryName)) { RASAUTO_TRACE1( "CreateConnection: lpRemoteName=%S is permanently disabled", RASAUTO_TRACESTRW(lpRemoteName)); goto done; } //
// If the network associated with this
// entry is connected, then ignore the
// request.
//
lpNetworkName = EntryToNetwork(lpEntryName); RASAUTO_TRACE2( "CreateConnection: network for entry %S is %S", lpEntryName, RASAUTO_TRACESTRW(lpNetworkName)); if (lpNetworkName != NULL) { LockNetworkMap(); bStatus = GetNetworkConnected(lpNetworkName, &bConnected); UnlockNetworkMap(); if (bStatus && bConnected) { RASAUTO_TRACE1( "CreateConnection: %S is already connected!", RASAUTO_TRACESTRW(lpEntryName)); fRasConnectSuccess = TRUE; goto done; } } //
// If the entry itself is connected,
// then ignore the request. We need
// to do this check as well as the one
// above, because the mapping may not
// have a network assigned to it yet.
//
for (i = 0; i < dwPreConnections; i++) { if (!_wcsicmp(lpEntryName, lpPreActiveEntries[i])) { RASAUTO_TRACE1( "CreateConnection: lpEntryName=%S is already connected!", lpEntryName); goto done; } } } //
// Check for a recent failed connection
// attempt.
//
if (fFailedConnection) { RASAUTO_TRACE1( "CreateConnection: RASADP_FailedConnectionTimeout=%d", dwTimeout); if (GetTickCount() - dwTicks < dwTimeout * 1000) { RASAUTO_TRACE2( "CreateConnection: lpRemoteName=%S is temporarily disabled (failed connection %d ticks ago)", RASAUTO_TRACESTRW(lpRemoteName), GetTickCount() - dwTicks); goto done; } else { //
// Reset last failed tick count.
//
fFailedConnection = FALSE; } } //
// If a mapping already exists for the address, then
// start rasphone with the address. Otherwise, simply
// have rasphone show the entire phonebook.
//
fEntryInvalid = FALSE; fRasConnectSuccess = StartAutoDialer( hProcess, pAddr, lpRemoteName, fMappingExists ? lpEntryName : NULL, FALSE, &fEntryInvalid); RASAUTO_TRACE1( "CreateConnection: StartDialer returned %d", fRasConnectSuccess); if (fRasConnectSuccess) { //
// Get the list of active connections again. We will
// compare the lists to determine which is the new
// entry.
//
dwPostConnections = ActiveConnections( TRUE, &lpPostActiveEntries, NULL); //
// If the number of active connections before and after
// the newly created connection differs by more than 1,
// then we have to skip saving the mapping in the registry,
// since we cannot determine which is the right one!
//
if (dwPostConnections - dwPreConnections == 1) { lpNewConnection = CompareConnectionLists( lpPreActiveEntries, dwPreConnections, lpPostActiveEntries, dwPostConnections); RASAUTO_TRACE2( "CreateConnection: mapped %S->%S", RASAUTO_TRACESTRW(lpRemoteName), RASAUTO_TRACESTRW(lpNewConnection)); LockAddressMap(); if (!fEntryInvalid) { //
// Store the new RAS phonebook entry, since
// it could be different from the one we
// retrieved in the mapping.
//
// #ifdef notdef
if(!fDefault) { //
// We do not want to do this because the
// user may have selected the wrong phonebook
// entry. We will let a successful connection
// notification map it for us.
//
fStatus = SetAddressDialingLocationEntry(lpRemoteName, lpNewConnection); // #endif
fStatus = SetAddressTag(lpRemoteName, ADDRMAP_TAG_USED); } } else { RESET_ENTRY_INFO resetEntryInfo;
//
// If the RAS phonebook entry in the mapping
// was invalid, then automatically
// remap all other mappings referencing that
// entry to the newly selected phonebook entry.
//
resetEntryInfo.pszOldEntryName = lpEntryName; resetEntryInfo.pszNewEntryName = lpNewConnection; EnumAddressMap(ResetEntryName, &resetEntryInfo); } //
// Flush this mapping to the registry now
// and reload the address info. We do this to
// get the network name for a new address/network
// pair.
//
FlushAddressMap(); ResetAddressMapAddress(lpRemoteName); if (lpNetworkName == NULL && GetAddressNetwork(lpRemoteName, &lpNetworkName)) { LockNetworkMap(); SetNetworkConnected(lpNetworkName, TRUE); UnlockNetworkMap(); } UnlockAddressMap(); if (!fStatus) RASAUTO_TRACE("CreateConnection: SetAddressEntryName failed"); } else { RASAUTO_TRACE1( "CreateConnection: %d (> 1) new RAS connections! (can't write registry)", dwPostConnections - dwPreConnections); } }
done: #ifdef notdef
// we only unload rasman.dll if we are going to exit
if (fRasLoaded) UnloadRasDlls(); #endif
if (!fFailedConnection && !fRasConnectSuccess) { //
// If the connection attempt wasn't successful,
// then we disable future connections to that
// address for a while.
//
RASAUTO_TRACE1("CreateConnection: disabling %S", RASAUTO_TRACESTRW(lpRemoteName)); LockAddressMap(); fStatus = SetAddressLastFailedConnectTime(lpRemoteName); UnlockAddressMap(); if (!fStatus) RASAUTO_TRACE("CreateConnection: SetAddressAttribute failed"); } //
// Free resources.
//
if (lpEntryName != NULL) LocalFree(lpEntryName); if (lpNetworkName != NULL) LocalFree(lpNetworkName); if (lpPreActiveEntries != NULL) FreeStringArray(lpPreActiveEntries, dwPreConnections); if (lpPostActiveEntries != NULL) FreeStringArray(lpPostActiveEntries, dwPostConnections);
return fRasConnectSuccess; } // CreateConnection
DWORD AcsRedialOnLinkFailureThread( LPVOID lpArg ) { DWORD dwErr; PREDIAL_ARGS pRedial = (PREDIAL_ARGS)lpArg; HANDLE hProcess = NULL;
RASAUTO_TRACE2( "AcsRedialOnLinkFailureThread: lpszPhonebook=%s, lpszEntry=%s", RASAUTO_TRACESTRW(pRedial->pszPhonebook), RASAUTO_TRACESTRW(pRedial->pszEntry));
//
// Make sure the current thread is impersonating
// the currently logged-on user. We need this
// so the RAS utilities run with the user's credentials.
//
if ((hProcess = RefreshImpersonation(hProcess)) == NULL) { RASAUTO_TRACE("AcsRedialOnLinkFailureThread: no currently logged-on user!"); return 0; } //
// Reset HKEY_CURRENT_USER to get the
// correct value with the new impersonation
// token.
//
// RegCloseKey(HKEY_CURRENT_USER);
/* Check that user has enabled redial on link failure.
*/ { BOOL fRedial = FALSE;
dwErr = (DWORD)(lpfnRasQueryRedialOnLinkFailureG)( pRedial->pszPhonebook, pRedial->pszEntry, &fRedial);
if(!fRedial) { PBUSER user;
dwErr = GetUserPreferences( NULL, &user, FALSE ); if (dwErr == 0) { fRedial = user.fRedialOnLinkFailure; DestroyUserPreferences( &user ); } }
if (!fRedial) { RASAUTO_TRACE1("Skip redial,e=%d",dwErr); return 0; } }
//
// Redial the entry.
//
dwErr = StartReDialer(hProcess, pRedial->pszPhonebook, pRedial->pszEntry); //
// Free the parameter block we were passed.
//
if (pRedial->pszPhonebook != NULL) LocalFree(pRedial->pszPhonebook); if (pRedial->pszEntry != NULL) LocalFree(pRedial->pszEntry); LocalFree(pRedial);
return dwErr; } // AcsRedialOnLinkFailureThread
VOID AcsRedialOnLinkFailure( IN LPSTR lpszPhonebook, IN LPSTR lpszEntry )
/*++
DESCRIPTION This is the redial-on-link-failure handler we give to rasman via RasRegisterRedialCallback. It gets called when the final port of a connection is disconnected due to a hardware failure. We package up the parameters rasman gives us an create a thread because the callback is made within rasman's worker thread context.
ARGUMENTS lpszPhonebook: the phonebook string of the connection
lpszEntry: the entry name of the connection
RETURN VALUE None.
--*/
{ PREDIAL_ARGS lpRedial = LocalAlloc(LPTR, sizeof (REDIAL_ARGS)); HANDLE hThread; DWORD dwThreadId;
if (lpRedial == NULL) return; lpRedial->pszPhonebook = AnsiStringToUnicodeString( lpszPhonebook, NULL, 0); if (lpszPhonebook != NULL && lpRedial->pszPhonebook == NULL) { RASAUTO_TRACE("AcsRedialOnLinkFailure: LocalAlloc failed"); LocalFree(lpRedial); return; } lpRedial->pszEntry = AnsiStringToUnicodeString( lpszEntry, NULL, 0); if (lpszEntry != NULL && lpRedial->pszEntry == NULL) { RASAUTO_TRACE("AcsRedialOnLinkFailure: LocalAlloc failed"); LocalFree(lpRedial->pszPhonebook); LocalFree(lpRedial); return; } //
// Start the connection.
//
hThread = CreateThread( NULL, 10000L, (LPTHREAD_START_ROUTINE)AcsRedialOnLinkFailureThread, (LPVOID)lpRedial, 0, &dwThreadId); if (hThread == NULL) { RASAUTO_TRACE1( "AcsRedialOnLinkFailure: CreateThread failed (error=0x%x)", GetLastError()); LocalFree(lpRedial->pszEntry); LocalFree(lpRedial->pszPhonebook); LocalFree(lpRedial); return; } CloseHandle(hThread); } // AcsRedialOnLinkFailure
|