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