93002: [distribution]Conn: Use dialup networking (.DUN) when launching

mozilla. r=dougt,sr=rpotts. Help Windows NT, 2000, and XP dialup a RAS
connection when a network address is unreachable. (c/osmeredith)
the controlling pref is "network.autodial-helper.enabled".


git-svn-id: svn://10.0.0.236/trunk@124314 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
tao%netscape.com 2002-06-28 23:03:52 +00:00
parent a5064d775f
commit bbe078f6ae
13 changed files with 1190 additions and 24 deletions

View File

@ -102,6 +102,12 @@ interface nsISocketTransportService : nsISupports
* A number of nsSocketTransport objects connected (this may include keep-alive idle connections)
*/
readonly attribute unsigned long connectedTransportCount;
/**
* Autodial helper is enabled via pref.
*/
attribute boolean autodialEnabled;
};
%{C++

View File

@ -77,6 +77,8 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
else
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
CPPSRCS += nsIOServiceWin.cpp
CPPSRCS += nsNativeConnectionHelper.cpp
CPPSRCS += nsAutodialWin.cpp
else
CPPSRCS += nsIOServiceUnix.cpp
endif

View File

@ -68,6 +68,8 @@ CPP_OBJS = \
.\$(OBJDIR)\nsStorageTransport.obj \
.\$(OBJDIR)\nsStreamListenerTee.obj \
.\$(OBJDIR)\nsURIChecker.obj \
.\$(OBJDIR)\nsNativeConnectionHelper.obj \
.\$(OBJDIR)\nsAutodialWin.obj \
$(NULL)
JSFILES = \

View File

@ -0,0 +1,674 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Steve Meredith <smeredith@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// This source is mostly a bunch of Windows API calls. It is only compiled for
// Windows builds.
#include <windows.h>
#include <winsvc.h>
#include "nsAutodialWin.h"
#include "prlog.h"
//
// Log module for autodial logging...
//
// To enable logging (see prlog.h for full details):
//
// set NSPR_LOG_MODULES=Autodial:5
// set NSPR_LOG_FILE=nspr.log
//
// this enables PR_LOG_DEBUG level information and places all output in
// the file nspr.log
//
#ifdef PR_LOGGING
static PRLogModuleInfo* gLog = nsnull;
#endif
#define LOGD(args) PR_LOG(gLog, PR_LOG_DEBUG, args)
#define LOGE(args) PR_LOG(gLog, PR_LOG_ERROR, args)
// Don't try to dial again within a few seconds of when user pressed cancel.
#define NO_RETRY_PERIOD_SEC 5
PRIntervalTime nsRASAutodial::mDontRetryUntil = 0;
tRASPHONEBOOKDLG nsRASAutodial::mpRasPhonebookDlg = nsnull;
tRASENUMCONNECTIONS nsRASAutodial::mpRasEnumConnections = nsnull;
tRASENUMENTRIES nsRASAutodial::mpRasEnumEntries = nsnull;
tRASDIALDLG nsRASAutodial::mpRasDialDlg = nsnull;
tRASSETAUTODIALADDRESS nsRASAutodial::mpRasSetAutodialAddress = nsnull;
tRASGETAUTODIALADDRESS nsRASAutodial::mpRasGetAutodialAddress = nsnull;
HINSTANCE nsRASAutodial::mhRASdlg = nsnull;
HINSTANCE nsRASAutodial::mhRASapi32 = nsnull;
// ctor.
nsRASAutodial::nsRASAutodial()
: mAutodialBehavior(AUTODIAL_NEVER),
mNumRASConnectionEntries(0)
{
mOSVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&mOSVerInfo);
#ifdef PR_LOGGING
if (!gLog)
gLog = PR_NewLogModule("Autodial");
#endif
// We only need to dial on nt based systems. For all other platforms,
// mAutodialBehavior will remain AUTODIAL_NEVER, and we can skip
// these initializations.
if ((mOSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
&& (mOSVerInfo.dwMajorVersion >= 3))
{
if (!mhRASdlg)
{
mhRASdlg = ::LoadLibrary("rasdlg.dll");
if ((UINT)mhRASdlg > 32)
{
// RasPhonebookDlg
mpRasPhonebookDlg =
(tRASPHONEBOOKDLG)::GetProcAddress(mhRASdlg, "RasPhonebookDlgA");
// RasDialDlg
mpRasDialDlg =
(tRASDIALDLG)::GetProcAddress(mhRASdlg, "RasDialDlgA");
}
if (!mhRASdlg || !mpRasPhonebookDlg || !mpRasDialDlg)
{
LOGE(("Autodial: Error loading RASDLG.DLL."));
}
}
if (!mhRASapi32)
{
mhRASapi32 = ::LoadLibrary("rasapi32.dll");
if ((UINT)mhRASapi32 > 32)
{
// RasEnumConnections
mpRasEnumConnections = (tRASENUMCONNECTIONS)
::GetProcAddress(mhRASapi32, "RasEnumConnectionsA");
// RasEnumEntries
mpRasEnumEntries = (tRASENUMENTRIES)
::GetProcAddress(mhRASapi32, "RasEnumEntriesA");
// RasSetAutodialAddress
mpRasSetAutodialAddress = (tRASSETAUTODIALADDRESS)
::GetProcAddress(mhRASapi32, "RasSetAutodialAddressA");
// RasSetAutodialAddress
mpRasGetAutodialAddress = (tRASGETAUTODIALADDRESS)
::GetProcAddress(mhRASapi32, "RasGetAutodialAddressA");
}
if (!mhRASapi32
|| !mpRasEnumConnections
|| !mpRasEnumEntries
|| !mpRasSetAutodialAddress
|| !mpRasGetAutodialAddress)
{
LOGE(("Autodial: Error loading RASAPI32.DLL."));
}
}
// Initializations that can be made again since RAS OS settings can
// change.
Init();
}
}
// dtor
nsRASAutodial::~nsRASAutodial()
{
}
// Get settings from the OS. These are settings that might change during
// the OS session. Call Init() again to pick up those changes if required.
// Returns NS_ERROR_FAILURE if error or NS_OK if success.
nsresult nsRASAutodial::Init()
{
mDefaultEntryName[0] = '\0';
mNumRASConnectionEntries = 0;
mAutodialBehavior = QueryAutodialBehavior();
// No need to continue in this case.
if (mAutodialBehavior == AUTODIAL_NEVER)
{
return NS_OK;
}
// Get the number of dialup entries in the phonebook.
mNumRASConnectionEntries = NumRASEntries();
// Get the name of the default entry.
nsresult result = GetDefaultEntryName(mDefaultEntryName,
RAS_MaxEntryName + 1);
return result;
}
// Should we attempt to dial on a network error? Yes if the Internet Options
// configured as such. Yes if the RAS autodial service is running (we'll
// force it to dail in that case by adding the network address to its db.)
PRBool nsRASAutodial::ShouldDialOnNetworkError()
{
// Don't try to dial again within a few seconds of when user pressed cancel.
if (mDontRetryUntil)
{
PRIntervalTime intervalNow = PR_IntervalNow();
if (intervalNow < mDontRetryUntil)
{
LOGD(("Autodial: Not dialing: too soon."));
return PR_FALSE;
}
}
return ((mAutodialBehavior == AUTODIAL_ALWAYS)
|| (mAutodialBehavior == AUTODIAL_ON_NETWORKERROR)
|| (mAutodialBehavior == AUTODIAL_USE_SERVICE));
}
// The autodial info is set in Control Panel | Internet Options | Connections.
// The values are stored in the registry. This function gets those values from
// the registry and determines if we should never dial, always dial, or dial
// when there is no network found.
int nsRASAutodial::QueryAutodialBehavior()
{
if (IsAutodialServiceRunning())
return AUTODIAL_USE_SERVICE;
HKEY hKey = 0;
LONG result = ::RegOpenKeyEx(
HKEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
0,
KEY_READ,
&hKey);
if (result != ERROR_SUCCESS)
{
return AUTODIAL_NEVER;
}
DWORD entryType = 0;
DWORD autodial = 0;
DWORD onDemand = 0;
DWORD paramSize = sizeof(DWORD);
result = ::RegQueryValueEx(hKey, "EnableAutodial", nsnull, &entryType, (LPBYTE)&autodial, &paramSize);
if (result != ERROR_SUCCESS)
{
::RegCloseKey(hKey);
return AUTODIAL_NEVER;
}
result = ::RegQueryValueEx(hKey, "NoNetAutodial", nsnull, &entryType, (LPBYTE)&onDemand, &paramSize);
if (result != ERROR_SUCCESS)
{
::RegCloseKey(hKey);
return AUTODIAL_NEVER;
}
::RegCloseKey(hKey);
if (!autodial)
{
return AUTODIAL_NEVER;
}
else
{
if (onDemand)
{
return AUTODIAL_ON_NETWORKERROR;
}
else
{
return AUTODIAL_ALWAYS;
}
}
}
// If the RAS autodial service is running, use it. Otherwise, dial
// the default RAS connection. There are two possible RAS dialogs:
// one that dials a single entry, and one that lets the user choose which
// to dial. If there is only one connection entry in the phone book, or
// there are multiple entries but one is defined as the default, we'll use
// the single entry dial dialog. If there are multiple connection entries,
// and none is specified as default, we'll bring up the diallog which lets
// the user select the connection entry to use.
nsresult nsRASAutodial::DialDefault(const char* hostName)
{
mDontRetryUntil = 0;
if (mAutodialBehavior == AUTODIAL_NEVER)
{
return NS_ERROR_FAILURE;
}
// If already a RAS connection, bail.
if (IsRASConnected())
{
LOGD(("Autodial: Not dialing: active connection."));
return NS_OK; //NS_ERROR_FAILURE;
}
// If no dialup connections configured, bail.
if (mNumRASConnectionEntries <= 0)
{
LOGD(("Autodial: Not dialing: no entries."));
return NS_ERROR_FAILURE;
}
// If autodial service is running, let it dial. In order for it to dial
// reliably, we have to add the target address to the autodial database.
// This is the only way the autodial service dial if there is a network
// adapter installed.
// See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rras/ras4over_3dwl.asp
if (mAutodialBehavior == AUTODIAL_USE_SERVICE)
{
if (!AddAddressToAutodialDirectory(hostName))
return NS_ERROR_FAILURE;
}
// Do the dialing ourselves.
else
{
// If a default dial entry is configured, use it.
if (mDefaultEntryName[0] != '\0')
{
LOGD(("Autodial: Dialing default: %s.",mDefaultEntryName));
RASDIALDLG rasDialDlg;
memset(&rasDialDlg, 0, sizeof(RASDIALDLG));
rasDialDlg.dwSize = sizeof(RASDIALDLG);
NS_ASSERTION(mpRasDialDlg != nsnull,
"RAS DLLs only loaded for NT-based OSs.");
if (!mpRasDialDlg)
{
return NS_ERROR_NULL_POINTER;
}
PRBool dialed =
(*mpRasDialDlg)(nsnull, mDefaultEntryName, nsnull, &rasDialDlg);
if (!dialed)
{
if (rasDialDlg.dwError != 0)
{
LOGE(("Autodial ::RasDialDlg failed: Error: %d.",
rasDialDlg.dwError));
}
else
{
mDontRetryUntil = PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC);
LOGD(("Autodial: User cancelled dial."));
}
return NS_ERROR_FAILURE;
}
LOGD(("Autodial: RAS dialup connection successful."));
}
// If no default connection specified, open the dialup dialog that lets
// the user specifiy which connection to dial.
else
{
LOGD(("Autodial: Prompting for phonebook entry."));
RASPBDLG rasPBDlg;
memset(&rasPBDlg, 0, sizeof(RASPBDLG));
rasPBDlg.dwSize = sizeof(RASPBDLG);
NS_ASSERTION(mpRasPhonebookDlg != nsnull,
"RAS DLLs only loaded for NT-based OSs.");
if (!mpRasPhonebookDlg)
{
return NS_ERROR_NULL_POINTER;
}
PRBool dialed = (*mpRasPhonebookDlg)(nsnull, nsnull, &rasPBDlg);
if (!dialed)
{
if (rasPBDlg.dwError != 0)
{
LOGE(("Autodial: ::RasPhonebookDlg failed: Error = %d.",
rasPBDlg.dwError));
}
else
{
mDontRetryUntil = PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC);
LOGD(("Autodial: User cancelled dial."));
}
return NS_ERROR_FAILURE;
}
LOGD(("Autodial: RAS dialup connection successful."));
}
}
return NS_OK;
}
// Check to see if RAS is already connected.
PRBool nsRASAutodial::IsRASConnected()
{
DWORD connections;
RASCONN rasConn;
rasConn.dwSize = sizeof(RASCONN);
DWORD structSize = sizeof(RASCONN);
NS_ASSERTION(mpRasEnumConnections != nsnull,
"RAS DLLs only loaded for NT-based OSs.");
if (!mpRasEnumConnections)
{
return NS_ERROR_NULL_POINTER;
}
DWORD result = (*mpRasEnumConnections)(&rasConn, &structSize, &connections);
// ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL)
{
return (connections > 0);
}
LOGE(("Autodial: ::RasEnumConnections failed: Error = %d", result));
return PR_FALSE;
}
// Get the first RAS dial entry name from the phonebook.
nsresult nsRASAutodial::GetFirstEntryName(char* entryName, int bufferSize)
{
RASENTRYNAME rasEntryName;
rasEntryName.dwSize = sizeof(RASENTRYNAME);
DWORD cb = sizeof(RASENTRYNAME);
DWORD cEntries = 0;
NS_ASSERTION(mpRasEnumEntries != nsnull,
"RAS DLLs only loaded for NT-based OSs.");
if (!mpRasEnumEntries)
{
return NS_ERROR_NULL_POINTER;
}
DWORD result =
(*mpRasEnumEntries)(nsnull, nsnull, &rasEntryName, &cb, &cEntries);
// ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL)
{
strncpy(entryName, rasEntryName.szEntryName, bufferSize);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
// Get the number of RAS dial entries in the phonebook.
int nsRASAutodial::NumRASEntries()
{
RASENTRYNAME rasEntryName;
rasEntryName.dwSize = sizeof(RASENTRYNAME);
DWORD cb = sizeof(RASENTRYNAME);
DWORD cEntries = 0;
NS_ASSERTION(mpRasEnumEntries != nsnull,
"RAS DLLs only loaded for NT-based OSs.");
if (!mpRasEnumEntries)
{
return 0;
}
DWORD result =
(*mpRasEnumEntries)(nsnull, nsnull, &rasEntryName, &cb, &cEntries);
// ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL)
{
return (int)cEntries;
}
return 0;
}
// Get the name of the default dial entry.
nsresult nsRASAutodial::GetDefaultEntryName(char* entryName, int bufferSize)
{
// No RAS dialup entries.
if (mNumRASConnectionEntries <= 0)
{
return NS_ERROR_FAILURE;
}
// Single RAS dialup entry. Use it as the default to autodail.
if (mNumRASConnectionEntries == 1)
{
return GetFirstEntryName(entryName, bufferSize);
}
// Multiple RAS dialup entries. If a default configured in the registry,
// use it.
//
// For Windows XP: HKCU/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
// or HKLM/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
// For Windows 2K: HKCU/RemoteAccess/InternetProfile.
char* key = nsnull;
char* val = nsnull;
HKEY hKey = 0;
LONG result = 0;
// Windows NT and 2000
if ((mOSVerInfo.dwMajorVersion == 4) // Windows NT
|| ((mOSVerInfo.dwMajorVersion == 5) && (mOSVerInfo.dwMinorVersion == 0))) // Windows 2000
{
key = "RemoteAccess";
val = "InternetProfile";
result = ::RegOpenKeyEx(
HKEY_CURRENT_USER,
key,
0,
KEY_READ,
&hKey);
if (result != ERROR_SUCCESS)
{
return NS_ERROR_FAILURE;
}
}
else // Windows XP
{
key = "Software\\Microsoft\\RAS Autodial\\Default";
val = "DefaultInternet";
// Try HKCU first.
result = ::RegOpenKeyEx(
HKEY_CURRENT_USER,
key,
0,
KEY_READ,
&hKey);
if (result != ERROR_SUCCESS)
{
// If not present, try HKLM.
result = ::RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
key,
0,
KEY_READ,
&hKey);
if (result != ERROR_SUCCESS)
{
return NS_ERROR_FAILURE;
}
}
}
DWORD entryType = 0;
DWORD buffSize = bufferSize;
result = ::RegQueryValueEx(hKey,
val,
nsnull,
&entryType,
(LPBYTE)entryName,
&buffSize);
::RegCloseKey(hKey);
if (result != ERROR_SUCCESS)
{
// Use first entry if none configured as default.
return NS_ERROR_FAILURE;
}
return NS_OK;
}
// Determine if the autodial service is running on this PC.
PRBool nsRASAutodial::IsAutodialServiceRunning()
{
SC_HANDLE hSCManager =
OpenSCManager(nsnull, SERVICES_ACTIVE_DATABASE, SERVICE_QUERY_STATUS);
if (hSCManager == nsnull)
{
LOGE(("Autodial: failed to open service control manager. Error %d.",
::GetLastError()));
return PR_FALSE;
}
SC_HANDLE hService =
OpenService(hSCManager, "RasAuto", SERVICE_QUERY_STATUS);
if (hSCManager == nsnull)
{
LOGE(("Autodial: failed to open RasAuto service."));
return PR_FALSE;
}
SERVICE_STATUS status;
if (!QueryServiceStatus(hService, &status))
{
LOGE(("Autodial: ::QueryServiceStatus() failed. Error: %d",
::GetLastError()));
return PR_FALSE;
}
return (status.dwCurrentState == SERVICE_RUNNING);
}
// Add the specified address to the autodial directory.
PRBool nsRASAutodial::AddAddressToAutodialDirectory(const char* hostName)
{
NS_ASSERTION(mpRasGetAutodialAddress != nsnull,
"RAS DLLs only loaded for NT-based OSs.");
NS_ASSERTION(mpRasSetAutodialAddress != nsnull,
"RAS DLLs only loaded for NT-based OSs.");
if (!mpRasGetAutodialAddress || !mpRasSetAutodialAddress)
{
return PR_FALSE;
}
// First see if there is already a db entry for this address.
DWORD size = 0;
DWORD entries = 0;
DWORD result = (*mpRasGetAutodialAddress)(hostName,
NULL,
NULL,
&size,
&entries);
// If there is already an entry in db for this address, return.
if (size != 0)
{
LOGD(("Autodial: Address %s already in autodial db.", hostName));
return PR_FALSE;
}
RASAUTODIALENTRY autodialEntry;
autodialEntry.dwSize = sizeof(RASAUTODIALENTRY);
autodialEntry.dwFlags = 0;
autodialEntry.dwDialingLocation = 1;
GetDefaultEntryName(autodialEntry.szEntry, RAS_MaxEntryName);
result = (*mpRasSetAutodialAddress)(hostName,
0,
&autodialEntry,
sizeof(RASAUTODIALENTRY),
1);
if (result != ERROR_SUCCESS)
{
LOGE(("Autodial ::RasSetAutodialAddress failed result %d.", result));
return PR_FALSE;
}
LOGD(("Autodial: Added address %s to RAS autodial db for entry %s.",
hostName, autodialEntry.szEntry));
return PR_TRUE;
}

View File

@ -0,0 +1,176 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Steve Meredith <smeredith@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsAutodialWin_h__
#define nsAutodialWin_h__
#include <windows.h>
#include <ras.h>
#include <rasdlg.h>
#include <raserror.h>
#include "nscore.h"
#include "nspr.h"
#if (WINVER < 0x401)
/* AutoDial address properties.
*/
typedef struct tagRASAUTODIALENTRYA {
DWORD dwSize;
DWORD dwFlags;
DWORD dwDialingLocation;
CHAR szEntry[RAS_MaxEntryName + 1];
} RASAUTODIALENTRYA, *LPRASAUTODIALENTRYA;
typedef RASAUTODIALENTRYA RASAUTODIALENTRY, *LPRASAUTODIALENTRY;
#endif // WINVER
// Loading the RAS DLL dynamically.
typedef DWORD (WINAPI* tRASPHONEBOOKDLG)(LPTSTR,LPTSTR,LPRASPBDLG);
typedef DWORD (WINAPI* tRASDIALDLG)(LPTSTR,LPTSTR,LPTSTR,LPRASDIALDLG);
typedef DWORD (WINAPI* tRASENUMCONNECTIONS)(LPRASCONN,LPDWORD,LPDWORD);
typedef DWORD (WINAPI* tRASENUMENTRIES)(LPTSTR,LPTSTR,LPRASENTRYNAME,LPDWORD,LPDWORD);
typedef DWORD (WINAPI* tRASSETAUTODIALADDRESS)(LPCTSTR,DWORD,LPRASAUTODIALENTRY,DWORD,DWORD);
typedef DWORD (WINAPI* tRASGETAUTODIALADDRESS)(LPCTSTR,LPDWORD,LPRASAUTODIALENTRY,LPDWORD,LPDWORD);
// For Windows NT 4, 2000, and XP, we sometimes want to open the RAS dialup
// window ourselves, since those versions aren't very nice about it.
// See bug 93002. If the RAS autodial service is running, (Remote Access
// Auto Connection Manager, aka RasAuto) we will force it to dial
// on network error by adding the target address to the autodial
// database. If the service is not running, we will open the RAS dialogs
// instead.
//
// The OS can be configured to always dial, or dial when there is no
// connection. We implement both by dialing when a network error occurs.
//
// An object of this class checks with the OS when it is constructed and
// remembers those settings. If required, it can be resynced with
// the OS at anytime with the Init() method. At the time of implementation,
// the caller creates an object of this class each time a network error occurs.
// In this case, the initialization overhead is not significant, and it will
// guaranteed to be in sync with OS.
//
// To use, create an instance and call ShouldDialOnNetworkError() to determine
// if you should dial or not. That function only return true for the
// target OS's so the caller doesn't have to deal with OS version checking.
//
class nsRASAutodial
{
private:
//
// Some helper functions to query the OS for autodial configuration.
//
// Determine if the autodial service is running on this PC.
PRBool IsAutodialServiceRunning();
// Get the number of RAS connection entries configured from the OS.
int NumRASEntries();
// Get the name of the default connection from the OS.
nsresult GetDefaultEntryName(char* entryName, int bufferSize);
// Get the name of the first RAS dial entry from the OS.
nsresult GetFirstEntryName(char* entryName, int bufferSize);
// Check to see if RAS already has a dialup connection going.
PRBool IsRASConnected();
// Get the autodial behavior from the OS.
int QueryAutodialBehavior();
// Add the specified address to the autodial directory.
PRBool AddAddressToAutodialDirectory(const char* hostName);
//
// Autodial behavior. This comes from the Windows registry, set in the ctor.
// Object won't pick up changes to the registry automatically, but can be
// refreshed at anytime by calling Init(). So if the user changed the
// autodial settings, they wouldn't be noticed unless Init() is called.
int mAutodialBehavior;
enum { AUTODIAL_NEVER = 1 }; // Never autodial.
enum { AUTODIAL_ALWAYS = 2 }; // Always autodial as set in Internet Options.
enum { AUTODIAL_ON_NETWORKERROR = 3 }; // Autodial when a connection error occurs as set in Internet Options.
enum { AUTODIAL_USE_SERVICE = 4 }; // Use the RAS autodial service to dial.
// Number of RAS connection entries in the phonebook.
int mNumRASConnectionEntries;
// Default connection entry name.
char mDefaultEntryName[RAS_MaxEntryName + 1];
// Don't try to dial again within a few seconds of when user pressed cancel.
static PRIntervalTime mDontRetryUntil;
// OS version info.
OSVERSIONINFO mOSVerInfo;
// DLL instance handles.
static HINSTANCE mhRASdlg;
static HINSTANCE mhRASapi32;
// DLL function pointers.
static tRASPHONEBOOKDLG mpRasPhonebookDlg;
static tRASENUMCONNECTIONS mpRasEnumConnections;
static tRASENUMENTRIES mpRasEnumEntries;
static tRASDIALDLG mpRasDialDlg;
static tRASSETAUTODIALADDRESS mpRasSetAutodialAddress;
static tRASGETAUTODIALADDRESS mpRasGetAutodialAddress;
public:
// ctor
nsRASAutodial();
// dtor
virtual ~nsRASAutodial();
// Get the autodial info from the OS and init this obj with it. Call it any
// time to refresh the object's settings from the OS.
nsresult Init();
// Dial the default RAS dialup connection.
nsresult DialDefault(const char* hostName);
// Should we try to dial on network error?
PRBool ShouldDialOnNetworkError();
};
#endif // !nsAutodialWin_h__

View File

@ -68,6 +68,7 @@
#define PORT_PREF_PREFIX "network.security.ports."
#define PORT_PREF(x) PORT_PREF_PREFIX x
#define AUTODIAL_PREF "network.autodial-helper.enabled"
static NS_DEFINE_CID(kFileTransportService, NS_FILETRANSPORTSERVICE_CID);
static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
@ -220,8 +221,10 @@ nsIOService::Init()
GetPrefBranch(getter_AddRefs(prefBranch));
if (prefBranch) {
nsCOMPtr<nsIPrefBranchInternal> pbi = do_QueryInterface(prefBranch);
if (pbi)
if (pbi) {
pbi->AddObserver(PORT_PREF_PREFIX, this, PR_TRUE);
pbi->AddObserver(AUTODIAL_PREF, this, PR_TRUE);
}
PrefsChanged(prefBranch);
}
@ -965,6 +968,16 @@ nsIOService::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
// ...as well as previous blocks to remove.
if (!pref || PL_strcmp(pref, PORT_PREF("banned.override")) == 0)
ParsePortList(prefs, PORT_PREF("banned.override"), PR_TRUE);
if (!pref || PL_strcmp(pref, AUTODIAL_PREF) == 0) {
PRBool enableAutodial = PR_FALSE;
nsresult rv = prefs->GetBoolPref(AUTODIAL_PREF, &enableAutodial);
// If pref not found, default to disabled.
if (NS_SUCCEEDED(rv)) {
if (mSocketTransportService)
mSocketTransportService->SetAutodialEnabled(enableAutodial);
}
}
}
void

View File

@ -0,0 +1,164 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@netscape.com>
* Steve Meredith <smeredith@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsNativeConnectionHelper.h"
#include "nsIEventQueueService.h"
#include "nsIServiceManager.h"
#include "nsCOMPtr.h"
#include "nsAutodialWin.h"
#include "nsIPref.h"
static NS_DEFINE_IID(kPrefServiceCID, NS_PREF_CID);
//-----------------------------------------------------------------------------
// nsSyncEvent - implements a synchronous event that runs on the main thread.
//
// XXX move this class into xpcom/proxy
//-----------------------------------------------------------------------------
class nsSyncEvent
{
public:
nsSyncEvent() {}
virtual ~nsSyncEvent() {}
nsresult Dispatch();
virtual void HandleEvent() = 0;
private:
static void* PR_CALLBACK EventHandler (PLEvent *self);
static void PR_CALLBACK EventDestructor (PLEvent *self);
};
nsresult
nsSyncEvent::Dispatch()
{
nsresult rv;
nsCOMPtr<nsIEventQueueService> eqs(
do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIEventQueue> eventQ;
rv = eqs->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE,
getter_AddRefs(eventQ));
if (NS_FAILED(rv)) return rv;
PRBool onCurrentThread = PR_FALSE;
eventQ->IsQueueOnCurrentThread(&onCurrentThread);
if (onCurrentThread) {
HandleEvent();
return NS_OK;
}
// Otherwise build up an event and post it to the main thread's event queue
// and block before returning.
PLEvent *ev = new PLEvent;
if (!ev)
return NS_ERROR_OUT_OF_MEMORY;
PL_InitEvent(ev, (void *) this, EventHandler, EventDestructor);
// block until the event is answered
eventQ->PostSynchronousEvent(ev, nsnull);
return NS_OK;
}
void* PR_CALLBACK
nsSyncEvent::EventHandler(PLEvent *self)
{
nsSyncEvent *ev = (nsSyncEvent *) PL_GetEventOwner(self);
if (ev)
ev->HandleEvent();
return nsnull;
}
void PR_CALLBACK
nsSyncEvent::EventDestructor(PLEvent *self)
{
delete self;
}
//-----------------------------------------------------------------------------
// nsOnConnectionFailedEvent
//-----------------------------------------------------------------------------
class nsOnConnectionFailedEvent : public nsSyncEvent
{
private:
nsRASAutodial mAutodial;
const char* mHostName;
public:
nsOnConnectionFailedEvent(const char* hostname) : mRetry(PR_FALSE), mHostName(hostname) { }
void HandleEvent();
PRBool mRetry;
};
void nsOnConnectionFailedEvent::HandleEvent()
{
mRetry = PR_FALSE;
if (mAutodial.ShouldDialOnNetworkError()) {
mRetry = NS_SUCCEEDED(mAutodial.DialDefault(mHostName));
}
}
//-----------------------------------------------------------------------------
// API typically invoked on the socket transport thread
//-----------------------------------------------------------------------------
PRBool
nsNativeConnectionHelper::OnConnectionFailed(const char* strHostName)
{
nsOnConnectionFailedEvent event(strHostName);
if (NS_SUCCEEDED(event.Dispatch()))
return event.mRetry;
return PR_FALSE;
}

View File

@ -0,0 +1,57 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsNativeConnectionHelper_h__
#define nsNativeConnectionHelper_h__
#include "nscore.h"
class nsISocketTransport;
class nsNativeConnectionHelper
{
public:
/**
* OnConnectionFailed
*
* Return PR_TRUE if the connection should be re-attempted.
*/
static PRBool OnConnectionFailed(const char* strHostName);
};
#endif // !nsNativeConnectionHelper_h__

View File

@ -63,6 +63,10 @@
#include "nsIProxyInfo.h"
#include "nsProxyRelease.h"
#if defined(XP_WIN)
#include "nsNativeConnectionHelper.h"
#endif
#if defined(PR_LOGGING)
static PRLogModuleInfo *gSocketTransportLog = nsnull;
#endif
@ -536,7 +540,9 @@ nsresult nsSocketTransport::Process(PRInt16 aSelectFlags)
// on connection failure, reuse next address if one exists
if (mStatus == NS_ERROR_CONNECTION_REFUSED) {
LOG(("connection failed [this=%x error=%x]\n", this, mStatus));
if (TryNextAddress()) {
// Try again? OnConnectionFailed() may exit and re-enter the monitor.
if (OnConnectionFailed(PR_TRUE)) {
done = PR_TRUE;
continue;
}
@ -563,7 +569,9 @@ nsresult nsSocketTransport::Process(PRInt16 aSelectFlags)
firedOnStart = mReadRequest->IsInitialized();
if (!firedOnStart && mWriteRequest)
firedOnStart = mWriteRequest->IsInitialized();
if (!firedOnStart && TryNextAddress()) {
// Try again? OnConnectionFailed() may exit and re-enter the monitor.
if (!firedOnStart && OnConnectionFailed(PR_TRUE)) {
// a little bit of hackery here so we'll end up in the
// WaitConnect state...
mCurrentState = eSocketState_WaitConnect;
@ -619,27 +627,60 @@ nsSocketTransport::Cancel(nsresult status)
return NS_OK;
}
// Try next address if param says to. Otherwise, try to use autodial. If a
// connection is made, we try the network again. Otherwise, we don't.
// Returns true to cause connection failures to try again.
//
PRBool
nsSocketTransport::TryNextAddress()
nsSocketTransport::OnConnectionFailed(PRBool tryNextAddress)
{
mNetAddress = mNetAddrList.GetNext(mNetAddress);
if (mNetAddress) {
LOG(("nsSocketTransport Entering OnConnectionFailed()"));
PRBool tryAgain = PR_FALSE;
// If required, retry using next address.
if (tryNextAddress) {
PRNetAddr* nextNetAddress = mNetAddrList.GetNext(mNetAddress);
if (nextNetAddress) {
mNetAddress = nextNetAddress;
#if defined(PR_LOGGING)
char buf[64];
PR_NetAddrToString(mNetAddress, buf, sizeof(buf));
LOG((" ...trying next address: %s\n", buf));
char buf[64];
PR_NetAddrToString(mNetAddress, buf, sizeof(buf));
LOG((" ...trying next address: %s\n", buf));
#endif
PR_Close(mSocketFD);
tryAgain = PR_TRUE;
}
}
// If not trying next address, try to make a connection using dialup.
// Retry if that connection is made.
if (!tryAgain && mService->mAutodialEnabled) {
#if defined(XP_WIN)
PR_ExitMonitor(mMonitor);
tryAgain = nsNativeConnectionHelper::OnConnectionFailed(GetSocketHost());
PR_EnterMonitor(mMonitor);
#endif
}
// Prepare to try again.
if (tryAgain)
{
if (mSocketFD)
PR_Close(mSocketFD);
mSocketFD = nsnull;
// mask error status so we'll return to this state
mStatus = NS_OK;
// need to re-enter Process() asynchronously
mService->AddToWorkQ(this);
return PR_TRUE;
}
return PR_FALSE;
LOG(("nsSocketTransport Leaving OnConnectionFailed() tryAgain = %d",tryAgain));
return tryAgain;
}
void
@ -1629,11 +1670,21 @@ nsSocketTransport::OnStopLookup(nsISupports *aContext,
// Release our reference to the DNS Request...
mDNSRequest = 0;
// If the lookup failed, set the status...
if (NS_FAILED(aStatus))
mStatus = aStatus;
else if (mNetAddress == nsnull)
// If the lookup failed...
if (NS_FAILED(aStatus)) {
// Retry? OnConnectionFailed() may exit and re-enter the monitor.
// Don't want to set the tryNextAddress param because DNS lookup has
// just failed so hostname has not been resolved yet.
if (aStatus != NS_BASE_STREAM_WOULD_BLOCK && OnConnectionFailed(PR_FALSE))
mStatus = NS_OK;
else
mStatus = aStatus;
}
else if (mNetAddress == nsnull) {
mStatus = NS_ERROR_ABORT;
}
// Start processing the transport again - if necessary...
if (GetFlag(eSocketDNS_Wait)) {

View File

@ -208,7 +208,7 @@ protected:
nsresult doReadWrite(PRInt16 aSelectFlags);
nsresult doResolveHost();
PRBool TryNextAddress();
PRBool OnConnectionFailed(PRBool tryNextAddress);
void CompleteAsyncRead();
void CompleteAsyncWrite();

View File

@ -69,7 +69,8 @@ static NS_DEFINE_CID(kDNSService, NS_DNSSERVICE_CID);
nsSocketTransportService::nsSocketTransportService () :
mConnectedTransports (0),
mTotalTransports (0)
mTotalTransports (0),
mAutodialEnabled(PR_FALSE)
{
#if defined(PR_LOGGING)
if (!gSocketTransportServiceLog)
@ -937,3 +938,18 @@ nsSocketTransportService::OnTransportConnected(const char *host, PRNetAddr *addr
}
#endif
}
NS_IMETHODIMP
nsSocketTransportService::GetAutodialEnabled(PRBool *enabled)
{
*enabled = mAutodialEnabled;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransportService::SetAutodialEnabled(PRBool enabled)
{
mAutodialEnabled = enabled;
return NS_OK;
}

View File

@ -115,6 +115,9 @@ public:
nsIDNSService *GetCachedDNSService() { return mDNSService.get(); }
nsIEventQueueService *GetCachedEventQueueService() { return mEventQService.get(); }
// Pref to enable autodial helper on windows.
PRBool mAutodialEnabled;
protected:
//
// mHostDB maps hostname -> nsHostEntry

View File

@ -1346,12 +1346,12 @@ nsDNSService::Run()
// convert InetHostInfo to nsHostEnt
lookup->ConvertHostEntry();
}
lookup->ProcessRequests();
if (lookup->IsNotCacheable()) {
EvictLookup(lookup);
} else {
AddToEvictionQ(lookup);
}
lookup->ProcessRequests();
NS_RELEASE(lookup);
}
@ -1376,12 +1376,12 @@ nsDNSService::Run()
// Got a request!!
NS_ADDREF(lookup); // keep the lookup while we process it
lookup->DoSyncLookup();
lookup->ProcessRequests();
if (lookup->IsNotCacheable()) {
EvictLookup(lookup);
} else {
AddToEvictionQ(lookup);
}
lookup->ProcessRequests();
NS_RELEASE(lookup);
} else {
// Woken up without a request --> shutdown
@ -1495,7 +1495,6 @@ nsDNSService::Lookup(const char* hostName,
if (NS_FAILED(rv)) goto exit;
if (lookup->IsComplete()) {
lookup->ProcessRequests(); // releases and re-acquires dns lock
if (lookup->IsNotCacheable()) {
// non-cacheable lookups are released here.
EvictLookup(lookup);
@ -1505,6 +1504,7 @@ nsDNSService::Lookup(const char* hostName,
AddToEvictionQ(lookup);
#endif
}
lookup->ProcessRequests(); // releases and re-acquires dns lock
}
exit:
@ -1639,8 +1639,10 @@ nsDNSService::AbortLookups()
while(lookup != &mPendingQ) {
PR_REMOVE_AND_INIT_LINK(lookup);
lookup->MarkComplete(NS_BINDING_ABORTED);
lookup->ProcessRequests();
NS_ADDREF(lookup);
EvictLookup(lookup);
lookup->ProcessRequests();
NS_RELEASE(lookup);
lookup = (nsDNSLookup *)PR_LIST_HEAD(&mPendingQ);
}
@ -1974,12 +1976,12 @@ nsDNSService::ProcessLookup(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
FreeMsgID(lookup->mMsgID);
PR_REMOVE_AND_INIT_LINK(lookup);
lookup->ProcessRequests();
if (lookup->IsNotCacheable()) {
EvictLookup(lookup);
} else {
AddToEvictionQ(lookup);
}
lookup->ProcessRequests();
NS_RELEASE(lookup);
return error ? -1 : 0;