Code:
/ 4.0 / 4.0 / untmp / 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.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- sqlcontext.cs
- HuffCodec.cs
- Viewport3DAutomationPeer.cs
- BaseValidator.cs
- WebPartConnectionsCloseVerb.cs
- BrowserInteropHelper.cs
- BinaryUtilClasses.cs
- TrackingExtract.cs
- MultiSelectRootGridEntry.cs
- TypeUsage.cs
- RTLAwareMessageBox.cs
- isolationinterop.cs
- EdmFunctions.cs
- QueryStoreStatusRequest.cs
- ContextMarshalException.cs
- InputLanguageCollection.cs
- SpAudioStreamWrapper.cs
- SelectionService.cs
- TextTreeTextBlock.cs
- RegexGroup.cs
- ValuePatternIdentifiers.cs
- ExpandoObject.cs
- CopyCodeAction.cs
- XmlReaderSettings.cs
- AccessorTable.cs
- CapabilitiesPattern.cs
- CodeArrayCreateExpression.cs
- ScrollEventArgs.cs
- DeferredSelectedIndexReference.cs
- StylusPlugin.cs
- TransactionFlowOption.cs
- MemberMemberBinding.cs
- TreeBuilderBamlTranslator.cs
- HttpListenerPrefixCollection.cs
- MetadataArtifactLoaderCompositeResource.cs
- EnumConverter.cs
- HealthMonitoringSection.cs
- SchemaMapping.cs
- OleDbFactory.cs
- DescriptionAttribute.cs
- dataprotectionpermission.cs
- ConfigurationSettings.cs
- BaseAppDomainProtocolHandler.cs
- UInt64.cs
- HwndSourceKeyboardInputSite.cs
- VirtualDirectoryMappingCollection.cs
- MultiAsyncResult.cs
- PeerUnsafeNativeCryptMethods.cs
- XmlCodeExporter.cs
- ListManagerBindingsCollection.cs
- TableRow.cs
- UrlMappingsSection.cs
- ImportRequest.cs
- SafePointer.cs
- SqlCacheDependencyDatabase.cs
- MediaElementAutomationPeer.cs
- UserControlCodeDomTreeGenerator.cs
- HtmlMeta.cs
- ClientSettings.cs
- Unit.cs
- SessionParameter.cs
- Brush.cs
- NumberFormatInfo.cs
- BezierSegment.cs
- Point4DValueSerializer.cs
- ValidationManager.cs
- ZoomPercentageConverter.cs
- XmlSchemaComplexType.cs
- ReachUIElementCollectionSerializerAsync.cs
- XsltOutput.cs
- WebPartManager.cs
- UndoEngine.cs
- GatewayDefinition.cs
- ListParagraph.cs
- XmlTextEncoder.cs
- SqlMultiplexer.cs
- RemoteHelper.cs
- HashStream.cs
- StorageSetMapping.cs
- EditorZone.cs
- base64Transforms.cs
- RecognizeCompletedEventArgs.cs
- DataGridHyperlinkColumn.cs
- SiteMapDataSource.cs
- StringPropertyBuilder.cs
- Duration.cs
- CollectionChangeEventArgs.cs
- WindowsRegion.cs
- _NegoStream.cs
- SystemWebExtensionsSectionGroup.cs
- unitconverter.cs
- SafeProcessHandle.cs
- MessageProtectionOrder.cs
- TraceContextEventArgs.cs
- DesignerActionItemCollection.cs
- ExtensionDataObject.cs
- VolatileResourceManager.cs
- HttpProcessUtility.cs
- EditorResources.cs
- MimeParameterWriter.cs