New tests.
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Tls / SslStreamBase.cs
1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3 // Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 // 
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 // 
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24
25 using System;
26 using System.Collections;
27 using System.IO;
28 using System.Net;
29 using System.Net.Sockets;
30 using System.Security.Cryptography;
31 using System.Security.Cryptography.X509Certificates;
32 using System.Threading;
33
34 namespace Mono.Security.Protocol.Tls
35 {
36         public abstract class SslStreamBase: Stream, IDisposable
37         {
38                 private delegate void AsyncHandshakeDelegate(InternalAsyncResult asyncResult, bool fromWrite);
39                 
40                 #region Fields
41
42                 static ManualResetEvent record_processing = new ManualResetEvent (true);
43
44                 private const int WaitTimeOut = 5 * 60 * 1000;
45
46                 internal Stream innerStream;
47                 internal MemoryStream inputBuffer;
48                 internal Context context;
49                 internal RecordProtocol protocol;
50                 internal bool ownsStream;
51                 private volatile bool disposed;
52                 private bool checkCertRevocationStatus;
53                 private object negotiate;
54                 private object read;
55                 private object write;
56                 private ManualResetEvent negotiationComplete;
57
58                 #endregion
59
60
61                 #region Constructors
62
63                 protected SslStreamBase(
64                         Stream stream,
65                         bool ownsStream)
66                 {
67                         if (stream == null)
68                         {
69                                 throw new ArgumentNullException("stream is null.");
70                         }
71                         if (!stream.CanRead || !stream.CanWrite)
72                         {
73                                 throw new ArgumentNullException("stream is not both readable and writable.");
74                         }
75
76                         this.inputBuffer = new MemoryStream();
77                         this.innerStream = stream;
78                         this.ownsStream = ownsStream;
79                         this.negotiate = new object();
80                         this.read = new object();
81                         this.write = new object();
82                         this.negotiationComplete = new ManualResetEvent(false);
83                 }
84
85                 #endregion
86
87                 #region Handshakes
88                 private void AsyncHandshakeCallback(IAsyncResult asyncResult)
89                 {
90                         InternalAsyncResult internalResult = asyncResult.AsyncState as InternalAsyncResult;
91
92                         try
93                         {
94                                 try
95                                 {
96                                         this.OnNegotiateHandshakeCallback(asyncResult);
97                                 }
98                                 catch (TlsException ex)
99                                 {
100                                         this.protocol.SendAlert(ex.Alert);
101
102                                         throw new IOException("The authentication or decryption has failed.", ex);
103                                 }
104                                 catch (Exception ex)
105                                 {
106                                         this.protocol.SendAlert(AlertDescription.InternalError);
107
108                                         throw new IOException("The authentication or decryption has failed.", ex);
109                                 }
110
111                                 if (internalResult.ProceedAfterHandshake)
112                                 {
113                                         //kick off the read or write process (whichever called us) after the handshake is complete
114                                         if (internalResult.FromWrite)
115                                         {
116                                                 InternalBeginWrite(internalResult);
117                                         }
118                                         else
119                                         {
120                                                 InternalBeginRead(internalResult);
121                                         }
122                                         negotiationComplete.Set();
123                                 }
124                                 else
125                                 {
126                                         negotiationComplete.Set();
127                                         internalResult.SetComplete();
128                                 }
129
130                         }
131                         catch (Exception ex)
132                         {
133                                 negotiationComplete.Set();
134                                 internalResult.SetComplete(ex);
135                         }
136                 }
137
138                 internal bool MightNeedHandshake
139                 {
140                         get
141                         {
142                                 if (this.context.HandshakeState == HandshakeState.Finished)
143                                 {
144                                         return false;
145                                 }
146                                 else
147                                 {
148                                         lock (this.negotiate)
149                                         {
150                                                 return (this.context.HandshakeState != HandshakeState.Finished);
151                                         }
152                                 }
153                         }
154                 }
155
156                 internal void NegotiateHandshake()
157                 {
158                         if (this.MightNeedHandshake)
159                         {
160                                 InternalAsyncResult ar = new InternalAsyncResult(null, null, null, 0, 0, false, false);
161
162                                 //if something already started negotiation, wait for it.
163                                 //otherwise end it ourselves.
164                                 if (!BeginNegotiateHandshake(ar))
165                                 {
166                                         this.negotiationComplete.WaitOne();
167                                 }
168                                 else
169                                 {
170                                         this.EndNegotiateHandshake(ar);
171                                 }
172                         }
173                 }
174
175                 #endregion
176
177                 #region Abstracts/Virtuals
178
179                 internal abstract IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state);
180                 internal abstract void OnNegotiateHandshakeCallback(IAsyncResult asyncResult);
181
182                 internal abstract X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates,
183                                                                                                                         X509Certificate serverCertificate,
184                                                                                                                         string targetHost,
185                                                                                                                         X509CertificateCollection serverRequestedCertificates);
186
187                 internal abstract bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors);
188                 internal abstract ValidationResult OnRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection);
189                 internal abstract bool HaveRemoteValidation2Callback { get; }
190
191                 internal abstract AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost);
192
193                 #endregion
194
195                 #region Event Methods
196
197                 internal X509Certificate RaiseLocalCertificateSelection(X509CertificateCollection certificates,
198                                                                                                                         X509Certificate remoteCertificate,
199                                                                                                                         string targetHost,
200                                                                                                                         X509CertificateCollection requestedCertificates)
201                 {
202                         return OnLocalCertificateSelection(certificates, remoteCertificate, targetHost, requestedCertificates);
203                 }
204
205                 internal bool RaiseRemoteCertificateValidation(X509Certificate certificate, int[] errors)
206                 {
207                         return OnRemoteCertificateValidation(certificate, errors);
208                 }
209
210                 internal ValidationResult RaiseRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
211                 {
212                         return OnRemoteCertificateValidation2 (collection);
213                 }
214
215                 internal AsymmetricAlgorithm RaiseLocalPrivateKeySelection(
216                         X509Certificate certificate,
217                         string targetHost)
218                 {
219                         return OnLocalPrivateKeySelection(certificate, targetHost);
220                 }
221                 #endregion
222
223                 #region Security Properties
224
225                 public bool CheckCertRevocationStatus
226                 {
227                         get { return this.checkCertRevocationStatus; }
228                         set { this.checkCertRevocationStatus = value; }
229                 }
230
231                 public CipherAlgorithmType CipherAlgorithm
232                 {
233                         get
234                         {
235                                 if (this.context.HandshakeState == HandshakeState.Finished)
236                                 {
237                                         return this.context.Current.Cipher.CipherAlgorithmType;
238                                 }
239
240                                 return CipherAlgorithmType.None;
241                         }
242                 }
243
244                 public int CipherStrength
245                 {
246                         get
247                         {
248                                 if (this.context.HandshakeState == HandshakeState.Finished)
249                                 {
250                                         return this.context.Current.Cipher.EffectiveKeyBits;
251                                 }
252
253                                 return 0;
254                         }
255                 }
256
257                 public HashAlgorithmType HashAlgorithm
258                 {
259                         get
260                         {
261                                 if (this.context.HandshakeState == HandshakeState.Finished)
262                                 {
263                                         return this.context.Current.Cipher.HashAlgorithmType;
264                                 }
265
266                                 return HashAlgorithmType.None;
267                         }
268                 }
269
270                 public int HashStrength
271                 {
272                         get
273                         {
274                                 if (this.context.HandshakeState == HandshakeState.Finished)
275                                 {
276                                         return this.context.Current.Cipher.HashSize * 8;
277                                 }
278
279                                 return 0;
280                         }
281                 }
282
283                 public int KeyExchangeStrength
284                 {
285                         get
286                         {
287                                 if (this.context.HandshakeState == HandshakeState.Finished)
288                                 {
289                                         return this.context.ServerSettings.Certificates[0].RSA.KeySize;
290                                 }
291
292                                 return 0;
293                         }
294                 }
295
296                 public ExchangeAlgorithmType KeyExchangeAlgorithm
297                 {
298                         get
299                         {
300                                 if (this.context.HandshakeState == HandshakeState.Finished)
301                                 {
302                                         return this.context.Current.Cipher.ExchangeAlgorithmType;
303                                 }
304
305                                 return ExchangeAlgorithmType.None;
306                         }
307                 }
308
309                 public SecurityProtocolType SecurityProtocol
310                 {
311                         get
312                         {
313                                 if (this.context.HandshakeState == HandshakeState.Finished)
314                                 {
315                                         return this.context.SecurityProtocol;
316                                 }
317
318                                 return 0;
319                         }
320                 }
321
322                 public X509Certificate ServerCertificate
323                 {
324                         get
325                         {
326                                 if (this.context.HandshakeState == HandshakeState.Finished)
327                                 {
328                                         if (this.context.ServerSettings.Certificates != null &&
329                                                 this.context.ServerSettings.Certificates.Count > 0)
330                                         {
331                                                 return new X509Certificate(this.context.ServerSettings.Certificates[0].RawData);
332                                         }
333                                 }
334
335                                 return null;
336                         }
337                 }
338
339                 // this is used by Mono's certmgr tool to download certificates
340                 internal Mono.Security.X509.X509CertificateCollection ServerCertificates
341                 {
342                         get { return context.ServerSettings.Certificates; }
343                 }
344
345                 #endregion
346
347                 #region Internal Async Result/State Class
348
349                 private class InternalAsyncResult : IAsyncResult
350                 {
351                         private object locker = new object ();
352                         private AsyncCallback _userCallback;
353                         private object _userState;
354                         private Exception _asyncException;
355                         private ManualResetEvent handle;
356                         private bool completed;
357                         private int _bytesRead;
358                         private bool _fromWrite;
359                         private bool _proceedAfterHandshake;
360
361                         private byte[] _buffer;
362                         private int _offset;
363                         private int _count;
364
365                         public InternalAsyncResult(AsyncCallback userCallback, object userState, byte[] buffer, int offset, int count, bool fromWrite, bool proceedAfterHandshake)
366                         {
367                                 _userCallback = userCallback;
368                                 _userState = userState;
369                                 _buffer = buffer;
370                                 _offset = offset;
371                                 _count = count;
372                                 _fromWrite = fromWrite;
373                                 _proceedAfterHandshake = proceedAfterHandshake;
374                         }
375
376                         public bool ProceedAfterHandshake
377                         {
378                                 get { return _proceedAfterHandshake; }
379                         }
380
381                         public bool FromWrite
382                         {
383                                 get { return _fromWrite; }
384                         }
385
386                         public byte[] Buffer
387                         {
388                                 get { return _buffer; }
389                         }
390
391                         public int Offset
392                         {
393                                 get { return _offset; }
394                         }
395
396                         public int Count
397                         {
398                                 get { return _count; }
399                         }
400
401                         public int BytesRead
402                         {
403                                 get { return _bytesRead; }
404                         }
405
406                         public object AsyncState
407                         {
408                                 get { return _userState; }
409                         }
410
411                         public Exception AsyncException
412                         {
413                                 get { return _asyncException; }
414                         }
415
416                         public bool CompletedWithError
417                         {
418                                 get {
419                                         if (IsCompleted == false)
420                                                 return false;
421                                         return null != _asyncException;
422                                 }
423                         }
424
425                         public WaitHandle AsyncWaitHandle
426                         {
427                                 get {
428                                         lock (locker) {
429                                                 if (handle == null)
430                                                         handle = new ManualResetEvent (completed);
431                                         }
432                                         return handle;
433                                 }
434                         }
435
436                         public bool CompletedSynchronously
437                         {
438                                 get { return false; }
439                         }
440
441                         public bool IsCompleted
442                         {
443                                 get {
444                                         lock (locker)
445                                                 return completed;
446                                 }
447                         }
448
449                         private void SetComplete(Exception ex, int bytesRead)
450                         {
451                                 lock (locker) {
452                                         if (completed)
453                                                 return;
454
455                                         completed = true;
456                                         _asyncException = ex;
457                                         _bytesRead = bytesRead;
458                                         if (handle != null)
459                                                 handle.Set ();
460                                 }
461                                 if (_userCallback != null)
462                                         _userCallback.BeginInvoke (this, null, null);
463                         }
464
465                         public void SetComplete(Exception ex)
466                         {
467                                 SetComplete(ex, 0);
468                         }
469
470                         public void SetComplete(int bytesRead)
471                         {
472                                 SetComplete(null, bytesRead);
473                         }
474
475                         public void SetComplete()
476                         {
477                                 SetComplete(null, 0);
478                         }
479                 }
480                 #endregion
481
482                 #region Stream Overrides and Async Stream Operations
483
484                 private bool BeginNegotiateHandshake(InternalAsyncResult asyncResult)
485                 {
486                         try
487                         {
488                                 lock (this.negotiate)
489                                 {
490                                         if (this.context.HandshakeState == HandshakeState.None)
491                                         {
492                                                 this.OnBeginNegotiateHandshake(new AsyncCallback(AsyncHandshakeCallback), asyncResult);
493
494                                                 return true;
495                                         }
496                                         else
497                                         {
498                                                 return false;
499                                         }
500                                 }
501                         }
502                         catch (TlsException ex)
503                         {
504                                 this.negotiationComplete.Set();
505                                 this.protocol.SendAlert(ex.Alert);
506
507                                 throw new IOException("The authentication or decryption has failed.", ex);
508                         }
509                         catch (Exception ex)
510                         {
511                                 this.negotiationComplete.Set();
512                                 this.protocol.SendAlert(AlertDescription.InternalError);
513
514                                 throw new IOException("The authentication or decryption has failed.", ex);
515                         }
516                 }
517
518                 private void EndNegotiateHandshake(InternalAsyncResult asyncResult)
519                 {
520                         if (asyncResult.IsCompleted == false)
521                                 asyncResult.AsyncWaitHandle.WaitOne();
522
523                         if (asyncResult.CompletedWithError)
524                         {
525                                 throw asyncResult.AsyncException;
526                         }
527                 }
528
529                 public override IAsyncResult BeginRead(
530                         byte[] buffer,
531                         int offset,
532                         int count,
533                         AsyncCallback callback,
534                         object state)
535                 {
536                         this.checkDisposed();
537
538                         if (buffer == null)
539                         {
540                                 throw new ArgumentNullException("buffer is a null reference.");
541                         }
542                         if (offset < 0)
543                         {
544                                 throw new ArgumentOutOfRangeException("offset is less than 0.");
545                         }
546                         if (offset > buffer.Length)
547                         {
548                                 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
549                         }
550                         if (count < 0)
551                         {
552                                 throw new ArgumentOutOfRangeException("count is less than 0.");
553                         }
554                         if (count > (buffer.Length - offset))
555                         {
556                                 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
557                         }
558
559                         InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, false, true);
560
561                         if (this.MightNeedHandshake)
562                         {
563                                 if (! BeginNegotiateHandshake(asyncResult))
564                                 {
565                                         //we made it down here so the handshake was not started.
566                                         //another thread must have started it in the mean time.
567                                         //wait for it to complete and then perform our original operation
568                                         this.negotiationComplete.WaitOne();
569
570                                         InternalBeginRead(asyncResult);
571                                 }
572                         }
573                         else
574                         {
575                                 InternalBeginRead(asyncResult);
576                         }
577
578                         return asyncResult;
579                 }
580
581                 // bigger than max record length for SSL/TLS
582                 private byte[] recbuf = new byte[16384];
583
584                 private void InternalBeginRead(InternalAsyncResult asyncResult)
585                 {
586                         try
587                         {
588                                 int preReadSize = 0;
589
590                                 lock (this.read)
591                                 {
592                                         // If actual buffer is fully read, reset it
593                                         bool shouldReset = this.inputBuffer.Position == this.inputBuffer.Length && this.inputBuffer.Length > 0;
594
595                                         // If the buffer isn't fully read, but does have data, we need to immediately
596                                         // read the info from the buffer and let the user know that they have more data.
597                                         bool shouldReadImmediately = (this.inputBuffer.Length > 0) && (asyncResult.Count > 0);
598
599                                         if (shouldReset)
600                                         {
601                                                 this.resetBuffer();
602                                         }
603                                         else if (shouldReadImmediately)
604                                         {
605                                                 preReadSize = this.inputBuffer.Read(asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
606                                         }
607                                 }
608
609                                 // This is explicitly done outside the synclock to avoid 
610                                 // any potential deadlocks in the delegate call.
611                                 if (0 < preReadSize)
612                                 {
613                                         asyncResult.SetComplete(preReadSize);
614                                 }
615                                 else if (!this.context.ReceivedConnectionEnd)
616                                 {
617                                         // this will read data from the network until we have (at least) one
618                                         // record to send back to the caller
619                                         this.innerStream.BeginRead(recbuf, 0, recbuf.Length,
620                                                 new AsyncCallback(InternalReadCallback), new object[] { recbuf, asyncResult });
621                                 }
622                                 else
623                                 {
624                                         // We're done with the connection so we need to let the caller know with 0 bytes read
625                                         asyncResult.SetComplete(0);
626                                 }
627                         }
628                         catch (TlsException ex)
629                         {
630                                 this.protocol.SendAlert(ex.Alert);
631
632                                 throw new IOException("The authentication or decryption has failed.", ex);
633                         }
634                         catch (Exception ex)
635                         {
636                                 throw new IOException("IO exception during read.", ex);
637                         }
638                 }
639
640
641                 private MemoryStream recordStream = new MemoryStream();
642
643                 // read encrypted data until we have enough to decrypt (at least) one
644                 // record and return are the records (may be more than one) we have
645                 private void InternalReadCallback(IAsyncResult result)
646                 {
647                         if (this.disposed)
648                                 return;
649
650                         object[] state = (object[])result.AsyncState;
651                         byte[] recbuf = (byte[])state[0];
652                         InternalAsyncResult internalResult = (InternalAsyncResult)state[1];
653
654                         try
655                         {
656                                 int n = innerStream.EndRead(result);
657                                 if (n > 0)
658                                 {
659                                         // Add the just received data to the waiting data
660                                         recordStream.Write(recbuf, 0, n);
661                                 }
662                                 else
663                                 {
664                                         // 0 length data means this read operation is done (lost connection in the case of a network stream).
665                                         internalResult.SetComplete(0);
666                                         return;
667                                 }
668
669                                 bool dataToReturn = false;
670                                 long pos = recordStream.Position;
671
672                                 recordStream.Position = 0;
673                                 byte[] record = null;
674
675                                 // don't try to decode record unless we have at least 5 bytes
676                                 // i.e. type (1), protocol (2) and length (2)
677                                 if (recordStream.Length >= 5)
678                                 {
679                                         record = this.protocol.ReceiveRecord(recordStream);
680                                 }
681
682                                 // a record of 0 length is valid (and there may be more record after it)
683                                 while (record != null)
684                                 {
685                                         // we probably received more stuff after the record, and we must keep it!
686                                         long remainder = recordStream.Length - recordStream.Position;
687                                         byte[] outofrecord = null;
688                                         if (remainder > 0)
689                                         {
690                                                 outofrecord = new byte[remainder];
691                                                 recordStream.Read(outofrecord, 0, outofrecord.Length);
692                                         }
693
694                                         lock (this.read)
695                                         {
696                                                 long position = this.inputBuffer.Position;
697
698                                                 if (record.Length > 0)
699                                                 {
700                                                         // Write new data to the inputBuffer
701                                                         this.inputBuffer.Seek(0, SeekOrigin.End);
702                                                         this.inputBuffer.Write(record, 0, record.Length);
703
704                                                         // Restore buffer position
705                                                         this.inputBuffer.Seek(position, SeekOrigin.Begin);
706                                                         dataToReturn = true;
707                                                 }
708                                         }
709
710                                         recordStream.SetLength(0);
711                                         record = null;
712
713                                         if (remainder > 0)
714                                         {
715                                                 recordStream.Write(outofrecord, 0, outofrecord.Length);
716                                                 // type (1), protocol (2) and length (2)
717                                                 if (recordStream.Length >= 5)
718                                                 {
719                                                         // try to see if another record is available
720                                                         recordStream.Position = 0;
721                                                         record = this.protocol.ReceiveRecord(recordStream);
722                                                         if (record == null)
723                                                                 pos = recordStream.Length;
724                                                 }
725                                                 else
726                                                         pos = remainder;
727                                         }
728                                         else
729                                                 pos = 0;
730                                 }
731
732                                 if (!dataToReturn && (n > 0))
733                                 {
734                                         if (context.ReceivedConnectionEnd) {
735                                                 internalResult.SetComplete (0);
736                                         } else {
737                                                 // there is no record to return to caller and (possibly) more data waiting
738                                                 // so continue reading from network (and appending to stream)
739                                                 recordStream.Position = recordStream.Length;
740                                                 this.innerStream.BeginRead(recbuf, 0, recbuf.Length,
741                                                         new AsyncCallback(InternalReadCallback), state);
742                                         }
743                                 }
744                                 else
745                                 {
746                                         // we have record(s) to return -or- no more available to read from network
747                                         // reset position for further reading
748                                         recordStream.Position = pos;
749
750                                         int bytesRead = 0;
751                                         lock (this.read)
752                                         {
753                                                 bytesRead = this.inputBuffer.Read(internalResult.Buffer, internalResult.Offset, internalResult.Count);
754                                         }
755
756                                         internalResult.SetComplete(bytesRead);
757                                 }
758                         }
759                         catch (Exception ex)
760                         {
761                                 internalResult.SetComplete(ex);
762                         }
763
764                 }
765
766                 private void InternalBeginWrite(InternalAsyncResult asyncResult)
767                 {
768                         try
769                         {
770                                 // Send the buffer as a TLS record
771
772                                 lock (this.write)
773                                 {
774                                         byte[] record = this.protocol.EncodeRecord(
775                                                 ContentType.ApplicationData, asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
776
777                                         this.innerStream.BeginWrite(
778                                                 record, 0, record.Length, new AsyncCallback(InternalWriteCallback), asyncResult);
779                                 }
780                         }
781                         catch (TlsException ex)
782                         {
783                                 this.protocol.SendAlert(ex.Alert);
784                                 this.Close();
785
786                                 throw new IOException("The authentication or decryption has failed.", ex);
787                         }
788                         catch (Exception ex)
789                         {
790                                 throw new IOException("IO exception during Write.", ex);
791                         }
792                 }
793
794                 private void InternalWriteCallback(IAsyncResult ar)
795                 {
796                         if (this.disposed)
797                                 return;
798                         
799                         InternalAsyncResult internalResult = (InternalAsyncResult)ar.AsyncState;
800
801                         try
802                         {
803                                 this.innerStream.EndWrite(ar);
804                                 internalResult.SetComplete();
805                         }
806                         catch (Exception ex)
807                         {
808                                 internalResult.SetComplete(ex);
809                         }
810                 }
811
812                 public override IAsyncResult BeginWrite(
813                         byte[] buffer,
814                         int offset,
815                         int count,
816                         AsyncCallback callback,
817                         object state)
818                 {
819                         this.checkDisposed();
820
821                         if (buffer == null)
822                         {
823                                 throw new ArgumentNullException("buffer is a null reference.");
824                         }
825                         if (offset < 0)
826                         {
827                                 throw new ArgumentOutOfRangeException("offset is less than 0.");
828                         }
829                         if (offset > buffer.Length)
830                         {
831                                 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
832                         }
833                         if (count < 0)
834                         {
835                                 throw new ArgumentOutOfRangeException("count is less than 0.");
836                         }
837                         if (count > (buffer.Length - offset))
838                         {
839                                 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
840                         }
841
842
843                         InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, true, true);
844
845                         if (this.MightNeedHandshake)
846                         {
847                                 if (! BeginNegotiateHandshake(asyncResult))
848                                 {
849                                         //we made it down here so the handshake was not started.
850                                         //another thread must have started it in the mean time.
851                                         //wait for it to complete and then perform our original operation
852                                         this.negotiationComplete.WaitOne();
853
854                                         InternalBeginWrite(asyncResult);
855                                 }
856                         }
857                         else
858                         {
859                                 InternalBeginWrite(asyncResult);
860                         }
861
862                         return asyncResult;
863                 }
864
865                 public override int EndRead(IAsyncResult asyncResult)
866                 {
867                         this.checkDisposed();
868
869                         InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
870                         if (internalResult == null)
871                         {
872                                 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
873                         }
874
875                         // Always wait until the read is complete
876                         if (!asyncResult.IsCompleted)
877                         {
878                                 if (!asyncResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
879                                         throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndRead");
880                         }
881
882                         if (internalResult.CompletedWithError)
883                         {
884                                 throw internalResult.AsyncException;
885                         }
886
887                         return internalResult.BytesRead;
888                 }
889
890                 public override void EndWrite(IAsyncResult asyncResult)
891                 {
892                         this.checkDisposed();
893
894                         InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
895                         if (internalResult == null)
896                         {
897                                 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginWrite.");
898                         }
899
900
901                         if (!asyncResult.IsCompleted)
902                         {
903                                 if (!internalResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
904                                         throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndWrite");
905                         }
906
907                         if (internalResult.CompletedWithError)
908                         {
909                                 throw internalResult.AsyncException;
910                         }
911                 }
912
913                 public override void Close()
914                 {
915                         base.Close ();
916                 }
917
918                 public override void Flush()
919                 {
920                         this.checkDisposed();
921
922                         this.innerStream.Flush();
923                 }
924
925                 public int Read(byte[] buffer)
926                 {
927                         return this.Read(buffer, 0, buffer.Length);
928                 }
929
930                 public override int Read(byte[] buffer, int offset, int count)
931                 {
932                         this.checkDisposed ();
933                         
934                         if (buffer == null)
935                         {
936                                 throw new ArgumentNullException ("buffer");
937                         }
938                         if (offset < 0)
939                         {
940                                 throw new ArgumentOutOfRangeException("offset is less than 0.");
941                         }
942                         if (offset > buffer.Length)
943                         {
944                                 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
945                         }
946                         if (count < 0)
947                         {
948                                 throw new ArgumentOutOfRangeException("count is less than 0.");
949                         }
950                         if (count > (buffer.Length - offset))
951                         {
952                                 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
953                         }
954
955                         if (this.context.HandshakeState != HandshakeState.Finished)
956                         {
957                                 this.NegotiateHandshake (); // Handshake negotiation
958                         }
959
960                         lock (this.read) {
961                                 try {
962                                         record_processing.Reset ();
963                                         // do we already have some decrypted data ?
964                                         if (this.inputBuffer.Position > 0) {
965                                                 // or maybe we used all the buffer before ?
966                                                 if (this.inputBuffer.Position == this.inputBuffer.Length) {
967                                                         this.inputBuffer.SetLength (0);
968                                                 } else {
969                                                         int n = this.inputBuffer.Read (buffer, offset, count);
970                                                         if (n > 0) {
971                                                                 record_processing.Set ();
972                                                                 return n;
973                                                         }
974                                                 }
975                                         }
976
977                                         bool needMoreData = false;
978                                         while (true) {
979                                                 // we first try to process the read with the data we already have
980                                                 if ((recordStream.Position == 0) || needMoreData) {
981                                                         needMoreData = false;
982                                                         // if we loop, then it either means we need more data
983                                                         byte[] recbuf = new byte[16384];
984                                                         int n = 0;
985                                                         if (count == 1) {
986                                                                 int value = innerStream.ReadByte ();
987                                                                 if (value >= 0) {
988                                                                         recbuf[0] = (byte) value;
989                                                                         n = 1;
990                                                                 }
991                                                         } else {
992                                                                 n = innerStream.Read (recbuf, 0, recbuf.Length);
993                                                         }
994                                                         if (n > 0) {
995                                                                 // Add the new received data to the waiting data
996                                                                 if ((recordStream.Length > 0) && (recordStream.Position != recordStream.Length))
997                                                                         recordStream.Seek (0, SeekOrigin.End);
998                                                                 recordStream.Write (recbuf, 0, n);
999                                                         } else {
1000                                                                 // or that the read operation is done (lost connection in the case of a network stream).
1001                                                                 record_processing.Set ();
1002                                                                 return 0;
1003                                                         }
1004                                                 }
1005
1006                                                 bool dataToReturn = false;
1007
1008                                                 recordStream.Position = 0;
1009                                                 byte[] record = null;
1010
1011                                                 // don't try to decode record unless we have at least 5 bytes
1012                                                 // i.e. type (1), protocol (2) and length (2)
1013                                                 if (recordStream.Length >= 5) {
1014                                                         record = this.protocol.ReceiveRecord (recordStream);
1015                                                         needMoreData = (record == null);
1016                                                 }
1017
1018                                                 // a record of 0 length is valid (and there may be more record after it)
1019                                                 while (record != null) {
1020                                                         // we probably received more stuff after the record, and we must keep it!
1021                                                         long remainder = recordStream.Length - recordStream.Position;
1022                                                         byte[] outofrecord = null;
1023                                                         if (remainder > 0) {
1024                                                                 outofrecord = new byte[remainder];
1025                                                                 recordStream.Read (outofrecord, 0, outofrecord.Length);
1026                                                         }
1027
1028                                                         long position = this.inputBuffer.Position;
1029
1030                                                         if (record.Length > 0) {
1031                                                                 // Write new data to the inputBuffer
1032                                                                 this.inputBuffer.Seek (0, SeekOrigin.End);
1033                                                                 this.inputBuffer.Write (record, 0, record.Length);
1034
1035                                                                 // Restore buffer position
1036                                                                 this.inputBuffer.Seek (position, SeekOrigin.Begin);
1037                                                                 dataToReturn = true;
1038                                                         }
1039
1040                                                         recordStream.SetLength (0);
1041                                                         record = null;
1042
1043                                                         if (remainder > 0) {
1044                                                                 recordStream.Write (outofrecord, 0, outofrecord.Length);
1045                                                         }
1046
1047                                                         if (dataToReturn) {
1048                                                                 // we have record(s) to return -or- no more available to read from network
1049                                                                 // reset position for further reading
1050                                                                 int i = inputBuffer.Read (buffer, offset, count);
1051                                                                 record_processing.Set ();
1052                                                                 return i;
1053                                                         }
1054                                                 }
1055                                         }
1056                                 }
1057                                 catch (TlsException ex)
1058                                 {
1059                                         throw new IOException("The authentication or decryption has failed.", ex);
1060                                 }
1061                                 catch (Exception ex)
1062                                 {
1063                                         throw new IOException("IO exception during read.", ex);
1064                                 }
1065                         }
1066                 }
1067
1068                 public override long Seek(long offset, SeekOrigin origin)
1069                 {
1070                         throw new NotSupportedException();
1071                 }
1072
1073                 public override void SetLength(long value)
1074                 {
1075                         throw new NotSupportedException();
1076                 }
1077
1078                 public void Write(byte[] buffer)
1079                 {
1080                         this.Write(buffer, 0, buffer.Length);
1081                 }
1082
1083                 public override void Write(byte[] buffer, int offset, int count)
1084                 {
1085                         this.checkDisposed ();
1086                         
1087                         if (buffer == null)
1088                         {
1089                                 throw new ArgumentNullException ("buffer");
1090                         }
1091                         if (offset < 0)
1092                         {
1093                                 throw new ArgumentOutOfRangeException("offset is less than 0.");
1094                         }
1095                         if (offset > buffer.Length)
1096                         {
1097                                 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
1098                         }
1099                         if (count < 0)
1100                         {
1101                                 throw new ArgumentOutOfRangeException("count is less than 0.");
1102                         }
1103                         if (count > (buffer.Length - offset))
1104                         {
1105                                 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
1106                         }
1107
1108                         if (this.context.HandshakeState != HandshakeState.Finished)
1109                         {
1110                                 this.NegotiateHandshake ();
1111                         }
1112
1113                         lock (this.write)
1114                         {
1115                                 try
1116                                 {
1117                                         // Send the buffer as a TLS record
1118                                         byte[] record = this.protocol.EncodeRecord (ContentType.ApplicationData, buffer, offset, count);
1119                                         this.innerStream.Write (record, 0, record.Length);
1120                                 }
1121                                 catch (TlsException ex)
1122                                 {
1123                                         this.protocol.SendAlert(ex.Alert);
1124                                         this.Close();
1125                                         throw new IOException("The authentication or decryption has failed.", ex);
1126                                 }
1127                                 catch (Exception ex)
1128                                 {
1129                                         throw new IOException("IO exception during Write.", ex);
1130                                 }
1131                         }
1132                 }
1133
1134                 public override bool CanRead
1135                 {
1136                         get { return this.innerStream.CanRead; }
1137                 }
1138
1139                 public override bool CanSeek
1140                 {
1141                         get { return false; }
1142                 }
1143
1144                 public override bool CanWrite
1145                 {
1146                         get { return this.innerStream.CanWrite; }
1147                 }
1148
1149                 public override long Length
1150                 {
1151                         get { throw new NotSupportedException(); }
1152                 }
1153
1154                 public override long Position
1155                 {
1156                         get
1157                         {
1158                                 throw new NotSupportedException();
1159                         }
1160                         set
1161                         {
1162                                 throw new NotSupportedException();
1163                         }
1164                 }
1165                 #endregion
1166
1167                 #region IDisposable Members and Finalizer
1168
1169                 ~SslStreamBase()
1170                 {
1171                         this.Dispose(false);
1172                 }
1173
1174                 protected override void Dispose (bool disposing)
1175                 {
1176                         if (!this.disposed)
1177                         {
1178                                 if (disposing)
1179                                 {
1180                                         if (this.innerStream != null)
1181                                         {
1182                                                 if (this.context.HandshakeState == HandshakeState.Finished &&
1183                                                         !this.context.SentConnectionEnd)
1184                                                 {
1185                                                         // Write close notify
1186                                                         try {
1187                                                                 this.protocol.SendAlert(AlertDescription.CloseNotify);
1188                                                         } catch {
1189                                                         }
1190                                                 }
1191
1192                                                 if (this.ownsStream)
1193                                                 {
1194                                                         // Close inner stream
1195                                                         this.innerStream.Close();
1196                                                 }
1197                                         }
1198                                         this.ownsStream = false;
1199                                         this.innerStream = null;
1200                                 }
1201
1202                                 this.disposed = true;
1203                                 base.Dispose (disposing);
1204                         }
1205                 }
1206
1207                 #endregion
1208
1209                 #region Misc Methods
1210
1211                 private void resetBuffer()
1212                 {
1213                         this.inputBuffer.SetLength(0);
1214                         this.inputBuffer.Position = 0;
1215                 }
1216
1217                 internal void checkDisposed()
1218                 {
1219                         if (this.disposed)
1220                         {
1221                                 throw new ObjectDisposedException("The Stream is closed.");
1222                         }
1223                 }
1224
1225                 #endregion
1226
1227         }
1228 }