/*++ 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; extern DWORD g_dwCritSecFlags; // // 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. // RasInitializeCriticalSection(&RequestListG.csLock, &dwErr); if(dwErr != ERROR_SUCCESS) { return; } g_dwCritSecFlags |= RASAUTO_CRITSEC_REQUESTLIST; RequestListG.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (RequestListG.hEvent == NULL) { RASAUTO_TRACE1( "AcsDoService: CreateEvent failed (error=0x%x)", GetLastError()); DeleteCriticalSection(&RequestListG.csLock); g_dwCritSecFlags &= ~(RASAUTO_CRITSEC_REQUESTLIST); 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()); // // .NET bug# 514423 new verbose RASAUTO/RASAUTOU prefast warnings // CloseHandle(RequestListG.hEvent); RequestListG.hEvent = NULL; DeleteCriticalSection(&RequestListG.csLock); g_dwCritSecFlags &= ~(RASAUTO_CRITSEC_REQUESTLIST); return; } // // 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()); // // .NET bug# 514423 new verbose RASAUTO/RASAUTOU prefast warnings // CloseHandle(hWorkerThread); CloseHandle(RequestListG.hEvent); RequestListG.hEvent = NULL; DeleteCriticalSection(&RequestListG.csLock); g_dwCritSecFlags &= ~(RASAUTO_CRITSEC_REQUESTLIST); 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); // // .NET bug# 514423 new verbose RASAUTO/RASAUTOU prefast warnings // CloseHandle(hNotif); CloseHandle(hWorkerThread); CloseHandle(RequestListG.hEvent); RequestListG.hEvent = NULL; DeleteCriticalSection(&RequestListG.csLock); g_dwCritSecFlags &= ~(RASAUTO_CRITSEC_REQUESTLIST); 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); g_dwCritSecFlags &= ~(RASAUTO_CRITSEC_REQUESTLIST); 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; 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