Moved chain building and validation from Mono.Security to System
[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.ConnectionEnd)
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                                         // there is no record to return to caller and (possibly) more data waiting
735                                         // so continue reading from network (and appending to stream)
736                                         recordStream.Position = recordStream.Length;
737                                         this.innerStream.BeginRead(recbuf, 0, recbuf.Length,
738                                                 new AsyncCallback(InternalReadCallback), state);
739                                 }
740                                 else
741                                 {
742                                         // we have record(s) to return -or- no more available to read from network
743                                         // reset position for further reading
744                                         recordStream.Position = pos;
745
746                                         int bytesRead = 0;
747                                         lock (this.read)
748                                         {
749                                                 bytesRead = this.inputBuffer.Read(internalResult.Buffer, internalResult.Offset, internalResult.Count);
750                                         }
751
752                                         internalResult.SetComplete(bytesRead);
753                                 }
754                         }
755                         catch (Exception ex)
756                         {
757                                 internalResult.SetComplete(ex);
758                         }
759
760                 }
761
762                 private void InternalBeginWrite(InternalAsyncResult asyncResult)
763                 {
764                         try
765                         {
766                                 // Send the buffer as a TLS record
767
768                                 lock (this.write)
769                                 {
770                                         byte[] record = this.protocol.EncodeRecord(
771                                                 ContentType.ApplicationData, asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
772
773                                         this.innerStream.BeginWrite(
774                                                 record, 0, record.Length, new AsyncCallback(InternalWriteCallback), asyncResult);
775                                 }
776                         }
777                         catch (TlsException ex)
778                         {
779                                 this.protocol.SendAlert(ex.Alert);
780                                 this.Close();
781
782                                 throw new IOException("The authentication or decryption has failed.", ex);
783                         }
784                         catch (Exception ex)
785                         {
786                                 throw new IOException("IO exception during Write.", ex);
787                         }
788                 }
789
790                 private void InternalWriteCallback(IAsyncResult ar)
791                 {
792                         if (this.disposed)
793                                 return;
794                         
795                         InternalAsyncResult internalResult = (InternalAsyncResult)ar.AsyncState;
796
797                         try
798                         {
799                                 this.innerStream.EndWrite(ar);
800                                 internalResult.SetComplete();
801                         }
802                         catch (Exception ex)
803                         {
804                                 internalResult.SetComplete(ex);
805                         }
806                 }
807
808                 public override IAsyncResult BeginWrite(
809                         byte[] buffer,
810                         int offset,
811                         int count,
812                         AsyncCallback callback,
813                         object state)
814                 {
815                         this.checkDisposed();
816
817                         if (buffer == null)
818                         {
819                                 throw new ArgumentNullException("buffer is a null reference.");
820                         }
821                         if (offset < 0)
822                         {
823                                 throw new ArgumentOutOfRangeException("offset is less than 0.");
824                         }
825                         if (offset > buffer.Length)
826                         {
827                                 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
828                         }
829                         if (count < 0)
830                         {
831                                 throw new ArgumentOutOfRangeException("count is less than 0.");
832                         }
833                         if (count > (buffer.Length - offset))
834                         {
835                                 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
836                         }
837
838
839                         InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, true, true);
840
841                         if (this.MightNeedHandshake)
842                         {
843                                 if (! BeginNegotiateHandshake(asyncResult))
844                                 {
845                                         //we made it down here so the handshake was not started.
846                                         //another thread must have started it in the mean time.
847                                         //wait for it to complete and then perform our original operation
848                                         this.negotiationComplete.WaitOne();
849
850                                         InternalBeginWrite(asyncResult);
851                                 }
852                         }
853                         else
854                         {
855                                 InternalBeginWrite(asyncResult);
856                         }
857
858                         return asyncResult;
859                 }
860
861                 public override int EndRead(IAsyncResult asyncResult)
862                 {
863                         this.checkDisposed();
864
865                         InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
866                         if (internalResult == null)
867                         {
868                                 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
869                         }
870
871                         // Always wait until the read is complete
872                         if (!asyncResult.IsCompleted)
873                         {
874                                 if (!asyncResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
875                                         throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndRead");
876                         }
877
878                         if (internalResult.CompletedWithError)
879                         {
880                                 throw internalResult.AsyncException;
881                         }
882
883                         return internalResult.BytesRead;
884                 }
885
886                 public override void EndWrite(IAsyncResult asyncResult)
887                 {
888                         this.checkDisposed();
889
890                         InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
891                         if (internalResult == null)
892                         {
893                                 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginWrite.");
894                         }
895
896
897                         if (!asyncResult.IsCompleted)
898                         {
899                                 if (!internalResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
900                                         throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndWrite");
901                         }
902
903                         if (internalResult.CompletedWithError)
904                         {
905                                 throw internalResult.AsyncException;
906                         }
907                 }
908
909                 public override void Close()
910                 {
911                         base.Close ();
912                 }
913
914                 public override void Flush()
915                 {
916                         this.checkDisposed();
917
918                         this.innerStream.Flush();
919                 }
920
921                 public int Read(byte[] buffer)
922                 {
923                         return this.Read(buffer, 0, buffer.Length);
924                 }
925
926                 public override int Read(byte[] buffer, int offset, int count)
927                 {
928                         this.checkDisposed ();
929                         
930                         if (buffer == null)
931                         {
932                                 throw new ArgumentNullException ("buffer");
933                         }
934                         if (offset < 0)
935                         {
936                                 throw new ArgumentOutOfRangeException("offset is less than 0.");
937                         }
938                         if (offset > buffer.Length)
939                         {
940                                 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
941                         }
942                         if (count < 0)
943                         {
944                                 throw new ArgumentOutOfRangeException("count is less than 0.");
945                         }
946                         if (count > (buffer.Length - offset))
947                         {
948                                 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
949                         }
950
951                         if (this.context.HandshakeState != HandshakeState.Finished)
952                         {
953                                 this.NegotiateHandshake (); // Handshake negotiation
954                         }
955
956                         lock (this.read) {
957                                 try {
958                                         record_processing.Reset ();
959                                         // do we already have some decrypted data ?
960                                         if (this.inputBuffer.Position > 0) {
961                                                 // or maybe we used all the buffer before ?
962                                                 if (this.inputBuffer.Position == this.inputBuffer.Length) {
963                                                         this.inputBuffer.SetLength (0);
964                                                 } else {
965                                                         int n = this.inputBuffer.Read (buffer, offset, count);
966                                                         if (n > 0) {
967                                                                 record_processing.Set ();
968                                                                 return n;
969                                                         }
970                                                 }
971                                         }
972
973                                         bool needMoreData = false;
974                                         while (true) {
975                                                 // we first try to process the read with the data we already have
976                                                 if ((recordStream.Position == 0) || needMoreData) {
977                                                         needMoreData = false;
978                                                         // if we loop, then it either means we need more data
979                                                         byte[] recbuf = new byte[16384];
980                                                         int n = 0;
981                                                         if (count == 1) {
982                                                                 int value = innerStream.ReadByte ();
983                                                                 if (value >= 0) {
984                                                                         recbuf[0] = (byte) value;
985                                                                         n = 1;
986                                                                 }
987                                                         } else {
988                                                                 n = innerStream.Read (recbuf, 0, recbuf.Length);
989                                                         }
990                                                         if (n > 0) {
991                                                                 // Add the new received data to the waiting data
992                                                                 if ((recordStream.Length > 0) && (recordStream.Position != recordStream.Length))
993                                                                         recordStream.Seek (0, SeekOrigin.End);
994                                                                 recordStream.Write (recbuf, 0, n);
995                                                         } else {
996                                                                 // or that the read operation is done (lost connection in the case of a network stream).
997                                                                 record_processing.Set ();
998                                                                 return 0;
999                                                         }
1000                                                 }
1001
1002                                                 bool dataToReturn = false;
1003
1004                                                 recordStream.Position = 0;
1005                                                 byte[] record = null;
1006
1007                                                 // don't try to decode record unless we have at least 5 bytes
1008                                                 // i.e. type (1), protocol (2) and length (2)
1009                                                 if (recordStream.Length >= 5) {
1010                                                         record = this.protocol.ReceiveRecord (recordStream);
1011                                                         needMoreData = (record == null);
1012                                                 }
1013
1014                                                 // a record of 0 length is valid (and there may be more record after it)
1015                                                 while (record != null) {
1016                                                         // we probably received more stuff after the record, and we must keep it!
1017                                                         long remainder = recordStream.Length - recordStream.Position;
1018                                                         byte[] outofrecord = null;
1019                                                         if (remainder > 0) {
1020                                                                 outofrecord = new byte[remainder];
1021                                                                 recordStream.Read (outofrecord, 0, outofrecord.Length);
1022                                                         }
1023
1024                                                         long position = this.inputBuffer.Position;
1025
1026                                                         if (record.Length > 0) {
1027                                                                 // Write new data to the inputBuffer
1028                                                                 this.inputBuffer.Seek (0, SeekOrigin.End);
1029                                                                 this.inputBuffer.Write (record, 0, record.Length);
1030
1031                                                                 // Restore buffer position
1032                                                                 this.inputBuffer.Seek (position, SeekOrigin.Begin);
1033                                                                 dataToReturn = true;
1034                                                         }
1035
1036                                                         recordStream.SetLength (0);
1037                                                         record = null;
1038
1039                                                         if (remainder > 0) {
1040                                                                 recordStream.Write (outofrecord, 0, outofrecord.Length);
1041                                                         }
1042
1043                                                         if (dataToReturn) {
1044                                                                 // we have record(s) to return -or- no more available to read from network
1045                                                                 // reset position for further reading
1046                                                                 int i = inputBuffer.Read (buffer, offset, count);
1047                                                                 record_processing.Set ();
1048                                                                 return i;
1049                                                         }
1050                                                 }
1051                                         }
1052                                 }
1053                                 catch (TlsException ex)
1054                                 {
1055                                         throw new IOException("The authentication or decryption has failed.", ex);
1056                                 }
1057                                 catch (Exception ex)
1058                                 {
1059                                         throw new IOException("IO exception during read.", ex);
1060                                 }
1061                         }
1062                 }
1063
1064                 public override long Seek(long offset, SeekOrigin origin)
1065                 {
1066                         throw new NotSupportedException();
1067                 }
1068
1069                 public override void SetLength(long value)
1070                 {
1071                         throw new NotSupportedException();
1072                 }
1073
1074                 public void Write(byte[] buffer)
1075                 {
1076                         this.Write(buffer, 0, buffer.Length);
1077                 }
1078
1079                 public override void Write(byte[] buffer, int offset, int count)
1080                 {
1081                         this.checkDisposed ();
1082                         
1083                         if (buffer == null)
1084                         {
1085                                 throw new ArgumentNullException ("buffer");
1086                         }
1087                         if (offset < 0)
1088                         {
1089                                 throw new ArgumentOutOfRangeException("offset is less than 0.");
1090                         }
1091                         if (offset > buffer.Length)
1092                         {
1093                                 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
1094                         }
1095                         if (count < 0)
1096                         {
1097                                 throw new ArgumentOutOfRangeException("count is less than 0.");
1098                         }
1099                         if (count > (buffer.Length - offset))
1100                         {
1101                                 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
1102                         }
1103
1104                         if (this.context.HandshakeState != HandshakeState.Finished)
1105                         {
1106                                 this.NegotiateHandshake ();
1107                         }
1108
1109                         lock (this.write)
1110                         {
1111                                 try
1112                                 {
1113                                         // Send the buffer as a TLS record
1114                                         byte[] record = this.protocol.EncodeRecord (ContentType.ApplicationData, buffer, offset, count);
1115                                         this.innerStream.Write (record, 0, record.Length);
1116                                 }
1117                                 catch (TlsException ex)
1118                                 {
1119                                         this.protocol.SendAlert(ex.Alert);
1120                                         this.Close();
1121                                         throw new IOException("The authentication or decryption has failed.", ex);
1122                                 }
1123                                 catch (Exception ex)
1124                                 {
1125                                         throw new IOException("IO exception during Write.", ex);
1126                                 }
1127                         }
1128                 }
1129
1130                 public override bool CanRead
1131                 {
1132                         get { return this.innerStream.CanRead; }
1133                 }
1134
1135                 public override bool CanSeek
1136                 {
1137                         get { return false; }
1138                 }
1139
1140                 public override bool CanWrite
1141                 {
1142                         get { return this.innerStream.CanWrite; }
1143                 }
1144
1145                 public override long Length
1146                 {
1147                         get { throw new NotSupportedException(); }
1148                 }
1149
1150                 public override long Position
1151                 {
1152                         get
1153                         {
1154                                 throw new NotSupportedException();
1155                         }
1156                         set
1157                         {
1158                                 throw new NotSupportedException();
1159                         }
1160                 }
1161                 #endregion
1162
1163                 #region IDisposable Members and Finalizer
1164
1165                 ~SslStreamBase()
1166                 {
1167                         this.Dispose(false);
1168                 }
1169
1170                 protected override void Dispose (bool disposing)
1171                 {
1172                         if (!this.disposed)
1173                         {
1174                                 if (disposing)
1175                                 {
1176                                         if (this.innerStream != null)
1177                                         {
1178                                                 if (this.context.HandshakeState == HandshakeState.Finished &&
1179                                                         !this.context.ConnectionEnd)
1180                                                 {
1181                                                         // Write close notify                                                   
1182                                                         this.protocol.SendAlert(AlertDescription.CloseNotify);
1183                                                 }
1184
1185                                                 if (this.ownsStream)
1186                                                 {
1187                                                         // Close inner stream
1188                                                         this.innerStream.Close();
1189                                                 }
1190                                         }
1191                                         this.ownsStream = false;
1192                                         this.innerStream = null;
1193                                 }
1194
1195                                 this.disposed = true;
1196                                 base.Dispose (disposing);
1197                         }
1198                 }
1199
1200                 #endregion
1201
1202                 #region Misc Methods
1203
1204                 private void resetBuffer()
1205                 {
1206                         this.inputBuffer.SetLength(0);
1207                         this.inputBuffer.Position = 0;
1208                 }
1209
1210                 internal void checkDisposed()
1211                 {
1212                         if (this.disposed)
1213                         {
1214                                 throw new ObjectDisposedException("The Stream is closed.");
1215                         }
1216                 }
1217
1218                 #endregion
1219
1220         }
1221 }