2 Copyright (c) Microsoft Corporation
9 Internal helper to support public SslStream class.
10 It would be nice to make it a partial class file once compiler gets this supported
13 Alexei Vopilov 22-Aug-2003
16 22-Aug-2003 New design that has obsoleted SslClientStream and SslServerStream class
19 #if MONO_FEATURE_NEW_TLS && SECURITY_DEP
20 namespace System.Net.Security {
23 using System.Security;
24 using System.Security.Principal;
25 using System.Security.Permissions;
26 using System.Threading;
27 using System.Collections.Generic;
28 using System.Net.Sockets;
31 // This is a wrapping stream that does data encryption/decryption based on a successfully authenticated SSPI context.
33 internal partial class _SslStream
35 private static AsyncCallback _WriteCallback = new AsyncCallback(WriteCallback);
36 private static AsyncCallback _MulitpleWriteCallback = new AsyncCallback(MulitpleWriteCallback);
37 private static AsyncProtocolCallback _ResumeAsyncWriteCallback = new AsyncProtocolCallback(ResumeAsyncWriteCallback);
38 private static AsyncProtocolCallback _ResumeAsyncReadCallback = new AsyncProtocolCallback(ResumeAsyncReadCallback);
39 private static AsyncProtocolCallback _ReadHeaderCallback = new AsyncProtocolCallback(ReadHeaderCallback);
40 private static AsyncProtocolCallback _ReadFrameCallback = new AsyncProtocolCallback(ReadFrameCallback);
42 private const int PinnableReadBufferSize = 4096*4 + 32; // We like to read in 16K chunks + headers
43 private static PinnableBufferCache s_PinnableReadBufferCache = new PinnableBufferCache("System.Net.SslStream", PinnableReadBufferSize);
44 private const int PinnableWriteBufferSize = 4096 + 1024; // We like to write in 4K chunks + encryption overhead
45 private static PinnableBufferCache s_PinnableWriteBufferCache = new PinnableBufferCache("System.Net.SslStream", PinnableWriteBufferSize);
47 private SslState _SslState;
48 private int _NestedWrite;
49 private int _NestedRead;
51 // never updated directly, special properties are used. This is the read buffer
52 private byte[] _InternalBuffer;
53 private bool _InternalBufferFromPinnableCache;
55 private byte[] _PinnableOutputBuffer; // Used for writes when we can do it.
56 private byte[] _PinnableOutputBufferInUse; // remembers what UNENCRYPTED buffer is using _PinnableOutputBuffer
58 private int _InternalOffset;
59 private int _InternalBufferCount;
61 FixedSizeReader _Reader;
63 internal _SslStream(SslState sslState)
65 if (PinnableBufferCacheEventSource.Log.IsEnabled())
67 PinnableBufferCacheEventSource.Log.DebugMessage1("CTOR: In System.Net._SslStream.SslStream", this.GetHashCode());
70 _Reader = new FixedSizeReader(_SslState.InnerStream);
73 // if we have a read buffer from the pinnable cache, return it
76 if (_InternalBufferFromPinnableCache)
78 s_PinnableReadBufferCache.FreeBuffer(_InternalBuffer);
79 _InternalBufferFromPinnableCache = false;
81 _InternalBuffer = null;
86 if (_InternalBufferFromPinnableCache)
88 if (PinnableBufferCacheEventSource.Log.IsEnabled())
90 PinnableBufferCacheEventSource.Log.DebugMessage2("DTOR: In System.Net._SslStream.~SslStream Freeing Read Buffer", this.GetHashCode(), PinnableBufferCacheEventSource.AddressOfByteArray(_InternalBuffer));
94 if (_PinnableOutputBuffer != null)
96 if (PinnableBufferCacheEventSource.Log.IsEnabled())
98 PinnableBufferCacheEventSource.Log.DebugMessage2("DTOR: In System.Net._SslStream.~SslStream Freeing Write Buffer", this.GetHashCode(), PinnableBufferCacheEventSource.AddressOfByteArray(_PinnableOutputBuffer));
100 s_PinnableWriteBufferCache.FreeBuffer(_PinnableOutputBuffer);
105 // Some of the Public Stream class contract
109 internal int Read(byte[] buffer, int offset, int count)
111 return ProcessRead(buffer, offset, count, null);
115 internal void Write(byte[] buffer, int offset, int count)
117 ProcessWrite(buffer, offset, count, null);
121 internal void Write(BufferOffsetSize[] buffers)
123 ProcessWrite(buffers, null);
127 internal IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
129 BufferAsyncResult bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback);
130 AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(bufferResult);
131 ProcessRead(buffer, offset, count, asyncRequest );
136 internal int EndRead(IAsyncResult asyncResult)
138 if (asyncResult == null)
140 throw new ArgumentNullException("asyncResult");
143 BufferAsyncResult bufferResult = asyncResult as BufferAsyncResult;
144 if (bufferResult == null)
146 throw new ArgumentException(SR.GetString(SR.net_io_async_result, asyncResult.GetType().FullName), "asyncResult");
149 if (Interlocked.Exchange(ref _NestedRead, 0) == 0)
151 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndRead"));
154 // No "artificial" timeouts implemented so far, InnerStream controls timeout.
155 bufferResult.InternalWaitForCompletion();
157 if (bufferResult.Result is Exception)
159 if (bufferResult.Result is IOException)
161 throw (Exception)bufferResult.Result;
163 throw new IOException(SR.GetString(SR.net_io_read), (Exception)bufferResult.Result);
165 return (int) bufferResult.Result;
169 internal IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
171 LazyAsyncResult lazyResult = new LazyAsyncResult(this, asyncState, asyncCallback);
172 AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(lazyResult);
173 ProcessWrite(buffer, offset, count, asyncRequest);
177 // Assumes that InnerStream type == typeof(NetworkStream)
179 internal IAsyncResult BeginWrite(BufferOffsetSize[] buffers, AsyncCallback asyncCallback, object asyncState)
181 LazyAsyncResult lazyResult = new LazyAsyncResult(this, asyncState, asyncCallback);
182 SplitWriteAsyncProtocolRequest asyncRequest = new SplitWriteAsyncProtocolRequest(lazyResult);
183 ProcessWrite(buffers, asyncRequest);
188 internal void EndWrite(IAsyncResult asyncResult)
190 if (asyncResult == null)
192 throw new ArgumentNullException("asyncResult");
195 LazyAsyncResult lazyResult = asyncResult as LazyAsyncResult;
196 if (lazyResult == null)
198 throw new ArgumentException(SR.GetString(SR.net_io_async_result, asyncResult.GetType().FullName), "asyncResult");
201 if (Interlocked.Exchange(ref _NestedWrite, 0) == 0)
203 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndWrite"));
206 // No "artificial" timeouts implemented so far, InnerStream controls timeout.
207 lazyResult.InternalWaitForCompletion();
209 if (lazyResult.Result is Exception)
211 if (lazyResult.Result is IOException)
213 throw (Exception)lazyResult.Result;
215 throw new IOException(SR.GetString(SR.net_io_write), (Exception)lazyResult.Result);
219 // Internal implemenation
224 internal bool DataAvailable {
225 get { return InternalBufferCount != 0;}
229 private byte[] InternalBuffer {
231 return _InternalBuffer;
236 private int InternalOffset {
238 return _InternalOffset;
243 private int InternalBufferCount {
245 return _InternalBufferCount;
250 private void DecrementInternalBufferCount(int decrCount)
252 _InternalOffset += decrCount;
253 _InternalBufferCount -= decrCount;
256 // This will set the internal offset to "curOffset" and ensure internal buffer.
257 // If not enough, reallocate and copy up to "curOffset"
259 private void EnsureInternalBufferSize(int curOffset, int addSize)
261 if (_InternalBuffer == null || _InternalBuffer.Length < addSize + curOffset)
264 bool wasPinnable = _InternalBufferFromPinnableCache;
265 byte[] saved = _InternalBuffer;
267 int newSize = addSize + curOffset;
268 if (newSize <= PinnableReadBufferSize)
270 if (PinnableBufferCacheEventSource.Log.IsEnabled())
272 PinnableBufferCacheEventSource.Log.DebugMessage2("In System.Net._SslStream.EnsureInternalBufferSize IS pinnable", this.GetHashCode(), newSize);
274 _InternalBufferFromPinnableCache = true;
275 _InternalBuffer = s_PinnableReadBufferCache.AllocateBuffer();
279 if (PinnableBufferCacheEventSource.Log.IsEnabled())
281 PinnableBufferCacheEventSource.Log.DebugMessage2("In System.Net._SslStream.EnsureInternalBufferSize NOT pinnable", this.GetHashCode(), newSize);
283 _InternalBufferFromPinnableCache = false;
284 _InternalBuffer = new byte[newSize];
287 if (saved != null && curOffset != 0)
289 Buffer.BlockCopy(saved, 0, _InternalBuffer, 0, curOffset);
294 s_PinnableReadBufferCache.FreeBuffer(saved);
297 _InternalOffset = curOffset;
298 _InternalBufferCount = curOffset + addSize;
301 // Validates user parameteres for all Read/Write methods
303 private void ValidateParameters(byte[] buffer, int offset, int count)
306 throw new ArgumentNullException("buffer");
309 throw new ArgumentOutOfRangeException("offset");
312 throw new ArgumentOutOfRangeException("count");
314 if (count > buffer.Length-offset)
315 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.net_offset_plus_count));
318 // Combined sync/async write method. For sync case asyncRequest==null
320 private void ProcessWrite(BufferOffsetSize[] buffers, SplitWriteAsyncProtocolRequest asyncRequest)
322 foreach (BufferOffsetSize buffer in buffers)
324 ValidateParameters(buffer.Buffer, buffer.Offset, buffer.Size);
327 if (Interlocked.Exchange(ref _NestedWrite, 1) == 1)
329 throw new NotSupportedException(SR.GetString(SR.net_io_invalidnestedcall, (asyncRequest != null? "BeginWrite":"Write"), "write"));
335 SplitWritesState splitWrite = new SplitWritesState(buffers);
336 if (asyncRequest != null)
337 asyncRequest.SetNextRequest(splitWrite, _ResumeAsyncWriteCallback);
339 StartWriting(splitWrite, asyncRequest);
343 _SslState.FinishWrite();
346 if (e is IOException) {
349 throw new IOException(SR.GetString(SR.net_io_write), e);
353 if (asyncRequest == null || failed)
360 // Combined sync/async write method. For sync case asyncRequest==null
362 private void ProcessWrite(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
364 if (_SslState.LastPayload != null)
367 // !!! LastPayload Only used in TlsStream for HTTP and it needs re-work for a general case !!!
369 BufferOffsetSize[] buffers = new BufferOffsetSize[1];
370 buffers[0] = new BufferOffsetSize(buffer, offset, count, false);
371 if (asyncRequest != null)
372 ProcessWrite(buffers, new SplitWriteAsyncProtocolRequest(asyncRequest.UserAsyncResult));
374 ProcessWrite(buffers, null);
378 ValidateParameters(buffer, offset, count);
380 if (Interlocked.Exchange(ref _NestedWrite, 1) == 1)
382 throw new NotSupportedException(SR.GetString(SR.net_io_invalidnestedcall, (asyncRequest != null? "BeginWrite":"Write"), "write"));
388 StartWriting(buffer, offset, count, asyncRequest);
392 _SslState.FinishWrite();
395 if (e is IOException) {
398 throw new IOException(SR.GetString(SR.net_io_write), e);
402 if (asyncRequest == null || failed)
409 // This method assumes that InnerStream type == typeof(NetwokrStream)
410 // It will produce a set of buffers for one MultipleWrite call
412 private void StartWriting(SplitWritesState splitWrite, SplitWriteAsyncProtocolRequest asyncRequest)
414 while (!splitWrite.IsDone)
416 // request a write IO slot
417 if (_SslState.CheckEnqueueWrite(asyncRequest))
419 // operation is async and has been queued, return.
423 byte[] lastHandshakePayload = null;
424 if (_SslState.LastPayload != null)
427 // !!! LastPayload Only used in TlsStream for HTTP and it needs re-work for a general case !!!
429 lastHandshakePayload = _SslState.LastPayload;
430 _SslState.LastPayloadConsumed();
433 BufferOffsetSize[] buffers = splitWrite.GetNextBuffers();
434 buffers = EncryptBuffers(buffers, lastHandshakePayload);
436 if (asyncRequest != null)
438 // prepare for the next request
439 IAsyncResult ar = ((NetworkStream)(_SslState.InnerStream)).BeginMultipleWrite(buffers, _MulitpleWriteCallback, asyncRequest);
440 if (!ar.CompletedSynchronously)
443 ((NetworkStream)(_SslState.InnerStream)).EndMultipleWrite(ar);
447 ((NetworkStream)(_SslState.InnerStream)).MultipleWrite(buffers);
450 // release write IO slot
451 _SslState.FinishWrite();
455 if (asyncRequest != null)
456 asyncRequest.CompleteUser();
460 // Performs encryption of an array of buffers, proceeds buffer by buffer, if the individual
461 // buffer size exceeds a SSL limit of SecureChannel.MaxDataSize,the buffers are then split into smaller ones.
462 // Returns the same array that is encrypted or a new array of encrypted buffers.
464 private BufferOffsetSize[] EncryptBuffers(BufferOffsetSize[] buffers, byte[] lastHandshakePayload)
467 List<BufferOffsetSize> arrayList = null;
468 SecurityStatus status = SecurityStatus.OK;
470 foreach(BufferOffsetSize buffer in buffers)
472 int chunkBytes = Math.Min(buffer.Size, _SslState.MaxDataSize);
474 byte[] outBuffer = null;
477 status = _SslState.EncryptData(buffer.Buffer, buffer.Offset, chunkBytes, ref outBuffer, out outSize);
478 if (status != SecurityStatus.OK)
481 if (chunkBytes != buffer.Size || arrayList != null)
483 if (arrayList == null)
485 arrayList = new List<BufferOffsetSize>(buffers.Length * (buffer.Size/chunkBytes+1));
486 if (lastHandshakePayload != null)
487 arrayList.Add(new BufferOffsetSize(lastHandshakePayload, false));
489 foreach(BufferOffsetSize oldBuffer in buffers)
491 if (oldBuffer == buffer)
493 arrayList.Add(oldBuffer);
496 arrayList.Add(new BufferOffsetSize(outBuffer, 0, outSize, false));
497 while ((buffer.Size-=chunkBytes) != 0)
499 buffer.Offset += chunkBytes;
500 chunkBytes = Math.Min(buffer.Size, _SslState.MaxDataSize);
502 status = _SslState.EncryptData(buffer.Buffer, buffer.Offset, chunkBytes, ref outBuffer, out outSize);
503 if (status != SecurityStatus.OK)
505 arrayList.Add(new BufferOffsetSize(outBuffer, 0, outSize, false));
510 buffer.Buffer = outBuffer;
512 buffer.Size = outSize;
514 if (status != SecurityStatus.OK)
518 if (status != SecurityStatus.OK)
521 ProtocolToken message = new ProtocolToken(null, status);
522 throw new IOException(SR.GetString(SR.net_io_encrypt), message.GetException());
525 if (arrayList != null)
526 buffers = arrayList.ToArray();
527 else if (lastHandshakePayload != null)
529 BufferOffsetSize[] result = new BufferOffsetSize[buffers.Length+1];
530 Array.Copy(buffers, 0, result, 1, buffers.Length);
531 result[0] = new BufferOffsetSize(lastHandshakePayload, false);
538 private void StartWriting(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
540 if (asyncRequest != null)
542 asyncRequest.SetNextRequest(buffer, offset, count, _ResumeAsyncWriteCallback);
545 // We loop to this method from the callback
546 // If the last chunk was just completed from async callback (count < 0), we complete user request
549 byte[] outBuffer = null;
550 if (_PinnableOutputBufferInUse == null) // The output buffer is not in use
552 if (_PinnableOutputBuffer == null) // Create one if needed
554 _PinnableOutputBuffer = s_PinnableWriteBufferCache.AllocateBuffer();
556 _PinnableOutputBufferInUse = buffer; // put it in use
557 outBuffer = _PinnableOutputBuffer;
558 if (PinnableBufferCacheEventSource.Log.IsEnabled())
560 PinnableBufferCacheEventSource.Log.DebugMessage3("In System.Net._SslStream.StartWriting Trying Pinnable", this.GetHashCode(), count, PinnableBufferCacheEventSource.AddressOfByteArray(outBuffer));
565 if (PinnableBufferCacheEventSource.Log.IsEnabled())
567 PinnableBufferCacheEventSource.Log.DebugMessage2("In System.Net._SslStream.StartWriting BufferInUse", this.GetHashCode(), count);
573 // request a write IO slot
574 if (_SslState.CheckEnqueueWrite(asyncRequest))
576 // operation is async and has been queued, return.
580 int chunkBytes = Math.Min(count, _SslState.MaxDataSize);
582 SecurityStatus errorCode = _SslState.EncryptData(buffer, offset, chunkBytes, ref outBuffer, out encryptedBytes);
583 if (errorCode != SecurityStatus.OK)
586 ProtocolToken message = new ProtocolToken(null, errorCode);
587 throw new IOException(SR.GetString(SR.net_io_encrypt), message.GetException());
590 if (PinnableBufferCacheEventSource.Log.IsEnabled())
592 PinnableBufferCacheEventSource.Log.DebugMessage3("In System.Net._SslStream.StartWriting Got Encrypted Buffer",
593 this.GetHashCode(), encryptedBytes, PinnableBufferCacheEventSource.AddressOfByteArray(outBuffer));
595 if (asyncRequest != null)
597 // prepare for the next request
598 asyncRequest.SetNextRequest(buffer, offset+chunkBytes, count-chunkBytes, _ResumeAsyncWriteCallback);
599 IAsyncResult ar = _SslState.InnerStream.BeginWrite(outBuffer, 0, encryptedBytes, _WriteCallback, asyncRequest);
600 if (!ar.CompletedSynchronously)
604 _SslState.InnerStream.EndWrite(ar);
609 _SslState.InnerStream.Write(outBuffer, 0, encryptedBytes);
611 offset += chunkBytes;
614 // release write IO slot
615 _SslState.FinishWrite();
617 } while (count != 0);
621 if (asyncRequest != null) {
622 asyncRequest.CompleteUser();
625 if (buffer == _PinnableOutputBufferInUse) // Did we put it in use?
628 _PinnableOutputBufferInUse = null;
629 if (PinnableBufferCacheEventSource.Log.IsEnabled())
631 PinnableBufferCacheEventSource.Log.DebugMessage1("In System.Net._SslStream.StartWriting Freeing buffer.", this.GetHashCode());
637 // Combined sync/async read method. For sync requet asyncRequest==null
638 // There is a little overheader because we need to pass buffer/offset/count used only in sync.
639 // Still the benefit is that we have a common sync/async code path.
641 private int ProcessRead(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
643 ValidateParameters(buffer, offset, count);
645 if (Interlocked.Exchange(ref _NestedRead, 1) == 1)
647 throw new NotSupportedException(SR.GetString(SR.net_io_invalidnestedcall, (asyncRequest!=null? "BeginRead":"Read"), "read"));
654 if (InternalBufferCount != 0)
656 copyBytes = InternalBufferCount > count? count: InternalBufferCount;
659 Buffer.BlockCopy(InternalBuffer, InternalOffset, buffer, offset, copyBytes);
660 DecrementInternalBufferCount(copyBytes);
662 if (asyncRequest != null) {
663 asyncRequest.CompleteUser((object) copyBytes);
667 // going into real IO
668 return StartReading(buffer, offset, count, asyncRequest);
672 _SslState.FinishRead(null);
674 if (e is IOException) {
677 throw new IOException(SR.GetString(SR.net_io_read), e);
681 // if sync request or exception
682 if (asyncRequest == null || failed)
689 // To avoid recursion when decrypted 0 bytes this method will loop until a decrypted result at least 1 byte.
691 private int StartReading(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
695 GlobalLog.Assert(InternalBufferCount == 0, "SslStream::StartReading()|Previous frame was not consumed. InternalBufferCount:{0}", InternalBufferCount);
699 if (asyncRequest != null)
701 asyncRequest.SetNextRequest(buffer, offset, count, _ResumeAsyncReadCallback);
703 int copyBytes = _SslState.CheckEnqueueRead(buffer, offset, count, asyncRequest);
706 //queued but not completed!
712 if (asyncRequest != null)
714 asyncRequest.CompleteUser((object) copyBytes);
719 // When we read -1 bytes means we have decrypted 0 bytes or rehandshaking, need looping.
720 while ((result = StartFrameHeader(buffer, offset, count, asyncRequest)) == -1);
725 // Need read frame size first
727 private int StartFrameHeader(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
732 // Always pass InternalBuffer for SSPI "in place" decryption.
733 // A user buffer can be shared by many threads in that case decryption/integrity check may fail cause of data corruption.
736 // reset internal buffer for a new frame
737 EnsureInternalBufferSize(0, SecureChannel.ReadHeaderSize);
739 if (asyncRequest != null)
741 asyncRequest.SetNextRequest(InternalBuffer, 0, SecureChannel.ReadHeaderSize, _ReadHeaderCallback);
742 _Reader.AsyncReadPacket(asyncRequest);
743 if (!asyncRequest.MustCompleteSynchronously)
747 readBytes = asyncRequest.Result;
751 readBytes = _Reader.ReadPacket(InternalBuffer, 0, SecureChannel.ReadHeaderSize);
753 return StartFrameBody(readBytes, buffer, offset, count, asyncRequest);
758 private int StartFrameBody(int readBytes, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
763 //Reset the buffer as we did not read anything into it
764 DecrementInternalBufferCount(InternalBufferCount);
765 if (asyncRequest != null)
767 asyncRequest.CompleteUser((object)0);
772 // Now readBytes is a payload size
773 readBytes = _SslState.GetRemainingFrameSize(InternalBuffer, readBytes);
775 // And the payload size must be >= 0
779 throw new IOException(SR.GetString(SR.net_frame_read_size));
782 EnsureInternalBufferSize(SecureChannel.ReadHeaderSize, readBytes);
784 if (asyncRequest != null) //Async
786 asyncRequest.SetNextRequest(InternalBuffer, SecureChannel.ReadHeaderSize, readBytes, _ReadFrameCallback);
788 _Reader.AsyncReadPacket(asyncRequest);
790 if (!asyncRequest.MustCompleteSynchronously)
794 readBytes = asyncRequest.Result;
798 readBytes = _Reader.ReadPacket(InternalBuffer, SecureChannel.ReadHeaderSize, readBytes);
800 return ProcessFrameBody(readBytes, buffer, offset, count, asyncRequest);
803 // readBytes == SSL Data Payload size on input or 0 on EOF
805 private int ProcessFrameBody(int readBytes, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
810 throw new IOException(SR.GetString(SR.net_io_eof));
813 //Set readBytes to total number of received bytes
814 readBytes += SecureChannel.ReadHeaderSize;
816 //Decrypt into internal buffer, change "readBytes" to count now _Decrypted Bytes_
819 SecurityStatus errorCode = _SslState.DecryptData(InternalBuffer, ref data_offset, ref readBytes);
821 if (errorCode != SecurityStatus.OK)
823 byte[] extraBuffer = null;
826 extraBuffer = new byte[readBytes];
827 Buffer.BlockCopy(InternalBuffer, data_offset, extraBuffer, 0, readBytes);
829 // Reset internal buffer count
830 DecrementInternalBufferCount(InternalBufferCount);
831 return ProcessReadErrorCode(errorCode, buffer, offset, count, asyncRequest, extraBuffer);
835 if (readBytes == 0 && count != 0)
837 //Read again since remote side has sent encrypted 0 bytes
838 DecrementInternalBufferCount(InternalBufferCount);
842 // Decrypted data start from "data_offset" offset, the total count can be shrinked after decryption
843 EnsureInternalBufferSize(0, data_offset + readBytes);
844 DecrementInternalBufferCount(data_offset);
846 if (readBytes > count)
850 Buffer.BlockCopy(InternalBuffer, InternalOffset, buffer, offset, readBytes);
852 // This will adjust both the remaining internal buffer count and the offset
853 DecrementInternalBufferCount(readBytes);
855 _SslState.FinishRead(null);
856 if (asyncRequest != null)
858 asyncRequest.CompleteUser((object)readBytes);
864 // Codes we process (Anything else - fail)
866 // - SEC_I_RENEGOTIATE
868 private int ProcessReadErrorCode(SecurityStatus errorCode, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest, byte[] extraBuffer)
870 // ERROR - examine what kind
871 ProtocolToken message = new ProtocolToken(null, errorCode);
873 GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::***Processing an error Status = " + message.Status.ToString());
875 if (message.Renegotiate)
877 _SslState.ReplyOnReAuthentication(extraBuffer);
881 if (message.CloseConnection) {
882 _SslState.FinishRead(null);
883 if (asyncRequest != null)
885 asyncRequest.CompleteUser((object)0);
889 // Otherwise bail out.
890 throw new IOException(SR.GetString(SR.net_io_decrypt), message.GetException());
895 private static void WriteCallback(IAsyncResult transportResult)
897 if (transportResult.CompletedSynchronously)
902 GlobalLog.Assert(transportResult.AsyncState is AsyncProtocolRequest , "SslStream::WriteCallback|State type is wrong, expected AsyncProtocolRequest.");
903 AsyncProtocolRequest asyncRequest = (AsyncProtocolRequest) transportResult.AsyncState;
905 _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
907 sslStream._SslState.InnerStream.EndWrite(transportResult);
908 sslStream._SslState.FinishWrite();
910 if (asyncRequest.Count == 0) {
911 // this was the last chunk
912 asyncRequest.Count = -1;
914 sslStream.StartWriting(asyncRequest.Buffer, asyncRequest.Offset, asyncRequest.Count, asyncRequest);
917 catch (Exception e) {
918 if (asyncRequest.IsUserCompleted) {
919 // This will throw on a worker thread.
922 sslStream._SslState.FinishWrite();
923 asyncRequest.CompleteWithError(e);
927 // Assuming InnerStream type == typeof(NetworkStream)
929 private static void MulitpleWriteCallback(IAsyncResult transportResult)
931 if (transportResult.CompletedSynchronously)
936 GlobalLog.Assert(transportResult.AsyncState is AsyncProtocolRequest, "SslStream::MulitpleWriteCallback|State type is wrong, expected AsyncProtocolRequest.");
938 SplitWriteAsyncProtocolRequest asyncRequest = (SplitWriteAsyncProtocolRequest)transportResult.AsyncState;
940 _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
942 ((NetworkStream)(sslStream._SslState.InnerStream)).EndMultipleWrite(transportResult);
943 sslStream._SslState.FinishWrite();
944 sslStream.StartWriting(asyncRequest.SplitWritesState, asyncRequest);
946 catch (Exception e) {
947 if (asyncRequest.IsUserCompleted) {
948 // This will throw on a worker thread.
951 sslStream._SslState.FinishWrite();
952 asyncRequest.CompleteWithError(e);
957 // This is used in a rare situation when async Read is resumed from completed handshake
959 private static void ResumeAsyncReadCallback(AsyncProtocolRequest request)
962 ((_SslStream)request.AsyncObject).StartReading(request.Buffer, request.Offset, request.Count, request);
964 catch (Exception e) {
965 if (request.IsUserCompleted) {
966 // This will throw on a worker thread.
969 ((_SslStream)request.AsyncObject)._SslState.FinishRead(null);
970 request.CompleteWithError(e);
975 // This is used in a rare situation when async Write is resumed from completed handshake
977 private static void ResumeAsyncWriteCallback(AsyncProtocolRequest asyncRequest)
980 SplitWriteAsyncProtocolRequest splitWriteRequest = asyncRequest as SplitWriteAsyncProtocolRequest;
981 if (splitWriteRequest != null)
982 ((_SslStream)asyncRequest.AsyncObject).StartWriting(splitWriteRequest.SplitWritesState, splitWriteRequest);
984 ((_SslStream)asyncRequest.AsyncObject).StartWriting(asyncRequest.Buffer, asyncRequest.Offset, asyncRequest.Count, asyncRequest);
986 catch (Exception e) {
987 if (asyncRequest.IsUserCompleted) {
988 // This will throw on a worker thread.
991 ((_SslStream)asyncRequest.AsyncObject)._SslState.FinishWrite();
992 asyncRequest.CompleteWithError(e);
997 private static void ReadHeaderCallback(AsyncProtocolRequest asyncRequest)
999 // Async ONLY completion
1002 _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
1003 BufferAsyncResult bufferResult = (BufferAsyncResult) asyncRequest.UserAsyncResult;
1004 if (-1 == sslStream.StartFrameBody(asyncRequest.Result, bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest))
1006 // in case we decrypted 0 bytes start another reading.
1007 sslStream.StartReading(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest);
1012 if (asyncRequest.IsUserCompleted) {
1013 // This will throw on a worker thread.
1016 asyncRequest.CompleteWithError(e);
1021 private static void ReadFrameCallback(AsyncProtocolRequest asyncRequest)
1023 // Async ONLY completion
1026 _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
1027 BufferAsyncResult bufferResult = (BufferAsyncResult) asyncRequest.UserAsyncResult;
1028 if (-1 == sslStream.ProcessFrameBody(asyncRequest.Result, bufferResult.Buffer, bufferResult.Offset, bufferResult.Count,asyncRequest))
1030 // in case we decrypted 0 bytes start another reading.
1031 sslStream.StartReading(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest);
1036 if (asyncRequest.IsUserCompleted) {
1037 // This will throw on a worker thread.
1040 asyncRequest.CompleteWithError(e);
1044 private class SplitWriteAsyncProtocolRequest: AsyncProtocolRequest
1046 internal SplitWritesState SplitWritesState; // If one buffer is no enough (such as for multiple writes)
1048 internal SplitWriteAsyncProtocolRequest(LazyAsyncResult userAsyncResult): base (userAsyncResult)
1052 internal void SetNextRequest(SplitWritesState splitWritesState, AsyncProtocolCallback callback)
1054 SplitWritesState = splitWritesState;
1055 SetNextRequest(null, 0, 0,callback);