netwerk/base/src/nsAutodialWin.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 // This source is mostly a bunch of Windows API calls. It is only compiled for
     6 // Windows builds.
     7 //
     8 // Registry entries for Autodial mappings are located here:
     9 //  HKEY_CURRENT_USER\Software\Microsoft\RAS Autodial\Addresses
    11 #include <windows.h>
    12 #include <winsvc.h>
    13 #include "nsString.h"
    14 #include "nsAutodialWin.h"
    15 #include "prlog.h"
    16 #include "nsWindowsHelpers.h"
    18 #define AUTODIAL_DEFAULT AUTODIAL_NEVER
    20 //
    21 // Log module for autodial logging...
    22 //
    23 // To enable logging (see prlog.h for full details):
    24 //
    25 //    set NSPR_LOG_MODULES=Autodial:5
    26 //    set NSPR_LOG_FILE=nspr.log
    27 //
    28 // this enables PR_LOG_DEBUG level information and places all output in
    29 // the file nspr.log
    30 //
    32 #ifdef PR_LOGGING
    33 static PRLogModuleInfo* gLog = nullptr;
    34 #endif
    36 #undef LOGD
    37 #undef LOGE
    38 #define LOGD(args) PR_LOG(gLog, PR_LOG_DEBUG, args)
    39 #define LOGE(args) PR_LOG(gLog, PR_LOG_ERROR, args)
    41 // Don't try to dial again within a few seconds of when user pressed cancel.
    42 #define NO_RETRY_PERIOD_SEC 5
    43 PRIntervalTime nsAutodial::mDontRetryUntil = 0;
    45 // ctor. 
    46 nsAutodial::nsAutodial()
    47 :   mAutodialBehavior(AUTODIAL_DEFAULT),
    48     mAutodialServiceDialingLocation(-1),
    49     mNumRASConnectionEntries(0)
    50 {
    51     // Initializations that can be made again since RAS OS settings can 
    52     // change.
    53     Init();
    54 }
    56 // dtor
    57 nsAutodial::~nsAutodial()
    58 {
    59 }
    62 // Get settings from the OS. These are settings that might change during
    63 // the OS session. Call Init() again to pick up those changes if required.
    64 // Returns NS_ERROR_FAILURE if error or NS_OK if success.
    65 nsresult nsAutodial::Init()
    66 {
    67 #ifdef PR_LOGGING
    68     if (!gLog)
    69         gLog = PR_NewLogModule("Autodial");
    70 #endif
    72     mDefaultEntryName[0] = '\0';
    73     mNumRASConnectionEntries = 0;
    74     mAutodialBehavior = QueryAutodialBehavior();
    76     // No need to continue in this case.
    77     if (mAutodialBehavior == AUTODIAL_NEVER)
    78     {
    79         return NS_OK;
    80     }
    83     // Get the number of dialup entries in the phonebook.
    84     mNumRASConnectionEntries = NumRASEntries();
    86     // Get the name of the default entry.
    87     nsresult result = GetDefaultEntryName(mDefaultEntryName,
    88                                           sizeof(mDefaultEntryName));
    90     return result;
    91 }
    94 // Should we attempt to dial on a network error? Yes if the Internet Options
    95 // configured as such. Yes if the RAS autodial service is running (we'll try to
    96 // force it to dial in that case by adding the network address to its db.)
    97 bool nsAutodial::ShouldDialOnNetworkError()
    98 {
    99     // Don't try to dial again within a few seconds of when user pressed cancel.
   100     if (mDontRetryUntil) 
   101     {
   102         PRIntervalTime intervalNow = PR_IntervalNow();
   103         if (intervalNow < mDontRetryUntil) 
   104         {
   105             LOGD(("Autodial: Not dialing: too soon."));
   106             return false;
   107         }
   108     }
   111     return ((mAutodialBehavior == AUTODIAL_ALWAYS) 
   112              || (mAutodialBehavior == AUTODIAL_ON_NETWORKERROR)
   113              || (mAutodialBehavior == AUTODIAL_USE_SERVICE));
   114 }
   117 // The autodial info is set in Control Panel | Internet Options | Connections.
   118 // The values are stored in the registry. This function gets those values from
   119 // the registry and determines if we should never dial, always dial, or dial
   120 // when there is no network found.
   121 int nsAutodial::QueryAutodialBehavior()
   122 {
   123     if (IsAutodialServiceRunning())
   124     {
   125         // Is Autodial service enabled for the current login session?
   126         DWORD disabled = 0;
   127         DWORD size = sizeof(DWORD);
   128         if (RasGetAutodialParamW(RASADP_LoginSessionDisable, &disabled, &size) == ERROR_SUCCESS)
   129         {
   130             if (!disabled)
   131             {
   132                 // If current dialing location has autodial on, we'll let the service dial.
   133                 mAutodialServiceDialingLocation = GetCurrentLocation();
   134                 if (IsAutodialServiceEnabled(mAutodialServiceDialingLocation))
   135                 {
   136                     return AUTODIAL_USE_SERVICE;
   137                 }
   138             }
   139         }
   140     }
   142     // If we get to here, then the service is not going to dial on error, so we
   143     // can dial ourselves if the control panel settings are set up that way.
   144     HKEY hKey = 0;
   145     LONG result = ::RegOpenKeyExW(
   146                     HKEY_CURRENT_USER, 
   147                     L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 
   148                     0, 
   149                     KEY_READ, 
   150                     &hKey);
   152     if (result != ERROR_SUCCESS)
   153     {
   154         LOGE(("Autodial: Error opening reg key Internet Settings"));
   155         return AUTODIAL_NEVER;
   156     }
   158     DWORD entryType = 0;
   159     DWORD autodial = 0;
   160     DWORD onDemand = 0;
   161     DWORD paramSize = sizeof(DWORD);
   163     result = ::RegQueryValueExW(hKey, L"EnableAutodial", nullptr, &entryType, (LPBYTE)&autodial, &paramSize);
   164     if (result != ERROR_SUCCESS)
   165     {
   166         ::RegCloseKey(hKey);
   167         LOGE(("Autodial: Error reading reg value EnableAutodial."));
   168         return AUTODIAL_NEVER;
   169     }
   171     result = ::RegQueryValueExW(hKey, L"NoNetAutodial", nullptr, &entryType, (LPBYTE)&onDemand, &paramSize);
   172     if (result != ERROR_SUCCESS)
   173     {
   174         ::RegCloseKey(hKey);
   175         LOGE(("Autodial: Error reading reg value NoNetAutodial."));
   176         return AUTODIAL_NEVER;
   177     }
   179     ::RegCloseKey(hKey);
   181     if (!autodial)
   182     {
   183         return AUTODIAL_NEVER;
   184     }
   185     else 
   186     {
   187         if (onDemand)
   188         {
   189             return AUTODIAL_ON_NETWORKERROR;
   190         }
   191         else
   192         {
   193             return AUTODIAL_ALWAYS;
   194         }
   195     }
   196 }
   198 // If the RAS autodial service is running, use it. Otherwise, dial
   199 // the default RAS connection. There are two possible RAS dialogs:
   200 // one that dials a single entry, and one that lets the user choose which
   201 // to dial. If there is only one connection entry in the phone book, or
   202 // there are multiple entries but one is defined as the default, we'll use
   203 // the single entry dial dialog. If there are multiple connection entries,
   204 // and none is specified as default, we'll bring up the diallog which lets
   205 // the user select the connection entry to use.
   206 //
   207 // Return values:
   208 //  NS_OK: dialing was successful and caller should retry
   209 //  all other values indicate that the caller should not retry
   210 nsresult nsAutodial::DialDefault(const char16_t* hostName)
   211 {
   212     mDontRetryUntil = 0;
   214     if (mAutodialBehavior == AUTODIAL_NEVER)
   215     {
   216         return NS_ERROR_FAILURE;    // don't retry the network error
   217     }
   219     // If already a RAS connection, bail.
   220     if (IsRASConnected())
   221     {
   222         LOGD(("Autodial: Not dialing: active connection."));
   223         return NS_ERROR_FAILURE;    // don't retry
   224     }
   226     // If no dialup connections configured, bail.
   227     if (mNumRASConnectionEntries <= 0)
   228     {
   229         LOGD(("Autodial: Not dialing: no entries."));
   230         return NS_ERROR_FAILURE;    // don't retry
   231     }
   234     // If autodial service is running, let it dial. In order for it to dial more
   235     // reliably, we have to add the target address to the autodial database.
   236     // This is the only way the autodial service dial if there is a network
   237     // adapter installed. But even then it might not dial. We have to assume that
   238     // it will though, or we could end up with two attempts to dial on the same
   239     // network error if the user cancels the first one: one from the service and
   240     // one from us.
   241     // See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rras/ras4over_3dwl.asp
   242     if (mAutodialBehavior == AUTODIAL_USE_SERVICE)
   243     {
   244         AddAddressToAutodialDirectory(hostName);
   245         return NS_ERROR_FAILURE;    // don't retry
   246     }
   248     // Do the dialing ourselves.
   249     else
   250     {
   251         // If a default dial entry is configured, use it.
   252         if (mDefaultEntryName[0] != '\0') 
   253         {
   254             LOGD(("Autodial: Dialing default: %s.",mDefaultEntryName));
   256             RASDIALDLG rasDialDlg;
   257             memset(&rasDialDlg, 0, sizeof(rasDialDlg));
   258             rasDialDlg.dwSize = sizeof(rasDialDlg);
   260             BOOL dialed = 
   261              RasDialDlgW(nullptr, mDefaultEntryName, nullptr, &rasDialDlg);
   263             if (!dialed)
   264             {
   265                 if (rasDialDlg.dwError != 0)
   266                 {
   267                     LOGE(("Autodial ::RasDialDlg failed: Error: %d.", 
   268                      rasDialDlg.dwError));
   269                 }
   270                 else
   271                 {
   272                     mDontRetryUntil = PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC);
   273                     LOGD(("Autodial: User cancelled dial."));
   274                 }
   275                 return NS_ERROR_FAILURE;    // don't retry
   276             }
   278             LOGD(("Autodial: RAS dialup connection successful."));
   279         }
   281         // If no default connection specified, open the dialup dialog that lets
   282         // the user specifiy which connection to dial.
   283         else
   284         {
   285             LOGD(("Autodial: Prompting for phonebook entry."));
   287             RASPBDLG rasPBDlg;
   288             memset(&rasPBDlg, 0, sizeof(rasPBDlg));
   289             rasPBDlg.dwSize = sizeof(rasPBDlg);
   291             BOOL dialed = RasPhonebookDlgW(nullptr, nullptr, &rasPBDlg);
   293             if (!dialed)
   294             {
   295                 if (rasPBDlg.dwError != 0)
   296                 {
   297                     LOGE(("Autodial: ::RasPhonebookDlg failed: Error = %d.", 
   298                      rasPBDlg.dwError));
   299                 }
   300                 else
   301                 {
   302                     mDontRetryUntil = PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC);
   303                     LOGD(("Autodial: User cancelled dial."));
   304                 }
   306                 return NS_ERROR_FAILURE;    // don't retry
   307             }
   309             LOGD(("Autodial: RAS dialup connection successful."));
   310         }
   311     }
   313     // Retry because we just established a dialup connection.
   314     return NS_OK;
   315 }
   318 // Check to see if RAS is already connected.
   319 bool nsAutodial::IsRASConnected()
   320 {
   321     DWORD connections;
   322     RASCONN rasConn;
   323     rasConn.dwSize = sizeof(rasConn);
   324     DWORD structSize = sizeof(rasConn);
   326     DWORD result = RasEnumConnectionsW(&rasConn, &structSize, &connections);
   328     // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
   329     if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL)
   330     {
   331         return (connections > 0);
   332     }
   334     LOGE(("Autodial: ::RasEnumConnections failed: Error = %d", result));
   335     return false;
   336 }
   338 // Get the first RAS dial entry name from the phonebook.
   339 nsresult nsAutodial::GetFirstEntryName(wchar_t* entryName, int bufferSize)
   340 {
   341     RASENTRYNAMEW rasEntryName;
   342     rasEntryName.dwSize = sizeof(rasEntryName);
   343     DWORD cb = sizeof(rasEntryName);
   344     DWORD cEntries = 0;
   346     DWORD result = 
   347      RasEnumEntriesW(nullptr, nullptr, &rasEntryName, &cb, &cEntries);
   349     // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
   350     if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL)
   351     {
   352         wcsncpy(entryName, rasEntryName.szEntryName,
   353                 bufferSize / sizeof(*entryName));
   354         return NS_OK;
   355     }
   357     return NS_ERROR_FAILURE;
   358 }
   360 // Get the number of RAS dial entries in the phonebook.
   361 int nsAutodial::NumRASEntries()
   362 {
   363     RASENTRYNAMEW rasEntryName;
   364     rasEntryName.dwSize = sizeof(rasEntryName);
   365     DWORD cb = sizeof(rasEntryName);
   366     DWORD cEntries = 0;
   369     DWORD result = 
   370      RasEnumEntriesW(nullptr, nullptr, &rasEntryName, &cb, &cEntries);
   372     // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
   373     if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL)
   374     {
   375         return (int)cEntries;
   376     }
   378     return 0;
   379 }
   381 // Get the name of the default dial entry.
   382 nsresult nsAutodial::GetDefaultEntryName(wchar_t* entryName, int bufferSize)
   383 {
   384     // No RAS dialup entries. 
   385     if (mNumRASConnectionEntries <= 0)
   386     {
   387         return NS_ERROR_FAILURE;
   388     }
   390     // Single RAS dialup entry. Use it as the default to autodial.
   391     if (mNumRASConnectionEntries == 1)
   392     {
   393         return GetFirstEntryName(entryName, bufferSize);
   394     }
   396     // Multiple RAS dialup entries. If a default configured in the registry,
   397     // use it. 
   398     //
   399     // For Windows XP: HKCU/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
   400     //              or HKLM/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
   402     const wchar_t* key = L"Software\\Microsoft\\RAS Autodial\\Default";
   403     const wchar_t* val = L"DefaultInternet";
   405     HKEY hKey = 0;
   406     LONG result = 0;
   409     // Try HKCU first.
   410     result = ::RegOpenKeyExW(
   411                 HKEY_CURRENT_USER, 
   412                 key, 
   413                 0, 
   414                 KEY_READ, 
   415                 &hKey);
   417     if (result != ERROR_SUCCESS)
   418     {
   419         // If not present, try HKLM.
   420         result = ::RegOpenKeyExW(
   421                     HKEY_LOCAL_MACHINE, 
   422                     key, 
   423                     0, 
   424                     KEY_READ, 
   425                     &hKey);
   427         if (result != ERROR_SUCCESS)
   428         {
   429             return NS_ERROR_FAILURE;
   430         }
   431     }
   434     DWORD entryType = 0;
   435     DWORD buffSize = bufferSize;
   437     result = ::RegQueryValueExW(hKey, 
   438                                 val, 
   439                                 nullptr, 
   440                                 &entryType, 
   441                                 (LPBYTE)entryName, 
   442                                 &buffSize);
   444     ::RegCloseKey(hKey);
   447     if (result != ERROR_SUCCESS)
   448     {
   449         // Results in a prompt for which to use at dial time.
   450         return NS_ERROR_FAILURE;
   451     }
   453     return NS_OK;
   454 }
   457 // Determine if the autodial service is running on this PC.
   458 bool nsAutodial::IsAutodialServiceRunning()
   459 {
   460     nsAutoServiceHandle hSCManager(OpenSCManager(nullptr, 
   461                                                  SERVICES_ACTIVE_DATABASE, 
   462                                                  SERVICE_QUERY_STATUS));
   464     if (hSCManager == nullptr)
   465     {
   466         LOGE(("Autodial: failed to open service control manager. Error %d.", 
   467           ::GetLastError()));
   469         return false;
   470     }
   472     nsAutoServiceHandle hService(OpenServiceW(hSCManager, 
   473                                               L"RasAuto", 
   474                                               SERVICE_QUERY_STATUS));
   476     if (hSCManager == nullptr)
   477     {
   478         LOGE(("Autodial: failed to open RasAuto service."));
   479         return false;
   480     }
   482     SERVICE_STATUS status;
   483     if (!QueryServiceStatus(hService, &status))
   484     {
   485         LOGE(("Autodial: ::QueryServiceStatus() failed. Error: %d", 
   486           ::GetLastError()));
   488         return false;
   489     }
   491     return (status.dwCurrentState == SERVICE_RUNNING);
   492 }
   494 // Add the specified address to the autodial directory.
   495 bool nsAutodial::AddAddressToAutodialDirectory(char16ptr_t hostName)
   496 {
   497     // First see if there is already a db entry for this address. 
   498     RASAUTODIALENTRYW autodialEntry;
   499     autodialEntry.dwSize = sizeof(autodialEntry);
   500     DWORD size = sizeof(autodialEntry);
   501     DWORD entries = 0;
   503     DWORD result = RasGetAutodialAddressW(hostName, 
   504                                           nullptr, 
   505                                           &autodialEntry, 
   506                                           &size, 
   507                                           &entries);
   509     // If there is already at least 1 entry in db for this address, return.
   510     if (result != ERROR_FILE_NOT_FOUND)
   511     {
   512         LOGD(("Autodial: Address %s already in autodial db.", hostName));
   513         return false;
   514     }
   516     autodialEntry.dwSize = sizeof(autodialEntry);
   517     autodialEntry.dwFlags = 0;
   518     autodialEntry.dwDialingLocation = mAutodialServiceDialingLocation;
   519     GetDefaultEntryName(autodialEntry.szEntry, sizeof(autodialEntry.szEntry));
   521     result = RasSetAutodialAddressW(hostName, 
   522                                     0, 
   523                                     &autodialEntry, 
   524                                     sizeof(autodialEntry), 
   525                                     1);
   527     if (result != ERROR_SUCCESS)
   528     {
   529         LOGE(("Autodial ::RasSetAutodialAddress failed result %d.", result));
   530         return false;
   531     }
   533     LOGD(("Autodial: Added address %s to RAS autodial db for entry %s.",
   534          hostName, NS_ConvertUTF16toUTF8(autodialEntry.szEntry).get()));
   536     return true;
   537 }
   539 // Get the current TAPI dialing location.
   540 int nsAutodial::GetCurrentLocation()
   541 {
   542     HKEY hKey = 0;
   543     LONG result = ::RegOpenKeyExW(
   544                     HKEY_LOCAL_MACHINE, 
   545                     L"Software\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations", 
   546                     0, 
   547                     KEY_READ, 
   548                     &hKey);
   550     if (result != ERROR_SUCCESS)
   551     {
   552         LOGE(("Autodial: Error opening reg key ...CurrentVersion\\Telephony\\Locations"));
   553         return -1;
   554     }
   556     DWORD entryType = 0;
   557     DWORD location = 0;
   558     DWORD paramSize = sizeof(DWORD);
   560     result = ::RegQueryValueExW(hKey, L"CurrentID", nullptr, &entryType, (LPBYTE)&location, &paramSize);
   561     if (result != ERROR_SUCCESS)
   562     {
   563         ::RegCloseKey(hKey);
   564         LOGE(("Autodial: Error reading reg value CurrentID."));
   565         return -1;
   566     }
   568     ::RegCloseKey(hKey);
   569     return location;
   571 }
   573 // Check to see if autodial for the specified location is enabled. 
   574 bool nsAutodial::IsAutodialServiceEnabled(int location)
   575 {
   576     if (location < 0)
   577         return false;
   579     BOOL enabled;
   580     if (RasGetAutodialEnableW(location, &enabled) != ERROR_SUCCESS)
   581     {
   582         LOGE(("Autodial: Error calling RasGetAutodialEnable()"));
   583         return false;
   584     }
   586     return enabled;
   587 }

mercurial