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 return Dns.GetHostByName (Dns.GetHostName ());
315 // IPv6 Changes: If IPv6 is enabled, we can't simply use the
316 // old IPv4 gethostbyname(null). Instead we need
317 // to do a more complete lookup.
319 if (Socket.SupportsIPv6)
322 // IPv6 enabled: use getaddrinfo() of the local host name
323 // to obtain this information. Need to get the machines
324 // name as well - do that here so that we don't need to
325 // Assert DNS permissions.
327 StringBuilder hostname = new StringBuilder(HostNameBufferLength);
328 SocketError errorCode =
329 UnsafeNclNativeMethods.OSSOCK.gethostname(
331 HostNameBufferLength);
333 if (errorCode != SocketError.Success)
335 throw new SocketException();
338 return Dns.GetHostByName(hostname.ToString());
343 // IPv6 disabled: use gethostbyname() to obtain information.
345 IntPtr nativePointer =
346 UnsafeNclNativeMethods.OSSOCK.gethostbyname(
349 if (nativePointer == IntPtr.Zero)
351 throw new SocketException();
354 return Dns.NativeToHostEntry(nativePointer);
360 internal static IPAddress[] LocalAddresses
364 IPAddress[] local = _LocalAddresses;
370 lock (LocalAddressesLock)
372 local = _LocalAddresses;
378 List<IPAddress> localList = new List<IPAddress>();
382 IPHostEntry hostEntry = GetLocalHost();
383 if (hostEntry != null)
385 if (hostEntry.HostName != null)
387 int dot = hostEntry.HostName.IndexOf('.');
390 _LocalDomainName = hostEntry.HostName.Substring(dot);
394 IPAddress[] ipAddresses = hostEntry.AddressList;
395 if (ipAddresses != null)
397 foreach (IPAddress ipAddress in ipAddresses)
399 localList.Add(ipAddress);
408 local = new IPAddress[localList.Count];
410 foreach (IPAddress ipAddress in localList)
412 local[index] = ipAddress;
415 _LocalAddresses = local;
421 #endif // !FEATURE_PAL
423 private static object LocalAddressesLock
427 if (_LocalAddressesLock == null)
429 Interlocked.CompareExchange(ref _LocalAddressesLock, new object(), null);
431 return _LocalAddressesLock;
436 internal static class NclConstants
438 internal static readonly object Sentinel = new object();
439 internal static readonly object[] EmptyObjectArray = new object[0];
440 internal static readonly Uri[] EmptyUriArray = new Uri[0];
442 internal static readonly byte[] CRLF = new byte[] {(byte) '\r', (byte) '\n'};
443 internal static readonly byte[] ChunkTerminator = new byte[] {(byte) '0', (byte) '\r', (byte) '\n', (byte) '\r', (byte) '\n'};
447 // A simple [....] point, useful for deferring work. Just an int value with helper methods.
448 // This is used by HttpWebRequest to syncronize Reads/Writes while waiting for a 100-Continue response.
450 internal struct InterlockedGate
454 // Not currently waiting for a response
455 internal const int Open = 0; // Initial state of gate.
456 // Starting the timer to wait for a response (async)
457 internal const int Triggering = 1; // Gate is being actively held by a thread - indeterminate state.
458 // Waiting for response
459 internal const int Triggered = 2; // The triggering event has occurred.
460 // Stopping the timer (got a response or timed out)
461 internal const int Signaling = 3;
462 // Got a response or timed out, may process the response.
463 internal const int Signaled = 4;
464 // Re/submitting data.
465 internal const int Completed = 5; // The gated event is done.
479 // Only call when all threads are guaranteed to be done with the gate.
480 internal void Reset()
485 // Returns false if the gate is not taken. If exclusive is true, throws if the gate is already triggered.
486 internal bool Trigger(bool exclusive)
488 int gate = Interlocked.CompareExchange(ref m_State, Triggered, Open);
489 if (exclusive && (gate == Triggering || gate == Triggered))
491 GlobalLog.Assert("InterlockedGate::Trigger", "Gate already triggered.");
492 throw new InternalException();
497 // Use StartTrigger() and FinishTrigger() to trigger the gate as a two step operation. This is useful to set up an invariant
498 // that must be ready by the time another thread closes the gate. Do not block between StartTrigger() and FinishTrigger(), just
499 // set up your state to be consistent. If this method returns true, FinishTrigger() *must* be called to avoid deadlock - do
502 // Returns false if the gate is not taken. If exclusive is true, throws if the gate is already triggering/ed.
503 internal bool StartTriggering(bool exclusive)
505 int gate = Interlocked.CompareExchange(ref m_State, Triggering, Open);
506 if (exclusive && (gate == Triggering || gate == Triggered))
508 GlobalLog.Assert("InterlockedGate::StartTriggering", "Gate already triggered.");
509 throw new InternalException();
514 // Gate must be held by StartTriggering().
515 internal void FinishTriggering()
517 int gate = Interlocked.CompareExchange(ref m_State, Triggered, Triggering);
518 if (gate != Triggering)
520 GlobalLog.Assert("InterlockedGate::FinishTriggering", "Gate not Triggering.");
521 throw new InternalException();
525 // Use StartSignaling() and FinishSignaling() to signal the gate as a two step operation. This is useful to
526 // set up an invariant that must be ready by the time another thread closes the gate. Do not block between
527 // StartSignaling() and FinishSignaling(), just set up your state to be consistent. If this method returns
528 // true, FinishSignaling() *must* be called to avoid deadlock - do it in a finally.
530 // Returns false if the gate is not taken. If exclusive is true, throws if the gate is already Signaling/ed.
531 internal bool StartSignaling(bool exclusive)
533 int gate = Interlocked.CompareExchange(ref m_State, Signaling, Triggered);
534 if (exclusive && (gate == Signaling || gate == Signaled)) //
536 GlobalLog.Assert("InterlockedGate::StartTrigger", "Gate already Signaled.");
537 throw new InternalException();
539 Debug.Assert(gate != Triggering, "Still Triggering");
540 return gate == Triggered;
543 // Gate must be held by StartSignaling().
544 internal void FinishSignaling()
546 int gate = Interlocked.CompareExchange(ref m_State, Signaled, Signaling);
547 if (gate != Signaling)
549 GlobalLog.Assert("InterlockedGate::FinishSignaling", "Gate not Signaling; " + gate);
550 throw new InternalException();
554 // Makes sure only one thread completes the opperation.
555 internal bool Complete()
557 int gate = Interlocked.CompareExchange(ref m_State, Completed, Signaled);
558 Debug.Assert(gate != Signaling, "Still Signaling");
559 return (gate == Signaled);
565 // A polling implementation of NetworkAddressChange.
567 internal class NetworkAddressChangePolled : IDisposable
569 private bool disposed;
570 private SafeCloseSocketAndEvent ipv4Socket = null;
571 private SafeCloseSocketAndEvent ipv6Socket = null;
574 internal unsafe NetworkAddressChangePolled()
576 Socket.InitializeSockets();
578 if (Socket.OSSupportsIPv4)
581 ipv4Socket = SafeCloseSocketAndEvent.CreateWSASocketWithEvent(AddressFamily.InterNetwork, SocketType.Dgram, (ProtocolType)0, true, false);
582 UnsafeNclNativeMethods.OSSOCK.ioctlsocket(ipv4Socket, IoctlSocketConstants.FIONBIO, ref blocking);
585 if(Socket.OSSupportsIPv6){
587 ipv6Socket = SafeCloseSocketAndEvent.CreateWSASocketWithEvent(AddressFamily.InterNetworkV6, SocketType.Dgram, (ProtocolType)0, true, false);
588 UnsafeNclNativeMethods.OSSOCK.ioctlsocket(ipv6Socket,IoctlSocketConstants.FIONBIO,ref blocking);
590 Setup(StartIPOptions.Both);
593 private unsafe void Setup(StartIPOptions startIPOptions)
596 SocketError errorCode;
599 if (Socket.OSSupportsIPv4 && (startIPOptions & StartIPOptions.StartIPv4) != 0){
600 errorCode = (SocketError)UnsafeNclNativeMethods.OSSOCK.WSAIoctl_Blocking(
601 ipv4Socket.DangerousGetHandle(),
602 (int) IOControlCode.AddressListChange,
605 SafeNativeOverlapped.Zero, IntPtr.Zero);
607 if (errorCode != SocketError.Success) {
608 NetworkInformationException exception = new NetworkInformationException();
609 if (exception.ErrorCode != (uint)SocketError.WouldBlock) {
615 errorCode = (SocketError)UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(ipv4Socket, ipv4Socket.GetEventHandle().SafeWaitHandle, AsyncEventBits.FdAddressListChange);
616 if (errorCode != SocketError.Success) {
622 if(Socket.OSSupportsIPv6 && (startIPOptions & StartIPOptions.StartIPv6) !=0){
623 errorCode = (SocketError) UnsafeNclNativeMethods.OSSOCK.WSAIoctl_Blocking(
624 ipv6Socket.DangerousGetHandle(),
625 (int) IOControlCode.AddressListChange,
628 SafeNativeOverlapped.Zero, IntPtr.Zero);
630 if (errorCode != SocketError.Success) {
631 NetworkInformationException exception = new NetworkInformationException();
632 if (exception.ErrorCode != (uint)SocketError.WouldBlock) {
638 errorCode = (SocketError)UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(ipv6Socket, ipv6Socket.GetEventHandle().SafeWaitHandle, AsyncEventBits.FdAddressListChange);
639 if (errorCode != SocketError.Success) {
646 internal bool CheckAndReset()
651 StartIPOptions options = StartIPOptions.None;
653 if (ipv4Socket != null && ipv4Socket.GetEventHandle().WaitOne(0, false)){
654 options|= StartIPOptions.StartIPv4;
656 if (ipv6Socket != null && ipv6Socket.GetEventHandle().WaitOne(0, false))
658 options|= StartIPOptions.StartIPv6;
661 if(options != StartIPOptions.None){
671 public void Dispose()
676 if(ipv6Socket != null){
680 if(ipv4Socket != null){
690 #endif // FEATURE_PAL
695 internal static class ComNetOS
697 private const string OSInstallTypeRegKey = @"Software\Microsoft\Windows NT\CurrentVersion";
698 private const string OSInstallTypeRegKeyPath = @"HKEY_LOCAL_MACHINE\" + OSInstallTypeRegKey;
699 private const string OSInstallTypeRegName = "InstallationType";
700 private const string InstallTypeStringClient = "Client";
701 private const string InstallTypeStringServer = "Server";
702 private const string InstallTypeStringServerCore = "Server Core";
703 private const string InstallTypeStringEmbedded = "Embedded";
705 // Minimum support for Windows 2008 is assumed.
706 internal static readonly bool IsAspNetServer; // ie: running under ASP+
707 internal static readonly bool IsWin7orLater; // Is Windows 7 or later
708 internal static readonly bool IsWin7Sp1orLater; // Is Windows 7 Sp1 or later (2008 R2 Sp1+)
709 internal static readonly bool IsWin8orLater; // Is Windows 8 or later
710 internal static readonly WindowsInstallationType InstallationType; // e.g. Client, Server, Server Core
712 // We use it safe so assert
713 [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)]
714 [ResourceExposure(ResourceScope.None)]
715 [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
718 OperatingSystem operatingSystem = Environment.OSVersion;
720 GlobalLog.Print("ComNetOS::.ctor(): " + operatingSystem.ToString());
722 Debug.Assert(operatingSystem.Platform != PlatformID.Win32Windows, "Windows 9x is not supported");
725 // Detect ASP+ as a platform running under NT
730 IsAspNetServer = (Thread.GetDomain().GetData(".appDomain") != null);
734 IsWin7orLater = (operatingSystem.Version >= new Version(6, 1));
736 IsWin7Sp1orLater = (operatingSystem.Version >= new Version(6, 1, 7601));
738 IsWin8orLater = (operatingSystem.Version >= new Version(6, 2));
740 InstallationType = GetWindowsInstallType();
741 if (Logging.On) Logging.PrintInfo(Logging.Web, SR.GetString(SR.net_osinstalltype, InstallationType));
744 [RegistryPermission(SecurityAction.Assert, Read = OSInstallTypeRegKeyPath)]
745 private static WindowsInstallationType GetWindowsInstallType()
749 using (RegistryKey installTypeKey = Registry.LocalMachine.OpenSubKey(OSInstallTypeRegKey))
751 string installType = installTypeKey.GetValue(OSInstallTypeRegName) as string;
753 if (string.IsNullOrEmpty(installType))
755 if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_empty_osinstalltype, OSInstallTypeRegKey + "\\" + OSInstallTypeRegName));
756 return WindowsInstallationType.Unknown;
760 if (String.Compare(installType, InstallTypeStringClient, StringComparison.OrdinalIgnoreCase) == 0)
762 return WindowsInstallationType.Client;
764 if (String.Compare(installType, InstallTypeStringServer, StringComparison.OrdinalIgnoreCase) == 0)
766 return WindowsInstallationType.Server;
768 if (String.Compare(installType, InstallTypeStringServerCore, StringComparison.OrdinalIgnoreCase) == 0)
770 return WindowsInstallationType.ServerCore;
772 if (String.Compare(installType, InstallTypeStringEmbedded, StringComparison.OrdinalIgnoreCase) == 0)
774 return WindowsInstallationType.Embedded;
777 if (Logging.On) Logging.PrintError(Logging.Web, SR.GetString(SR.net_unknown_osinstalltype, installType));
779 // Our default return is unknown when we don't recognize the SKU or if the registry value
780 // doesn't exist. As a result, the SKU-specific checks in System.Net will not limit the set
781 // of functionality available. This allows SKUs we are not aware of to use all of our
782 // functionality. Burden is on them to ensure that all our dependencies are present.
783 // The alternative would be for us to throw an exception here. If we did this, these other
784 // SKUs wouldn't be able to load this code and test their behavior. We would need to update
785 // this code to enable them to run.
786 return WindowsInstallationType.Unknown;
790 catch (UnauthorizedAccessException e)
792 if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_cant_determine_osinstalltype, OSInstallTypeRegKey, e.Message));
793 return WindowsInstallationType.Unknown;
795 catch (SecurityException e)
797 if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_cant_determine_osinstalltype, OSInstallTypeRegKey, e.Message));
798 return WindowsInstallationType.Unknown;
806 // support class for Validation related stuff.
808 internal static class ValidationHelper {
810 public static string [] EmptyArray = new string[0];
812 internal static readonly char[] InvalidMethodChars =
820 // invalid characters that cannot be found in a valid method-verb or http header
821 internal static readonly char[] InvalidParamChars =
846 public static string [] MakeEmptyArrayNull(string [] stringArray) {
847 if ( stringArray == null || stringArray.Length == 0 ) {
854 public static string MakeStringNull(string stringValue) {
855 if ( stringValue == null || stringValue.Length == 0) {
863 // Consider removing.
864 public static string MakeStringEmpty(string stringValue) {
865 if ( stringValue == null || stringValue.Length == 0) {
875 // Consider removing.
876 public static int HashCode(object objectValue) {
877 if (objectValue == null) {
880 return objectValue.GetHashCode();
886 public static string ExceptionMessage(Exception exception) {
887 if (exception==null) {
890 if (exception.InnerException==null) {
891 return exception.Message;
893 return exception.Message + " (" + ExceptionMessage(exception.InnerException) + ")";
896 public static string ToString(object objectValue) {
897 if (objectValue == null) {
899 } else if (objectValue is string && ((string)objectValue).Length==0) {
900 return "(string.empty)";
901 } else if (objectValue is Exception) {
902 return ExceptionMessage(objectValue as Exception);
903 } else if (objectValue is IntPtr) {
904 return "0x" + ((IntPtr)objectValue).ToString("x");
906 return objectValue.ToString();
909 public static string HashString(object objectValue) {
910 if (objectValue == null) {
912 } else if (objectValue is string && ((string)objectValue).Length==0) {
913 return "(string.empty)";
915 return objectValue.GetHashCode().ToString(NumberFormatInfo.InvariantInfo);
919 public static bool IsInvalidHttpString(string stringValue) {
920 return stringValue.IndexOfAny(InvalidParamChars)!=-1;
923 public static bool IsBlankString(string stringValue) {
924 return stringValue==null || stringValue.Length==0;
928 // Consider removing.
929 public static bool ValidateUInt32(long address) {
930 // on false, API should throw new ArgumentOutOfRangeException("address");
931 return address>=0x00000000 && address<=0xFFFFFFFF;
935 public static bool ValidateTcpPort(int port) {
936 // on false, API should throw new ArgumentOutOfRangeException("port");
937 return port>=IPEndPoint.MinPort && port<=IPEndPoint.MaxPort;
940 public static bool ValidateRange(int actual, int fromAllowed, int toAllowed) {
941 // on false, API should throw new ArgumentOutOfRangeException("argument");
942 return actual>=fromAllowed && actual<=toAllowed;
946 // Consider removing.
947 public static bool ValidateRange(long actual, long fromAllowed, long toAllowed) {
948 // on false, API should throw new ArgumentOutOfRangeException("argument");
949 return actual>=fromAllowed && actual<=toAllowed;
953 // There are threading tricks a malicious app can use to create an ArraySegment with mismatched
954 // array/offset/count. Copy locally and make sure they're valid before using them.
955 internal static void ValidateSegment(ArraySegment<byte> segment) {
956 if (segment == null || segment.Array == null) {
957 throw new ArgumentNullException("segment");
959 // Length zero is explicitly allowed
960 if (segment.Offset < 0 || segment.Count < 0
961 || segment.Count > (segment.Array.Length - segment.Offset)) {
962 throw new ArgumentOutOfRangeException("segment");
967 internal static class ExceptionHelper
970 internal static readonly KeyContainerPermission KeyContainerPermissionOpen = new KeyContainerPermission(KeyContainerPermissionFlags.Open);
971 internal static readonly WebPermission WebPermissionUnrestricted = new WebPermission(NetworkAccess.Connect);
972 internal static readonly SecurityPermission UnmanagedPermission = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
973 internal static readonly SocketPermission UnrestrictedSocketPermission = new SocketPermission(PermissionState.Unrestricted);
974 internal static readonly SecurityPermission InfrastructurePermission = new SecurityPermission(SecurityPermissionFlag.Infrastructure);
975 internal static readonly SecurityPermission ControlPolicyPermission = new SecurityPermission(SecurityPermissionFlag.ControlPolicy);
976 internal static readonly SecurityPermission ControlPrincipalPermission = new SecurityPermission(SecurityPermissionFlag.ControlPrincipal);
978 internal static NotImplementedException MethodNotImplementedException {
980 return new NotImplementedException(SR.GetString(SR.net_MethodNotImplementedException));
984 internal static NotImplementedException PropertyNotImplementedException {
986 return new NotImplementedException(SR.GetString(SR.net_PropertyNotImplementedException));
990 internal static NotSupportedException MethodNotSupportedException {
992 return new NotSupportedException(SR.GetString(SR.net_MethodNotSupportedException));
996 internal static NotSupportedException PropertyNotSupportedException {
998 return new NotSupportedException(SR.GetString(SR.net_PropertyNotSupportedException));
1002 internal static WebException IsolatedException {
1004 return new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.KeepAliveFailure),WebExceptionStatus.KeepAliveFailure, WebExceptionInternalStatus.Isolated, null);
1008 internal static WebException RequestAbortedException {
1010 return new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled);
1014 internal static WebException CacheEntryNotFoundException {
1016 return new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.CacheEntryNotFound), WebExceptionStatus.CacheEntryNotFound);
1020 internal static WebException RequestProhibitedByCachePolicyException {
1022 return new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestProhibitedByCachePolicy), WebExceptionStatus.RequestProhibitedByCachePolicy);
1027 internal enum WindowsInstallationType
1036 internal enum SecurityStatus
1038 // Success / Informational
1040 ContinueNeeded = unchecked((int)0x00090312),
1041 CompleteNeeded = unchecked((int)0x00090313),
1042 CompAndContinue = unchecked((int)0x00090314),
1043 ContextExpired = unchecked((int)0x00090317),
1044 CredentialsNeeded = unchecked((int)0x00090320),
1045 Renegotiate = unchecked((int)0x00090321),
1048 OutOfMemory = unchecked((int)0x80090300),
1049 InvalidHandle = unchecked((int)0x80090301),
1050 Unsupported = unchecked((int)0x80090302),
1051 TargetUnknown = unchecked((int)0x80090303),
1052 InternalError = unchecked((int)0x80090304),
1053 PackageNotFound = unchecked((int)0x80090305),
1054 NotOwner = unchecked((int)0x80090306),
1055 CannotInstall = unchecked((int)0x80090307),
1056 InvalidToken = unchecked((int)0x80090308),
1057 CannotPack = unchecked((int)0x80090309),
1058 QopNotSupported = unchecked((int)0x8009030A),
1059 NoImpersonation = unchecked((int)0x8009030B),
1060 LogonDenied = unchecked((int)0x8009030C),
1061 UnknownCredentials = unchecked((int)0x8009030D),
1062 NoCredentials = unchecked((int)0x8009030E),
1063 MessageAltered = unchecked((int)0x8009030F),
1064 OutOfSequence = unchecked((int)0x80090310),
1065 NoAuthenticatingAuthority = unchecked((int)0x80090311),
1066 IncompleteMessage = unchecked((int)0x80090318),
1067 IncompleteCredentials = unchecked((int)0x80090320),
1068 BufferNotEnough = unchecked((int)0x80090321),
1069 WrongPrincipal = unchecked((int)0x80090322),
1070 TimeSkew = unchecked((int)0x80090324),
1071 UntrustedRoot = unchecked((int)0x80090325),
1072 IllegalMessage = unchecked((int)0x80090326),
1073 CertUnknown = unchecked((int)0x80090327),
1074 CertExpired = unchecked((int)0x80090328),
1075 AlgorithmMismatch = unchecked((int)0x80090331),
1076 SecurityQosFailed = unchecked((int)0x80090332),
1077 SmartcardLogonRequired = unchecked((int)0x8009033E),
1078 UnsupportedPreauth = unchecked((int)0x80090343),
1079 BadBinding = unchecked((int)0x80090346)
1082 internal enum ContentTypeValues {
1083 ChangeCipherSpec = 0x14,
1087 Unrecognized = 0xFF,
1090 internal enum ContextAttribute {
1092 // look into <sspi.h> and <schannel.h>
1099 //KeyInfo = 0x05, must not be used, see ConnectionInfo instead
1101 // SECPKG_ATTR_PROTO_INFO = 7,
1102 // SECPKG_ATTR_PASSWORD_EXPIRY = 8,
1103 // SECPKG_ATTR_SESSION_KEY = 9,
1105 // SECPKG_ATTR_USER_FLAGS = 11,
1106 NegotiationInfo = 0x0C,
1107 // SECPKG_ATTR_NATIVE_NAMES = 13,
1108 // SECPKG_ATTR_FLAGS = 14,
1109 // SECPKG_ATTR_USE_VALIDATED = 15,
1110 // SECPKG_ATTR_CREDENTIAL_NAME = 16,
1111 // SECPKG_ATTR_TARGET_INFORMATION = 17,
1112 // SECPKG_ATTR_ACCESS_TOKEN = 18,
1113 // SECPKG_ATTR_TARGET = 19,
1114 // SECPKG_ATTR_AUTHENTICATION_ID = 20,
1115 UniqueBindings = 0x19,
1116 EndpointBindings = 0x1A,
1117 ClientSpecifiedSpn = 0x1B, // SECPKG_ATTR_CLIENT_SPECIFIED_TARGET = 27
1118 RemoteCertificate = 0x53,
1119 LocalCertificate = 0x54,
1121 IssuerListInfoEx = 0x59,
1122 ConnectionInfo = 0x5A,
1123 // SECPKG_ATTR_EAP_KEY_BLOCK 0x5b // returns SecPkgContext_EapKeyBlock
1124 // SECPKG_ATTR_MAPPED_CRED_ATTR 0x5c // returns SecPkgContext_MappedCredAttr
1125 // SECPKG_ATTR_SESSION_INFO 0x5d // returns SecPkgContext_SessionInfo
1126 // SECPKG_ATTR_APP_DATA 0x5e // sets/returns SecPkgContext_SessionAppData
1127 // SECPKG_ATTR_REMOTE_CERTIFICATES 0x5F // returns SecPkgContext_Certificates
1128 // SECPKG_ATTR_CLIENT_CERT_POLICY 0x60 // sets SecPkgCred_ClientCertCtlPolicy
1129 // SECPKG_ATTR_CC_POLICY_RESULT 0x61 // returns SecPkgContext_ClientCertPolicyResult
1130 // SECPKG_ATTR_USE_NCRYPT 0x62 // Sets the CRED_FLAG_USE_NCRYPT_PROVIDER FLAG on cred group
1131 // SECPKG_ATTR_LOCAL_CERT_INFO 0x63 // returns SecPkgContext_CertInfo
1132 // SECPKG_ATTR_CIPHER_INFO 0x64 // returns new CNG SecPkgContext_CipherInfo
1133 // SECPKG_ATTR_EAP_PRF_INFO 0x65 // sets SecPkgContext_EapPrfInfo
1134 // SECPKG_ATTR_SUPPORTED_SIGNATURES 0x66 // returns SecPkgContext_SupportedSignatures
1135 // SECPKG_ATTR_REMOTE_CERT_CHAIN 0x67 // returns PCCERT_CONTEXT
1136 UiInfo = 0x68, // sets SEcPkgContext_UiInfo
1139 internal enum Endianness {
1144 internal enum CredentialUse {
1150 internal enum BufferType {
1159 Padding = 0x09, // non-data padding
1161 ChannelBindings = 0x0E,
1163 ReadOnlyFlag = unchecked((int)0x80000000),
1164 ReadOnlyWithChecksum= 0x10000000
1167 internal enum ChainPolicyType {
1170 Authenticode_TS = 3,
1172 BasicConstraints = 5,
1176 internal enum IgnoreCertProblem {
1177 not_time_valid = 0x00000001,
1178 ctl_not_time_valid = 0x00000002,
1179 not_time_nested = 0x00000004,
1180 invalid_basic_constraints = 0x00000008,
1182 all_not_time_valid =
1184 ctl_not_time_valid |
1187 allow_unknown_ca = 0x00000010,
1188 wrong_usage = 0x00000020,
1189 invalid_name = 0x00000040,
1190 invalid_policy = 0x00000080,
1191 end_rev_unknown = 0x00000100,
1192 ctl_signer_rev_unknown = 0x00000200,
1193 ca_rev_unknown = 0x00000400,
1194 root_rev_unknown = 0x00000800,
1198 ctl_signer_rev_unknown |
1203 ctl_not_time_valid |
1205 invalid_basic_constraints |
1211 ctl_signer_rev_unknown |
1216 internal enum CertUsage {
1217 MatchTypeAnd = 0x00,
1223 [StructLayout(LayoutKind.Sequential)]
1224 internal unsafe struct ChainPolicyParameter {
1226 public uint dwFlags;
1227 public SSL_EXTRA_CERT_CHAIN_POLICY_PARA* pvExtraPolicyPara;
1229 public static readonly uint StructSize = (uint) Marshal.SizeOf(typeof(ChainPolicyParameter));
1232 [StructLayout(LayoutKind.Sequential)]
1233 internal unsafe struct SSL_EXTRA_CERT_CHAIN_POLICY_PARA {
1235 [StructLayout(LayoutKind.Explicit)]
1237 [FieldOffset(0)] internal uint cbStruct; //DWORD
1238 [FieldOffset(0)] internal uint cbSize; //DWORD
1241 internal int dwAuthType; //DWORD
1242 internal uint fdwChecks; //DWORD
1243 internal char* pwszServerName; //WCHAR* // used to check against CN=xxxx
1245 internal SSL_EXTRA_CERT_CHAIN_POLICY_PARA(bool amIServer)
1247 u.cbStruct = StructSize;
1248 u.cbSize = StructSize;
1249 //# define AUTHTYPE_CLIENT 1
1250 //# define AUTHTYPE_SERVER 2
1251 dwAuthType = amIServer? 1: 2;
1253 pwszServerName = null;
1255 static readonly uint StructSize = (uint) Marshal.SizeOf(typeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
1258 [StructLayout(LayoutKind.Sequential)]
1259 internal unsafe struct ChainPolicyStatus {
1261 public uint dwError;
1262 public uint lChainIndex;
1263 public uint lElementIndex;
1264 public void* pvExtraPolicyStatus;
1266 public static readonly uint StructSize = (uint) Marshal.SizeOf(typeof(ChainPolicyStatus));
1269 [StructLayout(LayoutKind.Sequential)]
1270 internal unsafe struct CertEnhKeyUse {
1272 public uint cUsageIdentifier;
1273 public void* rgpszUsageIdentifier;
1276 public override string ToString() {
1277 return "cUsageIdentifier="+cUsageIdentifier.ToString()+ " rgpszUsageIdentifier=" + new IntPtr(rgpszUsageIdentifier).ToString("x");
1282 [StructLayout(LayoutKind.Sequential)]
1283 internal struct CertUsageMatch {
1284 public CertUsage dwType;
1285 public CertEnhKeyUse Usage;
1287 public override string ToString() {
1288 return "dwType="+dwType.ToString()+" "+Usage.ToString();
1293 [StructLayout(LayoutKind.Sequential)]
1294 internal struct ChainParameters {
1296 public CertUsageMatch RequestedUsage;
1297 public CertUsageMatch RequestedIssuancePolicy;
1298 public uint UrlRetrievalTimeout;
1299 public int BoolCheckRevocationFreshnessTime;
1300 public uint RevocationFreshnessTime;
1303 public static readonly uint StructSize = (uint) Marshal.SizeOf(typeof(ChainParameters));
1305 public override string ToString() {
1306 return "cbSize="+cbSize.ToString()+" "+RequestedUsage.ToString();
1311 [StructLayout(LayoutKind.Sequential)]
1312 struct _CERT_CHAIN_ELEMENT
1315 public IntPtr pCertContext;
1316 // Since this structure is allocated by unmanaged code, we can
1317 // omit the fileds below since we don't need to access them
1318 // CERT_TRUST_STATUS TrustStatus;
1319 // IntPtr pRevocationInfo;
1320 // IntPtr pIssuanceUsage;
1321 // IntPtr pApplicationUsage;
1325 //[StructLayout(LayoutKind.Sequential)]
1326 //unsafe struct CryptoBlob {
1327 // // public uint cbData;
1328 // // public byte* pbData;
1329 // public uint dataSize;
1330 // public byte* dataBlob;
1333 // SecPkgContext_IssuerListInfoEx
1334 [StructLayout(LayoutKind.Sequential)]
1335 unsafe struct IssuerListInfoEx {
1336 public SafeHandle aIssuers;
1337 public uint cIssuers;
1339 public unsafe IssuerListInfoEx(SafeHandle handle, byte[] nativeBuffer) {
1341 fixed(byte* voidPtr = nativeBuffer) {
1342 // if this breaks on 64 bit, do the sizeof(IntPtr) trick
1343 cIssuers = *((uint*)(voidPtr + IntPtr.Size));
1349 [StructLayout(LayoutKind.Sequential)]
1350 internal struct SecureCredential {
1353 typedef struct _SCHANNEL_CRED
1355 DWORD dwVersion; // always SCHANNEL_CRED_VERSION
1357 PCCERT_CONTEXT *paCred;
1358 HCERTSTORE hRootStore;
1361 struct _HMAPPER **aphMappers;
1363 DWORD cSupportedAlgs;
1364 ALG_ID * palgSupportedAlgs;
1366 DWORD grbitEnabledProtocols;
1367 DWORD dwMinimumCipherStrength;
1368 DWORD dwMaximumCipherStrength;
1369 DWORD dwSessionLifespan;
1372 } SCHANNEL_CRED, *PSCHANNEL_CRED;
1375 public const int CurrentVersion = 0x4;
1380 // ptr to an array of pointers
1381 // There is a hack done with this field. AcquireCredentialsHandle requires an array of
1382 // certificate handles; we only ever use one. In order to avoid pinning a one element array,
1383 // we copy this value onto the stack, create a pointer on the stack to the copied value,
1384 // and replace this field with the pointer, during the call to AcquireCredentialsHandle.
1385 // Then we fix it up afterwards. Fine as long as all the SSPI credentials are not
1386 // supposed to be threadsafe.
1387 public IntPtr certContextArray;
1389 private readonly IntPtr rootStore; // == always null, OTHERWISE NOT RELIABLE
1390 public int cMappers;
1391 private readonly IntPtr phMappers; // == always null, OTHERWISE NOT RELIABLE
1392 public int cSupportedAlgs;
1393 private readonly IntPtr palgSupportedAlgs; // == always null, OTHERWISE NOT RELIABLE
1394 public SchProtocols grbitEnabledProtocols;
1395 public int dwMinimumCipherStrength;
1396 public int dwMaximumCipherStrength;
1397 public int dwSessionLifespan;
1398 public SecureCredential.Flags dwFlags;
1399 public int reserved;
1404 NoSystemMapper = 0x02,
1406 ValidateManual = 0x08,
1407 NoDefaultCred = 0x10,
1408 ValidateAuto = 0x20,
1409 SendAuxRecord = 0x00200000,
1410 UseStrongCrypto = 0x00400000,
1413 public SecureCredential(int version, X509Certificate certificate, SecureCredential.Flags flags, SchProtocols protocols, EncryptionPolicy policy) {
1414 // default values required for a struct
1415 rootStore = phMappers = palgSupportedAlgs = certContextArray = IntPtr.Zero;
1416 cCreds = cMappers = cSupportedAlgs = 0;
1418 if (policy == EncryptionPolicy.RequireEncryption) {
1419 // Prohibit null encryption cipher
1420 dwMinimumCipherStrength = 0;
1421 dwMaximumCipherStrength = 0;
1423 else if (policy == EncryptionPolicy.AllowNoEncryption) {
1424 // Allow null encryption cipher in addition to other ciphers
1425 dwMinimumCipherStrength = -1;
1426 dwMaximumCipherStrength = 0;
1428 else if (policy == EncryptionPolicy.NoEncryption) {
1429 // Suppress all encryption and require null encryption cipher only
1430 dwMinimumCipherStrength = -1;
1431 dwMaximumCipherStrength = -1;
1434 throw new ArgumentException(SR.GetString(SR.net_invalid_enum, "EncryptionPolicy"), "policy");
1437 dwSessionLifespan = reserved = 0;
1438 this.version = version;
1440 grbitEnabledProtocols = protocols;
1441 if (certificate != null) {
1442 certContextArray = certificate.Handle;
1447 [System.Diagnostics.Conditional("TRAVE")]
1448 internal void DebugDump() {
1449 GlobalLog.Print("SecureCredential #"+GetHashCode());
1450 GlobalLog.Print(" version = " + version);
1451 GlobalLog.Print(" cCreds = " + cCreds);
1452 GlobalLog.Print(" certContextArray = " + String.Format("0x{0:x}", certContextArray));
1453 GlobalLog.Print(" rootStore = " + String.Format("0x{0:x}", rootStore));
1454 GlobalLog.Print(" cMappers = " + cMappers);
1455 GlobalLog.Print(" phMappers = " + String.Format("0x{0:x}", phMappers));
1456 GlobalLog.Print(" cSupportedAlgs = " + cSupportedAlgs);
1457 GlobalLog.Print(" palgSupportedAlgs = " + String.Format("0x{0:x}", palgSupportedAlgs));
1458 GlobalLog.Print(" grbitEnabledProtocols = " + String.Format("0x{0:x}", grbitEnabledProtocols));
1459 GlobalLog.Print(" dwMinimumCipherStrength = " + dwMinimumCipherStrength);
1460 GlobalLog.Print(" dwMaximumCipherStrength = " + dwMaximumCipherStrength);
1461 GlobalLog.Print(" dwSessionLifespan = " + String.Format("0x{0:x}", dwSessionLifespan));
1462 GlobalLog.Print(" dwFlags = " + String.Format("0x{0:x}", dwFlags));
1463 GlobalLog.Print(" reserved = " + String.Format("0x{0:x}", reserved));
1466 } // SecureCredential
1468 #endif // !FEATURE_PAL
1470 [StructLayout(LayoutKind.Sequential)]
1471 internal unsafe struct SecurityBufferStruct {
1473 public BufferType type;
1474 public IntPtr token;
1476 public static readonly int Size = sizeof(SecurityBufferStruct);
1479 internal class SecurityBuffer {
1481 public BufferType type;
1482 public byte[] token;
1483 public SafeHandle unmanagedToken;
1486 public SecurityBuffer(byte[] data, int offset, int size, BufferType tokentype) {
1487 GlobalLog.Assert(offset >= 0 && offset <= (data == null ? 0 : data.Length), "SecurityBuffer::.ctor", "'offset' out of range. [" + offset + "]");
1488 GlobalLog.Assert(size >= 0 && size <= (data == null ? 0 : data.Length - offset), "SecurityBuffer::.ctor", "'size' out of range. [" + size + "]");
1490 this.offset = data == null || offset < 0 ? 0 : Math.Min(offset, data.Length);
1491 this.size = data == null || size < 0 ? 0 : Math.Min(size, data.Length - this.offset);
1492 this.type = tokentype;
1493 this.token = size == 0 ? null : data;
1496 public SecurityBuffer(byte[] data, BufferType tokentype) {
1497 this.size = data == null ? 0 : data.Length;
1498 this.type = tokentype;
1499 this.token = size == 0 ? null : data;
1502 public SecurityBuffer(int size, BufferType tokentype) {
1503 GlobalLog.Assert(size >= 0, "SecurityBuffer::.ctor", "'size' out of range. [" + size.ToString(NumberFormatInfo.InvariantInfo) + "]");
1506 this.type = tokentype;
1507 this.token = size == 0 ? null : new byte[size];
1510 public SecurityBuffer(ChannelBinding binding) {
1511 this.size = (binding == null ? 0 : binding.Size);
1512 this.type = BufferType.ChannelBindings;
1513 this.unmanagedToken = binding;
1517 [StructLayout(LayoutKind.Sequential)]
1518 internal unsafe class SecurityBufferDescriptor {
1520 typedef struct _SecBufferDesc {
1523 PSecBuffer pBuffers;
1524 } SecBufferDesc, * PSecBufferDesc;
1526 public readonly int Version;
1527 public readonly int Count;
1528 public void* UnmanagedPointer;
1530 public SecurityBufferDescriptor(int count) {
1533 UnmanagedPointer = null;
1536 [System.Diagnostics.Conditional("TRAVE")]
1537 internal void DebugDump() {
1538 GlobalLog.Print("SecurityBufferDescriptor #" + ValidationHelper.HashString(this));
1539 GlobalLog.Print(" version = " + Version);
1540 GlobalLog.Print(" count = " + Count);
1541 GlobalLog.Print(" securityBufferArray = 0x" + (new IntPtr(UnmanagedPointer)).ToString("x"));
1543 } // SecurityBufferDescriptor
1545 internal enum CertificateEncoding {
1547 X509AsnEncoding = unchecked((int)0x00000001),
1548 X509NdrEncoding = unchecked((int)0x00000002),
1549 Pkcs7AsnEncoding = unchecked((int)0x00010000),
1550 Pkcs7NdrEncoding = unchecked((int)0x00020000),
1551 AnyAsnEncoding = X509AsnEncoding|Pkcs7AsnEncoding
1554 internal enum CertificateProblem {
1556 TrustNOSIGNATURE = unchecked((int)0x800B0100),
1557 CertEXPIRED = unchecked((int)0x800B0101),
1558 CertVALIDITYPERIODNESTING = unchecked((int)0x800B0102),
1559 CertROLE = unchecked((int)0x800B0103),
1560 CertPATHLENCONST = unchecked((int)0x800B0104),
1561 CertCRITICAL = unchecked((int)0x800B0105),
1562 CertPURPOSE = unchecked((int)0x800B0106),
1563 CertISSUERCHAINING = unchecked((int)0x800B0107),
1564 CertMALFORMED = unchecked((int)0x800B0108),
1565 CertUNTRUSTEDROOT = unchecked((int)0x800B0109),
1566 CertCHAINING = unchecked((int)0x800B010A),
1567 CertREVOKED = unchecked((int)0x800B010C),
1568 CertUNTRUSTEDTESTROOT = unchecked((int)0x800B010D),
1569 CertREVOCATION_FAILURE = unchecked((int)0x800B010E),
1570 CertCN_NO_MATCH = unchecked((int)0x800B010F),
1571 CertWRONG_USAGE = unchecked((int)0x800B0110),
1572 TrustEXPLICITDISTRUST = unchecked((int)0x800B0111),
1573 CertUNTRUSTEDCA = unchecked((int)0x800B0112),
1574 CertINVALIDPOLICY = unchecked((int)0x800B0113),
1575 CertINVALIDNAME = unchecked((int)0x800B0114),
1577 CryptNOREVOCATIONCHECK = unchecked((int)0x80092012),
1578 CryptREVOCATIONOFFLINE = unchecked((int)0x80092013),
1580 TrustSYSTEMERROR = unchecked((int)0x80096001),
1581 TrustNOSIGNERCERT = unchecked((int)0x80096002),
1582 TrustCOUNTERSIGNER = unchecked((int)0x80096003),
1583 TrustCERTSIGNATURE = unchecked((int)0x80096004),
1584 TrustTIMESTAMP = unchecked((int)0x80096005),
1585 TrustBADDIGEST = unchecked((int)0x80096010),
1586 TrustBASICCONSTRAINTS = unchecked((int)0x80096019),
1587 TrustFINANCIALCRITERIA = unchecked((int)0x8009601E),
1590 [StructLayout(LayoutKind.Sequential)]
1591 internal class SecChannelBindings
1593 internal int dwInitiatorAddrType;
1594 internal int cbInitiatorLength;
1595 internal int dwInitiatorOffset;
1597 internal int dwAcceptorAddrType;
1598 internal int cbAcceptorLength;
1599 internal int dwAcceptorOffset;
1601 internal int cbApplicationDataLength;
1602 internal int dwApplicationDataOffset;
1606 // WebRequestPrefixElement
1608 // This is an element of the prefix list. It contains the prefix and the
1609 // interface to be called to create a request for that prefix.
1613 /// <para>[To be supplied.]</para>
1615 // internal class WebRequestPrefixElement {
1616 internal class WebRequestPrefixElement {
1619 /// <para>[To be supplied.]</para>
1621 public string Prefix;
1623 /// <para>[To be supplied.]</para>
1625 internal IWebRequestCreate creator;
1627 /// <para>[To be supplied.]</para>
1629 internal Type creatorType;
1631 public IWebRequestCreate Creator {
1633 if (creator == null && creatorType != null) {
1635 if (creator == null) {
1636 creator = (IWebRequestCreate)Activator.CreateInstance(
1638 BindingFlags.CreateInstance
1639 | BindingFlags.Instance
1640 | BindingFlags.NonPublic
1641 | BindingFlags.Public,
1643 new object[0], // no arguments
1644 CultureInfo.InvariantCulture
1658 public WebRequestPrefixElement(string P, Type creatorType) {
1659 // verify that its of the proper type of IWebRequestCreate
1660 if (!typeof(IWebRequestCreate).IsAssignableFrom(creatorType))
1662 throw new InvalidCastException(SR.GetString(SR.net_invalid_cast,
1663 creatorType.AssemblyQualifiedName,
1664 "IWebRequestCreate"));
1668 this.creatorType = creatorType;
1672 /// <para>[To be supplied.]</para>
1674 public WebRequestPrefixElement(string P, IWebRequestCreate C) {
1679 } // class PrefixListElement
1682 #if MONO_FEATURE_WEB_STACK
1685 // HttpRequestCreator.
1687 // This is the class that we use to create HTTP and HTTPS requests.
1690 internal class HttpRequestCreator : IWebRequestCreate {
1694 Create - Create an HttpWebRequest.
1696 This is our method to create an HttpWebRequest. We register
1697 for HTTP and HTTPS Uris, and this method is called when a request
1698 needs to be created for one of those.
1702 Uri - Uri for request being created.
1705 The newly created HttpWebRequest.
1709 public WebRequest Create( Uri Uri ) {
1711 // Note, DNS permissions check will not happen on WebRequest
1713 return new HttpWebRequest(Uri, null);
1716 } // class HttpRequestCreator
1719 // WebSocketHttpRequestCreator.
1721 // This is the class that we use to create WebSocket connection requests.
1724 internal class WebSocketHttpRequestCreator : IWebRequestCreate
1726 private string m_httpScheme;
1728 // This ctor is used to create a WebSocketHttpRequestCreator.
1729 // We will do a URI change to update the scheme with Http or Https scheme. The usingHttps boolean is
1730 // used to indicate whether the created HttpWebRequest should take the https scheme or not.
1731 public WebSocketHttpRequestCreator(bool usingHttps)
1733 m_httpScheme = usingHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
1738 Create - Create an HttpWebRequest.
1740 This is our method to create an HttpWebRequest for WebSocket connection. We register
1741 We will register it for custom Uri prefixes. And this method is called when a request
1742 needs to be created for one of those. The created HttpWebRequest will still be with Http or Https
1743 scheme, depending on the m_httpScheme field of this object.
1747 Uri - Uri for request being created.
1750 The newly created HttpWebRequest for WebSocket connection.
1754 public WebRequest Create(Uri Uri)
1756 UriBuilder uriBuilder = new UriBuilder(Uri);
1757 uriBuilder.Scheme = m_httpScheme;
1758 HttpWebRequest request = new HttpWebRequest(uriBuilder.Uri, null, true, "WebSocket" + Guid.NewGuid());
1759 WebSocketHelpers.PrepareWebRequest(ref request);
1763 } // class WebSocketHttpRequestCreator
1769 // CoreResponseData - Used to store result of HTTP header parsing and
1770 // response parsing. Also Contains new stream to use, and
1771 // is used as core of new Response
1773 internal class CoreResponseData {
1775 // Status Line Response Values
1776 public HttpStatusCode m_StatusCode;
1777 public string m_StatusDescription;
1778 public bool m_IsVersionHttp11;
1780 // Content Length needed for semantics, -1 if chunked
1781 public long m_ContentLength;
1784 public WebHeaderCollection m_ResponseHeaders;
1786 // ConnectStream - for reading actual data
1787 public Stream m_ConnectStream;
1789 internal CoreResponseData Clone() {
1790 CoreResponseData cloneResponseData = new CoreResponseData();
1791 cloneResponseData.m_StatusCode = m_StatusCode;
1792 cloneResponseData.m_StatusDescription = m_StatusDescription;
1793 cloneResponseData.m_IsVersionHttp11 = m_IsVersionHttp11;
1794 cloneResponseData.m_ContentLength = m_ContentLength;
1795 cloneResponseData.m_ResponseHeaders = m_ResponseHeaders;
1796 cloneResponseData.m_ConnectStream = m_ConnectStream;
1797 return cloneResponseData;
1804 internal delegate bool HttpAbortDelegate(HttpWebRequest request, WebException webException);
1807 // this class contains known header names
1810 internal static class HttpKnownHeaderNames {
1812 public const string CacheControl = "Cache-Control";
1813 public const string Connection = "Connection";
1814 public const string Date = "Date";
1815 public const string KeepAlive = "Keep-Alive";
1816 public const string Pragma = "Pragma";
1817 public const string ProxyConnection = "Proxy-Connection";
1818 public const string Trailer = "Trailer";
1819 public const string TransferEncoding = "Transfer-Encoding";
1820 public const string Upgrade = "Upgrade";
1821 public const string Via = "Via";
1822 public const string Warning = "Warning";
1823 public const string ContentLength = "Content-Length";
1824 public const string ContentType = "Content-Type";
1825 public const string ContentDisposition = "Content-Disposition";
1826 public const string ContentEncoding = "Content-Encoding";
1827 public const string ContentLanguage = "Content-Language";
1828 public const string ContentLocation = "Content-Location";
1829 public const string ContentRange = "Content-Range";
1830 public const string Expires = "Expires";
1831 public const string LastModified = "Last-Modified";
1832 public const string Age = "Age";
1833 public const string Location = "Location";
1834 public const string ProxyAuthenticate = "Proxy-Authenticate";
1835 public const string RetryAfter = "Retry-After";
1836 public const string Server = "Server";
1837 public const string SetCookie = "Set-Cookie";
1838 public const string SetCookie2 = "Set-Cookie2";
1839 public const string Vary = "Vary";
1840 public const string WWWAuthenticate = "WWW-Authenticate";
1841 public const string Accept = "Accept";
1842 public const string AcceptCharset = "Accept-Charset";
1843 public const string AcceptEncoding = "Accept-Encoding";
1844 public const string AcceptLanguage = "Accept-Language";
1845 public const string Authorization = "Authorization";
1846 public const string Cookie = "Cookie";
1847 public const string Cookie2 = "Cookie2";
1848 public const string Expect = "Expect";
1849 public const string From = "From";
1850 public const string Host = "Host";
1851 public const string IfMatch = "If-Match";
1852 public const string IfModifiedSince = "If-Modified-Since";
1853 public const string IfNoneMatch = "If-None-Match";
1854 public const string IfRange = "If-Range";
1855 public const string IfUnmodifiedSince = "If-Unmodified-Since";
1856 public const string MaxForwards = "Max-Forwards";
1857 public const string ProxyAuthorization = "Proxy-Authorization";
1858 public const string Referer = "Referer";
1859 public const string Range = "Range";
1860 public const string UserAgent = "User-Agent";
1861 public const string ContentMD5 = "Content-MD5";
1862 public const string ETag = "ETag";
1863 public const string TE = "TE";
1864 public const string Allow = "Allow";
1865 public const string AcceptRanges = "Accept-Ranges";
1866 public const string P3P = "P3P";
1867 public const string XPoweredBy = "X-Powered-By";
1868 public const string XAspNetVersion = "X-AspNet-Version";
1869 public const string SecWebSocketKey = "Sec-WebSocket-Key";
1870 public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
1871 public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
1872 public const string Origin = "Origin";
1873 public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
1874 public const string SecWebSocketVersion = "Sec-WebSocket-Version";
1879 /// Represents the method that will notify callers when a continue has been
1880 /// received by the client.
1883 // Delegate type for us to notify callers when we receive a continue
1884 public delegate void HttpContinueDelegate(int StatusCode, WebHeaderCollection httpHeaders);
1887 // HttpWriteMode - used to control the way in which an entity Body is posted.
1889 enum HttpWriteMode {
1897 // Used by Request to notify Connection that we are no longer holding the Connection (for NTLM connection sharing)
1898 delegate void UnlockConnectionDelegate();
1900 enum HttpBehaviour : byte {
1903 HTTP11PartiallyCompliant = 2,
1907 internal enum HttpProcessingResult {
1914 // HttpVerb - used to define various per Verb Properties
1918 // Note - this is a place holder for Verb properties,
1919 // the following two bools can most likely be combined into
1920 // a single Enum type. And the Verb can be incorporated.
1922 class KnownHttpVerb {
1923 internal string Name; // verb name
1925 internal bool RequireContentBody; // require content body to be sent
1926 internal bool ContentBodyNotAllowed; // not allowed to send content body
1927 internal bool ConnectRequest; // special semantics for a connect request
1928 internal bool ExpectNoContentResponse; // response will not have content body
1930 internal KnownHttpVerb(string name, bool requireContentBody, bool contentBodyNotAllowed, bool connectRequest, bool expectNoContentResponse) {
1932 RequireContentBody = requireContentBody;
1933 ContentBodyNotAllowed = contentBodyNotAllowed;
1934 ConnectRequest = connectRequest;
1935 ExpectNoContentResponse = expectNoContentResponse;
1938 // Force an an init, before we use them
1939 private static ListDictionary NamedHeaders;
1942 internal static KnownHttpVerb Get;
1943 internal static KnownHttpVerb Connect;
1944 internal static KnownHttpVerb Head;
1945 internal static KnownHttpVerb Put;
1946 internal static KnownHttpVerb Post;
1947 internal static KnownHttpVerb MkCol;
1950 // InitializeKnownVerbs - Does basic init for this object,
1951 // such as creating defaultings and filling them
1953 static KnownHttpVerb() {
1954 NamedHeaders = new ListDictionary(CaseInsensitiveAscii.StaticInstance);
1955 Get = new KnownHttpVerb("GET", false, true, false, false);
1956 Connect = new KnownHttpVerb("CONNECT", false, true, true, false);
1957 Head = new KnownHttpVerb("HEAD", false, true, false, true);
1958 Put = new KnownHttpVerb("PUT", true, false, false, false);
1959 Post = new KnownHttpVerb("POST", true, false, false, false);
1960 MkCol = new KnownHttpVerb("MKCOL",false,false,false,false);
1961 NamedHeaders[Get.Name] = Get;
1962 NamedHeaders[Connect.Name] = Connect;
1963 NamedHeaders[Head.Name] = Head;
1964 NamedHeaders[Put.Name] = Put;
1965 NamedHeaders[Post.Name] = Post;
1966 NamedHeaders[MkCol.Name] = MkCol;
1969 public bool Equals(KnownHttpVerb verb) {
1970 return this==verb || string.Compare(Name, verb.Name, StringComparison.OrdinalIgnoreCase)==0;
1973 public static KnownHttpVerb Parse(string name) {
1974 KnownHttpVerb knownHttpVerb = NamedHeaders[name] as KnownHttpVerb;
1975 if (knownHttpVerb==null) {
1976 // unknown verb, default behaviour
1977 knownHttpVerb = new KnownHttpVerb(name, false, false, false, false);
1979 return knownHttpVerb;
1984 // HttpProtocolUtils - A collection of utility functions for HTTP usage.
1987 internal class HttpProtocolUtils {
1989 private HttpProtocolUtils() {
1993 // extra buffers for build/parsing, recv/send HTTP data,
1994 // at some point we should consolidate
1998 // parse String to DateTime format.
1999 internal static DateTime string2date(String S) {
2001 if (HttpDateParse.ParseHttpDate(S,out dtOut)) {
2005 throw new ProtocolViolationException(SR.GetString(SR.net_baddate));
2010 // convert Date to String using RFC 1123 pattern
2011 internal static string date2string(DateTime D) {
2012 DateTimeFormatInfo dateFormat = new DateTimeFormatInfo();
2013 return D.ToUniversalTime().ToString("R", dateFormat);
2018 // Proxy class for linking between ICertificatePolicy <--> ICertificateDecider
2019 internal class PolicyWrapper {
2020 private const uint IgnoreUnmatchedCN = 0x00001000;
2021 private ICertificatePolicy fwdPolicy;
2022 private ServicePoint srvPoint;
2023 private WebRequest request;
2025 internal PolicyWrapper(ICertificatePolicy policy, ServicePoint sp, WebRequest wr) {
2026 this.fwdPolicy = policy;
2031 public bool Accept(X509Certificate Certificate, int CertificateProblem) {
2032 return fwdPolicy.CheckValidationResult(srvPoint, Certificate, request, CertificateProblem);
2035 internal static uint VerifyChainPolicy(SafeFreeCertChain chainContext, ref ChainPolicyParameter cpp) {
2036 GlobalLog.Enter("PolicyWrapper::VerifyChainPolicy", "chainContext="+ chainContext + ", options="+String.Format("0x{0:x}", cpp.dwFlags));
2037 ChainPolicyStatus status = new ChainPolicyStatus();
2038 status.cbSize = ChainPolicyStatus.StructSize;
2040 UnsafeNclNativeMethods.NativePKI.CertVerifyCertificateChainPolicy(
2041 (IntPtr) ChainPolicyType.SSL,
2046 GlobalLog.Print("PolicyWrapper::VerifyChainPolicy() CertVerifyCertificateChainPolicy returned: " + errorCode);
2048 GlobalLog.Print("PolicyWrapper::VerifyChainPolicy() error code: " + status.dwError+String.Format(" [0x{0:x8}", status.dwError) + " " + SecureChannel.MapSecurityStatus(status.dwError) + "]");
2050 GlobalLog.Leave("PolicyWrapper::VerifyChainPolicy", status.dwError.ToString());
2051 return status.dwError;
2054 private static IgnoreCertProblem MapErrorCode(uint errorCode) {
2055 switch ((CertificateProblem) errorCode) {
2057 case CertificateProblem.CertINVALIDNAME :
2058 case CertificateProblem.CertCN_NO_MATCH :
2059 return IgnoreCertProblem.invalid_name;
2061 case CertificateProblem.CertINVALIDPOLICY :
2062 case CertificateProblem.CertPURPOSE :
2063 return IgnoreCertProblem.invalid_policy;
2065 case CertificateProblem.CertEXPIRED :
2066 return IgnoreCertProblem.not_time_valid | IgnoreCertProblem.ctl_not_time_valid;
2068 case CertificateProblem.CertVALIDITYPERIODNESTING :
2069 return IgnoreCertProblem.not_time_nested;
2071 case CertificateProblem.CertCHAINING :
2072 case CertificateProblem.CertUNTRUSTEDCA :
2073 case CertificateProblem.CertUNTRUSTEDROOT :
2074 return IgnoreCertProblem.allow_unknown_ca;
2076 case CertificateProblem.CertREVOKED :
2077 case CertificateProblem.CertREVOCATION_FAILURE :
2078 case CertificateProblem.CryptNOREVOCATIONCHECK:
2079 case CertificateProblem.CryptREVOCATIONOFFLINE:
2080 return IgnoreCertProblem.all_rev_unknown;
2082 case CertificateProblem.CertROLE:
2083 case CertificateProblem.TrustBASICCONSTRAINTS:
2084 return IgnoreCertProblem.invalid_basic_constraints;
2086 case CertificateProblem.CertWRONG_USAGE :
2087 return IgnoreCertProblem.wrong_usage;
2095 private uint[] GetChainErrors(string hostName, X509Chain chain, ref bool fatalError)
2098 SafeFreeCertChain chainContext= new SafeFreeCertChain(chain.ChainContext);
2099 ArrayList certificateProblems = new ArrayList();
2102 ChainPolicyParameter cppStruct = new ChainPolicyParameter();
2103 cppStruct.cbSize = ChainPolicyParameter.StructSize;
2104 cppStruct.dwFlags = 0;
2106 SSL_EXTRA_CERT_CHAIN_POLICY_PARA eppStruct = new SSL_EXTRA_CERT_CHAIN_POLICY_PARA(false);
2107 cppStruct.pvExtraPolicyPara = &eppStruct;
2109 fixed (char* namePtr = hostName) {
2110 if (ServicePointManager.CheckCertificateName){
2111 eppStruct.pwszServerName = namePtr;
2115 status = VerifyChainPolicy(chainContext, ref cppStruct);
2116 uint ignoreErrorMask = (uint)MapErrorCode(status);
2118 certificateProblems.Add(status);
2120 if (status == 0) { // No more problems with the certificate?
2121 break; // Then break out of the callback loop
2124 if (ignoreErrorMask == 0) { // Unrecognized error encountered
2129 cppStruct.dwFlags |= ignoreErrorMask;
2130 if ((CertificateProblem)status == CertificateProblem.CertCN_NO_MATCH && ServicePointManager.CheckCertificateName) {
2131 eppStruct.fdwChecks = IgnoreUnmatchedCN;
2138 return (uint[]) certificateProblems.ToArray(typeof(uint));
2141 internal bool CheckErrors(string hostName, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
2143 if (sslPolicyErrors == 0)
2144 return Accept(certificate, 0);
2146 if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0)
2147 return Accept(certificate, (int) CertificateProblem.CertCRITICAL); // ToDO, Define an appropriate enum entry
2149 if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0 ||
2150 (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0)
2152 bool fatalError = false;
2153 uint[] certificateProblems = GetChainErrors(hostName, chain, ref fatalError);
2156 // By today design we cannot allow decider to ignore a fatal error.
2157 // This error is fatal.
2158 Accept(certificate, (int) SecurityStatus.InternalError);
2163 if (certificateProblems.Length == 0)
2164 return Accept(certificate, (int) CertificateProblem.OK);
2166 // Run each error through Accept().
2167 foreach (uint error in certificateProblems)
2168 if (!Accept(certificate, (int) error))
2175 /* CONSIDER: Use this code when we switch to managed X509 API
2176 internal static int MapStatusToWin32Error(X509ChainStatusFlags status)
2180 case X509ChainStatusFlags.NoError: return CertificateProblem.OK;
2181 case X509ChainStatusFlags.NotTimeValid: return CertificateProblem.CertEXPIRED;
2182 case X509ChainStatusFlags.NotTimeNested: return CertificateProblem.CertVALIDITYPERIODNESTING;
2183 case X509ChainStatusFlags.Revoked: return CertificateProblem.CertREVOKED;
2184 case X509ChainStatusFlags.NotSignatureValid:return CertificateProblem.TrustCERTSIGNATURE;
2185 case X509ChainStatusFlags.NotValidForUsage: return CertificateProblem.CertWRONG_USAGE;
2186 case X509ChainStatusFlags.UntrustedRoot: return CertificateProblem.CertUNTRUSTEDROOT;
2187 case X509ChainStatusFlags.RevocationStatusUnknown: return CertificateProblem.CryptNOREVOCATIONCHECK;
2188 case X509ChainStatusFlags.Cyclic: return CertificateProblem.CertCHAINING; //??
2189 case X509ChainStatusFlags.InvalidExtension: return CertificateProblem.CertCRITICAL; //??
2190 case X509ChainStatusFlags.InvalidPolicyConstraints: return CertificateProblem.CertINVALIDPOLICY;
2191 case X509ChainStatusFlags.InvalidBasicConstraints: return CertificateProblem.TrustBASICCONSTRAINTS;
2192 case X509ChainStatusFlagsInvalidNameConstraints: return CertificateProblem.CertINVALIDNAME;
2193 case X509ChainStatusFlags.HasNotSupportedNameConstraint: return CertificateProblem.CertINVALIDNAME; //??
2194 case X509ChainStatusFlags.HasNotDefinedNameConstraint: return CertificateProblem.CertINVALIDNAME; //??
2195 case X509ChainStatusFlags.HasNotPermittedNameConstraint: return CertificateProblem.CertINVALIDNAME; //??
2196 case X509ChainStatusFlags.HasExcludedNameConstraint: return CertificateProblem.CertINVALIDNAME; //??
2197 case X509ChainStatusFlags.PartialChain: return CertificateProblem.CertCHAINING; //??
2198 case X509ChainStatusFlags.CtlNotTimeValid: return CertificateProblem.CertEXPIRED;
2199 case X509ChainStatusFlags.CtlNotSignatureValid: return CertificateProblem.TrustCERTSIGNATURE;
2200 case X509ChainStatusFlags.CtlNotValidForUsage: return CertificateProblem.CertWRONG_USAGE;
2201 case X509ChainStatusFlags.OfflineRevocation: return CertificateProblem.CryptREVOCATIONOFFLINE;
2202 case X509ChainStatusFlags.NoIssuanceChainPolicy:return CertificateProblem.CertINVALIDPOLICY;
2203 default: return (int) CertificateProblem.TrustSYSTEMERROR; // unknown
2208 // Class implementing default certificate policy
2209 internal class DefaultCertPolicy : ICertificatePolicy {
2210 public bool CheckValidationResult(ServicePoint sp, X509Certificate cert, WebRequest request, int problem) {
2211 return problem == (int)CertificateProblem.OK;
2214 #endif // !FEATURE_PAL
2216 internal enum TriState {
2222 internal enum DefaultPorts {
2223 DEFAULT_FTP_PORT = 21,
2224 DEFAULT_GOPHER_PORT = 70,
2225 DEFAULT_HTTP_PORT = 80,
2226 DEFAULT_HTTPS_PORT = 443,
2227 DEFAULT_NNTP_PORT = 119,
2228 DEFAULT_SMTP_PORT = 25,
2229 DEFAULT_TELNET_PORT = 23
2232 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
2233 internal struct hostent {
2234 public IntPtr h_name;
2235 public IntPtr h_aliases;
2236 public short h_addrtype;
2237 public short h_length;
2238 public IntPtr h_addr_list;
2242 [StructLayout(LayoutKind.Sequential)]
2243 internal struct Blob {
2245 public int pBlobData;
2249 // This is only for internal code path i.e. TLS stream.
2250 // See comments on GetNextBuffer() method below.
2252 internal class SplitWritesState
2254 private const int c_SplitEncryptedBuffersSize = 64*1024;
2255 private BufferOffsetSize[] _UserBuffers;
2257 private int _LastBufferConsumed;
2258 private BufferOffsetSize[] _RealBuffers;
2261 internal SplitWritesState(BufferOffsetSize[] buffers)
2263 _UserBuffers = buffers;
2264 _LastBufferConsumed = 0;
2266 _RealBuffers = null;
2269 // Everything was handled
2271 internal bool IsDone {
2273 if (_LastBufferConsumed != 0)
2276 for (int index = _Index ;index < _UserBuffers.Length; ++index)
2277 if (_UserBuffers[index].Size != 0)
2283 // Encryption takes CPU and if the input is large (like 10 mb) then a delay may
2284 // be 30 sec or so. Hence split the ecnrypt and write operations in smaller chunks
2285 // up to c_SplitEncryptedBuffersSize total.
2286 // Note that upon return from here EncryptBuffers() may additonally split the input
2287 // into chunks each <= chkSecureChannel.MaxDataSize (~16k) yet it will complete them all as a single IO.
2289 // Returns null if done, returns the _buffers reference if everything is handled in one shot (also done)
2291 // Otheriwse returns subsequent BufferOffsetSize[] to encrypt and pass to base IO method
2293 internal BufferOffsetSize[] GetNextBuffers()
2295 int curIndex = _Index;
2296 int currentTotalSize = 0;
2297 int lastChunkSize = 0;
2299 int firstBufferConsumed = _LastBufferConsumed;
2301 for ( ;_Index < _UserBuffers.Length; ++_Index)
2303 lastChunkSize = _UserBuffers[_Index].Size-_LastBufferConsumed;
2305 currentTotalSize += lastChunkSize;
2307 if (currentTotalSize > c_SplitEncryptedBuffersSize)
2309 lastChunkSize -= (currentTotalSize - c_SplitEncryptedBuffersSize);
2310 currentTotalSize = c_SplitEncryptedBuffersSize;
2315 _LastBufferConsumed = 0;
2318 // Are we done done?
2319 if (currentTotalSize == 0)
2322 // Do all buffers fit the limit?
2323 if (firstBufferConsumed == 0 && curIndex == 0 && _Index == _UserBuffers.Length)
2324 return _UserBuffers;
2326 // We do have something to split and send out
2327 int buffersCount = lastChunkSize == 0? _Index-curIndex: _Index-curIndex+1;
2329 if (_RealBuffers == null || _RealBuffers.Length != buffersCount)
2330 _RealBuffers = new BufferOffsetSize[buffersCount];
2333 for (; curIndex < _Index; ++curIndex)
2335 _RealBuffers[j++] = new BufferOffsetSize(_UserBuffers[curIndex].Buffer, _UserBuffers[curIndex].Offset + firstBufferConsumed, _UserBuffers[curIndex].Size-firstBufferConsumed, false);
2336 firstBufferConsumed = 0;
2339 if (lastChunkSize != 0)
2341 _RealBuffers[j] = new BufferOffsetSize(_UserBuffers[curIndex].Buffer, _UserBuffers[curIndex].Offset + _LastBufferConsumed, lastChunkSize, false);
2342 if ((_LastBufferConsumed += lastChunkSize) == _UserBuffers[_Index].Size)
2345 _LastBufferConsumed = 0;
2349 return _RealBuffers;