_ProxyRegBlob.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Net / System / Net / _ProxyRegBlob.cs / 2 / _ProxyRegBlob.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Net { 
    using System; 
    using System.Security.Permissions;
    using System.Globalization; 
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Collections;
    using System.Collections.Specialized; 
    using System.Net.Sockets;
    using System.Threading; 
    using System.Runtime.InteropServices; 
#if USE_WINIET_AUTODETECT_CACHE
#if !FEATURE_PAL 
    using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
#endif // !FEATURE_PAL
#endif
    using Microsoft.Win32; 
    using System.Runtime.Versioning;
    using System.Diagnostics; 
 
    internal partial class RegBlobWebProxyDataBuilder : WebProxyDataBuilder
    { 

#if !FEATURE_PAL
        //
        // Allows us to grob through the registry and read the 
        //  IE binary format, note that this should be replaced,
        //  by code that calls Wininet directly, but it can be 
        //  expensive to load wininet, in order to do this. 
        //
 
        [Flags]
        private enum ProxyTypeFlags
        {
            PROXY_TYPE_DIRECT          = 0x00000001,   // direct to net 
            PROXY_TYPE_PROXY           = 0x00000002,   // via named proxy
            PROXY_TYPE_AUTO_PROXY_URL  = 0x00000004,   // autoproxy URL 
            PROXY_TYPE_AUTO_DETECT     = 0x00000008,   // use autoproxy detection 
        }
 
        internal const string PolicyKey = @"SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings";
        internal const string ProxyKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections";
        private const string DefaultConnectionSettings = "DefaultConnectionSettings";
        private const string ProxySettingsPerUser = "ProxySettingsPerUser"; 

#if USE_WINIET_AUTODETECT_CACHE 
        // Get the number of MilliSeconds in 7 days and then multiply by 10 because 
        // FILETIME stores data stores time in 100-nanosecond intervals.
        // 
        internal static UInt64 s_lkgScriptValidTime = (UInt64)(new TimeSpan(7, 0, 0, 0).Ticks); // 7 days
#endif

        const int IE50StrucSize = 60; 

        private byte[] m_RegistryBytes; 
        private int m_ByteOffset; 

        private string m_Connectoid; 
        private SafeRegistryHandle m_Registry;

        public RegBlobWebProxyDataBuilder(string connectoid, SafeRegistryHandle registry)
        { 
            Debug.Assert(registry != null);
 
            m_Registry = registry; 
            m_Connectoid = connectoid;
        } 

        // returns true - on successful read of proxy registry settings
        [RegistryPermission(SecurityAction.Assert, Read=@"HKEY_LOCAL_MACHINE\" + PolicyKey)]
        [ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)]
        private bool ReadRegSettings() 
        { 
            SafeRegistryHandle key = null;
            RegistryKey lmKey = null; 
            try {
                bool isPerUser = true;
                lmKey = Registry.LocalMachine.OpenSubKey(PolicyKey);
                if (lmKey != null) 
                {
                    object perUser = lmKey.GetValue(ProxySettingsPerUser); 
                    if (perUser != null && perUser.GetType() == typeof(int) && 0 == (int) perUser) 
                    {
                        isPerUser = false; 
                    }
                }

                uint errorCode; 
                if (isPerUser)
                { 
                    if (m_Registry != null) 
                    {
                        errorCode = m_Registry.RegOpenKeyEx(ProxyKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key); 
                    }
                    else
                    {
                        errorCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND; 
                    }
                } 
                else 
                {
                    errorCode = SafeRegistryHandle.RegOpenKeyEx(UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, ProxyKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key); 
                }
                if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                {
                    key = null; 
                }
                if (key != null) 
                { 
                    // When reading settings from the registry, if connectoid key is missing, the connectoid
                    // was never configured. In this case we have no settings (this is equivalent to always go direct). 
                    object data;
                    errorCode = key.QueryValue(m_Connectoid != null ? m_Connectoid : DefaultConnectionSettings, out data);
                    if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                    { 
                        m_RegistryBytes = (byte[]) data;
                    } 
                } 
            }
            catch (Exception exception) { 
                if (NclUtilities.IsFatal(exception)) throw;
            }
            finally
            { 
                if (lmKey != null)
                    lmKey.Close(); 
 
                if(key != null)
                    key.RegCloseKey(); 
            }
            return m_RegistryBytes != null;
        }
 
#if USE_WINIET_AUTODETECT_CACHE
        public FILETIME ReadFileTime() { 
            FILETIME ft = new FILETIME(); 
            ft.dwLowDateTime = ReadInt32();
            ft.dwHighDateTime = ReadInt32(); 
            return ft;
        }
#endif
 
        //
        // Reads a string from the byte buffer, cached 
        //  inside this object, and then updates the 
        //  offset, NOTE: Must be in the correct offset
        //  before reading, or will error 
        //
        public string ReadString() {
            string stringOut = null;
            int stringSize = ReadInt32(); 
            if (stringSize>0) {
                // prevent reading too much 
                int actualSize = m_RegistryBytes.Length - m_ByteOffset; 
                if (stringSize >= actualSize) {
                    stringSize = actualSize; 
                }
                stringOut = Encoding.UTF8.GetString(m_RegistryBytes, m_ByteOffset, stringSize);
                m_ByteOffset += stringSize;
            } 
            return stringOut;
        } 
 

        // 
        // Reads a DWORD into a Int32, used to read
        //  a int from the byte buffer.
        //
        internal unsafe int ReadInt32() { 
            int intValue = 0;
            int actualSize = m_RegistryBytes.Length - m_ByteOffset; 
            // copy bytes and increment offset 
            if (actualSize>=sizeof(int)) {
                fixed (byte* pBuffer = m_RegistryBytes) { 
                    if (sizeof(IntPtr)==4) {
                        intValue = *((int*)(pBuffer + m_ByteOffset));
                    }
                    else { 
                        intValue = Marshal.ReadInt32((IntPtr)pBuffer, m_ByteOffset);
                    } 
                } 
                m_ByteOffset += sizeof(int);
            } 
            // tell caller what we actually read
            return intValue;
        }
#else // !FEATURE_PAL 
        private static string ReadConfigString(string ConfigName) {
            const int parameterValueLength = 255; 
            StringBuilder parameterValue = new StringBuilder(parameterValueLength); 
            bool rc = UnsafeNclNativeMethods.FetchConfigurationString(true, ConfigName, parameterValue, parameterValueLength);
            if (rc) { 
                return parameterValue.ToString();
            }
            return "";
        } 
#endif // !FEATURE_PAL
    } 
 
    internal abstract class WebProxyDataBuilder
    { 
        private static readonly char[] s_AddressListSplitChars = new char[] { ';', '=' };
        private static readonly char[] s_BypassListDelimiter = new char[] { ';' };

        private WebProxyData m_Result; 

        public WebProxyData Build() 
        { 
            m_Result = new WebProxyData();
            BuildInternal(); 
            return m_Result;
        }

        protected abstract void BuildInternal(); 

        protected void SetProxyAndBypassList(string addressString, string bypassListString) 
        { 
            Uri actualAddress = null;
            Hashtable proxyHashTable = null; 
            if (addressString != null)
            {
                actualAddress = ParseProxyUri(addressString, true);
                if (actualAddress == null) 
                {
                    proxyHashTable = ParseProtocolProxies(addressString); 
                } 
                if ((actualAddress != null || proxyHashTable != null) && bypassListString != null)
                { 
                    bool bypassOnLocal = false;
                    m_Result.bypassList = ParseBypassList(bypassListString, out bypassOnLocal);
                    m_Result.bypassOnLocal = bypassOnLocal;
                } 
            }
 
            if (proxyHashTable != null) 
            {
                Debug.Assert(actualAddress == null); 
                actualAddress = proxyHashTable["http"] as Uri;
            }

            m_Result.proxyAddress = actualAddress; 
        }
 
        protected void SetAutoProxyUrl(string autoConfigUrl) 
        {
            if (!string.IsNullOrEmpty(autoConfigUrl)) 
            {
                Uri scriptLocation = null;
                if (Uri.TryCreate(autoConfigUrl, UriKind.Absolute, out scriptLocation))
                { 
                    m_Result.scriptLocation = scriptLocation;
                } 
            } 
        }
 
        protected void SetAutoDetectSettings(bool value)
        {
            m_Result.automaticallyDetectSettings = value;
        } 

        // 
        // Parses out a string from IE and turns it into a URI 
        //
        private static Uri ParseProxyUri(string proxyString, bool validate) { 
            Debug.Assert(proxyString != null);

            if (validate) {
                if (proxyString.Length == 0) { 
                    return null;
                } 
                if (proxyString.IndexOf('=') != -1) { 
                    return null;
                } 
            }
            if (proxyString.IndexOf("://") == -1) {
                proxyString = "http://" + proxyString;
            } 

            try 
            { 
                return new Uri(proxyString);
            } 
            catch (UriFormatException e)
            {
                if (Logging.On) Logging.PrintError(Logging.Web, e.Message);
            } 

            return null; // we weren't able to get an Uri instance out of proxyString 
        } 

        // 
        // Builds a hashtable containing the protocol and proxy URI to use for it.
        //
        private static Hashtable ParseProtocolProxies(string proxyListString) {
            Debug.Assert(proxyListString != null); 

            if (proxyListString.Length == 0) { 
                return null; 
            }
            // parse something like "http=http://http-proxy;https=http://https-proxy;ftp=http://ftp-proxy" 
            string[] proxyListStrings = proxyListString.Split(s_AddressListSplitChars);

            bool protocolPass = true;
            string protocolString = null; 
            Hashtable proxyListHashTable = new Hashtable(CaseInsensitiveAscii.StaticInstance);
            foreach (string elementString in proxyListStrings) { 
                string lowerElementString = elementString.Trim().ToLower(CultureInfo.InvariantCulture); 
                if (protocolPass) {
                    protocolString = lowerElementString; 
                }
                else {
                    proxyListHashTable[protocolString] = ParseProxyUri(lowerElementString, false);
                } 
                protocolPass = !protocolPass;
            } 
            if (proxyListHashTable.Count == 0) { 
                return null;
            } 
            return proxyListHashTable;
        }

        // 
        // Converts a simple IE regular expresion string into one
        //  that is compatible with Regex escape sequences. 
        // 
        private static string BypassStringEscape(string rawString) {
 
            Debug.Assert(rawString != null);

            // Break up raw string into scheme, host, port.
            // This regular expression is used to get the three components. 
            // Scheme and port are optional.
            // If Match fails just assume whole string is the host. 
            Regex parser = new 
                Regex("^(?.*://)?(?[^:]*)(?:[0-9]{1,5})?$",
                RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); 
            Match results = parser.Match(rawString);
            string scheme, host, port;
            if (results.Success) {
                scheme = results.Groups["scheme"].Value; 
                host = results.Groups["host"].Value;
                port = results.Groups["port"].Value; 
            } else { 
                // Match method failed - set host to whole bypass string
                scheme = string.Empty; 
                host = rawString;
                port = string.Empty;
            }
 
            // Escape any regex reserved chars before constructing final regex.
            scheme = ConvertRegexReservedChars(scheme); 
            host = ConvertRegexReservedChars(host); 
            port = ConvertRegexReservedChars(port);
 

            // If scheme or port not specified use regular
            // expression "wildcards" for them.
            if (scheme == string.Empty) { 
                // match any leading scheme plus separator
                // but don't require it 
                scheme = "(?:.*://)?"; 
            }
            if (port == string.Empty) { 
                // match a port but don't require it
                port = "(?::[0-9]{1,5})?";
            }
 
            // Construct and return final regular expression
            // with start-of-line and end-of-line anchors. 
            return "^" + scheme + host + port + "$"; 
        }
 

        private const string regexReserved = "#$()+.?[\\^{|";

        private static string ConvertRegexReservedChars(string rawString) { 

            Debug.Assert(rawString != null); 
 
            // Regular expressions reserve
            //   (1) "#$()+.?[\^{|" as special chars. 
            //   (2) "*" as a special char.
            //   (3) whitespace as a special char.
            // Convert any char "c" in above list to "\c".
            // Convert reserved char "*" to ".*". 
            // Leave whitespace as-is.
            if (rawString.Length == 0) 
                return rawString; 
            StringBuilder builder = new StringBuilder();
            foreach (char c in rawString) { 
                if (regexReserved.IndexOf(c) != -1) {
                    builder.Append('\\');
                } else if (c == '*') {
                    builder.Append('.'); 
                }
                builder.Append(c); 
            } 
            return builder.ToString();
        } 

        //
        // Parses out a string of bypass list entries and coverts it to Regex's that can be used
        //   to match against. 
        //
        private static ArrayList ParseBypassList(string bypassListString, out bool bypassOnLocal) { 
            string[] bypassListStrings = bypassListString.Split(s_BypassListDelimiter); 
            bypassOnLocal = false;
            if (bypassListStrings.Length == 0) { 
                return null;
            }
            ArrayList bypassList = null;
            foreach (string bypassString in bypassListStrings) { 
                if (bypassString!=null) {
                    string trimmedBypassString = bypassString.Trim(); 
                    if (trimmedBypassString.Length>0) { 
                        if (string.Compare(trimmedBypassString, "", StringComparison.OrdinalIgnoreCase)==0) {
                            bypassOnLocal = true; 
                        }
                        else {
                            trimmedBypassString = BypassStringEscape(trimmedBypassString);
                            if (bypassList==null) { 
                                bypassList = new ArrayList();
                            } 
                            GlobalLog.Print("WebProxyDataBuilder::ParseBypassList() bypassList.Count:" + bypassList.Count + " adding:" + ValidationHelper.ToString(trimmedBypassString)); 
                            if (!bypassList.Contains(trimmedBypassString)) {
                                bypassList.Add(trimmedBypassString); 
                                GlobalLog.Print("WebProxyDataBuilder::ParseBypassList() bypassList.Count:" + bypassList.Count + " added:" + ValidationHelper.ToString(trimmedBypassString));
                            }
                        }
                    } 
                }
            } 
            return bypassList; 
        }
 
    }

    internal partial class RegBlobWebProxyDataBuilder
    { 
        //
        // Updates an instance of WbeProxy with the proxy settings from IE for: 
        // the current user and a given connectoid. 
        //
        [ResourceExposure(ResourceScope.Machine)]  // Check scoping on this SafeRegistryHandle 
        [ResourceConsumption(ResourceScope.Machine)]
        protected override void BuildInternal()
        {
            GlobalLog.Enter("RegBlobWebProxyDataBuilder#" + ValidationHelper.HashString(this) + "::BuildInternal() m_Connectoid:" + ValidationHelper.ToString(m_Connectoid)); 

            // DON'T TOUCH THE ORDERING OF THE CALLS TO THE INSTANCE OF ProxyRegBlob 
            bool success = ReadRegSettings(); 
            if (success) {
                success = ReadInt32() >= IE50StrucSize; 
            }
            if (!success) {
                // if registry access fails rely on automatic detection
                SetAutoDetectSettings(true); 
                return;
            } 
            // read the rest of the items out 
            ReadInt32(); // incremental version# of current settings (ignored)
            ProxyTypeFlags proxyFlags = (ProxyTypeFlags)ReadInt32(); // flags 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() proxyFlags:" + ValidationHelper.ToString(proxyFlags));

            string addressString = ReadString(); // proxy name
            string proxyBypassString = ReadString(); // proxy bypass 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() proxyAddressString:" + ValidationHelper.ToString(addressString) + " proxyBypassString:" + ValidationHelper.ToString(proxyBypassString));
 
            // 
            // Once we verify that the flag for proxy is enabled,
            // Parse UriString that is stored, may be in the form, 
            //  of "http=http://http-proxy;ftp="ftp=http://..." must
            //  handle this case along with just a URI.
            //
            if ((proxyFlags & ProxyTypeFlags.PROXY_TYPE_PROXY) != 0) { 

                SetProxyAndBypassList(addressString, proxyBypassString); 
            } 

 
#if !FEATURE_PAL
            SetAutoDetectSettings((proxyFlags & ProxyTypeFlags.PROXY_TYPE_AUTO_DETECT) != 0);

            string autoConfigUrlString = ReadString(); // autoconfig url 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() scriptLocation:" + ValidationHelper.ToString(addressString));
            if ((proxyFlags & ProxyTypeFlags.PROXY_TYPE_AUTO_PROXY_URL) != 0) { 
                SetAutoProxyUrl(autoConfigUrlString); 
            }
 
            // The final straw against attempting to use the WinInet LKG script location was, it's invalid when IPs have changed even if the
            // connectoid hadn't.  Doing that validation didn't seem worth it (error-prone, expensive, unsupported).
#if USE_WINIET_AUTODETECT_CACHE
            proxyIE5Settings.ReadInt32(); // autodetect flags (ignored) 

            // reuse addressString for lkgScriptLocationString 
            addressString = proxyIE5Settings.ReadString(); // last known good auto-proxy url 

            // read ftLastKnownDetectTime 
            FILETIME ftLastKnownDetectTime = proxyIE5Settings.ReadFileTime();

            // Verify if this lkgScriptLocationString has timed out
            // 
            if (IsValidTimeForLkgScriptLocation(ftLastKnownDetectTime)) {
                // reuse address for lkgScriptLocation 
                GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() lkgScriptLocation:" + ValidationHelper.ToString(addressString)); 
                if (Uri.TryCreate(addressString, UriKind.Absolute, out address)) {
                    webProxyData.lkgScriptLocation = address; 
                }
            }
            else {
#if TRAVE 
                SYSTEMTIME st = new SYSTEMTIME();
                bool f = SafeNclNativeMethods.FileTimeToSystemTime(ref ftLastKnownDetectTime, ref st); 
                if (f) 
                    GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() ftLastKnownDetectTime:" + ValidationHelper.ToString(st));
#endif // TRAVE 
                GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() Ignoring Timed out lkgScriptLocation:" + ValidationHelper.ToString(addressString));

                // Now rely on automatic detection settings set above
                // based on the proxy flags (webProxyData.automaticallyDetectSettings). 
                //
            } 
#endif 
            /*
            // This is some of the rest of the proxy reg key blob parsing. 
            //
            // Read Inte---- IPs
            int iftCount = proxyIE5Settings.ReadInt32();
            for (int ift = 0; ift < iftCount; ++ift) { 
                proxyIE5Settings.ReadInt32();
            } 
 
            // Read lpszAutoconfigSecondaryUrl
            string autoconfigSecondaryUrl = proxyIE5Settings.ReadString(); 

            // Read dwAutoconfigReloadDelayMins
            int autoconfigReloadDelayMins = proxyIE5Settings.ReadInt32();
            */ 
#endif
            GlobalLog.Leave("RegBlobWebProxyDataBuilder#" + ValidationHelper.HashString(this) + "::BuildInternal()"); 
        } 

#if USE_WINIET_AUTODETECT_CACHE 
#if !FEATURE_PAL
        internal unsafe static bool IsValidTimeForLkgScriptLocation(FILETIME ftLastKnownDetectTime) {
            // Get Current System Time.
            FILETIME ftCurrentTime = new FILETIME(); 
            SafeNclNativeMethods.GetSystemTimeAsFileTime(ref ftCurrentTime);
 
            UInt64 ftDetect = (UInt64)ftLastKnownDetectTime.dwHighDateTime; 
            ftDetect <<= (sizeof(int) * 8);
            ftDetect |= (UInt64)(uint)ftLastKnownDetectTime.dwLowDateTime; 

            UInt64 ftCurrent = (UInt64)ftCurrentTime.dwHighDateTime;
            ftCurrent <<= (sizeof(int) * 8);
            ftCurrent |= (UInt64)(uint)ftCurrentTime.dwLowDateTime; 

            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() Detect Time:" + ValidationHelper.ToString(ftDetect)); 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() Current Time:" + ValidationHelper.ToString(ftCurrent)); 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() 7 days:" + ValidationHelper.ToString(s_lkgScriptValidTime));
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() Delta Time:" + ValidationHelper.ToString((UInt64)(ftCurrent - ftDetect))); 

            return (ftCurrent - ftDetect) < s_lkgScriptValidTime;
        }
#endif // !FEATURE_PAL 
#endif
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Net { 
    using System; 
    using System.Security.Permissions;
    using System.Globalization; 
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Collections;
    using System.Collections.Specialized; 
    using System.Net.Sockets;
    using System.Threading; 
    using System.Runtime.InteropServices; 
#if USE_WINIET_AUTODETECT_CACHE
#if !FEATURE_PAL 
    using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
#endif // !FEATURE_PAL
#endif
    using Microsoft.Win32; 
    using System.Runtime.Versioning;
    using System.Diagnostics; 
 
    internal partial class RegBlobWebProxyDataBuilder : WebProxyDataBuilder
    { 

#if !FEATURE_PAL
        //
        // Allows us to grob through the registry and read the 
        //  IE binary format, note that this should be replaced,
        //  by code that calls Wininet directly, but it can be 
        //  expensive to load wininet, in order to do this. 
        //
 
        [Flags]
        private enum ProxyTypeFlags
        {
            PROXY_TYPE_DIRECT          = 0x00000001,   // direct to net 
            PROXY_TYPE_PROXY           = 0x00000002,   // via named proxy
            PROXY_TYPE_AUTO_PROXY_URL  = 0x00000004,   // autoproxy URL 
            PROXY_TYPE_AUTO_DETECT     = 0x00000008,   // use autoproxy detection 
        }
 
        internal const string PolicyKey = @"SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings";
        internal const string ProxyKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections";
        private const string DefaultConnectionSettings = "DefaultConnectionSettings";
        private const string ProxySettingsPerUser = "ProxySettingsPerUser"; 

#if USE_WINIET_AUTODETECT_CACHE 
        // Get the number of MilliSeconds in 7 days and then multiply by 10 because 
        // FILETIME stores data stores time in 100-nanosecond intervals.
        // 
        internal static UInt64 s_lkgScriptValidTime = (UInt64)(new TimeSpan(7, 0, 0, 0).Ticks); // 7 days
#endif

        const int IE50StrucSize = 60; 

        private byte[] m_RegistryBytes; 
        private int m_ByteOffset; 

        private string m_Connectoid; 
        private SafeRegistryHandle m_Registry;

        public RegBlobWebProxyDataBuilder(string connectoid, SafeRegistryHandle registry)
        { 
            Debug.Assert(registry != null);
 
            m_Registry = registry; 
            m_Connectoid = connectoid;
        } 

        // returns true - on successful read of proxy registry settings
        [RegistryPermission(SecurityAction.Assert, Read=@"HKEY_LOCAL_MACHINE\" + PolicyKey)]
        [ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)]
        private bool ReadRegSettings() 
        { 
            SafeRegistryHandle key = null;
            RegistryKey lmKey = null; 
            try {
                bool isPerUser = true;
                lmKey = Registry.LocalMachine.OpenSubKey(PolicyKey);
                if (lmKey != null) 
                {
                    object perUser = lmKey.GetValue(ProxySettingsPerUser); 
                    if (perUser != null && perUser.GetType() == typeof(int) && 0 == (int) perUser) 
                    {
                        isPerUser = false; 
                    }
                }

                uint errorCode; 
                if (isPerUser)
                { 
                    if (m_Registry != null) 
                    {
                        errorCode = m_Registry.RegOpenKeyEx(ProxyKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key); 
                    }
                    else
                    {
                        errorCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND; 
                    }
                } 
                else 
                {
                    errorCode = SafeRegistryHandle.RegOpenKeyEx(UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, ProxyKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key); 
                }
                if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                {
                    key = null; 
                }
                if (key != null) 
                { 
                    // When reading settings from the registry, if connectoid key is missing, the connectoid
                    // was never configured. In this case we have no settings (this is equivalent to always go direct). 
                    object data;
                    errorCode = key.QueryValue(m_Connectoid != null ? m_Connectoid : DefaultConnectionSettings, out data);
                    if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                    { 
                        m_RegistryBytes = (byte[]) data;
                    } 
                } 
            }
            catch (Exception exception) { 
                if (NclUtilities.IsFatal(exception)) throw;
            }
            finally
            { 
                if (lmKey != null)
                    lmKey.Close(); 
 
                if(key != null)
                    key.RegCloseKey(); 
            }
            return m_RegistryBytes != null;
        }
 
#if USE_WINIET_AUTODETECT_CACHE
        public FILETIME ReadFileTime() { 
            FILETIME ft = new FILETIME(); 
            ft.dwLowDateTime = ReadInt32();
            ft.dwHighDateTime = ReadInt32(); 
            return ft;
        }
#endif
 
        //
        // Reads a string from the byte buffer, cached 
        //  inside this object, and then updates the 
        //  offset, NOTE: Must be in the correct offset
        //  before reading, or will error 
        //
        public string ReadString() {
            string stringOut = null;
            int stringSize = ReadInt32(); 
            if (stringSize>0) {
                // prevent reading too much 
                int actualSize = m_RegistryBytes.Length - m_ByteOffset; 
                if (stringSize >= actualSize) {
                    stringSize = actualSize; 
                }
                stringOut = Encoding.UTF8.GetString(m_RegistryBytes, m_ByteOffset, stringSize);
                m_ByteOffset += stringSize;
            } 
            return stringOut;
        } 
 

        // 
        // Reads a DWORD into a Int32, used to read
        //  a int from the byte buffer.
        //
        internal unsafe int ReadInt32() { 
            int intValue = 0;
            int actualSize = m_RegistryBytes.Length - m_ByteOffset; 
            // copy bytes and increment offset 
            if (actualSize>=sizeof(int)) {
                fixed (byte* pBuffer = m_RegistryBytes) { 
                    if (sizeof(IntPtr)==4) {
                        intValue = *((int*)(pBuffer + m_ByteOffset));
                    }
                    else { 
                        intValue = Marshal.ReadInt32((IntPtr)pBuffer, m_ByteOffset);
                    } 
                } 
                m_ByteOffset += sizeof(int);
            } 
            // tell caller what we actually read
            return intValue;
        }
#else // !FEATURE_PAL 
        private static string ReadConfigString(string ConfigName) {
            const int parameterValueLength = 255; 
            StringBuilder parameterValue = new StringBuilder(parameterValueLength); 
            bool rc = UnsafeNclNativeMethods.FetchConfigurationString(true, ConfigName, parameterValue, parameterValueLength);
            if (rc) { 
                return parameterValue.ToString();
            }
            return "";
        } 
#endif // !FEATURE_PAL
    } 
 
    internal abstract class WebProxyDataBuilder
    { 
        private static readonly char[] s_AddressListSplitChars = new char[] { ';', '=' };
        private static readonly char[] s_BypassListDelimiter = new char[] { ';' };

        private WebProxyData m_Result; 

        public WebProxyData Build() 
        { 
            m_Result = new WebProxyData();
            BuildInternal(); 
            return m_Result;
        }

        protected abstract void BuildInternal(); 

        protected void SetProxyAndBypassList(string addressString, string bypassListString) 
        { 
            Uri actualAddress = null;
            Hashtable proxyHashTable = null; 
            if (addressString != null)
            {
                actualAddress = ParseProxyUri(addressString, true);
                if (actualAddress == null) 
                {
                    proxyHashTable = ParseProtocolProxies(addressString); 
                } 
                if ((actualAddress != null || proxyHashTable != null) && bypassListString != null)
                { 
                    bool bypassOnLocal = false;
                    m_Result.bypassList = ParseBypassList(bypassListString, out bypassOnLocal);
                    m_Result.bypassOnLocal = bypassOnLocal;
                } 
            }
 
            if (proxyHashTable != null) 
            {
                Debug.Assert(actualAddress == null); 
                actualAddress = proxyHashTable["http"] as Uri;
            }

            m_Result.proxyAddress = actualAddress; 
        }
 
        protected void SetAutoProxyUrl(string autoConfigUrl) 
        {
            if (!string.IsNullOrEmpty(autoConfigUrl)) 
            {
                Uri scriptLocation = null;
                if (Uri.TryCreate(autoConfigUrl, UriKind.Absolute, out scriptLocation))
                { 
                    m_Result.scriptLocation = scriptLocation;
                } 
            } 
        }
 
        protected void SetAutoDetectSettings(bool value)
        {
            m_Result.automaticallyDetectSettings = value;
        } 

        // 
        // Parses out a string from IE and turns it into a URI 
        //
        private static Uri ParseProxyUri(string proxyString, bool validate) { 
            Debug.Assert(proxyString != null);

            if (validate) {
                if (proxyString.Length == 0) { 
                    return null;
                } 
                if (proxyString.IndexOf('=') != -1) { 
                    return null;
                } 
            }
            if (proxyString.IndexOf("://") == -1) {
                proxyString = "http://" + proxyString;
            } 

            try 
            { 
                return new Uri(proxyString);
            } 
            catch (UriFormatException e)
            {
                if (Logging.On) Logging.PrintError(Logging.Web, e.Message);
            } 

            return null; // we weren't able to get an Uri instance out of proxyString 
        } 

        // 
        // Builds a hashtable containing the protocol and proxy URI to use for it.
        //
        private static Hashtable ParseProtocolProxies(string proxyListString) {
            Debug.Assert(proxyListString != null); 

            if (proxyListString.Length == 0) { 
                return null; 
            }
            // parse something like "http=http://http-proxy;https=http://https-proxy;ftp=http://ftp-proxy" 
            string[] proxyListStrings = proxyListString.Split(s_AddressListSplitChars);

            bool protocolPass = true;
            string protocolString = null; 
            Hashtable proxyListHashTable = new Hashtable(CaseInsensitiveAscii.StaticInstance);
            foreach (string elementString in proxyListStrings) { 
                string lowerElementString = elementString.Trim().ToLower(CultureInfo.InvariantCulture); 
                if (protocolPass) {
                    protocolString = lowerElementString; 
                }
                else {
                    proxyListHashTable[protocolString] = ParseProxyUri(lowerElementString, false);
                } 
                protocolPass = !protocolPass;
            } 
            if (proxyListHashTable.Count == 0) { 
                return null;
            } 
            return proxyListHashTable;
        }

        // 
        // Converts a simple IE regular expresion string into one
        //  that is compatible with Regex escape sequences. 
        // 
        private static string BypassStringEscape(string rawString) {
 
            Debug.Assert(rawString != null);

            // Break up raw string into scheme, host, port.
            // This regular expression is used to get the three components. 
            // Scheme and port are optional.
            // If Match fails just assume whole string is the host. 
            Regex parser = new 
                Regex("^(?.*://)?(?[^:]*)(?:[0-9]{1,5})?$",
                RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); 
            Match results = parser.Match(rawString);
            string scheme, host, port;
            if (results.Success) {
                scheme = results.Groups["scheme"].Value; 
                host = results.Groups["host"].Value;
                port = results.Groups["port"].Value; 
            } else { 
                // Match method failed - set host to whole bypass string
                scheme = string.Empty; 
                host = rawString;
                port = string.Empty;
            }
 
            // Escape any regex reserved chars before constructing final regex.
            scheme = ConvertRegexReservedChars(scheme); 
            host = ConvertRegexReservedChars(host); 
            port = ConvertRegexReservedChars(port);
 

            // If scheme or port not specified use regular
            // expression "wildcards" for them.
            if (scheme == string.Empty) { 
                // match any leading scheme plus separator
                // but don't require it 
                scheme = "(?:.*://)?"; 
            }
            if (port == string.Empty) { 
                // match a port but don't require it
                port = "(?::[0-9]{1,5})?";
            }
 
            // Construct and return final regular expression
            // with start-of-line and end-of-line anchors. 
            return "^" + scheme + host + port + "$"; 
        }
 

        private const string regexReserved = "#$()+.?[\\^{|";

        private static string ConvertRegexReservedChars(string rawString) { 

            Debug.Assert(rawString != null); 
 
            // Regular expressions reserve
            //   (1) "#$()+.?[\^{|" as special chars. 
            //   (2) "*" as a special char.
            //   (3) whitespace as a special char.
            // Convert any char "c" in above list to "\c".
            // Convert reserved char "*" to ".*". 
            // Leave whitespace as-is.
            if (rawString.Length == 0) 
                return rawString; 
            StringBuilder builder = new StringBuilder();
            foreach (char c in rawString) { 
                if (regexReserved.IndexOf(c) != -1) {
                    builder.Append('\\');
                } else if (c == '*') {
                    builder.Append('.'); 
                }
                builder.Append(c); 
            } 
            return builder.ToString();
        } 

        //
        // Parses out a string of bypass list entries and coverts it to Regex's that can be used
        //   to match against. 
        //
        private static ArrayList ParseBypassList(string bypassListString, out bool bypassOnLocal) { 
            string[] bypassListStrings = bypassListString.Split(s_BypassListDelimiter); 
            bypassOnLocal = false;
            if (bypassListStrings.Length == 0) { 
                return null;
            }
            ArrayList bypassList = null;
            foreach (string bypassString in bypassListStrings) { 
                if (bypassString!=null) {
                    string trimmedBypassString = bypassString.Trim(); 
                    if (trimmedBypassString.Length>0) { 
                        if (string.Compare(trimmedBypassString, "", StringComparison.OrdinalIgnoreCase)==0) {
                            bypassOnLocal = true; 
                        }
                        else {
                            trimmedBypassString = BypassStringEscape(trimmedBypassString);
                            if (bypassList==null) { 
                                bypassList = new ArrayList();
                            } 
                            GlobalLog.Print("WebProxyDataBuilder::ParseBypassList() bypassList.Count:" + bypassList.Count + " adding:" + ValidationHelper.ToString(trimmedBypassString)); 
                            if (!bypassList.Contains(trimmedBypassString)) {
                                bypassList.Add(trimmedBypassString); 
                                GlobalLog.Print("WebProxyDataBuilder::ParseBypassList() bypassList.Count:" + bypassList.Count + " added:" + ValidationHelper.ToString(trimmedBypassString));
                            }
                        }
                    } 
                }
            } 
            return bypassList; 
        }
 
    }

    internal partial class RegBlobWebProxyDataBuilder
    { 
        //
        // Updates an instance of WbeProxy with the proxy settings from IE for: 
        // the current user and a given connectoid. 
        //
        [ResourceExposure(ResourceScope.Machine)]  // Check scoping on this SafeRegistryHandle 
        [ResourceConsumption(ResourceScope.Machine)]
        protected override void BuildInternal()
        {
            GlobalLog.Enter("RegBlobWebProxyDataBuilder#" + ValidationHelper.HashString(this) + "::BuildInternal() m_Connectoid:" + ValidationHelper.ToString(m_Connectoid)); 

            // DON'T TOUCH THE ORDERING OF THE CALLS TO THE INSTANCE OF ProxyRegBlob 
            bool success = ReadRegSettings(); 
            if (success) {
                success = ReadInt32() >= IE50StrucSize; 
            }
            if (!success) {
                // if registry access fails rely on automatic detection
                SetAutoDetectSettings(true); 
                return;
            } 
            // read the rest of the items out 
            ReadInt32(); // incremental version# of current settings (ignored)
            ProxyTypeFlags proxyFlags = (ProxyTypeFlags)ReadInt32(); // flags 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() proxyFlags:" + ValidationHelper.ToString(proxyFlags));

            string addressString = ReadString(); // proxy name
            string proxyBypassString = ReadString(); // proxy bypass 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() proxyAddressString:" + ValidationHelper.ToString(addressString) + " proxyBypassString:" + ValidationHelper.ToString(proxyBypassString));
 
            // 
            // Once we verify that the flag for proxy is enabled,
            // Parse UriString that is stored, may be in the form, 
            //  of "http=http://http-proxy;ftp="ftp=http://..." must
            //  handle this case along with just a URI.
            //
            if ((proxyFlags & ProxyTypeFlags.PROXY_TYPE_PROXY) != 0) { 

                SetProxyAndBypassList(addressString, proxyBypassString); 
            } 

 
#if !FEATURE_PAL
            SetAutoDetectSettings((proxyFlags & ProxyTypeFlags.PROXY_TYPE_AUTO_DETECT) != 0);

            string autoConfigUrlString = ReadString(); // autoconfig url 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() scriptLocation:" + ValidationHelper.ToString(addressString));
            if ((proxyFlags & ProxyTypeFlags.PROXY_TYPE_AUTO_PROXY_URL) != 0) { 
                SetAutoProxyUrl(autoConfigUrlString); 
            }
 
            // The final straw against attempting to use the WinInet LKG script location was, it's invalid when IPs have changed even if the
            // connectoid hadn't.  Doing that validation didn't seem worth it (error-prone, expensive, unsupported).
#if USE_WINIET_AUTODETECT_CACHE
            proxyIE5Settings.ReadInt32(); // autodetect flags (ignored) 

            // reuse addressString for lkgScriptLocationString 
            addressString = proxyIE5Settings.ReadString(); // last known good auto-proxy url 

            // read ftLastKnownDetectTime 
            FILETIME ftLastKnownDetectTime = proxyIE5Settings.ReadFileTime();

            // Verify if this lkgScriptLocationString has timed out
            // 
            if (IsValidTimeForLkgScriptLocation(ftLastKnownDetectTime)) {
                // reuse address for lkgScriptLocation 
                GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() lkgScriptLocation:" + ValidationHelper.ToString(addressString)); 
                if (Uri.TryCreate(addressString, UriKind.Absolute, out address)) {
                    webProxyData.lkgScriptLocation = address; 
                }
            }
            else {
#if TRAVE 
                SYSTEMTIME st = new SYSTEMTIME();
                bool f = SafeNclNativeMethods.FileTimeToSystemTime(ref ftLastKnownDetectTime, ref st); 
                if (f) 
                    GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() ftLastKnownDetectTime:" + ValidationHelper.ToString(st));
#endif // TRAVE 
                GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() Ignoring Timed out lkgScriptLocation:" + ValidationHelper.ToString(addressString));

                // Now rely on automatic detection settings set above
                // based on the proxy flags (webProxyData.automaticallyDetectSettings). 
                //
            } 
#endif 
            /*
            // This is some of the rest of the proxy reg key blob parsing. 
            //
            // Read Inte---- IPs
            int iftCount = proxyIE5Settings.ReadInt32();
            for (int ift = 0; ift < iftCount; ++ift) { 
                proxyIE5Settings.ReadInt32();
            } 
 
            // Read lpszAutoconfigSecondaryUrl
            string autoconfigSecondaryUrl = proxyIE5Settings.ReadString(); 

            // Read dwAutoconfigReloadDelayMins
            int autoconfigReloadDelayMins = proxyIE5Settings.ReadInt32();
            */ 
#endif
            GlobalLog.Leave("RegBlobWebProxyDataBuilder#" + ValidationHelper.HashString(this) + "::BuildInternal()"); 
        } 

#if USE_WINIET_AUTODETECT_CACHE 
#if !FEATURE_PAL
        internal unsafe static bool IsValidTimeForLkgScriptLocation(FILETIME ftLastKnownDetectTime) {
            // Get Current System Time.
            FILETIME ftCurrentTime = new FILETIME(); 
            SafeNclNativeMethods.GetSystemTimeAsFileTime(ref ftCurrentTime);
 
            UInt64 ftDetect = (UInt64)ftLastKnownDetectTime.dwHighDateTime; 
            ftDetect <<= (sizeof(int) * 8);
            ftDetect |= (UInt64)(uint)ftLastKnownDetectTime.dwLowDateTime; 

            UInt64 ftCurrent = (UInt64)ftCurrentTime.dwHighDateTime;
            ftCurrent <<= (sizeof(int) * 8);
            ftCurrent |= (UInt64)(uint)ftCurrentTime.dwLowDateTime; 

            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() Detect Time:" + ValidationHelper.ToString(ftDetect)); 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() Current Time:" + ValidationHelper.ToString(ftCurrent)); 
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() 7 days:" + ValidationHelper.ToString(s_lkgScriptValidTime));
            GlobalLog.Print("RegBlobWebProxyDataBuilder::BuildInternal() Delta Time:" + ValidationHelper.ToString((UInt64)(ftCurrent - ftDetect))); 

            return (ftCurrent - ftDetect) < s_lkgScriptValidTime;
        }
#endif // !FEATURE_PAL 
#endif
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK