2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / Mono.Security / Mono.Security.Protocol.Tls / SslStreamBase.cs
blobf347ccb2bdf72f6e32c170de56b59e468f05f6a5
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.
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;
34 namespace Mono.Security.Protocol.Tls
36 public abstract class SslStreamBase: Stream, IDisposable
38 private delegate void AsyncHandshakeDelegate(InternalAsyncResult asyncResult, bool fromWrite);
40 #region Fields
42 static ManualResetEvent record_processing = new ManualResetEvent (true);
44 private const int WaitTimeOut = 5 * 60 * 1000;
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;
58 #endregion
61 #region Constructors
63 protected SslStreamBase(
64 Stream stream,
65 bool ownsStream)
67 if (stream == null)
69 throw new ArgumentNullException("stream is null.");
71 if (!stream.CanRead || !stream.CanWrite)
73 throw new ArgumentNullException("stream is not both readable and writable.");
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);
85 #endregion
87 #region Handshakes
88 private void AsyncHandshakeCallback(IAsyncResult asyncResult)
90 InternalAsyncResult internalResult = asyncResult.AsyncState as InternalAsyncResult;
92 try
94 try
96 this.OnNegotiateHandshakeCallback(asyncResult);
98 catch (TlsException ex)
100 this.protocol.SendAlert(ex.Alert);
102 throw new IOException("The authentication or decryption has failed.", ex);
104 catch (Exception ex)
106 this.protocol.SendAlert(AlertDescription.InternalError);
108 throw new IOException("The authentication or decryption has failed.", ex);
111 if (internalResult.ProceedAfterHandshake)
113 //kick off the read or write process (whichever called us) after the handshake is complete
114 if (internalResult.FromWrite)
116 InternalBeginWrite(internalResult);
118 else
120 InternalBeginRead(internalResult);
122 negotiationComplete.Set();
124 else
126 negotiationComplete.Set();
127 internalResult.SetComplete();
131 catch (Exception ex)
133 negotiationComplete.Set();
134 internalResult.SetComplete(ex);
138 internal bool MightNeedHandshake
142 if (this.context.HandshakeState == HandshakeState.Finished)
144 return false;
146 else
148 lock (this.negotiate)
150 return (this.context.HandshakeState != HandshakeState.Finished);
156 internal void NegotiateHandshake()
158 if (this.MightNeedHandshake)
160 InternalAsyncResult ar = new InternalAsyncResult(null, null, null, 0, 0, false, false);
162 //if something already started negotiation, wait for it.
163 //otherwise end it ourselves.
164 if (!BeginNegotiateHandshake(ar))
166 this.negotiationComplete.WaitOne();
168 else
170 this.EndNegotiateHandshake(ar);
175 #endregion
177 #region Abstracts/Virtuals
179 internal abstract IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state);
180 internal abstract void OnNegotiateHandshakeCallback(IAsyncResult asyncResult);
182 internal abstract X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates,
183 X509Certificate serverCertificate,
184 string targetHost,
185 X509CertificateCollection serverRequestedCertificates);
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; }
191 internal abstract AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost);
193 #endregion
195 #region Event Methods
197 internal X509Certificate RaiseLocalCertificateSelection(X509CertificateCollection certificates,
198 X509Certificate remoteCertificate,
199 string targetHost,
200 X509CertificateCollection requestedCertificates)
202 return OnLocalCertificateSelection(certificates, remoteCertificate, targetHost, requestedCertificates);
205 internal bool RaiseRemoteCertificateValidation(X509Certificate certificate, int[] errors)
207 return OnRemoteCertificateValidation(certificate, errors);
210 internal ValidationResult RaiseRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
212 return OnRemoteCertificateValidation2 (collection);
215 internal AsymmetricAlgorithm RaiseLocalPrivateKeySelection(
216 X509Certificate certificate,
217 string targetHost)
219 return OnLocalPrivateKeySelection(certificate, targetHost);
221 #endregion
223 #region Security Properties
225 public bool CheckCertRevocationStatus
227 get { return this.checkCertRevocationStatus; }
228 set { this.checkCertRevocationStatus = value; }
231 public CipherAlgorithmType CipherAlgorithm
235 if (this.context.HandshakeState == HandshakeState.Finished)
237 return this.context.Current.Cipher.CipherAlgorithmType;
240 return CipherAlgorithmType.None;
244 public int CipherStrength
248 if (this.context.HandshakeState == HandshakeState.Finished)
250 return this.context.Current.Cipher.EffectiveKeyBits;
253 return 0;
257 public HashAlgorithmType HashAlgorithm
261 if (this.context.HandshakeState == HandshakeState.Finished)
263 return this.context.Current.Cipher.HashAlgorithmType;
266 return HashAlgorithmType.None;
270 public int HashStrength
274 if (this.context.HandshakeState == HandshakeState.Finished)
276 return this.context.Current.Cipher.HashSize * 8;
279 return 0;
283 public int KeyExchangeStrength
287 if (this.context.HandshakeState == HandshakeState.Finished)
289 return this.context.ServerSettings.Certificates[0].RSA.KeySize;
292 return 0;
296 public ExchangeAlgorithmType KeyExchangeAlgorithm
300 if (this.context.HandshakeState == HandshakeState.Finished)
302 return this.context.Current.Cipher.ExchangeAlgorithmType;
305 return ExchangeAlgorithmType.None;
309 public SecurityProtocolType SecurityProtocol
313 if (this.context.HandshakeState == HandshakeState.Finished)
315 return this.context.SecurityProtocol;
318 return 0;
322 public X509Certificate ServerCertificate
326 if (this.context.HandshakeState == HandshakeState.Finished)
328 if (this.context.ServerSettings.Certificates != null &&
329 this.context.ServerSettings.Certificates.Count > 0)
331 return new X509Certificate(this.context.ServerSettings.Certificates[0].RawData);
335 return null;
339 // this is used by Mono's certmgr tool to download certificates
340 internal Mono.Security.X509.X509CertificateCollection ServerCertificates
342 get { return context.ServerSettings.Certificates; }
345 #endregion
347 #region Internal Async Result/State Class
349 private class InternalAsyncResult : IAsyncResult
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;
361 private byte[] _buffer;
362 private int _offset;
363 private int _count;
365 public InternalAsyncResult(AsyncCallback userCallback, object userState, byte[] buffer, int offset, int count, bool fromWrite, bool proceedAfterHandshake)
367 _userCallback = userCallback;
368 _userState = userState;
369 _buffer = buffer;
370 _offset = offset;
371 _count = count;
372 _fromWrite = fromWrite;
373 _proceedAfterHandshake = proceedAfterHandshake;
376 public bool ProceedAfterHandshake
378 get { return _proceedAfterHandshake; }
381 public bool FromWrite
383 get { return _fromWrite; }
386 public byte[] Buffer
388 get { return _buffer; }
391 public int Offset
393 get { return _offset; }
396 public int Count
398 get { return _count; }
401 public int BytesRead
403 get { return _bytesRead; }
406 public object AsyncState
408 get { return _userState; }
411 public Exception AsyncException
413 get { return _asyncException; }
416 public bool CompletedWithError
418 get {
419 if (IsCompleted == false)
420 return false;
421 return null != _asyncException;
425 public WaitHandle AsyncWaitHandle
427 get {
428 lock (locker) {
429 if (handle == null)
430 handle = new ManualResetEvent (completed);
432 return handle;
436 public bool CompletedSynchronously
438 get { return false; }
441 public bool IsCompleted
443 get {
444 lock (locker)
445 return completed;
449 private void SetComplete(Exception ex, int bytesRead)
451 lock (locker) {
452 if (completed)
453 return;
455 completed = true;
456 _asyncException = ex;
457 _bytesRead = bytesRead;
458 if (handle != null)
459 handle.Set ();
461 if (_userCallback != null)
462 _userCallback.BeginInvoke (this, null, null);
465 public void SetComplete(Exception ex)
467 SetComplete(ex, 0);
470 public void SetComplete(int bytesRead)
472 SetComplete(null, bytesRead);
475 public void SetComplete()
477 SetComplete(null, 0);
480 #endregion
482 #region Stream Overrides and Async Stream Operations
484 private bool BeginNegotiateHandshake(InternalAsyncResult asyncResult)
488 lock (this.negotiate)
490 if (this.context.HandshakeState == HandshakeState.None)
492 this.OnBeginNegotiateHandshake(new AsyncCallback(AsyncHandshakeCallback), asyncResult);
494 return true;
496 else
498 return false;
502 catch (TlsException ex)
504 this.negotiationComplete.Set();
505 this.protocol.SendAlert(ex.Alert);
507 throw new IOException("The authentication or decryption has failed.", ex);
509 catch (Exception ex)
511 this.negotiationComplete.Set();
512 this.protocol.SendAlert(AlertDescription.InternalError);
514 throw new IOException("The authentication or decryption has failed.", ex);
518 private void EndNegotiateHandshake(InternalAsyncResult asyncResult)
520 if (asyncResult.IsCompleted == false)
521 asyncResult.AsyncWaitHandle.WaitOne();
523 if (asyncResult.CompletedWithError)
525 throw asyncResult.AsyncException;
529 public override IAsyncResult BeginRead(
530 byte[] buffer,
531 int offset,
532 int count,
533 AsyncCallback callback,
534 object state)
536 this.checkDisposed();
538 if (buffer == null)
540 throw new ArgumentNullException("buffer is a null reference.");
542 if (offset < 0)
544 throw new ArgumentOutOfRangeException("offset is less than 0.");
546 if (offset > buffer.Length)
548 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
550 if (count < 0)
552 throw new ArgumentOutOfRangeException("count is less than 0.");
554 if (count > (buffer.Length - offset))
556 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
559 InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, false, true);
561 if (this.MightNeedHandshake)
563 if (! BeginNegotiateHandshake(asyncResult))
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();
570 InternalBeginRead(asyncResult);
573 else
575 InternalBeginRead(asyncResult);
578 return asyncResult;
581 // bigger than max record length for SSL/TLS
582 private byte[] recbuf = new byte[16384];
584 private void InternalBeginRead(InternalAsyncResult asyncResult)
588 int preReadSize = 0;
590 lock (this.read)
592 // If actual buffer is fully read, reset it
593 bool shouldReset = this.inputBuffer.Position == this.inputBuffer.Length && this.inputBuffer.Length > 0;
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);
599 if (shouldReset)
601 this.resetBuffer();
603 else if (shouldReadImmediately)
605 preReadSize = this.inputBuffer.Read(asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
609 // This is explicitly done outside the synclock to avoid
610 // any potential deadlocks in the delegate call.
611 if (0 < preReadSize)
613 asyncResult.SetComplete(preReadSize);
615 else if (!this.context.ConnectionEnd)
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 });
622 else
624 // We're done with the connection so we need to let the caller know with 0 bytes read
625 asyncResult.SetComplete(0);
628 catch (TlsException ex)
630 this.protocol.SendAlert(ex.Alert);
632 throw new IOException("The authentication or decryption has failed.", ex);
634 catch (Exception ex)
636 throw new IOException("IO exception during read.", ex);
641 private MemoryStream recordStream = new MemoryStream();
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)
647 if (this.disposed)
648 return;
650 object[] state = (object[])result.AsyncState;
651 byte[] recbuf = (byte[])state[0];
652 InternalAsyncResult internalResult = (InternalAsyncResult)state[1];
656 int n = innerStream.EndRead(result);
657 if (n > 0)
659 // Add the just received data to the waiting data
660 recordStream.Write(recbuf, 0, n);
662 else
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;
669 bool dataToReturn = false;
670 long pos = recordStream.Position;
672 recordStream.Position = 0;
673 byte[] record = null;
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)
679 record = this.protocol.ReceiveRecord(recordStream);
682 // a record of 0 length is valid (and there may be more record after it)
683 while (record != null)
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)
690 outofrecord = new byte[remainder];
691 recordStream.Read(outofrecord, 0, outofrecord.Length);
694 lock (this.read)
696 long position = this.inputBuffer.Position;
698 if (record.Length > 0)
700 // Write new data to the inputBuffer
701 this.inputBuffer.Seek(0, SeekOrigin.End);
702 this.inputBuffer.Write(record, 0, record.Length);
704 // Restore buffer position
705 this.inputBuffer.Seek(position, SeekOrigin.Begin);
706 dataToReturn = true;
710 recordStream.SetLength(0);
711 record = null;
713 if (remainder > 0)
715 recordStream.Write(outofrecord, 0, outofrecord.Length);
716 // type (1), protocol (2) and length (2)
717 if (recordStream.Length >= 5)
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;
725 else
726 pos = remainder;
728 else
729 pos = 0;
732 if (!dataToReturn && (n > 0))
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);
740 else
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;
746 int bytesRead = 0;
747 lock (this.read)
749 bytesRead = this.inputBuffer.Read(internalResult.Buffer, internalResult.Offset, internalResult.Count);
752 internalResult.SetComplete(bytesRead);
755 catch (Exception ex)
757 internalResult.SetComplete(ex);
762 private void InternalBeginWrite(InternalAsyncResult asyncResult)
766 // Send the buffer as a TLS record
768 lock (this.write)
770 byte[] record = this.protocol.EncodeRecord(
771 ContentType.ApplicationData, asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
773 this.innerStream.BeginWrite(
774 record, 0, record.Length, new AsyncCallback(InternalWriteCallback), asyncResult);
777 catch (TlsException ex)
779 this.protocol.SendAlert(ex.Alert);
780 this.Close();
782 throw new IOException("The authentication or decryption has failed.", ex);
784 catch (Exception ex)
786 throw new IOException("IO exception during Write.", ex);
790 private void InternalWriteCallback(IAsyncResult ar)
792 if (this.disposed)
793 return;
795 InternalAsyncResult internalResult = (InternalAsyncResult)ar.AsyncState;
799 this.innerStream.EndWrite(ar);
800 internalResult.SetComplete();
802 catch (Exception ex)
804 internalResult.SetComplete(ex);
808 public override IAsyncResult BeginWrite(
809 byte[] buffer,
810 int offset,
811 int count,
812 AsyncCallback callback,
813 object state)
815 this.checkDisposed();
817 if (buffer == null)
819 throw new ArgumentNullException("buffer is a null reference.");
821 if (offset < 0)
823 throw new ArgumentOutOfRangeException("offset is less than 0.");
825 if (offset > buffer.Length)
827 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
829 if (count < 0)
831 throw new ArgumentOutOfRangeException("count is less than 0.");
833 if (count > (buffer.Length - offset))
835 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
839 InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, true, true);
841 if (this.MightNeedHandshake)
843 if (! BeginNegotiateHandshake(asyncResult))
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();
850 InternalBeginWrite(asyncResult);
853 else
855 InternalBeginWrite(asyncResult);
858 return asyncResult;
861 public override int EndRead(IAsyncResult asyncResult)
863 this.checkDisposed();
865 InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
866 if (internalResult == null)
868 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
871 // Always wait until the read is complete
872 if (!asyncResult.IsCompleted)
874 if (!asyncResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
875 throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndRead");
878 if (internalResult.CompletedWithError)
880 throw internalResult.AsyncException;
883 return internalResult.BytesRead;
886 public override void EndWrite(IAsyncResult asyncResult)
888 this.checkDisposed();
890 InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
891 if (internalResult == null)
893 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginWrite.");
897 if (!asyncResult.IsCompleted)
899 if (!internalResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
900 throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndWrite");
903 if (internalResult.CompletedWithError)
905 throw internalResult.AsyncException;
909 public override void Close()
911 base.Close ();
914 public override void Flush()
916 this.checkDisposed();
918 this.innerStream.Flush();
921 public int Read(byte[] buffer)
923 return this.Read(buffer, 0, buffer.Length);
926 public override int Read(byte[] buffer, int offset, int count)
928 this.checkDisposed ();
930 if (buffer == null)
932 throw new ArgumentNullException ("buffer");
934 if (offset < 0)
936 throw new ArgumentOutOfRangeException("offset is less than 0.");
938 if (offset > buffer.Length)
940 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
942 if (count < 0)
944 throw new ArgumentOutOfRangeException("count is less than 0.");
946 if (count > (buffer.Length - offset))
948 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
951 if (this.context.HandshakeState != HandshakeState.Finished)
953 this.NegotiateHandshake (); // Handshake negotiation
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;
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;
987 } else {
988 n = innerStream.Read (recbuf, 0, recbuf.Length);
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;
1002 bool dataToReturn = false;
1004 recordStream.Position = 0;
1005 byte[] record = null;
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);
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);
1024 long position = this.inputBuffer.Position;
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);
1031 // Restore buffer position
1032 this.inputBuffer.Seek (position, SeekOrigin.Begin);
1033 dataToReturn = true;
1036 recordStream.SetLength (0);
1037 record = null;
1039 if (remainder > 0) {
1040 recordStream.Write (outofrecord, 0, outofrecord.Length);
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;
1053 catch (TlsException ex)
1055 throw new IOException("The authentication or decryption has failed.", ex);
1057 catch (Exception ex)
1059 throw new IOException("IO exception during read.", ex);
1064 public override long Seek(long offset, SeekOrigin origin)
1066 throw new NotSupportedException();
1069 public override void SetLength(long value)
1071 throw new NotSupportedException();
1074 public void Write(byte[] buffer)
1076 this.Write(buffer, 0, buffer.Length);
1079 public override void Write(byte[] buffer, int offset, int count)
1081 this.checkDisposed ();
1083 if (buffer == null)
1085 throw new ArgumentNullException ("buffer");
1087 if (offset < 0)
1089 throw new ArgumentOutOfRangeException("offset is less than 0.");
1091 if (offset > buffer.Length)
1093 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
1095 if (count < 0)
1097 throw new ArgumentOutOfRangeException("count is less than 0.");
1099 if (count > (buffer.Length - offset))
1101 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
1104 if (this.context.HandshakeState != HandshakeState.Finished)
1106 this.NegotiateHandshake ();
1109 lock (this.write)
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);
1117 catch (TlsException ex)
1119 this.protocol.SendAlert(ex.Alert);
1120 this.Close();
1121 throw new IOException("The authentication or decryption has failed.", ex);
1123 catch (Exception ex)
1125 throw new IOException("IO exception during Write.", ex);
1130 public override bool CanRead
1132 get { return this.innerStream.CanRead; }
1135 public override bool CanSeek
1137 get { return false; }
1140 public override bool CanWrite
1142 get { return this.innerStream.CanWrite; }
1145 public override long Length
1147 get { throw new NotSupportedException(); }
1150 public override long Position
1154 throw new NotSupportedException();
1158 throw new NotSupportedException();
1161 #endregion
1163 #region IDisposable Members and Finalizer
1165 ~SslStreamBase()
1167 this.Dispose(false);
1170 protected override void Dispose (bool disposing)
1172 if (!this.disposed)
1174 if (disposing)
1176 if (this.innerStream != null)
1178 if (this.context.HandshakeState == HandshakeState.Finished &&
1179 !this.context.ConnectionEnd)
1181 // Write close notify
1182 this.protocol.SendAlert(AlertDescription.CloseNotify);
1185 if (this.ownsStream)
1187 // Close inner stream
1188 this.innerStream.Close();
1191 this.ownsStream = false;
1192 this.innerStream = null;
1195 this.disposed = true;
1196 base.Dispose (disposing);
1200 #endregion
1202 #region Misc Methods
1204 private void resetBuffer()
1206 this.inputBuffer.SetLength(0);
1207 this.inputBuffer.Position = 0;
1210 internal void checkDisposed()
1212 if (this.disposed)
1214 throw new ObjectDisposedException("The Stream is closed.");
1218 #endregion