1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsAutodialWin.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,587 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// This source is mostly a bunch of Windows API calls. It is only compiled for 1.9 +// Windows builds. 1.10 +// 1.11 +// Registry entries for Autodial mappings are located here: 1.12 +// HKEY_CURRENT_USER\Software\Microsoft\RAS Autodial\Addresses 1.13 + 1.14 +#include <windows.h> 1.15 +#include <winsvc.h> 1.16 +#include "nsString.h" 1.17 +#include "nsAutodialWin.h" 1.18 +#include "prlog.h" 1.19 +#include "nsWindowsHelpers.h" 1.20 + 1.21 +#define AUTODIAL_DEFAULT AUTODIAL_NEVER 1.22 + 1.23 +// 1.24 +// Log module for autodial logging... 1.25 +// 1.26 +// To enable logging (see prlog.h for full details): 1.27 +// 1.28 +// set NSPR_LOG_MODULES=Autodial:5 1.29 +// set NSPR_LOG_FILE=nspr.log 1.30 +// 1.31 +// this enables PR_LOG_DEBUG level information and places all output in 1.32 +// the file nspr.log 1.33 +// 1.34 + 1.35 +#ifdef PR_LOGGING 1.36 +static PRLogModuleInfo* gLog = nullptr; 1.37 +#endif 1.38 + 1.39 +#undef LOGD 1.40 +#undef LOGE 1.41 +#define LOGD(args) PR_LOG(gLog, PR_LOG_DEBUG, args) 1.42 +#define LOGE(args) PR_LOG(gLog, PR_LOG_ERROR, args) 1.43 + 1.44 +// Don't try to dial again within a few seconds of when user pressed cancel. 1.45 +#define NO_RETRY_PERIOD_SEC 5 1.46 +PRIntervalTime nsAutodial::mDontRetryUntil = 0; 1.47 + 1.48 +// ctor. 1.49 +nsAutodial::nsAutodial() 1.50 +: mAutodialBehavior(AUTODIAL_DEFAULT), 1.51 + mAutodialServiceDialingLocation(-1), 1.52 + mNumRASConnectionEntries(0) 1.53 +{ 1.54 + // Initializations that can be made again since RAS OS settings can 1.55 + // change. 1.56 + Init(); 1.57 +} 1.58 + 1.59 +// dtor 1.60 +nsAutodial::~nsAutodial() 1.61 +{ 1.62 +} 1.63 + 1.64 + 1.65 +// Get settings from the OS. These are settings that might change during 1.66 +// the OS session. Call Init() again to pick up those changes if required. 1.67 +// Returns NS_ERROR_FAILURE if error or NS_OK if success. 1.68 +nsresult nsAutodial::Init() 1.69 +{ 1.70 +#ifdef PR_LOGGING 1.71 + if (!gLog) 1.72 + gLog = PR_NewLogModule("Autodial"); 1.73 +#endif 1.74 + 1.75 + mDefaultEntryName[0] = '\0'; 1.76 + mNumRASConnectionEntries = 0; 1.77 + mAutodialBehavior = QueryAutodialBehavior(); 1.78 + 1.79 + // No need to continue in this case. 1.80 + if (mAutodialBehavior == AUTODIAL_NEVER) 1.81 + { 1.82 + return NS_OK; 1.83 + } 1.84 + 1.85 + 1.86 + // Get the number of dialup entries in the phonebook. 1.87 + mNumRASConnectionEntries = NumRASEntries(); 1.88 + 1.89 + // Get the name of the default entry. 1.90 + nsresult result = GetDefaultEntryName(mDefaultEntryName, 1.91 + sizeof(mDefaultEntryName)); 1.92 + 1.93 + return result; 1.94 +} 1.95 + 1.96 + 1.97 +// Should we attempt to dial on a network error? Yes if the Internet Options 1.98 +// configured as such. Yes if the RAS autodial service is running (we'll try to 1.99 +// force it to dial in that case by adding the network address to its db.) 1.100 +bool nsAutodial::ShouldDialOnNetworkError() 1.101 +{ 1.102 + // Don't try to dial again within a few seconds of when user pressed cancel. 1.103 + if (mDontRetryUntil) 1.104 + { 1.105 + PRIntervalTime intervalNow = PR_IntervalNow(); 1.106 + if (intervalNow < mDontRetryUntil) 1.107 + { 1.108 + LOGD(("Autodial: Not dialing: too soon.")); 1.109 + return false; 1.110 + } 1.111 + } 1.112 + 1.113 + 1.114 + return ((mAutodialBehavior == AUTODIAL_ALWAYS) 1.115 + || (mAutodialBehavior == AUTODIAL_ON_NETWORKERROR) 1.116 + || (mAutodialBehavior == AUTODIAL_USE_SERVICE)); 1.117 +} 1.118 + 1.119 + 1.120 +// The autodial info is set in Control Panel | Internet Options | Connections. 1.121 +// The values are stored in the registry. This function gets those values from 1.122 +// the registry and determines if we should never dial, always dial, or dial 1.123 +// when there is no network found. 1.124 +int nsAutodial::QueryAutodialBehavior() 1.125 +{ 1.126 + if (IsAutodialServiceRunning()) 1.127 + { 1.128 + // Is Autodial service enabled for the current login session? 1.129 + DWORD disabled = 0; 1.130 + DWORD size = sizeof(DWORD); 1.131 + if (RasGetAutodialParamW(RASADP_LoginSessionDisable, &disabled, &size) == ERROR_SUCCESS) 1.132 + { 1.133 + if (!disabled) 1.134 + { 1.135 + // If current dialing location has autodial on, we'll let the service dial. 1.136 + mAutodialServiceDialingLocation = GetCurrentLocation(); 1.137 + if (IsAutodialServiceEnabled(mAutodialServiceDialingLocation)) 1.138 + { 1.139 + return AUTODIAL_USE_SERVICE; 1.140 + } 1.141 + } 1.142 + } 1.143 + } 1.144 + 1.145 + // If we get to here, then the service is not going to dial on error, so we 1.146 + // can dial ourselves if the control panel settings are set up that way. 1.147 + HKEY hKey = 0; 1.148 + LONG result = ::RegOpenKeyExW( 1.149 + HKEY_CURRENT_USER, 1.150 + L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 1.151 + 0, 1.152 + KEY_READ, 1.153 + &hKey); 1.154 + 1.155 + if (result != ERROR_SUCCESS) 1.156 + { 1.157 + LOGE(("Autodial: Error opening reg key Internet Settings")); 1.158 + return AUTODIAL_NEVER; 1.159 + } 1.160 + 1.161 + DWORD entryType = 0; 1.162 + DWORD autodial = 0; 1.163 + DWORD onDemand = 0; 1.164 + DWORD paramSize = sizeof(DWORD); 1.165 + 1.166 + result = ::RegQueryValueExW(hKey, L"EnableAutodial", nullptr, &entryType, (LPBYTE)&autodial, ¶mSize); 1.167 + if (result != ERROR_SUCCESS) 1.168 + { 1.169 + ::RegCloseKey(hKey); 1.170 + LOGE(("Autodial: Error reading reg value EnableAutodial.")); 1.171 + return AUTODIAL_NEVER; 1.172 + } 1.173 + 1.174 + result = ::RegQueryValueExW(hKey, L"NoNetAutodial", nullptr, &entryType, (LPBYTE)&onDemand, ¶mSize); 1.175 + if (result != ERROR_SUCCESS) 1.176 + { 1.177 + ::RegCloseKey(hKey); 1.178 + LOGE(("Autodial: Error reading reg value NoNetAutodial.")); 1.179 + return AUTODIAL_NEVER; 1.180 + } 1.181 + 1.182 + ::RegCloseKey(hKey); 1.183 + 1.184 + if (!autodial) 1.185 + { 1.186 + return AUTODIAL_NEVER; 1.187 + } 1.188 + else 1.189 + { 1.190 + if (onDemand) 1.191 + { 1.192 + return AUTODIAL_ON_NETWORKERROR; 1.193 + } 1.194 + else 1.195 + { 1.196 + return AUTODIAL_ALWAYS; 1.197 + } 1.198 + } 1.199 +} 1.200 + 1.201 +// If the RAS autodial service is running, use it. Otherwise, dial 1.202 +// the default RAS connection. There are two possible RAS dialogs: 1.203 +// one that dials a single entry, and one that lets the user choose which 1.204 +// to dial. If there is only one connection entry in the phone book, or 1.205 +// there are multiple entries but one is defined as the default, we'll use 1.206 +// the single entry dial dialog. If there are multiple connection entries, 1.207 +// and none is specified as default, we'll bring up the diallog which lets 1.208 +// the user select the connection entry to use. 1.209 +// 1.210 +// Return values: 1.211 +// NS_OK: dialing was successful and caller should retry 1.212 +// all other values indicate that the caller should not retry 1.213 +nsresult nsAutodial::DialDefault(const char16_t* hostName) 1.214 +{ 1.215 + mDontRetryUntil = 0; 1.216 + 1.217 + if (mAutodialBehavior == AUTODIAL_NEVER) 1.218 + { 1.219 + return NS_ERROR_FAILURE; // don't retry the network error 1.220 + } 1.221 + 1.222 + // If already a RAS connection, bail. 1.223 + if (IsRASConnected()) 1.224 + { 1.225 + LOGD(("Autodial: Not dialing: active connection.")); 1.226 + return NS_ERROR_FAILURE; // don't retry 1.227 + } 1.228 + 1.229 + // If no dialup connections configured, bail. 1.230 + if (mNumRASConnectionEntries <= 0) 1.231 + { 1.232 + LOGD(("Autodial: Not dialing: no entries.")); 1.233 + return NS_ERROR_FAILURE; // don't retry 1.234 + } 1.235 + 1.236 + 1.237 + // If autodial service is running, let it dial. In order for it to dial more 1.238 + // reliably, we have to add the target address to the autodial database. 1.239 + // This is the only way the autodial service dial if there is a network 1.240 + // adapter installed. But even then it might not dial. We have to assume that 1.241 + // it will though, or we could end up with two attempts to dial on the same 1.242 + // network error if the user cancels the first one: one from the service and 1.243 + // one from us. 1.244 + // See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rras/ras4over_3dwl.asp 1.245 + if (mAutodialBehavior == AUTODIAL_USE_SERVICE) 1.246 + { 1.247 + AddAddressToAutodialDirectory(hostName); 1.248 + return NS_ERROR_FAILURE; // don't retry 1.249 + } 1.250 + 1.251 + // Do the dialing ourselves. 1.252 + else 1.253 + { 1.254 + // If a default dial entry is configured, use it. 1.255 + if (mDefaultEntryName[0] != '\0') 1.256 + { 1.257 + LOGD(("Autodial: Dialing default: %s.",mDefaultEntryName)); 1.258 + 1.259 + RASDIALDLG rasDialDlg; 1.260 + memset(&rasDialDlg, 0, sizeof(rasDialDlg)); 1.261 + rasDialDlg.dwSize = sizeof(rasDialDlg); 1.262 + 1.263 + BOOL dialed = 1.264 + RasDialDlgW(nullptr, mDefaultEntryName, nullptr, &rasDialDlg); 1.265 + 1.266 + if (!dialed) 1.267 + { 1.268 + if (rasDialDlg.dwError != 0) 1.269 + { 1.270 + LOGE(("Autodial ::RasDialDlg failed: Error: %d.", 1.271 + rasDialDlg.dwError)); 1.272 + } 1.273 + else 1.274 + { 1.275 + mDontRetryUntil = PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC); 1.276 + LOGD(("Autodial: User cancelled dial.")); 1.277 + } 1.278 + return NS_ERROR_FAILURE; // don't retry 1.279 + } 1.280 + 1.281 + LOGD(("Autodial: RAS dialup connection successful.")); 1.282 + } 1.283 + 1.284 + // If no default connection specified, open the dialup dialog that lets 1.285 + // the user specifiy which connection to dial. 1.286 + else 1.287 + { 1.288 + LOGD(("Autodial: Prompting for phonebook entry.")); 1.289 + 1.290 + RASPBDLG rasPBDlg; 1.291 + memset(&rasPBDlg, 0, sizeof(rasPBDlg)); 1.292 + rasPBDlg.dwSize = sizeof(rasPBDlg); 1.293 + 1.294 + BOOL dialed = RasPhonebookDlgW(nullptr, nullptr, &rasPBDlg); 1.295 + 1.296 + if (!dialed) 1.297 + { 1.298 + if (rasPBDlg.dwError != 0) 1.299 + { 1.300 + LOGE(("Autodial: ::RasPhonebookDlg failed: Error = %d.", 1.301 + rasPBDlg.dwError)); 1.302 + } 1.303 + else 1.304 + { 1.305 + mDontRetryUntil = PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC); 1.306 + LOGD(("Autodial: User cancelled dial.")); 1.307 + } 1.308 + 1.309 + return NS_ERROR_FAILURE; // don't retry 1.310 + } 1.311 + 1.312 + LOGD(("Autodial: RAS dialup connection successful.")); 1.313 + } 1.314 + } 1.315 + 1.316 + // Retry because we just established a dialup connection. 1.317 + return NS_OK; 1.318 +} 1.319 + 1.320 + 1.321 +// Check to see if RAS is already connected. 1.322 +bool nsAutodial::IsRASConnected() 1.323 +{ 1.324 + DWORD connections; 1.325 + RASCONN rasConn; 1.326 + rasConn.dwSize = sizeof(rasConn); 1.327 + DWORD structSize = sizeof(rasConn); 1.328 + 1.329 + DWORD result = RasEnumConnectionsW(&rasConn, &structSize, &connections); 1.330 + 1.331 + // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct. 1.332 + if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL) 1.333 + { 1.334 + return (connections > 0); 1.335 + } 1.336 + 1.337 + LOGE(("Autodial: ::RasEnumConnections failed: Error = %d", result)); 1.338 + return false; 1.339 +} 1.340 + 1.341 +// Get the first RAS dial entry name from the phonebook. 1.342 +nsresult nsAutodial::GetFirstEntryName(wchar_t* entryName, int bufferSize) 1.343 +{ 1.344 + RASENTRYNAMEW rasEntryName; 1.345 + rasEntryName.dwSize = sizeof(rasEntryName); 1.346 + DWORD cb = sizeof(rasEntryName); 1.347 + DWORD cEntries = 0; 1.348 + 1.349 + DWORD result = 1.350 + RasEnumEntriesW(nullptr, nullptr, &rasEntryName, &cb, &cEntries); 1.351 + 1.352 + // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct. 1.353 + if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL) 1.354 + { 1.355 + wcsncpy(entryName, rasEntryName.szEntryName, 1.356 + bufferSize / sizeof(*entryName)); 1.357 + return NS_OK; 1.358 + } 1.359 + 1.360 + return NS_ERROR_FAILURE; 1.361 +} 1.362 + 1.363 +// Get the number of RAS dial entries in the phonebook. 1.364 +int nsAutodial::NumRASEntries() 1.365 +{ 1.366 + RASENTRYNAMEW rasEntryName; 1.367 + rasEntryName.dwSize = sizeof(rasEntryName); 1.368 + DWORD cb = sizeof(rasEntryName); 1.369 + DWORD cEntries = 0; 1.370 + 1.371 + 1.372 + DWORD result = 1.373 + RasEnumEntriesW(nullptr, nullptr, &rasEntryName, &cb, &cEntries); 1.374 + 1.375 + // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct. 1.376 + if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL) 1.377 + { 1.378 + return (int)cEntries; 1.379 + } 1.380 + 1.381 + return 0; 1.382 +} 1.383 + 1.384 +// Get the name of the default dial entry. 1.385 +nsresult nsAutodial::GetDefaultEntryName(wchar_t* entryName, int bufferSize) 1.386 +{ 1.387 + // No RAS dialup entries. 1.388 + if (mNumRASConnectionEntries <= 0) 1.389 + { 1.390 + return NS_ERROR_FAILURE; 1.391 + } 1.392 + 1.393 + // Single RAS dialup entry. Use it as the default to autodial. 1.394 + if (mNumRASConnectionEntries == 1) 1.395 + { 1.396 + return GetFirstEntryName(entryName, bufferSize); 1.397 + } 1.398 + 1.399 + // Multiple RAS dialup entries. If a default configured in the registry, 1.400 + // use it. 1.401 + // 1.402 + // For Windows XP: HKCU/Software/Microsoft/RAS Autodial/Default/DefaultInternet. 1.403 + // or HKLM/Software/Microsoft/RAS Autodial/Default/DefaultInternet. 1.404 + 1.405 + const wchar_t* key = L"Software\\Microsoft\\RAS Autodial\\Default"; 1.406 + const wchar_t* val = L"DefaultInternet"; 1.407 + 1.408 + HKEY hKey = 0; 1.409 + LONG result = 0; 1.410 + 1.411 + 1.412 + // Try HKCU first. 1.413 + result = ::RegOpenKeyExW( 1.414 + HKEY_CURRENT_USER, 1.415 + key, 1.416 + 0, 1.417 + KEY_READ, 1.418 + &hKey); 1.419 + 1.420 + if (result != ERROR_SUCCESS) 1.421 + { 1.422 + // If not present, try HKLM. 1.423 + result = ::RegOpenKeyExW( 1.424 + HKEY_LOCAL_MACHINE, 1.425 + key, 1.426 + 0, 1.427 + KEY_READ, 1.428 + &hKey); 1.429 + 1.430 + if (result != ERROR_SUCCESS) 1.431 + { 1.432 + return NS_ERROR_FAILURE; 1.433 + } 1.434 + } 1.435 + 1.436 + 1.437 + DWORD entryType = 0; 1.438 + DWORD buffSize = bufferSize; 1.439 + 1.440 + result = ::RegQueryValueExW(hKey, 1.441 + val, 1.442 + nullptr, 1.443 + &entryType, 1.444 + (LPBYTE)entryName, 1.445 + &buffSize); 1.446 + 1.447 + ::RegCloseKey(hKey); 1.448 + 1.449 + 1.450 + if (result != ERROR_SUCCESS) 1.451 + { 1.452 + // Results in a prompt for which to use at dial time. 1.453 + return NS_ERROR_FAILURE; 1.454 + } 1.455 + 1.456 + return NS_OK; 1.457 +} 1.458 + 1.459 + 1.460 +// Determine if the autodial service is running on this PC. 1.461 +bool nsAutodial::IsAutodialServiceRunning() 1.462 +{ 1.463 + nsAutoServiceHandle hSCManager(OpenSCManager(nullptr, 1.464 + SERVICES_ACTIVE_DATABASE, 1.465 + SERVICE_QUERY_STATUS)); 1.466 + 1.467 + if (hSCManager == nullptr) 1.468 + { 1.469 + LOGE(("Autodial: failed to open service control manager. Error %d.", 1.470 + ::GetLastError())); 1.471 + 1.472 + return false; 1.473 + } 1.474 + 1.475 + nsAutoServiceHandle hService(OpenServiceW(hSCManager, 1.476 + L"RasAuto", 1.477 + SERVICE_QUERY_STATUS)); 1.478 + 1.479 + if (hSCManager == nullptr) 1.480 + { 1.481 + LOGE(("Autodial: failed to open RasAuto service.")); 1.482 + return false; 1.483 + } 1.484 + 1.485 + SERVICE_STATUS status; 1.486 + if (!QueryServiceStatus(hService, &status)) 1.487 + { 1.488 + LOGE(("Autodial: ::QueryServiceStatus() failed. Error: %d", 1.489 + ::GetLastError())); 1.490 + 1.491 + return false; 1.492 + } 1.493 + 1.494 + return (status.dwCurrentState == SERVICE_RUNNING); 1.495 +} 1.496 + 1.497 +// Add the specified address to the autodial directory. 1.498 +bool nsAutodial::AddAddressToAutodialDirectory(char16ptr_t hostName) 1.499 +{ 1.500 + // First see if there is already a db entry for this address. 1.501 + RASAUTODIALENTRYW autodialEntry; 1.502 + autodialEntry.dwSize = sizeof(autodialEntry); 1.503 + DWORD size = sizeof(autodialEntry); 1.504 + DWORD entries = 0; 1.505 + 1.506 + DWORD result = RasGetAutodialAddressW(hostName, 1.507 + nullptr, 1.508 + &autodialEntry, 1.509 + &size, 1.510 + &entries); 1.511 + 1.512 + // If there is already at least 1 entry in db for this address, return. 1.513 + if (result != ERROR_FILE_NOT_FOUND) 1.514 + { 1.515 + LOGD(("Autodial: Address %s already in autodial db.", hostName)); 1.516 + return false; 1.517 + } 1.518 + 1.519 + autodialEntry.dwSize = sizeof(autodialEntry); 1.520 + autodialEntry.dwFlags = 0; 1.521 + autodialEntry.dwDialingLocation = mAutodialServiceDialingLocation; 1.522 + GetDefaultEntryName(autodialEntry.szEntry, sizeof(autodialEntry.szEntry)); 1.523 + 1.524 + result = RasSetAutodialAddressW(hostName, 1.525 + 0, 1.526 + &autodialEntry, 1.527 + sizeof(autodialEntry), 1.528 + 1); 1.529 + 1.530 + if (result != ERROR_SUCCESS) 1.531 + { 1.532 + LOGE(("Autodial ::RasSetAutodialAddress failed result %d.", result)); 1.533 + return false; 1.534 + } 1.535 + 1.536 + LOGD(("Autodial: Added address %s to RAS autodial db for entry %s.", 1.537 + hostName, NS_ConvertUTF16toUTF8(autodialEntry.szEntry).get())); 1.538 + 1.539 + return true; 1.540 +} 1.541 + 1.542 +// Get the current TAPI dialing location. 1.543 +int nsAutodial::GetCurrentLocation() 1.544 +{ 1.545 + HKEY hKey = 0; 1.546 + LONG result = ::RegOpenKeyExW( 1.547 + HKEY_LOCAL_MACHINE, 1.548 + L"Software\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations", 1.549 + 0, 1.550 + KEY_READ, 1.551 + &hKey); 1.552 + 1.553 + if (result != ERROR_SUCCESS) 1.554 + { 1.555 + LOGE(("Autodial: Error opening reg key ...CurrentVersion\\Telephony\\Locations")); 1.556 + return -1; 1.557 + } 1.558 + 1.559 + DWORD entryType = 0; 1.560 + DWORD location = 0; 1.561 + DWORD paramSize = sizeof(DWORD); 1.562 + 1.563 + result = ::RegQueryValueExW(hKey, L"CurrentID", nullptr, &entryType, (LPBYTE)&location, ¶mSize); 1.564 + if (result != ERROR_SUCCESS) 1.565 + { 1.566 + ::RegCloseKey(hKey); 1.567 + LOGE(("Autodial: Error reading reg value CurrentID.")); 1.568 + return -1; 1.569 + } 1.570 + 1.571 + ::RegCloseKey(hKey); 1.572 + return location; 1.573 + 1.574 +} 1.575 + 1.576 +// Check to see if autodial for the specified location is enabled. 1.577 +bool nsAutodial::IsAutodialServiceEnabled(int location) 1.578 +{ 1.579 + if (location < 0) 1.580 + return false; 1.581 + 1.582 + BOOL enabled; 1.583 + if (RasGetAutodialEnableW(location, &enabled) != ERROR_SUCCESS) 1.584 + { 1.585 + LOGE(("Autodial: Error calling RasGetAutodialEnable()")); 1.586 + return false; 1.587 + } 1.588 + 1.589 + return enabled; 1.590 +}