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 _SslState.CheckThrow(authSuccessCheck: true, shutdownCheck: true);
324 foreach (BufferOffsetSize buffer in buffers)
326 ValidateParameters(buffer.Buffer, buffer.Offset, buffer.Size);
329 if (Interlocked.Exchange(ref _NestedWrite, 1) == 1)
331 throw new NotSupportedException(SR.GetString(SR.net_io_invalidnestedcall, (asyncRequest != null? "BeginWrite":"Write"), "write"));
337 SplitWritesState splitWrite = new SplitWritesState(buffers);
338 if (asyncRequest != null)
339 asyncRequest.SetNextRequest(splitWrite, _ResumeAsyncWriteCallback);
341 StartWriting(splitWrite, asyncRequest);
345 _SslState.FinishWrite();
348 if (e is IOException) {
351 throw new IOException(SR.GetString(SR.net_io_write), e);
355 if (asyncRequest == null || failed)
362 // Combined sync/async write method. For sync case asyncRequest==null
364 private void ProcessWrite(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
366 if (_SslState.LastPayload != null)
369 // !!! LastPayload Only used in TlsStream for HTTP and it needs re-work for a general case !!!
371 BufferOffsetSize[] buffers = new BufferOffsetSize[1];
372 buffers[0] = new BufferOffsetSize(buffer, offset, count, false);
373 if (asyncRequest != null)
374 ProcessWrite(buffers, new SplitWriteAsyncProtocolRequest(asyncRequest.UserAsyncResult));
376 ProcessWrite(buffers, null);
380 ValidateParameters(buffer, offset, count);
381 _SslState.CheckThrow(authSuccessCheck: true, shutdownCheck: true);
383 if (Interlocked.Exchange(ref _NestedWrite, 1) == 1)
385 throw new NotSupportedException(SR.GetString(SR.net_io_invalidnestedcall, (asyncRequest != null? "BeginWrite":"Write"), "write"));
391 StartWriting(buffer, offset, count, asyncRequest);
395 _SslState.FinishWrite();
398 if (e is IOException) {
401 throw new IOException(SR.GetString(SR.net_io_write), e);
405 if (asyncRequest == null || failed)
412 // This method assumes that InnerStream type == typeof(NetwokrStream)
413 // It will produce a set of buffers for one MultipleWrite call
415 private void StartWriting(SplitWritesState splitWrite, SplitWriteAsyncProtocolRequest asyncRequest)
417 while (!splitWrite.IsDone)
419 // request a write IO slot
420 if (_SslState.CheckEnqueueWrite(asyncRequest))
422 // operation is async and has been queued, return.
426 byte[] lastHandshakePayload = null;
427 if (_SslState.LastPayload != null)
430 // !!! LastPayload Only used in TlsStream for HTTP and it needs re-work for a general case !!!
432 lastHandshakePayload = _SslState.LastPayload;
433 _SslState.LastPayloadConsumed();
436 BufferOffsetSize[] buffers = splitWrite.GetNextBuffers();
437 buffers = EncryptBuffers(buffers, lastHandshakePayload);
439 if (asyncRequest != null)
441 // prepare for the next request
442 IAsyncResult ar = ((NetworkStream)(_SslState.InnerStream)).BeginMultipleWrite(buffers, _MulitpleWriteCallback, asyncRequest);
443 if (!ar.CompletedSynchronously)
446 ((NetworkStream)(_SslState.InnerStream)).EndMultipleWrite(ar);
450 ((NetworkStream)(_SslState.InnerStream)).MultipleWrite(buffers);
453 // release write IO slot
454 _SslState.FinishWrite();
458 if (asyncRequest != null)
459 asyncRequest.CompleteUser();
463 // Performs encryption of an array of buffers, proceeds buffer by buffer, if the individual
464 // buffer size exceeds a SSL limit of SecureChannel.MaxDataSize,the buffers are then split into smaller ones.
465 // Returns the same array that is encrypted or a new array of encrypted buffers.
467 private BufferOffsetSize[] EncryptBuffers(BufferOffsetSize[] buffers, byte[] lastHandshakePayload)
470 List<BufferOffsetSize> arrayList = null;
471 SecurityStatus status = SecurityStatus.OK;
473 foreach(BufferOffsetSize buffer in buffers)
475 int chunkBytes = Math.Min(buffer.Size, _SslState.MaxDataSize);
477 byte[] outBuffer = null;
480 status = _SslState.EncryptData(buffer.Buffer, buffer.Offset, chunkBytes, ref outBuffer, out outSize);
481 if (status != SecurityStatus.OK)
484 if (chunkBytes != buffer.Size || arrayList != null)
486 if (arrayList == null)
488 arrayList = new List<BufferOffsetSize>(buffers.Length * (buffer.Size/chunkBytes+1));
489 if (lastHandshakePayload != null)
490 arrayList.Add(new BufferOffsetSize(lastHandshakePayload, false));
492 foreach(BufferOffsetSize oldBuffer in buffers)
494 if (oldBuffer == buffer)
496 arrayList.Add(oldBuffer);
499 arrayList.Add(new BufferOffsetSize(outBuffer, 0, outSize, false));
500 while ((buffer.Size-=chunkBytes) != 0)
502 buffer.Offset += chunkBytes;
503 chunkBytes = Math.Min(buffer.Size, _SslState.MaxDataSize);
505 status = _SslState.EncryptData(buffer.Buffer, buffer.Offset, chunkBytes, ref outBuffer, out outSize);
506 if (status != SecurityStatus.OK)
508 arrayList.Add(new BufferOffsetSize(outBuffer, 0, outSize, false));
513 buffer.Buffer = outBuffer;
515 buffer.Size = outSize;
517 if (status != SecurityStatus.OK)
521 if (status != SecurityStatus.OK)
524 ProtocolToken message = new ProtocolToken(null, status);
525 throw new IOException(SR.GetString(SR.net_io_encrypt), message.GetException());
528 if (arrayList != null)
529 buffers = arrayList.ToArray();
530 else if (lastHandshakePayload != null)
532 BufferOffsetSize[] result = new BufferOffsetSize[buffers.Length+1];
533 Array.Copy(buffers, 0, result, 1, buffers.Length);
534 result[0] = new BufferOffsetSize(lastHandshakePayload, false);
541 private void StartWriting(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
543 if (asyncRequest != null)
545 asyncRequest.SetNextRequest(buffer, offset, count, _ResumeAsyncWriteCallback);
548 // We loop to this method from the callback
549 // If the last chunk was just completed from async callback (count < 0), we complete user request
552 byte[] outBuffer = null;
553 if (_PinnableOutputBufferInUse == null) // The output buffer is not in use
555 if (_PinnableOutputBuffer == null) // Create one if needed
557 _PinnableOutputBuffer = s_PinnableWriteBufferCache.AllocateBuffer();
559 _PinnableOutputBufferInUse = buffer; // put it in use
560 outBuffer = _PinnableOutputBuffer;
561 if (PinnableBufferCacheEventSource.Log.IsEnabled())
563 PinnableBufferCacheEventSource.Log.DebugMessage3("In System.Net._SslStream.StartWriting Trying Pinnable", this.GetHashCode(), count, PinnableBufferCacheEventSource.AddressOfByteArray(outBuffer));
568 if (PinnableBufferCacheEventSource.Log.IsEnabled())
570 PinnableBufferCacheEventSource.Log.DebugMessage2("In System.Net._SslStream.StartWriting BufferInUse", this.GetHashCode(), count);
576 // request a write IO slot
577 if (_SslState.CheckEnqueueWrite(asyncRequest))
579 // operation is async and has been queued, return.
583 int chunkBytes = Math.Min(count, _SslState.MaxDataSize);
585 SecurityStatus errorCode = _SslState.EncryptData(buffer, offset, chunkBytes, ref outBuffer, out encryptedBytes);
586 if (errorCode != SecurityStatus.OK)
589 ProtocolToken message = new ProtocolToken(null, errorCode);
590 throw new IOException(SR.GetString(SR.net_io_encrypt), message.GetException());
593 if (PinnableBufferCacheEventSource.Log.IsEnabled())
595 PinnableBufferCacheEventSource.Log.DebugMessage3("In System.Net._SslStream.StartWriting Got Encrypted Buffer",
596 this.GetHashCode(), encryptedBytes, PinnableBufferCacheEventSource.AddressOfByteArray(outBuffer));
598 if (asyncRequest != null)
600 // prepare for the next request
601 asyncRequest.SetNextRequest(buffer, offset+chunkBytes, count-chunkBytes, _ResumeAsyncWriteCallback);
602 IAsyncResult ar = _SslState.InnerStream.BeginWrite(outBuffer, 0, encryptedBytes, _WriteCallback, asyncRequest);
603 if (!ar.CompletedSynchronously)
607 _SslState.InnerStream.EndWrite(ar);
612 _SslState.InnerStream.Write(outBuffer, 0, encryptedBytes);
614 offset += chunkBytes;
617 // release write IO slot
618 _SslState.FinishWrite();
620 } while (count != 0);
624 if (asyncRequest != null) {
625 asyncRequest.CompleteUser();
628 if (buffer == _PinnableOutputBufferInUse) // Did we put it in use?
631 _PinnableOutputBufferInUse = null;
632 if (PinnableBufferCacheEventSource.Log.IsEnabled())
634 PinnableBufferCacheEventSource.Log.DebugMessage1("In System.Net._SslStream.StartWriting Freeing buffer.", this.GetHashCode());
640 // Combined sync/async read method. For sync requet asyncRequest==null
641 // There is a little overheader because we need to pass buffer/offset/count used only in sync.
642 // Still the benefit is that we have a common sync/async code path.
644 private int ProcessRead(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
646 ValidateParameters(buffer, offset, count);
648 if (Interlocked.Exchange(ref _NestedRead, 1) == 1)
650 throw new NotSupportedException(SR.GetString(SR.net_io_invalidnestedcall, (asyncRequest!=null? "BeginRead":"Read"), "read"));
657 if (InternalBufferCount != 0)
659 copyBytes = InternalBufferCount > count? count: InternalBufferCount;
662 Buffer.BlockCopy(InternalBuffer, InternalOffset, buffer, offset, copyBytes);
663 DecrementInternalBufferCount(copyBytes);
665 if (asyncRequest != null) {
666 asyncRequest.CompleteUser((object) copyBytes);
670 // going into real IO
671 return StartReading(buffer, offset, count, asyncRequest);
675 _SslState.FinishRead(null);
677 if (e is IOException) {
680 throw new IOException(SR.GetString(SR.net_io_read), e);
684 // if sync request or exception
685 if (asyncRequest == null || failed)
692 // To avoid recursion when decrypted 0 bytes this method will loop until a decrypted result at least 1 byte.
694 private int StartReading(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
698 GlobalLog.Assert(InternalBufferCount == 0, "SslStream::StartReading()|Previous frame was not consumed. InternalBufferCount:{0}", InternalBufferCount);
702 if (asyncRequest != null)
704 asyncRequest.SetNextRequest(buffer, offset, count, _ResumeAsyncReadCallback);
706 int copyBytes = _SslState.CheckEnqueueRead(buffer, offset, count, asyncRequest);
709 //queued but not completed!
715 if (asyncRequest != null)
717 asyncRequest.CompleteUser((object) copyBytes);
722 // When we read -1 bytes means we have decrypted 0 bytes or rehandshaking, need looping.
723 while ((result = StartFrameHeader(buffer, offset, count, asyncRequest)) == -1);
728 // Need read frame size first
730 private int StartFrameHeader(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
735 // Always pass InternalBuffer for SSPI "in place" decryption.
736 // A user buffer can be shared by many threads in that case decryption/integrity check may fail cause of data corruption.
739 // reset internal buffer for a new frame
740 EnsureInternalBufferSize(0, SecureChannel.ReadHeaderSize);
742 if (asyncRequest != null)
744 asyncRequest.SetNextRequest(InternalBuffer, 0, SecureChannel.ReadHeaderSize, _ReadHeaderCallback);
745 _Reader.AsyncReadPacket(asyncRequest);
746 if (!asyncRequest.MustCompleteSynchronously)
750 readBytes = asyncRequest.Result;
754 readBytes = _Reader.ReadPacket(InternalBuffer, 0, SecureChannel.ReadHeaderSize);
756 return StartFrameBody(readBytes, buffer, offset, count, asyncRequest);
761 private int StartFrameBody(int readBytes, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
766 //Reset the buffer as we did not read anything into it
767 DecrementInternalBufferCount(InternalBufferCount);
768 if (asyncRequest != null)
770 asyncRequest.CompleteUser((object)0);
775 // Now readBytes is a payload size
776 readBytes = _SslState.GetRemainingFrameSize(InternalBuffer, readBytes);
778 // And the payload size must be >= 0
782 throw new IOException(SR.GetString(SR.net_frame_read_size));
785 EnsureInternalBufferSize(SecureChannel.ReadHeaderSize, readBytes);
787 if (asyncRequest != null) //Async
789 asyncRequest.SetNextRequest(InternalBuffer, SecureChannel.ReadHeaderSize, readBytes, _ReadFrameCallback);
791 _Reader.AsyncReadPacket(asyncRequest);
793 if (!asyncRequest.MustCompleteSynchronously)
797 readBytes = asyncRequest.Result;
801 readBytes = _Reader.ReadPacket(InternalBuffer, SecureChannel.ReadHeaderSize, readBytes);
803 return ProcessFrameBody(readBytes, buffer, offset, count, asyncRequest);
806 // readBytes == SSL Data Payload size on input or 0 on EOF
808 private int ProcessFrameBody(int readBytes, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
813 throw new IOException(SR.GetString(SR.net_io_eof));
816 //Set readBytes to total number of received bytes
817 readBytes += SecureChannel.ReadHeaderSize;
819 //Decrypt into internal buffer, change "readBytes" to count now _Decrypted Bytes_
822 SecurityStatus errorCode = _SslState.DecryptData(InternalBuffer, ref data_offset, ref readBytes);
824 if (errorCode != SecurityStatus.OK)
826 byte[] extraBuffer = null;
829 extraBuffer = new byte[readBytes];
830 Buffer.BlockCopy(InternalBuffer, data_offset, extraBuffer, 0, readBytes);
832 // Reset internal buffer count
833 DecrementInternalBufferCount(InternalBufferCount);
834 return ProcessReadErrorCode(errorCode, buffer, offset, count, asyncRequest, extraBuffer);
838 if (readBytes == 0 && count != 0)
840 //Read again since remote side has sent encrypted 0 bytes
841 DecrementInternalBufferCount(InternalBufferCount);
845 // Decrypted data start from "data_offset" offset, the total count can be shrinked after decryption
846 EnsureInternalBufferSize(0, data_offset + readBytes);
847 DecrementInternalBufferCount(data_offset);
849 if (readBytes > count)
853 Buffer.BlockCopy(InternalBuffer, InternalOffset, buffer, offset, readBytes);
855 // This will adjust both the remaining internal buffer count and the offset
856 DecrementInternalBufferCount(readBytes);
858 _SslState.FinishRead(null);
859 if (asyncRequest != null)
861 asyncRequest.CompleteUser((object)readBytes);
867 // Codes we process (Anything else - fail)
869 // - SEC_I_RENEGOTIATE
871 private int ProcessReadErrorCode(SecurityStatus errorCode, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest, byte[] extraBuffer)
873 // ERROR - examine what kind
874 ProtocolToken message = new ProtocolToken(null, errorCode);
876 GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::***Processing an error Status = " + message.Status.ToString());
878 if (message.Renegotiate)
880 _SslState.ReplyOnReAuthentication(extraBuffer);
884 if (message.CloseConnection) {
885 _SslState.FinishRead(null);
886 if (asyncRequest != null)
888 asyncRequest.CompleteUser((object)0);
892 // Otherwise bail out.
893 throw new IOException(SR.GetString(SR.net_io_decrypt), message.GetException());
898 private static void WriteCallback(IAsyncResult transportResult)
900 if (transportResult.CompletedSynchronously)
905 GlobalLog.Assert(transportResult.AsyncState is AsyncProtocolRequest , "SslStream::WriteCallback|State type is wrong, expected AsyncProtocolRequest.");
906 AsyncProtocolRequest asyncRequest = (AsyncProtocolRequest) transportResult.AsyncState;
908 _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
910 sslStream._SslState.InnerStream.EndWrite(transportResult);
911 sslStream._SslState.FinishWrite();
913 if (asyncRequest.Count == 0) {
914 // this was the last chunk
915 asyncRequest.Count = -1;
917 sslStream.StartWriting(asyncRequest.Buffer, asyncRequest.Offset, asyncRequest.Count, asyncRequest);
920 catch (Exception e) {
921 if (asyncRequest.IsUserCompleted) {
922 // This will throw on a worker thread.
925 sslStream._SslState.FinishWrite();
926 asyncRequest.CompleteWithError(e);
930 // Assuming InnerStream type == typeof(NetworkStream)
932 private static void MulitpleWriteCallback(IAsyncResult transportResult)
934 if (transportResult.CompletedSynchronously)
939 GlobalLog.Assert(transportResult.AsyncState is AsyncProtocolRequest, "SslStream::MulitpleWriteCallback|State type is wrong, expected AsyncProtocolRequest.");
941 SplitWriteAsyncProtocolRequest asyncRequest = (SplitWriteAsyncProtocolRequest)transportResult.AsyncState;
943 _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
945 ((NetworkStream)(sslStream._SslState.InnerStream)).EndMultipleWrite(transportResult);
946 sslStream._SslState.FinishWrite();
947 sslStream.StartWriting(asyncRequest.SplitWritesState, asyncRequest);
949 catch (Exception e) {
950 if (asyncRequest.IsUserCompleted) {
951 // This will throw on a worker thread.
954 sslStream._SslState.FinishWrite();
955 asyncRequest.CompleteWithError(e);
960 // This is used in a rare situation when async Read is resumed from completed handshake
962 private static void ResumeAsyncReadCallback(AsyncProtocolRequest request)
965 ((_SslStream)request.AsyncObject).StartReading(request.Buffer, request.Offset, request.Count, request);
967 catch (Exception e) {
968 if (request.IsUserCompleted) {
969 // This will throw on a worker thread.
972 ((_SslStream)request.AsyncObject)._SslState.FinishRead(null);
973 request.CompleteWithError(e);
978 // This is used in a rare situation when async Write is resumed from completed handshake
980 private static void ResumeAsyncWriteCallback(AsyncProtocolRequest asyncRequest)
983 SplitWriteAsyncProtocolRequest splitWriteRequest = asyncRequest as SplitWriteAsyncProtocolRequest;
984 if (splitWriteRequest != null)
985 ((_SslStream)asyncRequest.AsyncObject).StartWriting(splitWriteRequest.SplitWritesState, splitWriteRequest);
987 ((_SslStream)asyncRequest.AsyncObject).StartWriting(asyncRequest.Buffer, asyncRequest.Offset, asyncRequest.Count, asyncRequest);
989 catch (Exception e) {
990 if (asyncRequest.IsUserCompleted) {
991 // This will throw on a worker thread.
994 ((_SslStream)asyncRequest.AsyncObject)._SslState.FinishWrite();
995 asyncRequest.CompleteWithError(e);
1000 private static void ReadHeaderCallback(AsyncProtocolRequest asyncRequest)
1002 // Async ONLY completion
1005 _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
1006 BufferAsyncResult bufferResult = (BufferAsyncResult) asyncRequest.UserAsyncResult;
1007 if (-1 == sslStream.StartFrameBody(asyncRequest.Result, bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest))
1009 // in case we decrypted 0 bytes start another reading.
1010 sslStream.StartReading(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest);
1015 if (asyncRequest.IsUserCompleted) {
1016 // This will throw on a worker thread.
1019 asyncRequest.CompleteWithError(e);
1024 private static void ReadFrameCallback(AsyncProtocolRequest asyncRequest)
1026 // Async ONLY completion
1029 _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
1030 BufferAsyncResult bufferResult = (BufferAsyncResult) asyncRequest.UserAsyncResult;
1031 if (-1 == sslStream.ProcessFrameBody(asyncRequest.Result, bufferResult.Buffer, bufferResult.Offset, bufferResult.Count,asyncRequest))
1033 // in case we decrypted 0 bytes start another reading.
1034 sslStream.StartReading(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest);
1039 if (asyncRequest.IsUserCompleted) {
1040 // This will throw on a worker thread.
1043 asyncRequest.CompleteWithError(e);
1047 private class SplitWriteAsyncProtocolRequest: AsyncProtocolRequest
1049 internal SplitWritesState SplitWritesState; // If one buffer is no enough (such as for multiple writes)
1051 internal SplitWriteAsyncProtocolRequest(LazyAsyncResult userAsyncResult): base (userAsyncResult)
1055 internal void SetNextRequest(SplitWritesState splitWritesState, AsyncProtocolCallback callback)
1057 SplitWritesState = splitWritesState;
1058 SetNextRequest(null, 0, 0,callback);