Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System / net / System / Net / SecureProtocols / _SslStream.cs
1 /*++
2 Copyright (c) Microsoft Corporation
3
4 Module Name:
5
6     _SslStream.cs
7
8 Abstract:
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
11
12 Author:
13     Alexei Vopilov    22-Aug-2003
14
15 Revision History:
16     22-Aug-2003 New design that has obsoleted SslClientStream and SslServerStream class
17
18 --*/
19 #if MONO_FEATURE_NEW_TLS && SECURITY_DEP
20 namespace System.Net.Security {
21     using System;
22     using System.IO;
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;
29
30     //
31     // This is a wrapping stream that does data encryption/decryption based on a successfully authenticated SSPI context.
32     //
33     internal partial class _SslStream
34     {
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);
41
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);
46
47         private SslState    _SslState;
48         private int         _NestedWrite;
49         private int         _NestedRead;
50
51         // never updated directly, special properties are used.  This is the read buffer
52         private byte[]      _InternalBuffer;
53         private bool        _InternalBufferFromPinnableCache;
54
55         private byte[]      _PinnableOutputBuffer;                        // Used for writes when we can do it. 
56         private byte[]      _PinnableOutputBufferInUse;                   // remembers what UNENCRYPTED buffer is using _PinnableOutputBuffer
57
58         private int         _InternalOffset;
59         private int         _InternalBufferCount;
60
61         FixedSizeReader     _Reader;
62
63         internal _SslStream(SslState sslState)
64         {
65             if (PinnableBufferCacheEventSource.Log.IsEnabled())
66             {
67                 PinnableBufferCacheEventSource.Log.DebugMessage1("CTOR: In System.Net._SslStream.SslStream", this.GetHashCode());
68             }
69             _SslState = sslState;
70             _Reader = new FixedSizeReader(_SslState.InnerStream);
71         }
72
73         // if we have a read buffer from the pinnable cache, return it
74         void FreeReadBuffer()
75         {
76             if (_InternalBufferFromPinnableCache)
77             {
78                 s_PinnableReadBufferCache.FreeBuffer(_InternalBuffer);
79                 _InternalBufferFromPinnableCache = false;
80             }
81             _InternalBuffer = null;
82         }
83
84         ~_SslStream()
85         {        
86             if (_InternalBufferFromPinnableCache)  
87             {
88                 if (PinnableBufferCacheEventSource.Log.IsEnabled())
89                 {
90                     PinnableBufferCacheEventSource.Log.DebugMessage2("DTOR: In System.Net._SslStream.~SslStream Freeing Read Buffer", this.GetHashCode(), PinnableBufferCacheEventSource.AddressOfByteArray(_InternalBuffer));
91                 }
92                 FreeReadBuffer();
93             }
94             if (_PinnableOutputBuffer != null)
95             {
96                 if (PinnableBufferCacheEventSource.Log.IsEnabled())
97                 {
98                     PinnableBufferCacheEventSource.Log.DebugMessage2("DTOR: In System.Net._SslStream.~SslStream Freeing Write Buffer", this.GetHashCode(), PinnableBufferCacheEventSource.AddressOfByteArray(_PinnableOutputBuffer));
99                 }
100                 s_PinnableWriteBufferCache.FreeBuffer(_PinnableOutputBuffer);
101             }
102         }
103
104         //
105         // Some of the Public Stream class contract
106         //
107         //
108         //
109         internal int Read(byte[] buffer, int offset, int count)
110         {
111             return ProcessRead(buffer, offset, count, null);
112         }
113         //
114         //
115         internal void Write(byte[] buffer, int offset, int count)
116         {
117             ProcessWrite(buffer, offset, count, null);
118         }
119         //
120         //
121         internal void Write(BufferOffsetSize[] buffers)
122         {
123             ProcessWrite(buffers, null);
124         }
125         //
126         //
127         internal IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
128         {
129             BufferAsyncResult bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback);
130             AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(bufferResult);
131             ProcessRead(buffer, offset, count, asyncRequest );
132             return bufferResult;
133         }
134         //
135         //
136         internal int EndRead(IAsyncResult asyncResult)
137         {
138             if (asyncResult == null)
139             {
140                 throw new ArgumentNullException("asyncResult");
141             }
142
143             BufferAsyncResult bufferResult = asyncResult as BufferAsyncResult;
144             if (bufferResult == null)
145             {
146                 throw new ArgumentException(SR.GetString(SR.net_io_async_result, asyncResult.GetType().FullName), "asyncResult");
147             }
148
149             if (Interlocked.Exchange(ref _NestedRead, 0) == 0)
150             {
151                 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndRead"));
152             }
153
154             // No "artificial" timeouts implemented so far, InnerStream controls timeout.
155             bufferResult.InternalWaitForCompletion();
156
157             if (bufferResult.Result is Exception)
158             {
159                 if (bufferResult.Result is IOException)
160                 {
161                     throw (Exception)bufferResult.Result;
162                 }
163                 throw new IOException(SR.GetString(SR.net_io_read), (Exception)bufferResult.Result);
164             }
165             return (int) bufferResult.Result;
166         }
167         //
168         //
169         internal IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
170         {
171             LazyAsyncResult lazyResult = new LazyAsyncResult(this, asyncState, asyncCallback);
172             AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(lazyResult);
173             ProcessWrite(buffer, offset, count, asyncRequest);
174             return lazyResult;
175         }
176         //
177         //  Assumes that InnerStream type == typeof(NetworkStream)
178         //
179         internal IAsyncResult BeginWrite(BufferOffsetSize[] buffers, AsyncCallback asyncCallback, object asyncState)
180         {
181             LazyAsyncResult lazyResult = new LazyAsyncResult(this, asyncState, asyncCallback);
182             SplitWriteAsyncProtocolRequest asyncRequest = new SplitWriteAsyncProtocolRequest(lazyResult);
183             ProcessWrite(buffers, asyncRequest);
184             return lazyResult;
185         }
186         //
187         //
188         internal void EndWrite(IAsyncResult asyncResult)
189         {
190             if (asyncResult == null)
191             {
192                 throw new ArgumentNullException("asyncResult");
193             }
194
195             LazyAsyncResult lazyResult = asyncResult as LazyAsyncResult;
196             if (lazyResult == null)
197             {
198                 throw new ArgumentException(SR.GetString(SR.net_io_async_result, asyncResult.GetType().FullName), "asyncResult");
199             }
200
201             if (Interlocked.Exchange(ref _NestedWrite, 0) == 0)
202             {
203                 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndWrite"));
204             }
205
206             // No "artificial" timeouts implemented so far, InnerStream controls timeout.
207             lazyResult.InternalWaitForCompletion();
208
209             if (lazyResult.Result is Exception)
210             {
211                 if (lazyResult.Result is IOException)
212                 {
213                     throw (Exception)lazyResult.Result;
214                 }
215                 throw new IOException(SR.GetString(SR.net_io_write), (Exception)lazyResult.Result);
216             }
217         }
218         //
219         // Internal implemenation
220         //
221
222         //
223         //
224         internal bool DataAvailable {
225             get { return  InternalBufferCount != 0;}
226         }
227         //
228         //
229         private byte[] InternalBuffer {
230             get {
231                 return _InternalBuffer;
232             }
233         }
234         //
235         //
236         private int InternalOffset {
237             get {
238                 return _InternalOffset;
239             }
240         }
241         //
242         //
243         private int InternalBufferCount {
244             get {
245                 return _InternalBufferCount;
246             }
247         }
248         //
249         //
250         private void DecrementInternalBufferCount(int decrCount)
251         {
252             _InternalOffset += decrCount;
253             _InternalBufferCount -= decrCount;
254         }
255         //
256         // This will set the internal offset to "curOffset" and ensure internal buffer.
257         // If not enough, reallocate and copy up to "curOffset"
258         //
259         private void EnsureInternalBufferSize(int curOffset, int addSize)
260         {
261             if (_InternalBuffer == null || _InternalBuffer.Length < addSize + curOffset)
262             {
263
264                 bool wasPinnable = _InternalBufferFromPinnableCache;
265                 byte[] saved = _InternalBuffer;
266
267                 int newSize = addSize + curOffset;
268                 if (newSize <= PinnableReadBufferSize)
269                 {
270                     if (PinnableBufferCacheEventSource.Log.IsEnabled())
271                     {
272                         PinnableBufferCacheEventSource.Log.DebugMessage2("In System.Net._SslStream.EnsureInternalBufferSize IS pinnable", this.GetHashCode(), newSize);
273                     }
274                     _InternalBufferFromPinnableCache = true;
275                     _InternalBuffer = s_PinnableReadBufferCache.AllocateBuffer();
276                 }
277                 else  
278                 {
279                     if (PinnableBufferCacheEventSource.Log.IsEnabled())
280                     {
281                         PinnableBufferCacheEventSource.Log.DebugMessage2("In System.Net._SslStream.EnsureInternalBufferSize NOT pinnable", this.GetHashCode(), newSize);
282                     }
283                     _InternalBufferFromPinnableCache = false;
284                     _InternalBuffer = new byte[newSize];
285                 }
286
287                 if (saved != null && curOffset != 0)
288                 {
289                     Buffer.BlockCopy(saved, 0, _InternalBuffer, 0, curOffset);
290                 }
291
292                 if (wasPinnable)
293                 {
294                     s_PinnableReadBufferCache.FreeBuffer(saved);
295                 }
296             }
297             _InternalOffset = curOffset;
298             _InternalBufferCount = curOffset + addSize;
299         }
300         //
301         // Validates user parameteres for all Read/Write methods
302         //
303         private void ValidateParameters(byte[] buffer, int offset, int count)
304         {
305             if (buffer == null)
306                 throw new ArgumentNullException("buffer");
307
308             if (offset < 0)
309                 throw new ArgumentOutOfRangeException("offset");
310
311             if (count < 0)
312                 throw new ArgumentOutOfRangeException("count");
313
314             if (count > buffer.Length-offset)
315                 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.net_offset_plus_count));
316         }
317         //
318         // Combined sync/async write method. For sync case asyncRequest==null
319         //
320         private void ProcessWrite(BufferOffsetSize[] buffers, SplitWriteAsyncProtocolRequest asyncRequest)
321         {
322             _SslState.CheckThrow(authSuccessCheck: true, shutdownCheck: true);
323
324             foreach (BufferOffsetSize buffer in buffers)
325             {
326                 ValidateParameters(buffer.Buffer, buffer.Offset, buffer.Size);
327             }
328
329             if (Interlocked.Exchange(ref _NestedWrite, 1) == 1)
330             {
331                 throw new NotSupportedException(SR.GetString(SR.net_io_invalidnestedcall, (asyncRequest != null? "BeginWrite":"Write"), "write"));
332             }
333
334             bool failed = false;
335             try
336             {
337                 SplitWritesState splitWrite = new SplitWritesState(buffers);
338                 if (asyncRequest != null)
339                     asyncRequest.SetNextRequest(splitWrite, _ResumeAsyncWriteCallback);
340
341                 StartWriting(splitWrite, asyncRequest);
342             }
343             catch (Exception e)
344             {
345                 _SslState.FinishWrite();
346
347                 failed = true;
348                 if (e is IOException) {
349                     throw;
350                 }
351                 throw new IOException(SR.GetString(SR.net_io_write), e);
352             }
353             finally
354             {
355                 if (asyncRequest == null || failed)
356                 {
357                     _NestedWrite = 0;
358                 }
359             }
360         }
361         //
362         // Combined sync/async write method. For sync case asyncRequest==null
363         //
364         private void ProcessWrite(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
365         {
366             if (_SslState.LastPayload != null)
367             {
368                 //
369                 // !!! LastPayload Only used in TlsStream  for HTTP and it needs re-work for a general case !!!
370                 //
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));
375                 else
376                     ProcessWrite(buffers, null);
377                 return;
378             }
379
380             ValidateParameters(buffer, offset, count);
381             _SslState.CheckThrow(authSuccessCheck: true, shutdownCheck: true);
382
383             if (Interlocked.Exchange(ref _NestedWrite, 1) == 1)
384             {
385                 throw new NotSupportedException(SR.GetString(SR.net_io_invalidnestedcall, (asyncRequest != null? "BeginWrite":"Write"), "write"));
386             }
387
388             bool failed = false;
389             try
390             {
391                 StartWriting(buffer, offset, count, asyncRequest);
392             }
393             catch (Exception e)
394             {
395                 _SslState.FinishWrite();
396
397                 failed = true;
398                 if (e is IOException) {
399                     throw;
400                 }
401                 throw new IOException(SR.GetString(SR.net_io_write), e);
402             }
403             finally
404             {
405                 if (asyncRequest == null || failed)
406                 {
407                     _NestedWrite = 0;
408                 }
409             }
410         }
411         //
412         // This method assumes that InnerStream type == typeof(NetwokrStream)
413         // It will produce a set of buffers for one MultipleWrite call
414         //
415         private void StartWriting(SplitWritesState splitWrite, SplitWriteAsyncProtocolRequest asyncRequest)
416         {
417             while (!splitWrite.IsDone)
418             {
419                 // request a write IO slot
420                 if (_SslState.CheckEnqueueWrite(asyncRequest))
421                 {
422                     // operation is async and has been queued, return.
423                     return;
424                 }
425
426                 byte[] lastHandshakePayload = null;
427                 if (_SslState.LastPayload != null)
428                 {
429                     //
430                     // !!! LastPayload Only used in TlsStream for HTTP and it needs re-work for a general case !!!
431                     //
432                     lastHandshakePayload = _SslState.LastPayload;
433                     _SslState.LastPayloadConsumed();
434                 }
435
436                 BufferOffsetSize[] buffers = splitWrite.GetNextBuffers();
437                 buffers = EncryptBuffers(buffers, lastHandshakePayload);
438
439                 if (asyncRequest != null)
440                 {
441                     // prepare for the next request
442                     IAsyncResult ar = ((NetworkStream)(_SslState.InnerStream)).BeginMultipleWrite(buffers, _MulitpleWriteCallback, asyncRequest);
443                     if (!ar.CompletedSynchronously)
444                         return;
445
446                     ((NetworkStream)(_SslState.InnerStream)).EndMultipleWrite(ar);
447                 }
448                 else
449                 {
450                     ((NetworkStream)(_SslState.InnerStream)).MultipleWrite(buffers);
451                 }
452
453                 // release write IO slot
454                 _SslState.FinishWrite();
455
456             }
457
458             if (asyncRequest != null)
459                 asyncRequest.CompleteUser();
460         }
461
462         //
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.
466         //
467         private BufferOffsetSize[] EncryptBuffers(BufferOffsetSize[] buffers, byte[] lastHandshakePayload)
468         {
469
470             List<BufferOffsetSize> arrayList = null;
471             SecurityStatus status = SecurityStatus.OK;
472
473             foreach(BufferOffsetSize buffer in buffers)
474             {
475                 int chunkBytes = Math.Min(buffer.Size, _SslState.MaxDataSize);
476
477                 byte[] outBuffer = null;
478                 int    outSize;
479
480                 status = _SslState.EncryptData(buffer.Buffer, buffer.Offset, chunkBytes, ref outBuffer, out outSize);
481                 if (status != SecurityStatus.OK)
482                     break;
483
484                 if (chunkBytes != buffer.Size || arrayList != null)
485                 {
486                     if (arrayList == null)
487                     {
488                         arrayList = new List<BufferOffsetSize>(buffers.Length * (buffer.Size/chunkBytes+1));
489                         if (lastHandshakePayload != null)
490                             arrayList.Add(new BufferOffsetSize(lastHandshakePayload, false));
491
492                         foreach(BufferOffsetSize oldBuffer in buffers)
493                         {
494                             if (oldBuffer == buffer)
495                                 break;
496                             arrayList.Add(oldBuffer);
497                         }
498                     }
499                     arrayList.Add(new BufferOffsetSize(outBuffer, 0, outSize, false));
500                     while ((buffer.Size-=chunkBytes) != 0)
501                     {
502                         buffer.Offset += chunkBytes;
503                         chunkBytes = Math.Min(buffer.Size, _SslState.MaxDataSize);
504                         outBuffer = null;
505                         status = _SslState.EncryptData(buffer.Buffer, buffer.Offset, chunkBytes, ref outBuffer, out outSize);
506                         if (status != SecurityStatus.OK)
507                             break;
508                         arrayList.Add(new BufferOffsetSize(outBuffer, 0, outSize, false));
509                     }
510                 }
511                 else
512                 {
513                     buffer.Buffer = outBuffer;
514                     buffer.Offset = 0;
515                     buffer.Size   = outSize;
516                 }
517                 if (status != SecurityStatus.OK)
518                     break;
519             }
520
521             if (status != SecurityStatus.OK)
522             {
523                 //
524                 ProtocolToken message = new ProtocolToken(null, status);
525                 throw new IOException(SR.GetString(SR.net_io_encrypt), message.GetException());
526             }
527
528             if (arrayList != null)
529                 buffers = arrayList.ToArray();
530             else if (lastHandshakePayload != null)
531             {
532                 BufferOffsetSize[] result = new BufferOffsetSize[buffers.Length+1];
533                 Array.Copy(buffers, 0, result, 1, buffers.Length);
534                 result[0] = new BufferOffsetSize(lastHandshakePayload, false);
535                 buffers = result;
536             }
537
538             return buffers;
539         }
540         //
541         private void StartWriting(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
542         {
543             if (asyncRequest != null)
544             {
545                 asyncRequest.SetNextRequest(buffer, offset, count, _ResumeAsyncWriteCallback);
546             }
547
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
550             if (count >= 0 )
551             {
552                 byte[] outBuffer = null;
553                 if (_PinnableOutputBufferInUse == null)                // The output buffer is not in use
554                 {
555                     if (_PinnableOutputBuffer == null)                // Create one if needed
556                     {
557                         _PinnableOutputBuffer = s_PinnableWriteBufferCache.AllocateBuffer();
558                     }
559                     _PinnableOutputBufferInUse = buffer;        // put it in use
560                     outBuffer = _PinnableOutputBuffer;
561                     if (PinnableBufferCacheEventSource.Log.IsEnabled())
562                     {
563                         PinnableBufferCacheEventSource.Log.DebugMessage3("In System.Net._SslStream.StartWriting Trying Pinnable", this.GetHashCode(), count, PinnableBufferCacheEventSource.AddressOfByteArray(outBuffer));
564                     }
565                 }
566                 else
567                 {
568                     if (PinnableBufferCacheEventSource.Log.IsEnabled())
569                     {
570                         PinnableBufferCacheEventSource.Log.DebugMessage2("In System.Net._SslStream.StartWriting BufferInUse", this.GetHashCode(), count);
571                     }
572                 }
573
574                 do
575                 {
576                     // request a write IO slot
577                     if (_SslState.CheckEnqueueWrite(asyncRequest))
578                     {
579                         // operation is async and has been queued, return.
580                         return;
581                     }
582
583                     int chunkBytes = Math.Min(count, _SslState.MaxDataSize);
584                     int encryptedBytes;
585                     SecurityStatus errorCode =  _SslState.EncryptData(buffer, offset, chunkBytes, ref outBuffer, out encryptedBytes);
586                     if (errorCode != SecurityStatus.OK)
587                     {
588                         //
589                         ProtocolToken message = new ProtocolToken(null, errorCode);
590                         throw new IOException(SR.GetString(SR.net_io_encrypt), message.GetException());
591                     }
592
593                     if (PinnableBufferCacheEventSource.Log.IsEnabled())
594                     {
595                         PinnableBufferCacheEventSource.Log.DebugMessage3("In System.Net._SslStream.StartWriting Got Encrypted Buffer", 
596                             this.GetHashCode(), encryptedBytes, PinnableBufferCacheEventSource.AddressOfByteArray(outBuffer));
597                     }
598                     if (asyncRequest != null)
599                     {
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)
604                         {
605                             return;
606                         }
607                         _SslState.InnerStream.EndWrite(ar);
608
609                     }
610                     else
611                     {
612                         _SslState.InnerStream.Write(outBuffer, 0, encryptedBytes);
613                     }
614                     offset += chunkBytes;
615                     count  -= chunkBytes;
616
617                     // release write IO slot
618                     _SslState.FinishWrite();
619
620                 } while (count != 0);
621         
622             }
623
624             if (asyncRequest != null) {
625                 asyncRequest.CompleteUser();
626             }
627
628             if (buffer == _PinnableOutputBufferInUse)                // Did we put it in use?
629             {
630                 // Then free it 
631                 _PinnableOutputBufferInUse = null;
632                 if (PinnableBufferCacheEventSource.Log.IsEnabled())
633                 {
634                     PinnableBufferCacheEventSource.Log.DebugMessage1("In System.Net._SslStream.StartWriting Freeing buffer.", this.GetHashCode());
635                 }
636             }
637         }
638
639         //
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.
643         //
644         private int ProcessRead(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
645         {
646             ValidateParameters(buffer, offset, count);
647
648             if (Interlocked.Exchange(ref _NestedRead, 1) == 1)
649             {
650                 throw new NotSupportedException(SR.GetString(SR.net_io_invalidnestedcall, (asyncRequest!=null? "BeginRead":"Read"), "read"));
651             }
652
653             bool failed = false;
654             try
655             {
656                 int copyBytes;
657                 if (InternalBufferCount != 0)
658                 {
659                     copyBytes = InternalBufferCount > count? count: InternalBufferCount;
660                     if (copyBytes != 0)
661                     {
662                         Buffer.BlockCopy(InternalBuffer, InternalOffset, buffer, offset, copyBytes);
663                         DecrementInternalBufferCount(copyBytes);
664                     }
665                     if (asyncRequest != null) {
666                         asyncRequest.CompleteUser((object) copyBytes);
667                     }
668                     return copyBytes;
669                 }
670                 // going into real IO
671                 return StartReading(buffer, offset, count, asyncRequest);
672             }
673             catch (Exception e)
674             {
675                 _SslState.FinishRead(null);
676                 failed = true;
677                 if (e is IOException) {
678                     throw;
679                 }
680                 throw new IOException(SR.GetString(SR.net_io_read), e);
681             }
682             finally
683             {
684                 // if sync request or exception
685                 if (asyncRequest == null || failed)
686                 {
687                     _NestedRead = 0;
688                 }
689             }
690         }
691         //
692         // To avoid recursion when decrypted 0 bytes this method will loop until a decrypted result at least 1 byte.
693         //
694         private int StartReading(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
695         {
696             int result = 0;
697
698             GlobalLog.Assert(InternalBufferCount == 0, "SslStream::StartReading()|Previous frame was not consumed. InternalBufferCount:{0}", InternalBufferCount);
699
700             do
701             {
702                 if (asyncRequest != null)
703                 {
704                     asyncRequest.SetNextRequest(buffer, offset, count, _ResumeAsyncReadCallback);
705                 }
706                 int copyBytes = _SslState.CheckEnqueueRead(buffer, offset, count, asyncRequest);
707                 if (copyBytes == 0)
708                 {
709                     //queued but not completed!
710                     return 0;
711                 }
712
713                 if (copyBytes != -1)
714                 {
715                     if (asyncRequest != null)
716                     {
717                         asyncRequest.CompleteUser((object) copyBytes);
718                     }
719                     return copyBytes;
720                 }
721             }
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);
724
725             return result;
726         }
727         //
728         // Need read frame size first
729         //
730         private int StartFrameHeader(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
731         {
732             int readBytes = 0;
733
734             //
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.
737             //
738
739             // reset internal buffer for a new frame
740             EnsureInternalBufferSize(0, SecureChannel.ReadHeaderSize);
741
742             if (asyncRequest != null)
743             {
744                 asyncRequest.SetNextRequest(InternalBuffer, 0, SecureChannel.ReadHeaderSize, _ReadHeaderCallback);
745                 _Reader.AsyncReadPacket(asyncRequest);
746                 if (!asyncRequest.MustCompleteSynchronously)
747                 {
748                     return 0;
749                 }
750                 readBytes = asyncRequest.Result;
751             }
752             else
753             {
754                 readBytes = _Reader.ReadPacket(InternalBuffer, 0, SecureChannel.ReadHeaderSize);
755             }
756             return StartFrameBody(readBytes, buffer, offset, count, asyncRequest);
757         }
758         //
759         //
760         //
761         private int StartFrameBody(int readBytes, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
762         {
763             if (readBytes == 0)
764             {
765                 //EOF
766                 //Reset the buffer as we did not read anything into it
767                 DecrementInternalBufferCount(InternalBufferCount);
768                 if (asyncRequest != null)
769                 {
770                     asyncRequest.CompleteUser((object)0);
771                 }
772                 return 0;
773             }
774
775             // Now readBytes is a payload size
776             readBytes = _SslState.GetRemainingFrameSize(InternalBuffer, readBytes);
777             //
778             // And the payload size must be >= 0
779             //
780             if (readBytes  < 0)
781             {
782                 throw new IOException(SR.GetString(SR.net_frame_read_size));
783             }
784
785             EnsureInternalBufferSize(SecureChannel.ReadHeaderSize, readBytes);
786
787             if (asyncRequest != null) //Async
788             {
789                 asyncRequest.SetNextRequest(InternalBuffer, SecureChannel.ReadHeaderSize, readBytes, _ReadFrameCallback);
790
791                 _Reader.AsyncReadPacket(asyncRequest);
792
793                 if (!asyncRequest.MustCompleteSynchronously)
794                 {
795                     return 0;
796                 }
797                 readBytes = asyncRequest.Result;
798             }
799             else //Sync
800             {
801                 readBytes = _Reader.ReadPacket(InternalBuffer, SecureChannel.ReadHeaderSize, readBytes);
802             }
803             return ProcessFrameBody(readBytes, buffer, offset, count, asyncRequest);
804         }
805         //
806         // readBytes == SSL Data Payload size on input or 0 on EOF
807         //
808         private int ProcessFrameBody(int readBytes, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest)
809         {
810             if (readBytes == 0)
811             {
812                 // Eof
813                 throw new IOException(SR.GetString(SR.net_io_eof));
814             }
815
816             //Set readBytes to total number of received bytes
817             readBytes += SecureChannel.ReadHeaderSize;
818
819             //Decrypt into internal buffer, change "readBytes" to count now _Decrypted Bytes_
820             int data_offset = 0;
821
822             SecurityStatus errorCode = _SslState.DecryptData(InternalBuffer, ref data_offset, ref readBytes);
823
824             if (errorCode != SecurityStatus.OK)
825             {
826                 byte[] extraBuffer = null;
827                 if (readBytes != 0)
828                 {
829                     extraBuffer = new byte[readBytes];
830                     Buffer.BlockCopy(InternalBuffer, data_offset, extraBuffer, 0, readBytes);
831                 }
832                 // Reset internal buffer count
833                 DecrementInternalBufferCount(InternalBufferCount);
834                 return ProcessReadErrorCode(errorCode, buffer, offset, count, asyncRequest, extraBuffer);
835             }
836
837
838             if (readBytes == 0 && count != 0)
839             {
840                 //Read again since remote side has sent encrypted 0 bytes
841                 DecrementInternalBufferCount(InternalBufferCount);
842                 return -1;
843             }
844
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);
848
849             if (readBytes > count)
850             {
851                 readBytes = count;
852             }
853             Buffer.BlockCopy(InternalBuffer, InternalOffset, buffer, offset, readBytes);
854
855             // This will adjust both the remaining internal buffer count and the offset
856             DecrementInternalBufferCount(readBytes);
857
858             _SslState.FinishRead(null);
859             if (asyncRequest != null)
860             {
861                 asyncRequest.CompleteUser((object)readBytes);
862             }
863
864             return readBytes;
865         }
866         //
867         // Codes we process (Anything else - fail)
868         //
869         // - SEC_I_RENEGOTIATE
870         //
871         private int ProcessReadErrorCode(SecurityStatus errorCode, byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest, byte[] extraBuffer)
872         {
873             // ERROR - examine what kind
874             ProtocolToken message = new ProtocolToken(null, errorCode);
875
876             GlobalLog.Print("SecureChannel#" + ValidationHelper.HashString(this) + "::***Processing an error Status = " + message.Status.ToString());
877
878             if (message.Renegotiate)
879             {
880                 _SslState.ReplyOnReAuthentication(extraBuffer);
881                 // loop on read
882                 return -1;
883             }
884             if (message.CloseConnection) {
885                 _SslState.FinishRead(null);
886                 if (asyncRequest != null)
887                 {
888                     asyncRequest.CompleteUser((object)0);
889                 }
890                 return 0;
891             }
892             // Otherwise bail out.
893             throw new IOException(SR.GetString(SR.net_io_decrypt), message.GetException());
894         }
895         //
896         //
897         //
898         private static void WriteCallback(IAsyncResult transportResult)
899         {
900             if (transportResult.CompletedSynchronously)
901             {
902                 return;
903             }
904
905             GlobalLog.Assert(transportResult.AsyncState is AsyncProtocolRequest , "SslStream::WriteCallback|State type is wrong, expected AsyncProtocolRequest.");
906             AsyncProtocolRequest asyncRequest = (AsyncProtocolRequest) transportResult.AsyncState;
907
908             _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
909             try {
910                 sslStream._SslState.InnerStream.EndWrite(transportResult);
911                 sslStream._SslState.FinishWrite();
912
913                 if (asyncRequest.Count == 0) {
914                     // this was the last chunk
915                     asyncRequest.Count = -1;
916                 }
917                 sslStream.StartWriting(asyncRequest.Buffer, asyncRequest.Offset, asyncRequest.Count, asyncRequest);
918
919             }
920             catch (Exception e) {
921                 if (asyncRequest.IsUserCompleted) {
922                     // This will throw on a worker thread.
923                     throw;
924                 }
925                 sslStream._SslState.FinishWrite();
926                 asyncRequest.CompleteWithError(e);
927             }
928         }
929         //
930         // Assuming InnerStream type == typeof(NetworkStream)
931         //
932         private static void MulitpleWriteCallback(IAsyncResult transportResult)
933         {
934             if (transportResult.CompletedSynchronously)
935             {
936                 return;
937             }
938
939             GlobalLog.Assert(transportResult.AsyncState is AsyncProtocolRequest, "SslStream::MulitpleWriteCallback|State type is wrong, expected AsyncProtocolRequest.");
940
941             SplitWriteAsyncProtocolRequest asyncRequest = (SplitWriteAsyncProtocolRequest)transportResult.AsyncState;
942
943             _SslStream sslStream = (_SslStream)asyncRequest.AsyncObject;
944             try {
945                 ((NetworkStream)(sslStream._SslState.InnerStream)).EndMultipleWrite(transportResult);
946                 sslStream._SslState.FinishWrite();
947                 sslStream.StartWriting(asyncRequest.SplitWritesState, asyncRequest);
948             }
949             catch (Exception e) {
950                 if (asyncRequest.IsUserCompleted) {
951                     // This will throw on a worker thread.
952                     throw;
953                 }
954                 sslStream._SslState.FinishWrite();
955                 asyncRequest.CompleteWithError(e);
956             }
957         }
958
959         //
960         // This is used in a rare situation when async Read is resumed from completed handshake
961         //
962         private static void ResumeAsyncReadCallback(AsyncProtocolRequest request)
963         {
964             try {
965                 ((_SslStream)request.AsyncObject).StartReading(request.Buffer, request.Offset, request.Count, request);
966             }
967             catch (Exception e) {
968                 if (request.IsUserCompleted) {
969                     // This will throw on a worker thread.
970                     throw;
971                 }
972                 ((_SslStream)request.AsyncObject)._SslState.FinishRead(null);
973                 request.CompleteWithError(e);
974             }
975         }
976
977         //
978         // This is used in a rare situation when async Write is resumed from completed handshake
979         //
980         private static void ResumeAsyncWriteCallback(AsyncProtocolRequest asyncRequest)
981         {
982             try {
983                 SplitWriteAsyncProtocolRequest splitWriteRequest = asyncRequest as SplitWriteAsyncProtocolRequest;
984                 if (splitWriteRequest != null)
985                     ((_SslStream)asyncRequest.AsyncObject).StartWriting(splitWriteRequest.SplitWritesState, splitWriteRequest);
986                 else
987                     ((_SslStream)asyncRequest.AsyncObject).StartWriting(asyncRequest.Buffer, asyncRequest.Offset, asyncRequest.Count, asyncRequest);
988             }
989             catch (Exception e) {
990                 if (asyncRequest.IsUserCompleted) {
991                     // This will throw on a worker thread.
992                     throw;
993                 }
994                 ((_SslStream)asyncRequest.AsyncObject)._SslState.FinishWrite();
995                 asyncRequest.CompleteWithError(e);
996             }
997         }
998         //
999         //
1000         private static void ReadHeaderCallback(AsyncProtocolRequest asyncRequest)
1001         {
1002             // Async ONLY completion
1003             try
1004             {
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)) 
1008                 {
1009                     // in case we decrypted 0 bytes start another reading.
1010                     sslStream.StartReading(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest);
1011                 }
1012             }
1013             catch (Exception e)
1014             {
1015                 if (asyncRequest.IsUserCompleted) {
1016                     // This will throw on a worker thread.
1017                     throw;
1018                 }
1019                 asyncRequest.CompleteWithError(e);
1020             }
1021         }
1022         //
1023         //
1024         private static void ReadFrameCallback(AsyncProtocolRequest asyncRequest)
1025         {
1026             // Async ONLY completion
1027             try
1028             {
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))
1032                 {
1033                     // in case we decrypted 0 bytes start another reading.
1034                     sslStream.StartReading(bufferResult.Buffer, bufferResult.Offset, bufferResult.Count, asyncRequest);
1035                 }
1036             }
1037             catch (Exception e)
1038             {
1039                 if (asyncRequest.IsUserCompleted) {
1040                     // This will throw on a worker thread.
1041                     throw;
1042                 }
1043                 asyncRequest.CompleteWithError(e);
1044             }
1045         }
1046
1047         private class SplitWriteAsyncProtocolRequest: AsyncProtocolRequest
1048         {
1049             internal SplitWritesState SplitWritesState;  // If one buffer is no enough (such as for multiple writes)
1050
1051             internal SplitWriteAsyncProtocolRequest(LazyAsyncResult userAsyncResult): base (userAsyncResult)
1052             {
1053             }
1054
1055             internal void SetNextRequest(SplitWritesState splitWritesState, AsyncProtocolCallback callback)
1056             {
1057                 SplitWritesState = splitWritesState;
1058                 SetNextRequest(null, 0, 0,callback);
1059             }
1060         }
1061         //
1062     }
1063 }
1064 #endif