Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / Net / _AutoWebProxyScriptEngine.cs / 1305376 / _AutoWebProxyScriptEngine.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Net { using System.IO; using System.Collections; using System.Collections.Specialized; using System.Threading; using System.Text; using System.Net.Cache; #if !FEATURE_PAL using System.Net.NetworkInformation; using System.Security.Principal; #endif using System.Globalization; using System.Net.Configuration; using System.Security.Permissions; using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Win32; // This class (and its helper classes implementing IWebRequestFinder interface) are responsible for // determining the location of the PAC file, download and execute it, in order to retrieve proxy // information. // This class also monitors the Registry and re-reads proxy configuration settings, if the corresponding // Registry values change. internal class AutoWebProxyScriptEngine { private bool automaticallyDetectSettings; private Uri automaticConfigurationScript; private WebProxy webProxy; private IWebProxyFinder webProxyFinder; // Used by abortable lock. private bool m_LockHeld; private bool m_UseRegistry; #if !FEATURE_PAL // Used to get notifications of network changes and do AutoDetection (which are global). private int m_NetworkChangeStatus; private AutoDetector m_AutoDetector; // This has to hold on to the creating user's registry hive and impersonation context. private SafeRegistryHandle hkcu; private WindowsIdentity m_Identity; #endif // !FEATURE_PAL [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)] internal AutoWebProxyScriptEngine(WebProxy proxy, bool useRegistry) { GlobalLog.Assert(proxy != null, "'proxy' must be assigned."); webProxy = proxy; m_UseRegistry = useRegistry; #if !FEATURE_PAL m_AutoDetector = AutoDetector.CurrentAutoDetector; m_NetworkChangeStatus = m_AutoDetector.NetworkChangeStatus; SafeRegistryHandle.RegOpenCurrentUser(UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out hkcu); if (m_UseRegistry) { ListenForRegistry(); // Keep track of the identity we used to read the registry, in case we need to read it again later. m_Identity = WindowsIdentity.GetCurrent(); } #endif // !FEATURE_PAL // In Win2003 winhttp added a Windows Service handling the auto-proxy discovery. In XP using winhttp // APIs will load, compile and execute the wpad file in-process. This will also load COM, since // WinHttp requires COM to compile the file. For these reasons, we don't use WinHttp on XP, but // only on newer OS versions where the "WinHTTP Web Proxy Auto-Discovery Service" exists. if (ComNetOS.IsWin2k3) { webProxyFinder = new HybridWebProxyFinder(this); } else { webProxyFinder = new NetWebProxyFinder(this); } } // AutoWebProxyScriptEngine has special abortable locking. No one should ever lock (this) except the locking helper methods below. private static class SyncStatus { internal const int Unlocked = 0; internal const int Locking = 1; internal const int LockOwner = 2; internal const int AbortedLocked = 3; internal const int Aborted = 4; } private void EnterLock(ref int syncStatus) { if (syncStatus == SyncStatus.Unlocked) { lock (this) { if (syncStatus != SyncStatus.Aborted) { syncStatus = SyncStatus.Locking; while (true) { if (!m_LockHeld) { syncStatus = SyncStatus.LockOwner; m_LockHeld = true; return; } Monitor.Wait(this); if (syncStatus == SyncStatus.Aborted) { Monitor.Pulse(this); // This is to ensure that a Pulse meant to let someone take the lock isn't lost. return; } } } } } } private void ExitLock(ref int syncStatus) { if (syncStatus != SyncStatus.Unlocked && syncStatus != SyncStatus.Aborted) { lock (this) { m_LockHeld = false; if (syncStatus == SyncStatus.AbortedLocked) { webProxyFinder.Reset(); syncStatus = SyncStatus.Aborted; } else { syncStatus = SyncStatus.Unlocked; } Monitor.Pulse(this); } } } internal void Abort(ref int syncStatus) { lock (this) { switch (syncStatus) { case SyncStatus.Unlocked: syncStatus = SyncStatus.Aborted; break; case SyncStatus.Locking: syncStatus = SyncStatus.Aborted; Monitor.PulseAll(this); break; case SyncStatus.LockOwner: syncStatus = SyncStatus.AbortedLocked; webProxyFinder.Abort(); break; } } } // End of locking helper methods. // The lock is always held while these three are modified. internal bool AutomaticallyDetectSettings { get { return automaticallyDetectSettings; } set { if (automaticallyDetectSettings != value) { automaticallyDetectSettings = value; webProxyFinder.Reset(); } } } internal Uri AutomaticConfigurationScript { get { return automaticConfigurationScript; } set { if (!object.Equals(automaticConfigurationScript, value)) { automaticConfigurationScript = value; webProxyFinder.Reset(); } } } internal ICredentials Credentials { get { return webProxy.Credentials; } } internal bool GetProxies(Uri destination, out IListproxyList) { int syncStatus = SyncStatus.Unlocked; return GetProxies(destination, out proxyList, ref syncStatus); } internal bool GetProxies(Uri destination, out IList proxyList, ref int syncStatus) { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::GetProxies()"); proxyList = null; #if !FEATURE_PAL // See if we need to reinitialize based on registry or other changes. #if !AUTOPROXY_SKIP_CHECK CheckForChanges(ref syncStatus); #endif #endif // !FEATURE_PAL if (!webProxyFinder.IsValid) { // This is to improve performance on e.g. home networks, where auto-detect will always // fail, but IE settings turn auto-detect ON by default. I.e. in home networks on each // call we would try to retrieve the PAC location. // The downside of this approach is that if after some time the PAC file can be downloaded, // we don't do it. An application restart, changes in the proxy Registry settings, or a // connection change (e.g. dial-up/VPN) are required in order to retry to retrieve the PAC file. return false; } // This whole thing has to be locked, both to prevent simultaneous downloading / compilation, and // because the script isn't threadsafe. try { EnterLock(ref syncStatus); if (syncStatus != SyncStatus.LockOwner) { // This is typically because a download got aborted. return false; } return webProxyFinder.GetProxies(destination, out proxyList); } finally { ExitLock(ref syncStatus); GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::GetProxies() proxies:" + ValidationHelper.ToString(proxyList)); } } internal WebProxyData GetWebProxyData() { // PS DevDiv bug #217205 / TFS Dev10 #588370: use winhttp.WinhttpGetIEProxyConfigForCurrentUser WebProxyDataBuilder builder = null; if (ComNetOS.IsWin7) { builder = new WinHttpWebProxyBuilder(); } else { builder = new RegBlobWebProxyDataBuilder(m_AutoDetector.Connectoid, hkcu); } return builder.Build(); } internal void Close() { #if !FEATURE_PAL // m_AutoDetector is always set up in the constructor, use it to lock if (m_AutoDetector != null) { int syncStatus = SyncStatus.Unlocked; try { EnterLock(ref syncStatus); GlobalLog.Assert(syncStatus == SyncStatus.LockOwner, "AutoWebProxyScriptEngine#{0}::Close()|Failed to acquire lock.", ValidationHelper.HashString(this)); if (m_AutoDetector != null) { registrySuppress = true; if (registryChangeEventPolicy != null) { registryChangeEventPolicy.Close(); registryChangeEventPolicy = null; } if (registryChangeEventLM != null) { registryChangeEventLM.Close(); registryChangeEventLM = null; } if (registryChangeEvent != null) { registryChangeEvent.Close(); registryChangeEvent = null; } if (regKeyPolicy != null && !regKeyPolicy.IsInvalid) { regKeyPolicy.Close(); } if (regKeyLM != null && !regKeyLM.IsInvalid) { regKeyLM.Close(); } if (regKey != null && !regKey.IsInvalid) { regKey.Close(); } if (hkcu != null) { hkcu.RegCloseKey(); hkcu = null; } if (m_Identity != null) { m_Identity.Dispose(); m_Identity = null; } webProxyFinder.Dispose(); m_AutoDetector = null; } } finally { ExitLock(ref syncStatus); } } #endif // !FEATURE_PAL } #if !FEATURE_PAL private SafeRegistryHandle regKey; private SafeRegistryHandle regKeyLM; private SafeRegistryHandle regKeyPolicy; private AutoResetEvent registryChangeEvent; private AutoResetEvent registryChangeEventLM; private AutoResetEvent registryChangeEventPolicy; private bool registryChangeDeferred; private bool registryChangeLMDeferred; private bool registryChangePolicyDeferred; private bool needRegistryUpdate; private bool needConnectoidUpdate; private bool registrySuppress; internal void ListenForRegistry() { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry()"); if (!registrySuppress) { if (registryChangeEvent == null) { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() hooking HKCU."); ListenForRegistryHelper(ref regKey, ref registryChangeEvent, IntPtr.Zero, RegBlobWebProxyDataBuilder.ProxyKey); } if (registryChangeEventLM == null) { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() hooking HKLM."); ListenForRegistryHelper(ref regKeyLM, ref registryChangeEventLM, UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.ProxyKey); } if (registryChangeEventPolicy == null) { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() hooking HKLM/Policies."); ListenForRegistryHelper(ref regKeyPolicy, ref registryChangeEventPolicy, UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.PolicyKey); } // If any succeeded, we should monitor it. if (registryChangeEvent == null && registryChangeEventLM == null && registryChangeEventPolicy == null) { registrySuppress = true; } } } private void ListenForRegistryHelper(ref SafeRegistryHandle key, ref AutoResetEvent changeEvent, IntPtr baseKey, string subKey) { uint errorCode = 0; // First time through? if (key == null || key.IsInvalid) { if (baseKey == IntPtr.Zero) { // Impersonation requires extra effort. GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegOpenCurrentUser() using hkcu:" + hkcu.DangerousGetHandle().ToString("x")); if (hkcu != null) { errorCode = hkcu.RegOpenKeyEx(subKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key); GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegOpenKeyEx() returned errorCode:" + errorCode + " key:" + key.DangerousGetHandle().ToString("x")); } else { errorCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND; } } else { errorCode = SafeRegistryHandle.RegOpenKeyEx(baseKey, subKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key); //GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegOpenKeyEx() returned errorCode:" + errorCode + " key:" + key.DangerousGetHandle().ToString("x")); } if (errorCode == 0) { changeEvent = new AutoResetEvent(false); } } if (errorCode == 0) { // accessing Handle is protected by a link demand, OK for System.dll errorCode = key.RegNotifyChangeKeyValue(true, UnsafeNclNativeMethods.RegistryHelper.REG_NOTIFY_CHANGE_LAST_SET, changeEvent.SafeWaitHandle, true); GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegNotifyChangeKeyValue() returned errorCode:" + errorCode); } if (errorCode != 0) { if (key != null && !key.IsInvalid) { try { errorCode = key.RegCloseKey(); } catch (Exception exception) { if (NclUtilities.IsFatal(exception)) throw; } GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegCloseKey() returned errorCode:" + errorCode); } key = null; if (changeEvent != null) { changeEvent.Close(); changeEvent = null; } } } private void RegistryChanged() { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::RegistryChanged()"); if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_system_setting_update)); // always refresh settings because they might have changed WebProxyData webProxyData; using (m_Identity.Impersonate()) { webProxyData = GetWebProxyData(); } webProxy.Update(webProxyData); } private void ConnectoidChanged() { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ConnectoidChanged()"); if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_update_due_to_ip_config_change)); // Get the new connectoid/detector. Only do this after detecting a change, to avoid ----s with other people detecting changes. // (We don't want to end up using a detector/connectoid that doesn't match what we read from the registry.) m_AutoDetector = AutoDetector.CurrentAutoDetector; if (m_UseRegistry) { // update the engine and proxy WebProxyData webProxyData; using (m_Identity.Impersonate()) { webProxyData = GetWebProxyData(); } webProxy.Update(webProxyData); } // Always uninitialized if the connectoid/address changed and we are autodetecting. if (automaticallyDetectSettings) webProxyFinder.Reset(); } internal void CheckForChanges() { int syncStatus = SyncStatus.Unlocked; CheckForChanges(ref syncStatus); } [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)] private void CheckForChanges(ref int syncStatus) { // Catch ObjectDisposedException instead of synchronizing with Close(). try { bool changed = AutoDetector.CheckForNetworkChanges(ref m_NetworkChangeStatus); bool ignoreRegistryChange = false; if (changed || needConnectoidUpdate) { try { EnterLock(ref syncStatus); if (changed || needConnectoidUpdate) // Make sure no one else took care of it before we got the lock. { needConnectoidUpdate = syncStatus != SyncStatus.LockOwner; if (!needConnectoidUpdate) { ConnectoidChanged(); // We usually get a registry change at the same time. Since the connectoid change does more, // we can skip reading the registry info twice. ignoreRegistryChange = true; } } } finally { ExitLock(ref syncStatus); } } if (!m_UseRegistry) { return; } bool forReal = false; AutoResetEvent tempEvent = registryChangeEvent; if (registryChangeDeferred || (forReal = (tempEvent != null && tempEvent.WaitOne(0, false)))) { try { EnterLock(ref syncStatus); if (forReal || registryChangeDeferred) // Check if someone else handled it before I got the lock. { registryChangeDeferred = syncStatus != SyncStatus.LockOwner; if (!registryChangeDeferred && registryChangeEvent != null) { try { using (m_Identity.Impersonate()) { ListenForRegistryHelper(ref regKey, ref registryChangeEvent, IntPtr.Zero, RegBlobWebProxyDataBuilder.ProxyKey); } } catch { throw; } needRegistryUpdate = true; } } } finally { ExitLock(ref syncStatus); } } forReal = false; tempEvent = registryChangeEventLM; if (registryChangeLMDeferred || (forReal = (tempEvent != null && tempEvent.WaitOne(0, false)))) { try { EnterLock(ref syncStatus); if (forReal || registryChangeLMDeferred) // Check if someone else handled it before I got the lock. { registryChangeLMDeferred = syncStatus != SyncStatus.LockOwner; if (!registryChangeLMDeferred && registryChangeEventLM != null) { try { using (m_Identity.Impersonate()) { ListenForRegistryHelper(ref regKeyLM, ref registryChangeEventLM, UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.ProxyKey); } } catch { throw; } needRegistryUpdate = true; } } } finally { ExitLock(ref syncStatus); } } forReal = false; tempEvent = registryChangeEventPolicy; if (registryChangePolicyDeferred || (forReal = (tempEvent != null && tempEvent.WaitOne(0, false)))) { try { EnterLock(ref syncStatus); if (forReal || registryChangePolicyDeferred) // Check if someone else handled it before I got the lock. { registryChangePolicyDeferred = syncStatus != SyncStatus.LockOwner; if (!registryChangePolicyDeferred && registryChangeEventPolicy != null) { try { using (m_Identity.Impersonate()) { ListenForRegistryHelper(ref regKeyPolicy, ref registryChangeEventPolicy, UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.PolicyKey); } } catch { throw; } needRegistryUpdate = true; } } } finally { ExitLock(ref syncStatus); } } if (needRegistryUpdate) { try { EnterLock(ref syncStatus); if (needRegistryUpdate && syncStatus == SyncStatus.LockOwner) { needRegistryUpdate = false; // We don't need to process this now if we just did it for the connectoid. if (!ignoreRegistryChange) { RegistryChanged(); } } } finally { ExitLock(ref syncStatus); } } } catch (ObjectDisposedException) { } } private class AutoDetector { private static NetworkAddressChangePolled s_AddressChange; private static UnsafeNclNativeMethods.RasHelper s_RasHelper; private static int s_CurrentVersion = 0; private volatile static AutoDetector s_CurrentAutoDetector; private volatile static bool s_Initialized; private static object s_LockObject; static AutoDetector() { s_LockObject = new object(); } private static void Initialize() { if (!s_Initialized) { lock (s_LockObject) { if (!s_Initialized) { s_CurrentAutoDetector = new AutoDetector(UnsafeNclNativeMethods.RasHelper.GetCurrentConnectoid(), 1); if (NetworkChange.CanListenForNetworkChanges) { s_AddressChange = new NetworkAddressChangePolled(); } if (UnsafeNclNativeMethods.RasHelper.RasSupported) { s_RasHelper = new UnsafeNclNativeMethods.RasHelper(); } s_CurrentVersion = 1; s_Initialized = true; } } } } internal static bool CheckForNetworkChanges(ref int changeStatus) { Initialize(); CheckForChanges(); int oldStatus = changeStatus; changeStatus = s_CurrentVersion; return oldStatus != changeStatus; } private static void CheckForChanges() { bool changed = false; if (s_RasHelper != null && s_RasHelper.HasChanged) { s_RasHelper.Reset(); changed = true; } if (s_AddressChange != null && s_AddressChange.CheckAndReset()) { changed = true; } if (changed) { Interlocked.Increment(ref s_CurrentVersion); s_CurrentAutoDetector = new AutoDetector(UnsafeNclNativeMethods.RasHelper.GetCurrentConnectoid(), s_CurrentVersion); } } internal static AutoDetector CurrentAutoDetector { get { Initialize(); return s_CurrentAutoDetector; } } private readonly string m_Connectoid; private readonly int m_CurrentVersion; private AutoDetector(string connectoid, int currentVersion) { m_Connectoid = connectoid; m_CurrentVersion = currentVersion; } internal string Connectoid { get { return m_Connectoid; } } internal int NetworkChangeStatus { get { return m_CurrentVersion; } } } #endif } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Net { using System.IO; using System.Collections; using System.Collections.Specialized; using System.Threading; using System.Text; using System.Net.Cache; #if !FEATURE_PAL using System.Net.NetworkInformation; using System.Security.Principal; #endif using System.Globalization; using System.Net.Configuration; using System.Security.Permissions; using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Win32; // This class (and its helper classes implementing IWebRequestFinder interface) are responsible for // determining the location of the PAC file, download and execute it, in order to retrieve proxy // information. // This class also monitors the Registry and re-reads proxy configuration settings, if the corresponding // Registry values change. internal class AutoWebProxyScriptEngine { private bool automaticallyDetectSettings; private Uri automaticConfigurationScript; private WebProxy webProxy; private IWebProxyFinder webProxyFinder; // Used by abortable lock. private bool m_LockHeld; private bool m_UseRegistry; #if !FEATURE_PAL // Used to get notifications of network changes and do AutoDetection (which are global). private int m_NetworkChangeStatus; private AutoDetector m_AutoDetector; // This has to hold on to the creating user's registry hive and impersonation context. private SafeRegistryHandle hkcu; private WindowsIdentity m_Identity; #endif // !FEATURE_PAL [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)] internal AutoWebProxyScriptEngine(WebProxy proxy, bool useRegistry) { GlobalLog.Assert(proxy != null, "'proxy' must be assigned."); webProxy = proxy; m_UseRegistry = useRegistry; #if !FEATURE_PAL m_AutoDetector = AutoDetector.CurrentAutoDetector; m_NetworkChangeStatus = m_AutoDetector.NetworkChangeStatus; SafeRegistryHandle.RegOpenCurrentUser(UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out hkcu); if (m_UseRegistry) { ListenForRegistry(); // Keep track of the identity we used to read the registry, in case we need to read it again later. m_Identity = WindowsIdentity.GetCurrent(); } #endif // !FEATURE_PAL // In Win2003 winhttp added a Windows Service handling the auto-proxy discovery. In XP using winhttp // APIs will load, compile and execute the wpad file in-process. This will also load COM, since // WinHttp requires COM to compile the file. For these reasons, we don't use WinHttp on XP, but // only on newer OS versions where the "WinHTTP Web Proxy Auto-Discovery Service" exists. if (ComNetOS.IsWin2k3) { webProxyFinder = new HybridWebProxyFinder(this); } else { webProxyFinder = new NetWebProxyFinder(this); } } // AutoWebProxyScriptEngine has special abortable locking. No one should ever lock (this) except the locking helper methods below. private static class SyncStatus { internal const int Unlocked = 0; internal const int Locking = 1; internal const int LockOwner = 2; internal const int AbortedLocked = 3; internal const int Aborted = 4; } private void EnterLock(ref int syncStatus) { if (syncStatus == SyncStatus.Unlocked) { lock (this) { if (syncStatus != SyncStatus.Aborted) { syncStatus = SyncStatus.Locking; while (true) { if (!m_LockHeld) { syncStatus = SyncStatus.LockOwner; m_LockHeld = true; return; } Monitor.Wait(this); if (syncStatus == SyncStatus.Aborted) { Monitor.Pulse(this); // This is to ensure that a Pulse meant to let someone take the lock isn't lost. return; } } } } } } private void ExitLock(ref int syncStatus) { if (syncStatus != SyncStatus.Unlocked && syncStatus != SyncStatus.Aborted) { lock (this) { m_LockHeld = false; if (syncStatus == SyncStatus.AbortedLocked) { webProxyFinder.Reset(); syncStatus = SyncStatus.Aborted; } else { syncStatus = SyncStatus.Unlocked; } Monitor.Pulse(this); } } } internal void Abort(ref int syncStatus) { lock (this) { switch (syncStatus) { case SyncStatus.Unlocked: syncStatus = SyncStatus.Aborted; break; case SyncStatus.Locking: syncStatus = SyncStatus.Aborted; Monitor.PulseAll(this); break; case SyncStatus.LockOwner: syncStatus = SyncStatus.AbortedLocked; webProxyFinder.Abort(); break; } } } // End of locking helper methods. // The lock is always held while these three are modified. internal bool AutomaticallyDetectSettings { get { return automaticallyDetectSettings; } set { if (automaticallyDetectSettings != value) { automaticallyDetectSettings = value; webProxyFinder.Reset(); } } } internal Uri AutomaticConfigurationScript { get { return automaticConfigurationScript; } set { if (!object.Equals(automaticConfigurationScript, value)) { automaticConfigurationScript = value; webProxyFinder.Reset(); } } } internal ICredentials Credentials { get { return webProxy.Credentials; } } internal bool GetProxies(Uri destination, out IListproxyList) { int syncStatus = SyncStatus.Unlocked; return GetProxies(destination, out proxyList, ref syncStatus); } internal bool GetProxies(Uri destination, out IList proxyList, ref int syncStatus) { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::GetProxies()"); proxyList = null; #if !FEATURE_PAL // See if we need to reinitialize based on registry or other changes. #if !AUTOPROXY_SKIP_CHECK CheckForChanges(ref syncStatus); #endif #endif // !FEATURE_PAL if (!webProxyFinder.IsValid) { // This is to improve performance on e.g. home networks, where auto-detect will always // fail, but IE settings turn auto-detect ON by default. I.e. in home networks on each // call we would try to retrieve the PAC location. // The downside of this approach is that if after some time the PAC file can be downloaded, // we don't do it. An application restart, changes in the proxy Registry settings, or a // connection change (e.g. dial-up/VPN) are required in order to retry to retrieve the PAC file. return false; } // This whole thing has to be locked, both to prevent simultaneous downloading / compilation, and // because the script isn't threadsafe. try { EnterLock(ref syncStatus); if (syncStatus != SyncStatus.LockOwner) { // This is typically because a download got aborted. return false; } return webProxyFinder.GetProxies(destination, out proxyList); } finally { ExitLock(ref syncStatus); GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::GetProxies() proxies:" + ValidationHelper.ToString(proxyList)); } } internal WebProxyData GetWebProxyData() { // PS DevDiv bug #217205 / TFS Dev10 #588370: use winhttp.WinhttpGetIEProxyConfigForCurrentUser WebProxyDataBuilder builder = null; if (ComNetOS.IsWin7) { builder = new WinHttpWebProxyBuilder(); } else { builder = new RegBlobWebProxyDataBuilder(m_AutoDetector.Connectoid, hkcu); } return builder.Build(); } internal void Close() { #if !FEATURE_PAL // m_AutoDetector is always set up in the constructor, use it to lock if (m_AutoDetector != null) { int syncStatus = SyncStatus.Unlocked; try { EnterLock(ref syncStatus); GlobalLog.Assert(syncStatus == SyncStatus.LockOwner, "AutoWebProxyScriptEngine#{0}::Close()|Failed to acquire lock.", ValidationHelper.HashString(this)); if (m_AutoDetector != null) { registrySuppress = true; if (registryChangeEventPolicy != null) { registryChangeEventPolicy.Close(); registryChangeEventPolicy = null; } if (registryChangeEventLM != null) { registryChangeEventLM.Close(); registryChangeEventLM = null; } if (registryChangeEvent != null) { registryChangeEvent.Close(); registryChangeEvent = null; } if (regKeyPolicy != null && !regKeyPolicy.IsInvalid) { regKeyPolicy.Close(); } if (regKeyLM != null && !regKeyLM.IsInvalid) { regKeyLM.Close(); } if (regKey != null && !regKey.IsInvalid) { regKey.Close(); } if (hkcu != null) { hkcu.RegCloseKey(); hkcu = null; } if (m_Identity != null) { m_Identity.Dispose(); m_Identity = null; } webProxyFinder.Dispose(); m_AutoDetector = null; } } finally { ExitLock(ref syncStatus); } } #endif // !FEATURE_PAL } #if !FEATURE_PAL private SafeRegistryHandle regKey; private SafeRegistryHandle regKeyLM; private SafeRegistryHandle regKeyPolicy; private AutoResetEvent registryChangeEvent; private AutoResetEvent registryChangeEventLM; private AutoResetEvent registryChangeEventPolicy; private bool registryChangeDeferred; private bool registryChangeLMDeferred; private bool registryChangePolicyDeferred; private bool needRegistryUpdate; private bool needConnectoidUpdate; private bool registrySuppress; internal void ListenForRegistry() { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry()"); if (!registrySuppress) { if (registryChangeEvent == null) { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() hooking HKCU."); ListenForRegistryHelper(ref regKey, ref registryChangeEvent, IntPtr.Zero, RegBlobWebProxyDataBuilder.ProxyKey); } if (registryChangeEventLM == null) { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() hooking HKLM."); ListenForRegistryHelper(ref regKeyLM, ref registryChangeEventLM, UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.ProxyKey); } if (registryChangeEventPolicy == null) { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() hooking HKLM/Policies."); ListenForRegistryHelper(ref regKeyPolicy, ref registryChangeEventPolicy, UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.PolicyKey); } // If any succeeded, we should monitor it. if (registryChangeEvent == null && registryChangeEventLM == null && registryChangeEventPolicy == null) { registrySuppress = true; } } } private void ListenForRegistryHelper(ref SafeRegistryHandle key, ref AutoResetEvent changeEvent, IntPtr baseKey, string subKey) { uint errorCode = 0; // First time through? if (key == null || key.IsInvalid) { if (baseKey == IntPtr.Zero) { // Impersonation requires extra effort. GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegOpenCurrentUser() using hkcu:" + hkcu.DangerousGetHandle().ToString("x")); if (hkcu != null) { errorCode = hkcu.RegOpenKeyEx(subKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key); GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegOpenKeyEx() returned errorCode:" + errorCode + " key:" + key.DangerousGetHandle().ToString("x")); } else { errorCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND; } } else { errorCode = SafeRegistryHandle.RegOpenKeyEx(baseKey, subKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key); //GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegOpenKeyEx() returned errorCode:" + errorCode + " key:" + key.DangerousGetHandle().ToString("x")); } if (errorCode == 0) { changeEvent = new AutoResetEvent(false); } } if (errorCode == 0) { // accessing Handle is protected by a link demand, OK for System.dll errorCode = key.RegNotifyChangeKeyValue(true, UnsafeNclNativeMethods.RegistryHelper.REG_NOTIFY_CHANGE_LAST_SET, changeEvent.SafeWaitHandle, true); GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegNotifyChangeKeyValue() returned errorCode:" + errorCode); } if (errorCode != 0) { if (key != null && !key.IsInvalid) { try { errorCode = key.RegCloseKey(); } catch (Exception exception) { if (NclUtilities.IsFatal(exception)) throw; } GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegCloseKey() returned errorCode:" + errorCode); } key = null; if (changeEvent != null) { changeEvent.Close(); changeEvent = null; } } } private void RegistryChanged() { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::RegistryChanged()"); if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_system_setting_update)); // always refresh settings because they might have changed WebProxyData webProxyData; using (m_Identity.Impersonate()) { webProxyData = GetWebProxyData(); } webProxy.Update(webProxyData); } private void ConnectoidChanged() { GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ConnectoidChanged()"); if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_update_due_to_ip_config_change)); // Get the new connectoid/detector. Only do this after detecting a change, to avoid ----s with other people detecting changes. // (We don't want to end up using a detector/connectoid that doesn't match what we read from the registry.) m_AutoDetector = AutoDetector.CurrentAutoDetector; if (m_UseRegistry) { // update the engine and proxy WebProxyData webProxyData; using (m_Identity.Impersonate()) { webProxyData = GetWebProxyData(); } webProxy.Update(webProxyData); } // Always uninitialized if the connectoid/address changed and we are autodetecting. if (automaticallyDetectSettings) webProxyFinder.Reset(); } internal void CheckForChanges() { int syncStatus = SyncStatus.Unlocked; CheckForChanges(ref syncStatus); } [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)] private void CheckForChanges(ref int syncStatus) { // Catch ObjectDisposedException instead of synchronizing with Close(). try { bool changed = AutoDetector.CheckForNetworkChanges(ref m_NetworkChangeStatus); bool ignoreRegistryChange = false; if (changed || needConnectoidUpdate) { try { EnterLock(ref syncStatus); if (changed || needConnectoidUpdate) // Make sure no one else took care of it before we got the lock. { needConnectoidUpdate = syncStatus != SyncStatus.LockOwner; if (!needConnectoidUpdate) { ConnectoidChanged(); // We usually get a registry change at the same time. Since the connectoid change does more, // we can skip reading the registry info twice. ignoreRegistryChange = true; } } } finally { ExitLock(ref syncStatus); } } if (!m_UseRegistry) { return; } bool forReal = false; AutoResetEvent tempEvent = registryChangeEvent; if (registryChangeDeferred || (forReal = (tempEvent != null && tempEvent.WaitOne(0, false)))) { try { EnterLock(ref syncStatus); if (forReal || registryChangeDeferred) // Check if someone else handled it before I got the lock. { registryChangeDeferred = syncStatus != SyncStatus.LockOwner; if (!registryChangeDeferred && registryChangeEvent != null) { try { using (m_Identity.Impersonate()) { ListenForRegistryHelper(ref regKey, ref registryChangeEvent, IntPtr.Zero, RegBlobWebProxyDataBuilder.ProxyKey); } } catch { throw; } needRegistryUpdate = true; } } } finally { ExitLock(ref syncStatus); } } forReal = false; tempEvent = registryChangeEventLM; if (registryChangeLMDeferred || (forReal = (tempEvent != null && tempEvent.WaitOne(0, false)))) { try { EnterLock(ref syncStatus); if (forReal || registryChangeLMDeferred) // Check if someone else handled it before I got the lock. { registryChangeLMDeferred = syncStatus != SyncStatus.LockOwner; if (!registryChangeLMDeferred && registryChangeEventLM != null) { try { using (m_Identity.Impersonate()) { ListenForRegistryHelper(ref regKeyLM, ref registryChangeEventLM, UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.ProxyKey); } } catch { throw; } needRegistryUpdate = true; } } } finally { ExitLock(ref syncStatus); } } forReal = false; tempEvent = registryChangeEventPolicy; if (registryChangePolicyDeferred || (forReal = (tempEvent != null && tempEvent.WaitOne(0, false)))) { try { EnterLock(ref syncStatus); if (forReal || registryChangePolicyDeferred) // Check if someone else handled it before I got the lock. { registryChangePolicyDeferred = syncStatus != SyncStatus.LockOwner; if (!registryChangePolicyDeferred && registryChangeEventPolicy != null) { try { using (m_Identity.Impersonate()) { ListenForRegistryHelper(ref regKeyPolicy, ref registryChangeEventPolicy, UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.PolicyKey); } } catch { throw; } needRegistryUpdate = true; } } } finally { ExitLock(ref syncStatus); } } if (needRegistryUpdate) { try { EnterLock(ref syncStatus); if (needRegistryUpdate && syncStatus == SyncStatus.LockOwner) { needRegistryUpdate = false; // We don't need to process this now if we just did it for the connectoid. if (!ignoreRegistryChange) { RegistryChanged(); } } } finally { ExitLock(ref syncStatus); } } } catch (ObjectDisposedException) { } } private class AutoDetector { private static NetworkAddressChangePolled s_AddressChange; private static UnsafeNclNativeMethods.RasHelper s_RasHelper; private static int s_CurrentVersion = 0; private volatile static AutoDetector s_CurrentAutoDetector; private volatile static bool s_Initialized; private static object s_LockObject; static AutoDetector() { s_LockObject = new object(); } private static void Initialize() { if (!s_Initialized) { lock (s_LockObject) { if (!s_Initialized) { s_CurrentAutoDetector = new AutoDetector(UnsafeNclNativeMethods.RasHelper.GetCurrentConnectoid(), 1); if (NetworkChange.CanListenForNetworkChanges) { s_AddressChange = new NetworkAddressChangePolled(); } if (UnsafeNclNativeMethods.RasHelper.RasSupported) { s_RasHelper = new UnsafeNclNativeMethods.RasHelper(); } s_CurrentVersion = 1; s_Initialized = true; } } } } internal static bool CheckForNetworkChanges(ref int changeStatus) { Initialize(); CheckForChanges(); int oldStatus = changeStatus; changeStatus = s_CurrentVersion; return oldStatus != changeStatus; } private static void CheckForChanges() { bool changed = false; if (s_RasHelper != null && s_RasHelper.HasChanged) { s_RasHelper.Reset(); changed = true; } if (s_AddressChange != null && s_AddressChange.CheckAndReset()) { changed = true; } if (changed) { Interlocked.Increment(ref s_CurrentVersion); s_CurrentAutoDetector = new AutoDetector(UnsafeNclNativeMethods.RasHelper.GetCurrentConnectoid(), s_CurrentVersion); } } internal static AutoDetector CurrentAutoDetector { get { Initialize(); return s_CurrentAutoDetector; } } private readonly string m_Connectoid; private readonly int m_CurrentVersion; private AutoDetector(string connectoid, int currentVersion) { m_Connectoid = connectoid; m_CurrentVersion = currentVersion; } internal string Connectoid { get { return m_Connectoid; } } internal int NetworkChangeStatus { get { return m_CurrentVersion; } } } #endif } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PartialCachingAttribute.cs
- WriteableBitmap.cs
- DataTableExtensions.cs
- EnumMember.cs
- SpinWait.cs
- XmlQueryCardinality.cs
- OneOfConst.cs
- Freezable.cs
- SqlError.cs
- ResourceDisplayNameAttribute.cs
- InvalidBodyAccessException.cs
- DesignColumnCollection.cs
- FactoryRecord.cs
- MsmqInputSessionChannel.cs
- PagedDataSource.cs
- ProtocolsConfiguration.cs
- DATA_BLOB.cs
- FlowchartDesigner.Helpers.cs
- StorageRoot.cs
- TimersDescriptionAttribute.cs
- complextypematerializer.cs
- ColumnMap.cs
- ZipIOModeEnforcingStream.cs
- BinaryReader.cs
- ResourceManager.cs
- Marshal.cs
- GridViewCellAutomationPeer.cs
- DockPanel.cs
- DaylightTime.cs
- DBDataPermission.cs
- DeflateStreamAsyncResult.cs
- DefaultTextStore.cs
- XPathCompileException.cs
- PageWrapper.cs
- PeerNameRecord.cs
- GridViewColumnCollection.cs
- AutomationProperty.cs
- WriteFileContext.cs
- UdpAnnouncementEndpoint.cs
- WorkerRequest.cs
- VersionPair.cs
- SystemDiagnosticsSection.cs
- BufferModeSettings.cs
- VBIdentifierName.cs
- ListDictionaryInternal.cs
- CommandField.cs
- ControlPaint.cs
- WebPartCatalogAddVerb.cs
- FrameAutomationPeer.cs
- ZipIOCentralDirectoryDigitalSignature.cs
- FilterQuery.cs
- WebHttpBinding.cs
- WsdlWriter.cs
- SynchronizationHandlesCodeDomSerializer.cs
- EntryIndex.cs
- DropDownButton.cs
- CharKeyFrameCollection.cs
- IdentityModelDictionary.cs
- ListItemCollection.cs
- ScrollViewerAutomationPeer.cs
- Variant.cs
- CompModSwitches.cs
- MessageAction.cs
- XmlAttributeHolder.cs
- HostSecurityManager.cs
- CalendarDateRange.cs
- StorageSetMapping.cs
- CommonObjectSecurity.cs
- SQLBinary.cs
- MgmtConfigurationRecord.cs
- DesignerTransactionCloseEvent.cs
- KnowledgeBase.cs
- SyndicationContent.cs
- Claim.cs
- CqlWriter.cs
- HtmlFormWrapper.cs
- SelectionProviderWrapper.cs
- SystemSounds.cs
- PersonalizationProviderHelper.cs
- XmlSchemaComplexContentRestriction.cs
- RepeatInfo.cs
- SecurityVerifiedMessage.cs
- ServiceDocument.cs
- Shape.cs
- AttributeEmitter.cs
- ListMarkerLine.cs
- WindowsGraphics2.cs
- brushes.cs
- ObjectDataSourceStatusEventArgs.cs
- CheckBox.cs
- GregorianCalendarHelper.cs
- FileNameEditor.cs
- InitializationEventAttribute.cs
- InfocardInteractiveChannelInitializer.cs
- ItemsControl.cs
- XmlArrayItemAttribute.cs
- XmlUtilWriter.cs
- XmlValueConverter.cs
- XmlDataSource.cs
- QilExpression.cs