1 //------------------------------------------------------------------------------
2 // <copyright file="Internal.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
9 using System.Reflection;
10 using System.Collections;
11 using System.Collections.Specialized;
12 using System.Globalization;
13 using System.Net.Sockets;
14 using System.Net.WebSockets;
15 using System.Runtime.InteropServices;
16 using System.Runtime.Versioning;
17 using System.Security.Authentication.ExtendedProtection;
18 using System.Security.Cryptography.X509Certificates;
19 using System.Security.Permissions;
20 using System.Diagnostics;
21 using System.Threading;
22 using System.Security;
23 using System.Net.Security;
24 using System.Net.NetworkInformation;
25 using System.Runtime.Serialization;
26 using Microsoft.Win32;
27 using System.Collections.Generic;
29 internal static class IntPtrHelper {
32 internal static IntPtr Add(IntPtr a, IntPtr b) {
33 return (IntPtr) ((long)a + (long)b);
36 internal static IntPtr Add(IntPtr a, int b) {
37 return (IntPtr) ((long)a + (long)b);
40 internal static long Subtract(IntPtr a, IntPtr b) {
41 return ((long)a - (long)b);
45 internal class InternalException : SystemException
47 internal InternalException()
49 GlobalLog.Assert("InternalException thrown.");
52 internal InternalException(SerializationInfo serializationInfo, StreamingContext streamingContext) :
53 base(serializationInfo, streamingContext)
57 internal static class NclUtilities
61 /// Indicates true if the threadpool is low on threads,
62 /// in this case we need to refuse to start new requests,
63 /// and avoid blocking.
66 internal static bool IsThreadPoolLow()
69 if (ComNetOS.IsAspNetServer)
75 int workerThreads, completionPortThreads;
76 ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
79 return workerThreads < 2 || (completionPortThreads < 2);
81 GlobalLog.Assert(completionPortThreads == 0, "completionPortThreads should be zero on the PAL");
82 return workerThreads < 2;
87 internal static bool HasShutdownStarted
91 return Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload();
95 // This only works for context-destroying errors.
96 internal static bool IsCredentialFailure(SecurityStatus error)
98 return error == SecurityStatus.LogonDenied ||
99 error == SecurityStatus.UnknownCredentials ||
100 error == SecurityStatus.NoImpersonation ||
101 error == SecurityStatus.NoAuthenticatingAuthority ||
102 error == SecurityStatus.UntrustedRoot ||
103 error == SecurityStatus.CertExpired ||
104 error == SecurityStatus.SmartcardLogonRequired ||
105 error == SecurityStatus.BadBinding;
108 // This only works for context-destroying errors.
109 internal static bool IsClientFault(SecurityStatus error)
111 return error == SecurityStatus.InvalidToken ||
112 error == SecurityStatus.CannotPack ||
113 error == SecurityStatus.QopNotSupported ||
114 error == SecurityStatus.NoCredentials ||
115 error == SecurityStatus.MessageAltered ||
116 error == SecurityStatus.OutOfSequence ||
117 error == SecurityStatus.IncompleteMessage ||
118 error == SecurityStatus.IncompleteCredentials ||
119 error == SecurityStatus.WrongPrincipal ||
120 error == SecurityStatus.TimeSkew ||
121 error == SecurityStatus.IllegalMessage ||
122 error == SecurityStatus.CertUnknown ||
123 error == SecurityStatus.AlgorithmMismatch ||
124 error == SecurityStatus.SecurityQosFailed ||
125 error == SecurityStatus.UnsupportedPreauth;
129 // ContextRelativeDemand
130 // Allows easily demanding a permission against a given ExecutionContext.
131 // Have requested the CLR to provide this method on ExecutionContext.
132 private static volatile ContextCallback s_ContextRelativeDemandCallback;
134 internal static ContextCallback ContextRelativeDemandCallback
138 if (s_ContextRelativeDemandCallback == null)
139 s_ContextRelativeDemandCallback = new ContextCallback(DemandCallback);
140 return s_ContextRelativeDemandCallback;
144 private static void DemandCallback(object state)
147 ((CodeAccessPermission) state).Demand();
151 // This is for checking if a hostname probably refers to this machine without going to DNS.
152 internal static bool GuessWhetherHostIsLoopback(string host)
154 string hostLower = host.ToLowerInvariant();
155 if (hostLower == "localhost" || hostLower == "loopback")
161 IPGlobalProperties ip = IPGlobalProperties.InternalGetIPGlobalProperties();
162 string hostnameLower = ip.HostName.ToLowerInvariant();
163 return hostLower == hostnameLower || hostLower == hostnameLower + "." + ip.DomainName.ToLowerInvariant();
169 internal static bool IsFatal(Exception exception)
171 return exception != null && (exception is OutOfMemoryException || exception is StackOverflowException || exception is ThreadAbortException);
174 // Need a fast cached list of local addresses for internal use.
175 private static volatile IPAddress[] _LocalAddresses;
176 private static object _LocalAddressesLock;
180 private static volatile NetworkAddressChangePolled s_AddressChange;
182 internal static IPAddress[] LocalAddresses
186 if (s_AddressChange != null && s_AddressChange.CheckAndReset())
188 return (_LocalAddresses = GetLocalAddresses());
191 if (_LocalAddresses != null)
193 return _LocalAddresses;
196 lock (LocalAddressesLock)
198 if (_LocalAddresses != null)
200 return _LocalAddresses;
203 s_AddressChange = new NetworkAddressChangePolled();
205 return (_LocalAddresses = GetLocalAddresses());
210 private static IPAddress[] GetLocalAddresses()
214 ArrayList collections = new ArrayList(16);
217 SafeLocalFree buffer = null;
218 GetAdaptersAddressesFlags gaaFlags = GetAdaptersAddressesFlags.SkipAnycast | GetAdaptersAddressesFlags.SkipMulticast |
219 GetAdaptersAddressesFlags.SkipFriendlyName | GetAdaptersAddressesFlags.SkipDnsServer;
221 uint result = UnsafeNetInfoNativeMethods.GetAdaptersAddresses(AddressFamily.Unspecified, (uint)gaaFlags, IntPtr.Zero, SafeLocalFree.Zero, ref size);
222 while (result == IpHelperErrors.ErrorBufferOverflow)
226 buffer = SafeLocalFree.LocalAlloc((int)size);
227 result = UnsafeNetInfoNativeMethods.GetAdaptersAddresses(AddressFamily.Unspecified, (uint)gaaFlags, IntPtr.Zero, buffer, ref size);
229 if (result == IpHelperErrors.Success)
231 IntPtr nextAdapter = buffer.DangerousGetHandle();
233 while (nextAdapter != IntPtr.Zero)
235 IpAdapterAddresses adapterAddresses = (IpAdapterAddresses)Marshal.PtrToStructure(
236 nextAdapter, typeof(IpAdapterAddresses));
238 if (adapterAddresses.firstUnicastAddress != IntPtr.Zero)
240 UnicastIPAddressInformationCollection coll =
241 SystemUnicastIPAddressInformation.MarshalUnicastIpAddressInformationCollection(
242 adapterAddresses.firstUnicastAddress);
244 collections.Add(coll);
247 nextAdapter = adapterAddresses.next;
259 if (result != IpHelperErrors.Success && result != IpHelperErrors.ErrorNoData)
261 throw new NetworkInformationException((int)result);
264 local = new IPAddress[total];
266 foreach (UnicastIPAddressInformationCollection coll in collections)
268 foreach (IPAddressInformation info in coll)
270 local[i++] = info.Address;
277 internal static bool IsAddressLocal(IPAddress ipAddress) {
278 IPAddress[] localAddresses = NclUtilities.LocalAddresses;
279 for (int i = 0; i < localAddresses.Length; i++)
281 if (ipAddress.Equals(localAddresses[i], false))
289 #else // !FEATURE_PAL
291 internal static bool IsAddressLocal(IPAddress ipAddress) {
292 IPAddress[] localAddresses = NclUtilities.LocalAddresses;
293 for (int i = 0; i < localAddresses.Length; i++)
295 if (ipAddress.Equals(localAddresses[i], false))
303 private const int HostNameBufferLength = 256;
304 internal static string _LocalDomainName;
306 // Copied from the old version of DNS.cs
307 // Returns a list of our local addresses by calling gethostbyname with null.
309 private static IPHostEntry GetLocalHost()
312 #pragma warning disable 618
313 return Dns.GetHostByName (Dns.GetHostName ());
314 #pragma warning restore
317 // IPv6 Changes: If IPv6 is enabled, we can't simply use the
318 // old IPv4 gethostbyname(null). Instead we need
319 // to do a more complete lookup.
321 if (Socket.SupportsIPv6)
324 // IPv6 enabled: use getaddrinfo() of the local host name
325 // to obtain this information. Need to get the machines
326 // name as well - do that here so that we don't need to
327 // Assert DNS permissions.
329 StringBuilder hostname = new StringBuilder(HostNameBufferLength);
330 SocketError errorCode =
331 UnsafeNclNativeMethods.OSSOCK.gethostname(
333 HostNameBufferLength);
335 if (errorCode != SocketError.Success)
337 throw new SocketException();
340 return Dns.GetHostByName(hostname.ToString());
345 // IPv6 disabled: use gethostbyname() to obtain information.
347 IntPtr nativePointer =
348 UnsafeNclNativeMethods.OSSOCK.gethostbyname(
351 if (nativePointer == IntPtr.Zero)
353 throw new SocketException();
356 return Dns.NativeToHostEntry(nativePointer);
362 internal static IPAddress[] LocalAddresses
366 IPAddress[] local = _LocalAddresses;
372 lock (LocalAddressesLock)
374 local = _LocalAddresses;
380 List<IPAddress> localList = new List<IPAddress>();
384 IPHostEntry hostEntry = GetLocalHost();
385 if (hostEntry != null)
387 if (hostEntry.HostName != null)
389 int dot = hostEntry.HostName.IndexOf('.');
392 _LocalDomainName = hostEntry.HostName.Substring(dot);
396 IPAddress[] ipAddresses = hostEntry.AddressList;
397 if (ipAddresses != null)
399 foreach (IPAddress ipAddress in ipAddresses)
401 localList.Add(ipAddress);
410 local = new IPAddress[localList.Count];
412 foreach (IPAddress ipAddress in localList)
414 local[index] = ipAddress;
417 _LocalAddresses = local;
423 #endif // !FEATURE_PAL
425 private static object LocalAddressesLock
429 if (_LocalAddressesLock == null)
431 Interlocked.CompareExchange(ref _LocalAddressesLock, new object(), null);
433 return _LocalAddressesLock;
438 internal static class NclConstants
440 internal static readonly object Sentinel = new object();
441 internal static readonly object[] EmptyObjectArray = new object[0];
442 internal static readonly Uri[] EmptyUriArray = new Uri[0];
444 internal static readonly byte[] CRLF = new byte[] {(byte) '\r', (byte) '\n'};
445 internal static readonly byte[] ChunkTerminator = new byte[] {(byte) '0', (byte) '\r', (byte) '\n', (byte) '\r', (byte) '\n'};
449 // A simple [....] point, useful for deferring work. Just an int value with helper methods.
450 // This is used by HttpWebRequest to syncronize Reads/Writes while waiting for a 100-Continue response.
452 internal struct InterlockedGate
456 // Not currently waiting for a response
457 internal const int Open = 0; // Initial state of gate.
458 // Starting the timer to wait for a response (async)
459 internal const int Triggering = 1; // Gate is being actively held by a thread - indeterminate state.
460 // Waiting for response
461 internal const int Triggered = 2; // The triggering event has occurred.
462 // Stopping the timer (got a response or timed out)
463 internal const int Signaling = 3;
464 // Got a response or timed out, may process the response.
465 internal const int Signaled = 4;
466 // Re/submitting data.
467 internal const int Completed = 5; // The gated event is done.
481 // Only call when all threads are guaranteed to be done with the gate.
482 internal void Reset()
487 // Returns false if the gate is not taken. If exclusive is true, throws if the gate is already triggered.
488 internal bool Trigger(bool exclusive)
490 int gate = Interlocked.CompareExchange(ref m_State, Triggered, Open);
491 if (exclusive && (gate == Triggering || gate == Triggered))
493 GlobalLog.Assert("InterlockedGate::Trigger", "Gate already triggered.");
494 throw new InternalException();
499 // Use StartTrigger() and FinishTrigger() to trigger the gate as a two step operation. This is useful to set up an invariant
500 // that must be ready by the time another thread closes the gate. Do not block between StartTrigger() and FinishTrigger(), just
501 // set up your state to be consistent. If this method returns true, FinishTrigger() *must* be called to avoid deadlock - do
504 // Returns false if the gate is not taken. If exclusive is true, throws if the gate is already triggering/ed.
505 internal bool StartTriggering(bool exclusive)
507 int gate = Interlocked.CompareExchange(ref m_State, Triggering, Open);
508 if (exclusive && (gate == Triggering || gate == Triggered))
510 GlobalLog.Assert("InterlockedGate::StartTriggering", "Gate already triggered.");
511 throw new InternalException();
516 // Gate must be held by StartTriggering().
517 internal void FinishTriggering()
519 int gate = Interlocked.CompareExchange(ref m_State, Triggered, Triggering);
520 if (gate != Triggering)
522 GlobalLog.Assert("InterlockedGate::FinishTriggering", "Gate not Triggering.");
523 throw new InternalException();
527 // Use StartSignaling() and FinishSignaling() to signal the gate as a two step operation. This is useful to
528 // set up an invariant that must be ready by the time another thread closes the gate. Do not block between
529 // StartSignaling() and FinishSignaling(), just set up your state to be consistent. If this method returns
530 // true, FinishSignaling() *must* be called to avoid deadlock - do it in a finally.
532 // Returns false if the gate is not taken. If exclusive is true, throws if the gate is already Signaling/ed.
533 internal bool StartSignaling(bool exclusive)
535 int gate = Interlocked.CompareExchange(ref m_State, Signaling, Triggered);
536 if (exclusive && (gate == Signaling || gate == Signaled)) //
538 GlobalLog.Assert("InterlockedGate::StartTrigger", "Gate already Signaled.");
539 throw new InternalException();
541 Debug.Assert(gate != Triggering, "Still Triggering");
542 return gate == Triggered;
545 // Gate must be held by StartSignaling().
546 internal void FinishSignaling()
548 int gate = Interlocked.CompareExchange(ref m_State, Signaled, Signaling);
549 if (gate != Signaling)
551 GlobalLog.Assert("InterlockedGate::FinishSignaling", "Gate not Signaling; " + gate);
552 throw new InternalException();
556 // Makes sure only one thread completes the opperation.
557 internal bool Complete()
559 int gate = Interlocked.CompareExchange(ref m_State, Completed, Signaled);
560 Debug.Assert(gate != Signaling, "Still Signaling");
561 return (gate == Signaled);
567 // A polling implementation of NetworkAddressChange.
569 internal class NetworkAddressChangePolled : IDisposable
571 private bool disposed;
572 private SafeCloseSocketAndEvent ipv4Socket = null;
573 private SafeCloseSocketAndEvent ipv6Socket = null;
576 internal unsafe NetworkAddressChangePolled()
578 Socket.InitializeSockets();
580 if (Socket.OSSupportsIPv4)
583 ipv4Socket = SafeCloseSocketAndEvent.CreateWSASocketWithEvent(AddressFamily.InterNetwork, SocketType.Dgram, (ProtocolType)0, true, false);
584 UnsafeNclNativeMethods.OSSOCK.ioctlsocket(ipv4Socket, IoctlSocketConstants.FIONBIO, ref blocking);
587 if(Socket.OSSupportsIPv6){
589 ipv6Socket = SafeCloseSocketAndEvent.CreateWSASocketWithEvent(AddressFamily.InterNetworkV6, SocketType.Dgram, (ProtocolType)0, true, false);
590 UnsafeNclNativeMethods.OSSOCK.ioctlsocket(ipv6Socket,IoctlSocketConstants.FIONBIO,ref blocking);
592 Setup(StartIPOptions.Both);
595 private unsafe void Setup(StartIPOptions startIPOptions)
598 SocketError errorCode;
601 if (Socket.OSSupportsIPv4 && (startIPOptions & StartIPOptions.StartIPv4) != 0){
602 errorCode = (SocketError)UnsafeNclNativeMethods.OSSOCK.WSAIoctl_Blocking(
603 ipv4Socket.DangerousGetHandle(),
604 (int) IOControlCode.AddressListChange,
607 SafeNativeOverlapped.Zero, IntPtr.Zero);
609 if (errorCode != SocketError.Success) {
610 NetworkInformationException exception = new NetworkInformationException();
611 if (exception.ErrorCode != (uint)SocketError.WouldBlock) {
617 errorCode = (SocketError)UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(ipv4Socket, ipv4Socket.GetEventHandle().SafeWaitHandle, AsyncEventBits.FdAddressListChange);
618 if (errorCode != SocketError.Success) {
624 if(Socket.OSSupportsIPv6 && (startIPOptions & StartIPOptions.StartIPv6) !=0){
625 errorCode = (SocketError) UnsafeNclNativeMethods.OSSOCK.WSAIoctl_Blocking(
626 ipv6Socket.DangerousGetHandle(),
627 (int) IOControlCode.AddressListChange,
630 SafeNativeOverlapped.Zero, IntPtr.Zero);
632 if (errorCode != SocketError.Success) {
633 NetworkInformationException exception = new NetworkInformationException();
634 if (exception.ErrorCode != (uint)SocketError.WouldBlock) {
640 errorCode = (SocketError)UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(ipv6Socket, ipv6Socket.GetEventHandle().SafeWaitHandle, AsyncEventBits.FdAddressListChange);
641 if (errorCode != SocketError.Success) {
648 internal bool CheckAndReset()
653 StartIPOptions options = StartIPOptions.None;
655 if (ipv4Socket != null && ipv4Socket.GetEventHandle().WaitOne(0, false)){
656 options|= StartIPOptions.StartIPv4;
658 if (ipv6Socket != null && ipv6Socket.GetEventHandle().WaitOne(0, false))
660 options|= StartIPOptions.StartIPv6;
663 if(options != StartIPOptions.None){
673 public void Dispose()
678 if(ipv6Socket != null){
682 if(ipv4Socket != null){
692 #endif // FEATURE_PAL
697 internal static class ComNetOS
699 private const string OSInstallTypeRegKey = @"Software\Microsoft\Windows NT\CurrentVersion";
700 private const string OSInstallTypeRegKeyPath = @"HKEY_LOCAL_MACHINE\" + OSInstallTypeRegKey;
701 private const string OSInstallTypeRegName = "InstallationType";
702 private const string InstallTypeStringClient = "Client";
703 private const string InstallTypeStringServer = "Server";
704 private const string InstallTypeStringServerCore = "Server Core";
705 private const string InstallTypeStringEmbedded = "Embedded";
707 // Minimum support for Windows 2008 is assumed.
708 internal static readonly bool IsAspNetServer; // ie: running under ASP+
709 internal static readonly bool IsWin7orLater; // Is Windows 7 or later
710 internal static readonly bool IsWin7Sp1orLater; // Is Windows 7 Sp1 or later (2008 R2 Sp1+)
711 internal static readonly bool IsWin8orLater; // Is Windows 8 or later
712 internal static readonly WindowsInstallationType InstallationType; // e.g. Client, Server, Server Core
714 // We use it safe so assert
715 [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)]
716 [ResourceExposure(ResourceScope.None)]
717 [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
720 OperatingSystem operatingSystem = Environment.OSVersion;
722 GlobalLog.Print("ComNetOS::.ctor(): " + operatingSystem.ToString());
724 Debug.Assert(operatingSystem.Platform != PlatformID.Win32Windows, "Windows 9x is not supported");
727 // Detect ASP+ as a platform running under NT
732 IsAspNetServer = (Thread.GetDomain().GetData(".appDomain") != null);
736 IsWin7orLater = (operatingSystem.Version >= new Version(6, 1));
738 IsWin7Sp1orLater = (operatingSystem.Version >= new Version(6, 1, 7601));
740 IsWin8orLater = (operatingSystem.Version >= new Version(6, 2));
742 InstallationType = GetWindowsInstallType();
743 if (Logging.On) Logging.PrintInfo(Logging.Web, SR.GetString(SR.net_osinstalltype, InstallationType));
746 [RegistryPermission(SecurityAction.Assert, Read = OSInstallTypeRegKeyPath)]
747 private static WindowsInstallationType GetWindowsInstallType()
751 using (RegistryKey installTypeKey = Registry.LocalMachine.OpenSubKey(OSInstallTypeRegKey))
753 string installType = installTypeKey.GetValue(OSInstallTypeRegName) as string;
755 if (string.IsNullOrEmpty(installType))
757 if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_empty_osinstalltype, OSInstallTypeRegKey + "\\" + OSInstallTypeRegName));
758 return WindowsInstallationType.Unknown;
762 if (String.Compare(installType, InstallTypeStringClient, StringComparison.OrdinalIgnoreCase) == 0)
764 return WindowsInstallationType.Client;
766 if (String.Compare(installType, InstallTypeStringServer, StringComparison.OrdinalIgnoreCase) == 0)
768 return WindowsInstallationType.Server;
770 if (String.Compare(installType, InstallTypeStringServerCore, StringComparison.OrdinalIgnoreCase) == 0)
772 return WindowsInstallationType.ServerCore;
774 if (String.Compare(installType, InstallTypeStringEmbedded, StringComparison.OrdinalIgnoreCase) == 0)
776 return WindowsInstallationType.Embedded;
779 if (Logging.On) Logging.PrintError(Logging.Web, SR.GetString(SR.net_unknown_osinstalltype, installType));
781 // Our default return is unknown when we don't recognize the SKU or if the registry value
782 // doesn't exist. As a result, the SKU-specific checks in System.Net will not limit the set
783 // of functionality available. This allows SKUs we are not aware of to use all of our
784 // functionality. Burden is on them to ensure that all our dependencies are present.
785 // The alternative would be for us to throw an exception here. If we did this, these other
786 // SKUs wouldn't be able to load this code and test their behavior. We would need to update
787 // this code to enable them to run.
788 return WindowsInstallationType.Unknown;
792 catch (UnauthorizedAccessException e)
794 if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_cant_determine_osinstalltype, OSInstallTypeRegKey, e.Message));
795 return WindowsInstallationType.Unknown;
797 catch (SecurityException e)
799 if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_cant_determine_osinstalltype, OSInstallTypeRegKey, e.Message));
800 return WindowsInstallationType.Unknown;
808 // support class for Validation related stuff.
810 internal static class ValidationHelper {
812 public static string [] EmptyArray = new string[0];
814 internal static readonly char[] InvalidMethodChars =
822 // invalid characters that cannot be found in a valid method-verb or http header
823 internal static readonly char[] InvalidParamChars =
848 public static string [] MakeEmptyArrayNull(string [] stringArray) {
849 if ( stringArray == null || stringArray.Length == 0 ) {
856 public static string MakeStringNull(string stringValue) {
857 if ( stringValue == null || stringValue.Length == 0) {
865 // Consider removing.
866 public static string MakeStringEmpty(string stringValue) {
867 if ( stringValue == null || stringValue.Length == 0) {
877 // Consider removing.
878 public static int HashCode(object objectValue) {
879 if (objectValue == null) {
882 return objectValue.GetHashCode();
888 public static string ExceptionMessage(Exception exception) {
889 if (exception==null) {
892 if (exception.InnerException==null) {
893 return exception.Message;
895 return exception.Message + " (" + ExceptionMessage(exception.InnerException) + ")";
898 public static string ToString(object objectValue) {
899 if (objectValue == null) {
901 } else if (objectValue is string && ((string)objectValue).Length==0) {
902 return "(string.empty)";
903 } else if (objectValue is Exception) {
904 return ExceptionMessage(objectValue as Exception);
905 } else if (objectValue is IntPtr) {
906 return "0x" + ((IntPtr)objectValue).ToString("x");
908 return objectValue.ToString();
911 public static string HashString(object objectValue) {
912 if (objectValue == null) {
914 } else if (objectValue is string && ((string)objectValue).Length==0) {
915 return "(string.empty)";
917 return objectValue.GetHashCode().ToString(NumberFormatInfo.InvariantInfo);
921 public static bool IsInvalidHttpString(string stringValue) {
922 return stringValue.IndexOfAny(InvalidParamChars)!=-1;
925 public static bool IsBlankString(string stringValue) {
926 return stringValue==null || stringValue.Length==0;
930 // Consider removing.
931 public static bool ValidateUInt32(long address) {
932 // on false, API should throw new ArgumentOutOfRangeException("address");
933 return address>=0x00000000 && address<=0xFFFFFFFF;
937 public static bool ValidateTcpPort(int port) {
938 // on false, API should throw new ArgumentOutOfRangeException("port");
939 return port>=IPEndPoint.MinPort && port<=IPEndPoint.MaxPort;
942 public static bool ValidateRange(int actual, int fromAllowed, int toAllowed) {
943 // on false, API should throw new ArgumentOutOfRangeException("argument");
944 return actual>=fromAllowed && actual<=toAllowed;
948 // Consider removing.
949 public static bool ValidateRange(long actual, long fromAllowed, long toAllowed) {
950 // on false, API should throw new ArgumentOutOfRangeException("argument");
951 return actual>=fromAllowed && actual<=toAllowed;
955 // There are threading tricks a malicious app can use to create an ArraySegment with mismatched
956 // array/offset/count. Copy locally and make sure they're valid before using them.
957 internal static void ValidateSegment(ArraySegment<byte> segment) {
958 if (/*segment == null ||*/ segment.Array == null) {
959 throw new ArgumentNullException("segment");
961 // Length zero is explicitly allowed
962 if (segment.Offset < 0 || segment.Count < 0
963 || segment.Count > (segment.Array.Length - segment.Offset)) {
964 throw new ArgumentOutOfRangeException("segment");
969 internal static class ExceptionHelper
972 internal static readonly KeyContainerPermission KeyContainerPermissionOpen = new KeyContainerPermission(KeyContainerPermissionFlags.Open);
973 internal static readonly WebPermission WebPermissionUnrestricted = new WebPermission(NetworkAccess.Connect);
974 internal static readonly SecurityPermission UnmanagedPermission = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
975 internal static readonly SocketPermission UnrestrictedSocketPermission = new SocketPermission(PermissionState.Unrestricted);
976 internal static readonly SecurityPermission InfrastructurePermission = new SecurityPermission(SecurityPermissionFlag.Infrastructure);
977 internal static readonly SecurityPermission ControlPolicyPermission = new SecurityPermission(SecurityPermissionFlag.ControlPolicy);
978 internal static readonly SecurityPermission ControlPrincipalPermission = new SecurityPermission(SecurityPermissionFlag.ControlPrincipal);
980 internal static NotImplementedException MethodNotImplementedException {
982 return new NotImplementedException(SR.GetString(SR.net_MethodNotImplementedException));
986 internal static NotImplementedException PropertyNotImplementedException {
988 return new NotImplementedException(SR.GetString(SR.net_PropertyNotImplementedException));
992 internal static NotSupportedException MethodNotSupportedException {
994 return new NotSupportedException(SR.GetString(SR.net_MethodNotSupportedException));
998 internal static NotSupportedException PropertyNotSupportedException {
1000 return new NotSupportedException(SR.GetString(SR.net_PropertyNotSupportedException));
1004 internal static WebException IsolatedException {
1006 return new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.KeepAliveFailure),WebExceptionStatus.KeepAliveFailure, WebExceptionInternalStatus.Isolated, null);
1010 internal static WebException RequestAbortedException {
1012 return new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled);
1016 internal static WebException CacheEntryNotFoundException {
1018 return new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.CacheEntryNotFound), WebExceptionStatus.CacheEntryNotFound);
1022 internal static WebException RequestProhibitedByCachePolicyException {
1024 return new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestProhibitedByCachePolicy), WebExceptionStatus.RequestProhibitedByCachePolicy);
1029 internal enum WindowsInstallationType
1038 internal enum SecurityStatus
1040 // Success / Informational
1042 ContinueNeeded = unchecked((int)0x00090312),
1043 CompleteNeeded = unchecked((int)0x00090313),
1044 CompAndContinue = unchecked((int)0x00090314),
1045 ContextExpired = unchecked((int)0x00090317),
1046 CredentialsNeeded = unchecked((int)0x00090320),
1047 Renegotiate = unchecked((int)0x00090321),
1050 OutOfMemory = unchecked((int)0x80090300),
1051 InvalidHandle = unchecked((int)0x80090301),
1052 Unsupported = unchecked((int)0x80090302),
1053 TargetUnknown = unchecked((int)0x80090303),
1054 InternalError = unchecked((int)0x80090304),
1055 PackageNotFound = unchecked((int)0x80090305),
1056 NotOwner = unchecked((int)0x80090306),
1057 CannotInstall = unchecked((int)0x80090307),
1058 InvalidToken = unchecked((int)0x80090308),
1059 CannotPack = unchecked((int)0x80090309),
1060 QopNotSupported = unchecked((int)0x8009030A),
1061 NoImpersonation = unchecked((int)0x8009030B),
1062 LogonDenied = unchecked((int)0x8009030C),
1063 UnknownCredentials = unchecked((int)0x8009030D),
1064 NoCredentials = unchecked((int)0x8009030E),
1065 MessageAltered = unchecked((int)0x8009030F),
1066 OutOfSequence = unchecked((int)0x80090310),
1067 NoAuthenticatingAuthority = unchecked((int)0x80090311),
1068 IncompleteMessage = unchecked((int)0x80090318),
1069 IncompleteCredentials = unchecked((int)0x80090320),
1070 BufferNotEnough = unchecked((int)0x80090321),
1071 WrongPrincipal = unchecked((int)0x80090322),
1072 TimeSkew = unchecked((int)0x80090324),
1073 UntrustedRoot = unchecked((int)0x80090325),
1074 IllegalMessage = unchecked((int)0x80090326),
1075 CertUnknown = unchecked((int)0x80090327),
1076 CertExpired = unchecked((int)0x80090328),
1077 AlgorithmMismatch = unchecked((int)0x80090331),
1078 SecurityQosFailed = unchecked((int)0x80090332),
1079 SmartcardLogonRequired = unchecked((int)0x8009033E),
1080 UnsupportedPreauth = unchecked((int)0x80090343),
1081 BadBinding = unchecked((int)0x80090346)
1084 internal enum ContentTypeValues {
1085 ChangeCipherSpec = 0x14,
1089 Unrecognized = 0xFF,
1092 internal enum ContextAttribute {
1094 // look into <sspi.h> and <schannel.h>
1101 //KeyInfo = 0x05, must not be used, see ConnectionInfo instead
1103 // SECPKG_ATTR_PROTO_INFO = 7,
1104 // SECPKG_ATTR_PASSWORD_EXPIRY = 8,
1105 // SECPKG_ATTR_SESSION_KEY = 9,
1107 // SECPKG_ATTR_USER_FLAGS = 11,
1108 NegotiationInfo = 0x0C,
1109 // SECPKG_ATTR_NATIVE_NAMES = 13,
1110 // SECPKG_ATTR_FLAGS = 14,
1111 // SECPKG_ATTR_USE_VALIDATED = 15,
1112 // SECPKG_ATTR_CREDENTIAL_NAME = 16,
1113 // SECPKG_ATTR_TARGET_INFORMATION = 17,
1114 // SECPKG_ATTR_ACCESS_TOKEN = 18,
1115 // SECPKG_ATTR_TARGET = 19,
1116 // SECPKG_ATTR_AUTHENTICATION_ID = 20,
1117 UniqueBindings = 0x19,
1118 EndpointBindings = 0x1A,
1119 ClientSpecifiedSpn = 0x1B, // SECPKG_ATTR_CLIENT_SPECIFIED_TARGET = 27
1120 RemoteCertificate = 0x53,
1121 LocalCertificate = 0x54,
1123 IssuerListInfoEx = 0x59,
1124 ConnectionInfo = 0x5A,
1125 // SECPKG_ATTR_EAP_KEY_BLOCK 0x5b // returns SecPkgContext_EapKeyBlock
1126 // SECPKG_ATTR_MAPPED_CRED_ATTR 0x5c // returns SecPkgContext_MappedCredAttr
1127 // SECPKG_ATTR_SESSION_INFO 0x5d // returns SecPkgContext_SessionInfo
1128 // SECPKG_ATTR_APP_DATA 0x5e // sets/returns SecPkgContext_SessionAppData
1129 // SECPKG_ATTR_REMOTE_CERTIFICATES 0x5F // returns SecPkgContext_Certificates
1130 // SECPKG_ATTR_CLIENT_CERT_POLICY 0x60 // sets SecPkgCred_ClientCertCtlPolicy
1131 // SECPKG_ATTR_CC_POLICY_RESULT 0x61 // returns SecPkgContext_ClientCertPolicyResult
1132 // SECPKG_ATTR_USE_NCRYPT 0x62 // Sets the CRED_FLAG_USE_NCRYPT_PROVIDER FLAG on cred group
1133 // SECPKG_ATTR_LOCAL_CERT_INFO 0x63 // returns SecPkgContext_CertInfo
1134 // SECPKG_ATTR_CIPHER_INFO 0x64 // returns new CNG SecPkgContext_CipherInfo
1135 // SECPKG_ATTR_EAP_PRF_INFO 0x65 // sets SecPkgContext_EapPrfInfo
1136 // SECPKG_ATTR_SUPPORTED_SIGNATURES 0x66 // returns SecPkgContext_SupportedSignatures
1137 // SECPKG_ATTR_REMOTE_CERT_CHAIN 0x67 // returns PCCERT_CONTEXT
1138 UiInfo = 0x68, // sets SEcPkgContext_UiInfo
1141 internal enum Endianness {
1146 internal enum CredentialUse {
1152 internal enum BufferType {
1161 Padding = 0x09, // non-data padding
1163 ChannelBindings = 0x0E,
1165 ReadOnlyFlag = unchecked((int)0x80000000),
1166 ReadOnlyWithChecksum= 0x10000000
1169 internal enum ChainPolicyType {
1172 Authenticode_TS = 3,
1174 BasicConstraints = 5,
1178 internal enum IgnoreCertProblem {
1179 not_time_valid = 0x00000001,
1180 ctl_not_time_valid = 0x00000002,
1181 not_time_nested = 0x00000004,
1182 invalid_basic_constraints = 0x00000008,
1184 all_not_time_valid =
1186 ctl_not_time_valid |
1189 allow_unknown_ca = 0x00000010,
1190 wrong_usage = 0x00000020,
1191 invalid_name = 0x00000040,
1192 invalid_policy = 0x00000080,
1193 end_rev_unknown = 0x00000100,
1194 ctl_signer_rev_unknown = 0x00000200,
1195 ca_rev_unknown = 0x00000400,
1196 root_rev_unknown = 0x00000800,
1200 ctl_signer_rev_unknown |
1205 ctl_not_time_valid |
1207 invalid_basic_constraints |
1213 ctl_signer_rev_unknown |
1218 internal enum CertUsage {
1219 MatchTypeAnd = 0x00,
1225 [StructLayout(LayoutKind.Sequential)]
1226 internal unsafe struct ChainPolicyParameter {
1228 public uint dwFlags;
1229 public SSL_EXTRA_CERT_CHAIN_POLICY_PARA* pvExtraPolicyPara;
1231 public static readonly uint StructSize = (uint) Marshal.SizeOf(typeof(ChainPolicyParameter));
1234 [StructLayout(LayoutKind.Sequential)]
1235 internal unsafe struct SSL_EXTRA_CERT_CHAIN_POLICY_PARA {
1237 [StructLayout(LayoutKind.Explicit)]
1239 [FieldOffset(0)] internal uint cbStruct; //DWORD
1240 [FieldOffset(0)] internal uint cbSize; //DWORD
1243 internal int dwAuthType; //DWORD
1244 internal uint fdwChecks; //DWORD
1245 internal char* pwszServerName; //WCHAR* // used to check against CN=xxxx
1247 internal SSL_EXTRA_CERT_CHAIN_POLICY_PARA(bool amIServer)
1249 u.cbStruct = StructSize;
1250 u.cbSize = StructSize;
1251 //# define AUTHTYPE_CLIENT 1
1252 //# define AUTHTYPE_SERVER 2
1253 dwAuthType = amIServer? 1: 2;
1255 pwszServerName = null;
1257 static readonly uint StructSize = (uint) Marshal.SizeOf(typeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
1260 [StructLayout(LayoutKind.Sequential)]
1261 internal unsafe struct ChainPolicyStatus {
1263 public uint dwError;
1264 public uint lChainIndex;
1265 public uint lElementIndex;
1266 public void* pvExtraPolicyStatus;
1268 public static readonly uint StructSize = (uint) Marshal.SizeOf(typeof(ChainPolicyStatus));
1271 [StructLayout(LayoutKind.Sequential)]
1272 internal unsafe struct CertEnhKeyUse {
1274 public uint cUsageIdentifier;
1275 public void* rgpszUsageIdentifier;
1278 public override string ToString() {
1279 return "cUsageIdentifier="+cUsageIdentifier.ToString()+ " rgpszUsageIdentifier=" + new IntPtr(rgpszUsageIdentifier).ToString("x");
1284 [StructLayout(LayoutKind.Sequential)]
1285 internal struct CertUsageMatch {
1286 public CertUsage dwType;
1287 public CertEnhKeyUse Usage;
1289 public override string ToString() {
1290 return "dwType="+dwType.ToString()+" "+Usage.ToString();
1295 [StructLayout(LayoutKind.Sequential)]
1296 internal struct ChainParameters {
1298 public CertUsageMatch RequestedUsage;
1299 public CertUsageMatch RequestedIssuancePolicy;
1300 public uint UrlRetrievalTimeout;
1301 public int BoolCheckRevocationFreshnessTime;
1302 public uint RevocationFreshnessTime;
1305 public static readonly uint StructSize = (uint) Marshal.SizeOf(typeof(ChainParameters));
1307 public override string ToString() {
1308 return "cbSize="+cbSize.ToString()+" "+RequestedUsage.ToString();
1313 [StructLayout(LayoutKind.Sequential)]
1314 struct _CERT_CHAIN_ELEMENT
1317 public IntPtr pCertContext;
1318 // Since this structure is allocated by unmanaged code, we can
1319 // omit the fileds below since we don't need to access them
1320 // CERT_TRUST_STATUS TrustStatus;
1321 // IntPtr pRevocationInfo;
1322 // IntPtr pIssuanceUsage;
1323 // IntPtr pApplicationUsage;
1327 //[StructLayout(LayoutKind.Sequential)]
1328 //unsafe struct CryptoBlob {
1329 // // public uint cbData;
1330 // // public byte* pbData;
1331 // public uint dataSize;
1332 // public byte* dataBlob;
1335 // SecPkgContext_IssuerListInfoEx
1336 [StructLayout(LayoutKind.Sequential)]
1337 unsafe struct IssuerListInfoEx {
1338 public SafeHandle aIssuers;
1339 public uint cIssuers;
1341 public unsafe IssuerListInfoEx(SafeHandle handle, byte[] nativeBuffer) {
1343 fixed(byte* voidPtr = nativeBuffer) {
1344 // if this breaks on 64 bit, do the sizeof(IntPtr) trick
1345 cIssuers = *((uint*)(voidPtr + IntPtr.Size));
1351 [StructLayout(LayoutKind.Sequential)]
1352 internal struct SecureCredential {
1355 typedef struct _SCHANNEL_CRED
1357 DWORD dwVersion; // always SCHANNEL_CRED_VERSION
1359 PCCERT_CONTEXT *paCred;
1360 HCERTSTORE hRootStore;
1363 struct _HMAPPER **aphMappers;
1365 DWORD cSupportedAlgs;
1366 ALG_ID * palgSupportedAlgs;
1368 DWORD grbitEnabledProtocols;
1369 DWORD dwMinimumCipherStrength;
1370 DWORD dwMaximumCipherStrength;
1371 DWORD dwSessionLifespan;
1374 } SCHANNEL_CRED, *PSCHANNEL_CRED;
1377 public const int CurrentVersion = 0x4;
1382 // ptr to an array of pointers
1383 // There is a hack done with this field. AcquireCredentialsHandle requires an array of
1384 // certificate handles; we only ever use one. In order to avoid pinning a one element array,
1385 // we copy this value onto the stack, create a pointer on the stack to the copied value,
1386 // and replace this field with the pointer, during the call to AcquireCredentialsHandle.
1387 // Then we fix it up afterwards. Fine as long as all the SSPI credentials are not
1388 // supposed to be threadsafe.
1389 public IntPtr certContextArray;
1391 private readonly IntPtr rootStore; // == always null, OTHERWISE NOT RELIABLE
1392 public int cMappers;
1393 private readonly IntPtr phMappers; // == always null, OTHERWISE NOT RELIABLE
1394 public int cSupportedAlgs;
1395 private readonly IntPtr palgSupportedAlgs; // == always null, OTHERWISE NOT RELIABLE
1396 public SchProtocols grbitEnabledProtocols;
1397 public int dwMinimumCipherStrength;
1398 public int dwMaximumCipherStrength;
1399 public int dwSessionLifespan;
1400 public SecureCredential.Flags dwFlags;
1401 public int reserved;
1406 NoSystemMapper = 0x02,
1408 ValidateManual = 0x08,
1409 NoDefaultCred = 0x10,
1410 ValidateAuto = 0x20,
1411 SendAuxRecord = 0x00200000,
1412 UseStrongCrypto = 0x00400000,
1415 public SecureCredential(int version, X509Certificate certificate, SecureCredential.Flags flags, SchProtocols protocols, EncryptionPolicy policy) {
1416 // default values required for a struct
1417 rootStore = phMappers = palgSupportedAlgs = certContextArray = IntPtr.Zero;
1418 cCreds = cMappers = cSupportedAlgs = 0;
1420 if (policy == EncryptionPolicy.RequireEncryption) {
1421 // Prohibit null encryption cipher
1422 dwMinimumCipherStrength = 0;
1423 dwMaximumCipherStrength = 0;
1425 else if (policy == EncryptionPolicy.AllowNoEncryption) {
1426 // Allow null encryption cipher in addition to other ciphers
1427 dwMinimumCipherStrength = -1;
1428 dwMaximumCipherStrength = 0;
1430 else if (policy == EncryptionPolicy.NoEncryption) {
1431 // Suppress all encryption and require null encryption cipher only
1432 dwMinimumCipherStrength = -1;
1433 dwMaximumCipherStrength = -1;
1436 throw new ArgumentException(SR.GetString(SR.net_invalid_enum, "EncryptionPolicy"), "policy");
1439 dwSessionLifespan = reserved = 0;
1440 this.version = version;
1442 grbitEnabledProtocols = protocols;
1443 if (certificate != null) {
1444 certContextArray = certificate.Handle;
1449 [System.Diagnostics.Conditional("TRAVE")]
1450 internal void DebugDump() {
1451 GlobalLog.Print("SecureCredential #"+GetHashCode());
1452 GlobalLog.Print(" version = " + version);
1453 GlobalLog.Print(" cCreds = " + cCreds);
1454 GlobalLog.Print(" certContextArray = " + String.Format("0x{0:x}", certContextArray));
1455 GlobalLog.Print(" rootStore = " + String.Format("0x{0:x}", rootStore));
1456 GlobalLog.Print(" cMappers = " + cMappers);
1457 GlobalLog.Print(" phMappers = " + String.Format("0x{0:x}", phMappers));
1458 GlobalLog.Print(" cSupportedAlgs = " + cSupportedAlgs);
1459 GlobalLog.Print(" palgSupportedAlgs = " + String.Format("0x{0:x}", palgSupportedAlgs));
1460 GlobalLog.Print(" grbitEnabledProtocols = " + String.Format("0x{0:x}", grbitEnabledProtocols));
1461 GlobalLog.Print(" dwMinimumCipherStrength = " + dwMinimumCipherStrength);
1462 GlobalLog.Print(" dwMaximumCipherStrength = " + dwMaximumCipherStrength);
1463 GlobalLog.Print(" dwSessionLifespan = " + String.Format("0x{0:x}", dwSessionLifespan));
1464 GlobalLog.Print(" dwFlags = " + String.Format("0x{0:x}", dwFlags));
1465 GlobalLog.Print(" reserved = " + String.Format("0x{0:x}", reserved));
1468 } // SecureCredential
1470 #endif // !FEATURE_PAL
1472 [StructLayout(LayoutKind.Sequential)]
1473 internal unsafe struct SecurityBufferStruct {
1475 public BufferType type;
1476 public IntPtr token;
1478 public static readonly int Size = sizeof(SecurityBufferStruct);
1481 internal class SecurityBuffer {
1483 public BufferType type;
1484 public byte[] token;
1485 public SafeHandle unmanagedToken;
1488 public SecurityBuffer(byte[] data, int offset, int size, BufferType tokentype) {
1489 GlobalLog.Assert(offset >= 0 && offset <= (data == null ? 0 : data.Length), "SecurityBuffer::.ctor", "'offset' out of range. [" + offset + "]");
1490 GlobalLog.Assert(size >= 0 && size <= (data == null ? 0 : data.Length - offset), "SecurityBuffer::.ctor", "'size' out of range. [" + size + "]");
1492 this.offset = data == null || offset < 0 ? 0 : Math.Min(offset, data.Length);
1493 this.size = data == null || size < 0 ? 0 : Math.Min(size, data.Length - this.offset);
1494 this.type = tokentype;
1495 this.token = size == 0 ? null : data;
1498 public SecurityBuffer(byte[] data, BufferType tokentype) {
1499 this.size = data == null ? 0 : data.Length;
1500 this.type = tokentype;
1501 this.token = size == 0 ? null : data;
1504 public SecurityBuffer(int size, BufferType tokentype) {
1505 GlobalLog.Assert(size >= 0, "SecurityBuffer::.ctor", "'size' out of range. [" + size.ToString(NumberFormatInfo.InvariantInfo) + "]");
1508 this.type = tokentype;
1509 this.token = size == 0 ? null : new byte[size];
1512 public SecurityBuffer(ChannelBinding binding) {
1513 this.size = (binding == null ? 0 : binding.Size);
1514 this.type = BufferType.ChannelBindings;
1515 this.unmanagedToken = binding;
1519 [StructLayout(LayoutKind.Sequential)]
1520 internal unsafe class SecurityBufferDescriptor {
1522 typedef struct _SecBufferDesc {
1525 PSecBuffer pBuffers;
1526 } SecBufferDesc, * PSecBufferDesc;
1528 public readonly int Version;
1529 public readonly int Count;
1530 public void* UnmanagedPointer;
1532 public SecurityBufferDescriptor(int count) {
1535 UnmanagedPointer = null;
1538 [System.Diagnostics.Conditional("TRAVE")]
1539 internal void DebugDump() {
1540 GlobalLog.Print("SecurityBufferDescriptor #" + ValidationHelper.HashString(this));
1541 GlobalLog.Print(" version = " + Version);
1542 GlobalLog.Print(" count = " + Count);
1543 GlobalLog.Print(" securityBufferArray = 0x" + (new IntPtr(UnmanagedPointer)).ToString("x"));
1545 } // SecurityBufferDescriptor
1547 internal enum CertificateEncoding {
1549 X509AsnEncoding = unchecked((int)0x00000001),
1550 X509NdrEncoding = unchecked((int)0x00000002),
1551 Pkcs7AsnEncoding = unchecked((int)0x00010000),
1552 Pkcs7NdrEncoding = unchecked((int)0x00020000),
1553 AnyAsnEncoding = X509AsnEncoding|Pkcs7AsnEncoding
1556 internal enum CertificateProblem {
1558 TrustNOSIGNATURE = unchecked((int)0x800B0100),
1559 CertEXPIRED = unchecked((int)0x800B0101),
1560 CertVALIDITYPERIODNESTING = unchecked((int)0x800B0102),
1561 CertROLE = unchecked((int)0x800B0103),
1562 CertPATHLENCONST = unchecked((int)0x800B0104),
1563 CertCRITICAL = unchecked((int)0x800B0105),
1564 CertPURPOSE = unchecked((int)0x800B0106),
1565 CertISSUERCHAINING = unchecked((int)0x800B0107),
1566 CertMALFORMED = unchecked((int)0x800B0108),
1567 CertUNTRUSTEDROOT = unchecked((int)0x800B0109),
1568 CertCHAINING = unchecked((int)0x800B010A),
1569 CertREVOKED = unchecked((int)0x800B010C),
1570 CertUNTRUSTEDTESTROOT = unchecked((int)0x800B010D),
1571 CertREVOCATION_FAILURE = unchecked((int)0x800B010E),
1572 CertCN_NO_MATCH = unchecked((int)0x800B010F),
1573 CertWRONG_USAGE = unchecked((int)0x800B0110),
1574 TrustEXPLICITDISTRUST = unchecked((int)0x800B0111),
1575 CertUNTRUSTEDCA = unchecked((int)0x800B0112),
1576 CertINVALIDPOLICY = unchecked((int)0x800B0113),
1577 CertINVALIDNAME = unchecked((int)0x800B0114),
1579 CryptNOREVOCATIONCHECK = unchecked((int)0x80092012),
1580 CryptREVOCATIONOFFLINE = unchecked((int)0x80092013),
1582 TrustSYSTEMERROR = unchecked((int)0x80096001),
1583 TrustNOSIGNERCERT = unchecked((int)0x80096002),
1584 TrustCOUNTERSIGNER = unchecked((int)0x80096003),
1585 TrustCERTSIGNATURE = unchecked((int)0x80096004),
1586 TrustTIMESTAMP = unchecked((int)0x80096005),
1587 TrustBADDIGEST = unchecked((int)0x80096010),
1588 TrustBASICCONSTRAINTS = unchecked((int)0x80096019),
1589 TrustFINANCIALCRITERIA = unchecked((int)0x8009601E),
1592 [StructLayout(LayoutKind.Sequential)]
1593 internal class SecChannelBindings
1595 internal int dwInitiatorAddrType;
1596 internal int cbInitiatorLength;
1597 internal int dwInitiatorOffset;
1599 internal int dwAcceptorAddrType;
1600 internal int cbAcceptorLength;
1601 internal int dwAcceptorOffset;
1603 internal int cbApplicationDataLength;
1604 internal int dwApplicationDataOffset;
1608 // WebRequestPrefixElement
1610 // This is an element of the prefix list. It contains the prefix and the
1611 // interface to be called to create a request for that prefix.
1615 /// <para>[To be supplied.]</para>
1617 // internal class WebRequestPrefixElement {
1618 internal class WebRequestPrefixElement {
1621 /// <para>[To be supplied.]</para>
1623 public string Prefix;
1625 /// <para>[To be supplied.]</para>
1627 internal IWebRequestCreate creator;
1629 /// <para>[To be supplied.]</para>
1631 internal Type creatorType;
1633 public IWebRequestCreate Creator {
1635 if (creator == null && creatorType != null) {
1637 if (creator == null) {
1638 creator = (IWebRequestCreate)Activator.CreateInstance(
1640 BindingFlags.CreateInstance
1641 | BindingFlags.Instance
1642 | BindingFlags.NonPublic
1643 | BindingFlags.Public,
1645 new object[0], // no arguments
1646 CultureInfo.InvariantCulture
1660 public WebRequestPrefixElement(string P, Type creatorType) {
1661 // verify that its of the proper type of IWebRequestCreate
1662 if (!typeof(IWebRequestCreate).IsAssignableFrom(creatorType))
1664 throw new InvalidCastException(SR.GetString(SR.net_invalid_cast,
1665 creatorType.AssemblyQualifiedName,
1666 "IWebRequestCreate"));
1670 this.creatorType = creatorType;
1674 /// <para>[To be supplied.]</para>
1676 public WebRequestPrefixElement(string P, IWebRequestCreate C) {
1681 } // class PrefixListElement
1684 #if MONO_FEATURE_WEB_STACK
1687 // HttpRequestCreator.
1689 // This is the class that we use to create HTTP and HTTPS requests.
1692 internal class HttpRequestCreator : IWebRequestCreate {
1696 Create - Create an HttpWebRequest.
1698 This is our method to create an HttpWebRequest. We register
1699 for HTTP and HTTPS Uris, and this method is called when a request
1700 needs to be created for one of those.
1704 Uri - Uri for request being created.
1707 The newly created HttpWebRequest.
1711 public WebRequest Create( Uri Uri ) {
1713 // Note, DNS permissions check will not happen on WebRequest
1715 return new HttpWebRequest(Uri, null);
1718 } // class HttpRequestCreator
1721 // WebSocketHttpRequestCreator.
1723 // This is the class that we use to create WebSocket connection requests.
1726 internal class WebSocketHttpRequestCreator : IWebRequestCreate
1728 private string m_httpScheme;
1730 // This ctor is used to create a WebSocketHttpRequestCreator.
1731 // We will do a URI change to update the scheme with Http or Https scheme. The usingHttps boolean is
1732 // used to indicate whether the created HttpWebRequest should take the https scheme or not.
1733 public WebSocketHttpRequestCreator(bool usingHttps)
1735 m_httpScheme = usingHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
1740 Create - Create an HttpWebRequest.
1742 This is our method to create an HttpWebRequest for WebSocket connection. We register
1743 We will register it for custom Uri prefixes. And this method is called when a request
1744 needs to be created for one of those. The created HttpWebRequest will still be with Http or Https
1745 scheme, depending on the m_httpScheme field of this object.
1749 Uri - Uri for request being created.
1752 The newly created HttpWebRequest for WebSocket connection.
1756 public WebRequest Create(Uri Uri)
1758 UriBuilder uriBuilder = new UriBuilder(Uri);
1759 uriBuilder.Scheme = m_httpScheme;
1760 HttpWebRequest request = new HttpWebRequest(uriBuilder.Uri, null, true, "WebSocket" + Guid.NewGuid());
1761 WebSocketHelpers.PrepareWebRequest(ref request);
1765 } // class WebSocketHttpRequestCreator
1771 // CoreResponseData - Used to store result of HTTP header parsing and
1772 // response parsing. Also Contains new stream to use, and
1773 // is used as core of new Response
1775 internal class CoreResponseData {
1777 // Status Line Response Values
1778 public HttpStatusCode m_StatusCode;
1779 public string m_StatusDescription;
1780 public bool m_IsVersionHttp11;
1782 // Content Length needed for semantics, -1 if chunked
1783 public long m_ContentLength;
1786 public WebHeaderCollection m_ResponseHeaders;
1788 // ConnectStream - for reading actual data
1789 public Stream m_ConnectStream;
1791 internal CoreResponseData Clone() {
1792 CoreResponseData cloneResponseData = new CoreResponseData();
1793 cloneResponseData.m_StatusCode = m_StatusCode;
1794 cloneResponseData.m_StatusDescription = m_StatusDescription;
1795 cloneResponseData.m_IsVersionHttp11 = m_IsVersionHttp11;
1796 cloneResponseData.m_ContentLength = m_ContentLength;
1797 cloneResponseData.m_ResponseHeaders = m_ResponseHeaders;
1798 cloneResponseData.m_ConnectStream = m_ConnectStream;
1799 return cloneResponseData;
1806 internal delegate bool HttpAbortDelegate(HttpWebRequest request, WebException webException);
1809 // this class contains known header names
1812 internal static class HttpKnownHeaderNames {
1814 public const string CacheControl = "Cache-Control";
1815 public const string Connection = "Connection";
1816 public const string Date = "Date";
1817 public const string KeepAlive = "Keep-Alive";
1818 public const string Pragma = "Pragma";
1819 public const string ProxyConnection = "Proxy-Connection";
1820 public const string Trailer = "Trailer";
1821 public const string TransferEncoding = "Transfer-Encoding";
1822 public const string Upgrade = "Upgrade";
1823 public const string Via = "Via";
1824 public const string Warning = "Warning";
1825 public const string ContentLength = "Content-Length";
1826 public const string ContentType = "Content-Type";
1827 public const string ContentDisposition = "Content-Disposition";
1828 public const string ContentEncoding = "Content-Encoding";
1829 public const string ContentLanguage = "Content-Language";
1830 public const string ContentLocation = "Content-Location";
1831 public const string ContentRange = "Content-Range";
1832 public const string Expires = "Expires";
1833 public const string LastModified = "Last-Modified";
1834 public const string Age = "Age";
1835 public const string Location = "Location";
1836 public const string ProxyAuthenticate = "Proxy-Authenticate";
1837 public const string RetryAfter = "Retry-After";
1838 public const string Server = "Server";
1839 public const string SetCookie = "Set-Cookie";
1840 public const string SetCookie2 = "Set-Cookie2";
1841 public const string Vary = "Vary";
1842 public const string WWWAuthenticate = "WWW-Authenticate";
1843 public const string Accept = "Accept";
1844 public const string AcceptCharset = "Accept-Charset";
1845 public const string AcceptEncoding = "Accept-Encoding";
1846 public const string AcceptLanguage = "Accept-Language";
1847 public const string Authorization = "Authorization";
1848 public const string Cookie = "Cookie";
1849 public const string Cookie2 = "Cookie2";
1850 public const string Expect = "Expect";
1851 public const string From = "From";
1852 public const string Host = "Host";
1853 public const string IfMatch = "If-Match";
1854 public const string IfModifiedSince = "If-Modified-Since";
1855 public const string IfNoneMatch = "If-None-Match";
1856 public const string IfRange = "If-Range";
1857 public const string IfUnmodifiedSince = "If-Unmodified-Since";
1858 public const string MaxForwards = "Max-Forwards";
1859 public const string ProxyAuthorization = "Proxy-Authorization";
1860 public const string Referer = "Referer";
1861 public const string Range = "Range";
1862 public const string UserAgent = "User-Agent";
1863 public const string ContentMD5 = "Content-MD5";
1864 public const string ETag = "ETag";
1865 public const string TE = "TE";
1866 public const string Allow = "Allow";
1867 public const string AcceptRanges = "Accept-Ranges";
1868 public const string P3P = "P3P";
1869 public const string XPoweredBy = "X-Powered-By";
1870 public const string XAspNetVersion = "X-AspNet-Version";
1871 public const string SecWebSocketKey = "Sec-WebSocket-Key";
1872 public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
1873 public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
1874 public const string Origin = "Origin";
1875 public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
1876 public const string SecWebSocketVersion = "Sec-WebSocket-Version";
1881 /// Represents the method that will notify callers when a continue has been
1882 /// received by the client.
1885 // Delegate type for us to notify callers when we receive a continue
1886 public delegate void HttpContinueDelegate(int StatusCode, WebHeaderCollection httpHeaders);
1889 // HttpWriteMode - used to control the way in which an entity Body is posted.
1891 enum HttpWriteMode {
1899 // Used by Request to notify Connection that we are no longer holding the Connection (for NTLM connection sharing)
1900 delegate void UnlockConnectionDelegate();
1902 enum HttpBehaviour : byte {
1905 HTTP11PartiallyCompliant = 2,
1909 internal enum HttpProcessingResult {
1916 // HttpVerb - used to define various per Verb Properties
1920 // Note - this is a place holder for Verb properties,
1921 // the following two bools can most likely be combined into
1922 // a single Enum type. And the Verb can be incorporated.
1924 class KnownHttpVerb {
1925 internal string Name; // verb name
1927 internal bool RequireContentBody; // require content body to be sent
1928 internal bool ContentBodyNotAllowed; // not allowed to send content body
1929 internal bool ConnectRequest; // special semantics for a connect request
1930 internal bool ExpectNoContentResponse; // response will not have content body
1932 internal KnownHttpVerb(string name, bool requireContentBody, bool contentBodyNotAllowed, bool connectRequest, bool expectNoContentResponse) {
1934 RequireContentBody = requireContentBody;
1935 ContentBodyNotAllowed = contentBodyNotAllowed;
1936 ConnectRequest = connectRequest;
1937 ExpectNoContentResponse = expectNoContentResponse;
1940 // Force an an init, before we use them
1941 private static ListDictionary NamedHeaders;
1944 internal static KnownHttpVerb Get;
1945 internal static KnownHttpVerb Connect;
1946 internal static KnownHttpVerb Head;
1947 internal static KnownHttpVerb Put;
1948 internal static KnownHttpVerb Post;
1949 internal static KnownHttpVerb MkCol;
1952 // InitializeKnownVerbs - Does basic init for this object,
1953 // such as creating defaultings and filling them
1955 static KnownHttpVerb() {
1956 NamedHeaders = new ListDictionary(CaseInsensitiveAscii.StaticInstance);
1957 Get = new KnownHttpVerb("GET", false, true, false, false);
1958 Connect = new KnownHttpVerb("CONNECT", false, true, true, false);
1959 Head = new KnownHttpVerb("HEAD", false, true, false, true);
1960 Put = new KnownHttpVerb("PUT", true, false, false, false);
1961 Post = new KnownHttpVerb("POST", true, false, false, false);
1962 MkCol = new KnownHttpVerb("MKCOL",false,false,false,false);
1963 NamedHeaders[Get.Name] = Get;
1964 NamedHeaders[Connect.Name] = Connect;
1965 NamedHeaders[Head.Name] = Head;
1966 NamedHeaders[Put.Name] = Put;
1967 NamedHeaders[Post.Name] = Post;
1968 NamedHeaders[MkCol.Name] = MkCol;
1971 public bool Equals(KnownHttpVerb verb) {
1972 return this==verb || string.Compare(Name, verb.Name, StringComparison.OrdinalIgnoreCase)==0;
1975 public static KnownHttpVerb Parse(string name) {
1976 KnownHttpVerb knownHttpVerb = NamedHeaders[name] as KnownHttpVerb;
1977 if (knownHttpVerb==null) {
1978 // unknown verb, default behaviour
1979 knownHttpVerb = new KnownHttpVerb(name, false, false, false, false);
1981 return knownHttpVerb;
1986 // HttpProtocolUtils - A collection of utility functions for HTTP usage.
1989 internal class HttpProtocolUtils {
1991 private HttpProtocolUtils() {
1995 // extra buffers for build/parsing, recv/send HTTP data,
1996 // at some point we should consolidate
2000 // parse String to DateTime format.
2001 internal static DateTime string2date(String S) {
2003 if (HttpDateParse.ParseHttpDate(S,out dtOut)) {
2007 throw new ProtocolViolationException(SR.GetString(SR.net_baddate));
2012 // convert Date to String using RFC 1123 pattern
2013 internal static string date2string(DateTime D) {
2014 DateTimeFormatInfo dateFormat = new DateTimeFormatInfo();
2015 return D.ToUniversalTime().ToString("R", dateFormat);
2020 // Proxy class for linking between ICertificatePolicy <--> ICertificateDecider
2021 internal class PolicyWrapper {
2022 private const uint IgnoreUnmatchedCN = 0x00001000;
2023 private ICertificatePolicy fwdPolicy;
2024 private ServicePoint srvPoint;
2025 private WebRequest request;
2027 internal PolicyWrapper(ICertificatePolicy policy, ServicePoint sp, WebRequest wr) {
2028 this.fwdPolicy = policy;
2033 public bool Accept(X509Certificate Certificate, int CertificateProblem) {
2034 return fwdPolicy.CheckValidationResult(srvPoint, Certificate, request, CertificateProblem);
2037 internal static uint VerifyChainPolicy(SafeFreeCertChain chainContext, ref ChainPolicyParameter cpp) {
2038 GlobalLog.Enter("PolicyWrapper::VerifyChainPolicy", "chainContext="+ chainContext + ", options="+String.Format("0x{0:x}", cpp.dwFlags));
2039 ChainPolicyStatus status = new ChainPolicyStatus();
2040 status.cbSize = ChainPolicyStatus.StructSize;
2042 UnsafeNclNativeMethods.NativePKI.CertVerifyCertificateChainPolicy(
2043 (IntPtr) ChainPolicyType.SSL,
2048 GlobalLog.Print("PolicyWrapper::VerifyChainPolicy() CertVerifyCertificateChainPolicy returned: " + errorCode);
2050 GlobalLog.Print("PolicyWrapper::VerifyChainPolicy() error code: " + status.dwError+String.Format(" [0x{0:x8}", status.dwError) + " " + SecureChannel.MapSecurityStatus(status.dwError) + "]");
2052 GlobalLog.Leave("PolicyWrapper::VerifyChainPolicy", status.dwError.ToString());
2053 return status.dwError;
2056 private static IgnoreCertProblem MapErrorCode(uint errorCode) {
2057 switch ((CertificateProblem) errorCode) {
2059 case CertificateProblem.CertINVALIDNAME :
2060 case CertificateProblem.CertCN_NO_MATCH :
2061 return IgnoreCertProblem.invalid_name;
2063 case CertificateProblem.CertINVALIDPOLICY :
2064 case CertificateProblem.CertPURPOSE :
2065 return IgnoreCertProblem.invalid_policy;
2067 case CertificateProblem.CertEXPIRED :
2068 return IgnoreCertProblem.not_time_valid | IgnoreCertProblem.ctl_not_time_valid;
2070 case CertificateProblem.CertVALIDITYPERIODNESTING :
2071 return IgnoreCertProblem.not_time_nested;
2073 case CertificateProblem.CertCHAINING :
2074 case CertificateProblem.CertUNTRUSTEDCA :
2075 case CertificateProblem.CertUNTRUSTEDROOT :
2076 return IgnoreCertProblem.allow_unknown_ca;
2078 case CertificateProblem.CertREVOKED :
2079 case CertificateProblem.CertREVOCATION_FAILURE :
2080 case CertificateProblem.CryptNOREVOCATIONCHECK:
2081 case CertificateProblem.CryptREVOCATIONOFFLINE:
2082 return IgnoreCertProblem.all_rev_unknown;
2084 case CertificateProblem.CertROLE:
2085 case CertificateProblem.TrustBASICCONSTRAINTS:
2086 return IgnoreCertProblem.invalid_basic_constraints;
2088 case CertificateProblem.CertWRONG_USAGE :
2089 return IgnoreCertProblem.wrong_usage;
2097 private uint[] GetChainErrors(string hostName, X509Chain chain, ref bool fatalError)
2100 SafeFreeCertChain chainContext= new SafeFreeCertChain(chain.ChainContext);
2101 ArrayList certificateProblems = new ArrayList();
2104 ChainPolicyParameter cppStruct = new ChainPolicyParameter();
2105 cppStruct.cbSize = ChainPolicyParameter.StructSize;
2106 cppStruct.dwFlags = 0;
2108 SSL_EXTRA_CERT_CHAIN_POLICY_PARA eppStruct = new SSL_EXTRA_CERT_CHAIN_POLICY_PARA(false);
2109 cppStruct.pvExtraPolicyPara = &eppStruct;
2111 fixed (char* namePtr = hostName) {
2112 if (ServicePointManager.CheckCertificateName){
2113 eppStruct.pwszServerName = namePtr;
2117 status = VerifyChainPolicy(chainContext, ref cppStruct);
2118 uint ignoreErrorMask = (uint)MapErrorCode(status);
2120 certificateProblems.Add(status);
2122 if (status == 0) { // No more problems with the certificate?
2123 break; // Then break out of the callback loop
2126 if (ignoreErrorMask == 0) { // Unrecognized error encountered
2131 cppStruct.dwFlags |= ignoreErrorMask;
2132 if ((CertificateProblem)status == CertificateProblem.CertCN_NO_MATCH && ServicePointManager.CheckCertificateName) {
2133 eppStruct.fdwChecks = IgnoreUnmatchedCN;
2140 return (uint[]) certificateProblems.ToArray(typeof(uint));
2143 internal bool CheckErrors(string hostName, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
2145 if (sslPolicyErrors == 0)
2146 return Accept(certificate, 0);
2148 if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0)
2149 return Accept(certificate, (int) CertificateProblem.CertCRITICAL); // ToDO, Define an appropriate enum entry
2151 if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0 ||
2152 (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0)
2154 bool fatalError = false;
2155 uint[] certificateProblems = GetChainErrors(hostName, chain, ref fatalError);
2158 // By today design we cannot allow decider to ignore a fatal error.
2159 // This error is fatal.
2160 Accept(certificate, (int) SecurityStatus.InternalError);
2165 if (certificateProblems.Length == 0)
2166 return Accept(certificate, (int) CertificateProblem.OK);
2168 // Run each error through Accept().
2169 foreach (uint error in certificateProblems)
2170 if (!Accept(certificate, (int) error))
2177 /* CONSIDER: Use this code when we switch to managed X509 API
2178 internal static int MapStatusToWin32Error(X509ChainStatusFlags status)
2182 case X509ChainStatusFlags.NoError: return CertificateProblem.OK;
2183 case X509ChainStatusFlags.NotTimeValid: return CertificateProblem.CertEXPIRED;
2184 case X509ChainStatusFlags.NotTimeNested: return CertificateProblem.CertVALIDITYPERIODNESTING;
2185 case X509ChainStatusFlags.Revoked: return CertificateProblem.CertREVOKED;
2186 case X509ChainStatusFlags.NotSignatureValid:return CertificateProblem.TrustCERTSIGNATURE;
2187 case X509ChainStatusFlags.NotValidForUsage: return CertificateProblem.CertWRONG_USAGE;
2188 case X509ChainStatusFlags.UntrustedRoot: return CertificateProblem.CertUNTRUSTEDROOT;
2189 case X509ChainStatusFlags.RevocationStatusUnknown: return CertificateProblem.CryptNOREVOCATIONCHECK;
2190 case X509ChainStatusFlags.Cyclic: return CertificateProblem.CertCHAINING; //??
2191 case X509ChainStatusFlags.InvalidExtension: return CertificateProblem.CertCRITICAL; //??
2192 case X509ChainStatusFlags.InvalidPolicyConstraints: return CertificateProblem.CertINVALIDPOLICY;
2193 case X509ChainStatusFlags.InvalidBasicConstraints: return CertificateProblem.TrustBASICCONSTRAINTS;
2194 case X509ChainStatusFlagsInvalidNameConstraints: return CertificateProblem.CertINVALIDNAME;
2195 case X509ChainStatusFlags.HasNotSupportedNameConstraint: return CertificateProblem.CertINVALIDNAME; //??
2196 case X509ChainStatusFlags.HasNotDefinedNameConstraint: return CertificateProblem.CertINVALIDNAME; //??
2197 case X509ChainStatusFlags.HasNotPermittedNameConstraint: return CertificateProblem.CertINVALIDNAME; //??
2198 case X509ChainStatusFlags.HasExcludedNameConstraint: return CertificateProblem.CertINVALIDNAME; //??
2199 case X509ChainStatusFlags.PartialChain: return CertificateProblem.CertCHAINING; //??
2200 case X509ChainStatusFlags.CtlNotTimeValid: return CertificateProblem.CertEXPIRED;
2201 case X509ChainStatusFlags.CtlNotSignatureValid: return CertificateProblem.TrustCERTSIGNATURE;
2202 case X509ChainStatusFlags.CtlNotValidForUsage: return CertificateProblem.CertWRONG_USAGE;
2203 case X509ChainStatusFlags.OfflineRevocation: return CertificateProblem.CryptREVOCATIONOFFLINE;
2204 case X509ChainStatusFlags.NoIssuanceChainPolicy:return CertificateProblem.CertINVALIDPOLICY;
2205 default: return (int) CertificateProblem.TrustSYSTEMERROR; // unknown
2210 // Class implementing default certificate policy
2211 internal class DefaultCertPolicy : ICertificatePolicy {
2212 public bool CheckValidationResult(ServicePoint sp, X509Certificate cert, WebRequest request, int problem) {
2213 return problem == (int)CertificateProblem.OK;
2216 #endif // !FEATURE_PAL
2218 internal enum TriState {
2224 internal enum DefaultPorts {
2225 DEFAULT_FTP_PORT = 21,
2226 DEFAULT_GOPHER_PORT = 70,
2227 DEFAULT_HTTP_PORT = 80,
2228 DEFAULT_HTTPS_PORT = 443,
2229 DEFAULT_NNTP_PORT = 119,
2230 DEFAULT_SMTP_PORT = 25,
2231 DEFAULT_TELNET_PORT = 23
2234 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
2235 internal struct hostent {
2236 public IntPtr h_name;
2237 public IntPtr h_aliases;
2238 public short h_addrtype;
2239 public short h_length;
2240 public IntPtr h_addr_list;
2244 [StructLayout(LayoutKind.Sequential)]
2245 internal struct Blob {
2247 public int pBlobData;
2251 // This is only for internal code path i.e. TLS stream.
2252 // See comments on GetNextBuffer() method below.
2254 internal class SplitWritesState
2256 private const int c_SplitEncryptedBuffersSize = 64*1024;
2257 private BufferOffsetSize[] _UserBuffers;
2259 private int _LastBufferConsumed;
2260 private BufferOffsetSize[] _RealBuffers;
2263 internal SplitWritesState(BufferOffsetSize[] buffers)
2265 _UserBuffers = buffers;
2266 _LastBufferConsumed = 0;
2268 _RealBuffers = null;
2271 // Everything was handled
2273 internal bool IsDone {
2275 if (_LastBufferConsumed != 0)
2278 for (int index = _Index ;index < _UserBuffers.Length; ++index)
2279 if (_UserBuffers[index].Size != 0)
2285 // Encryption takes CPU and if the input is large (like 10 mb) then a delay may
2286 // be 30 sec or so. Hence split the ecnrypt and write operations in smaller chunks
2287 // up to c_SplitEncryptedBuffersSize total.
2288 // Note that upon return from here EncryptBuffers() may additonally split the input
2289 // into chunks each <= chkSecureChannel.MaxDataSize (~16k) yet it will complete them all as a single IO.
2291 // Returns null if done, returns the _buffers reference if everything is handled in one shot (also done)
2293 // Otheriwse returns subsequent BufferOffsetSize[] to encrypt and pass to base IO method
2295 internal BufferOffsetSize[] GetNextBuffers()
2297 int curIndex = _Index;
2298 int currentTotalSize = 0;
2299 int lastChunkSize = 0;
2301 int firstBufferConsumed = _LastBufferConsumed;
2303 for ( ;_Index < _UserBuffers.Length; ++_Index)
2305 lastChunkSize = _UserBuffers[_Index].Size-_LastBufferConsumed;
2307 currentTotalSize += lastChunkSize;
2309 if (currentTotalSize > c_SplitEncryptedBuffersSize)
2311 lastChunkSize -= (currentTotalSize - c_SplitEncryptedBuffersSize);
2312 currentTotalSize = c_SplitEncryptedBuffersSize;
2317 _LastBufferConsumed = 0;
2320 // Are we done done?
2321 if (currentTotalSize == 0)
2324 // Do all buffers fit the limit?
2325 if (firstBufferConsumed == 0 && curIndex == 0 && _Index == _UserBuffers.Length)
2326 return _UserBuffers;
2328 // We do have something to split and send out
2329 int buffersCount = lastChunkSize == 0? _Index-curIndex: _Index-curIndex+1;
2331 if (_RealBuffers == null || _RealBuffers.Length != buffersCount)
2332 _RealBuffers = new BufferOffsetSize[buffersCount];
2335 for (; curIndex < _Index; ++curIndex)
2337 _RealBuffers[j++] = new BufferOffsetSize(_UserBuffers[curIndex].Buffer, _UserBuffers[curIndex].Offset + firstBufferConsumed, _UserBuffers[curIndex].Size-firstBufferConsumed, false);
2338 firstBufferConsumed = 0;
2341 if (lastChunkSize != 0)
2343 _RealBuffers[j] = new BufferOffsetSize(_UserBuffers[curIndex].Buffer, _UserBuffers[curIndex].Offset + _LastBufferConsumed, lastChunkSize, false);
2344 if ((_LastBufferConsumed += lastChunkSize) == _UserBuffers[_Index].Size)
2347 _LastBufferConsumed = 0;
2351 return _RealBuffers;