1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3 // Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
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:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
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.
26 using System.Collections;
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);
42 static ManualResetEvent record_processing = new ManualResetEvent (true);
44 internal Stream innerStream;
45 internal MemoryStream inputBuffer;
46 internal Context context;
47 internal RecordProtocol protocol;
48 internal bool ownsStream;
49 private volatile bool disposed;
50 private bool checkCertRevocationStatus;
51 private object negotiate;
54 private ManualResetEvent negotiationComplete;
61 protected SslStreamBase(
67 throw new ArgumentNullException("stream is null.");
69 if (!stream.CanRead || !stream.CanWrite)
71 throw new ArgumentNullException("stream is not both readable and writable.");
74 this.inputBuffer = new MemoryStream();
75 this.innerStream = stream;
76 this.ownsStream = ownsStream;
77 this.negotiate = new object();
78 this.read = new object();
79 this.write = new object();
80 this.negotiationComplete = new ManualResetEvent(false);
86 private void AsyncHandshakeCallback(IAsyncResult asyncResult)
88 InternalAsyncResult internalResult = asyncResult.AsyncState as InternalAsyncResult;
94 this.OnNegotiateHandshakeCallback(asyncResult);
96 catch (TlsException ex)
98 this.protocol.SendAlert(ex.Alert);
100 throw new IOException("The authentication or decryption has failed.", ex);
104 this.protocol.SendAlert(AlertDescription.InternalError);
106 throw new IOException("The authentication or decryption has failed.", ex);
109 if (internalResult.ProceedAfterHandshake)
111 //kick off the read or write process (whichever called us) after the handshake is complete
112 if (internalResult.FromWrite)
114 InternalBeginWrite(internalResult);
118 InternalBeginRead(internalResult);
120 negotiationComplete.Set();
124 negotiationComplete.Set();
125 internalResult.SetComplete();
131 negotiationComplete.Set();
132 internalResult.SetComplete(ex);
136 internal bool MightNeedHandshake
140 if (this.context.HandshakeState == HandshakeState.Finished)
146 lock (this.negotiate)
148 return (this.context.HandshakeState != HandshakeState.Finished);
154 internal void NegotiateHandshake()
156 if (this.MightNeedHandshake)
158 InternalAsyncResult ar = new InternalAsyncResult(null, null, null, 0, 0, false, false);
160 //if something already started negotiation, wait for it.
161 //otherwise end it ourselves.
162 if (!BeginNegotiateHandshake(ar))
164 this.negotiationComplete.WaitOne();
168 this.EndNegotiateHandshake(ar);
175 #region Abstracts/Virtuals
177 internal abstract IAsyncResult OnBeginNegotiateHandshake(AsyncCallback callback, object state);
178 internal abstract void OnNegotiateHandshakeCallback(IAsyncResult asyncResult);
180 internal abstract X509Certificate OnLocalCertificateSelection(X509CertificateCollection clientCertificates,
181 X509Certificate serverCertificate,
183 X509CertificateCollection serverRequestedCertificates);
185 internal abstract bool OnRemoteCertificateValidation(X509Certificate certificate, int[] errors);
186 internal abstract ValidationResult OnRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection);
187 internal abstract bool HaveRemoteValidation2Callback { get; }
189 internal abstract AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost);
193 #region Event Methods
195 internal X509Certificate RaiseLocalCertificateSelection(X509CertificateCollection certificates,
196 X509Certificate remoteCertificate,
198 X509CertificateCollection requestedCertificates)
200 return OnLocalCertificateSelection(certificates, remoteCertificate, targetHost, requestedCertificates);
203 internal bool RaiseRemoteCertificateValidation(X509Certificate certificate, int[] errors)
205 return OnRemoteCertificateValidation(certificate, errors);
208 internal ValidationResult RaiseRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
210 return OnRemoteCertificateValidation2 (collection);
213 internal AsymmetricAlgorithm RaiseLocalPrivateKeySelection(
214 X509Certificate certificate,
217 return OnLocalPrivateKeySelection(certificate, targetHost);
221 #region Security Properties
223 public bool CheckCertRevocationStatus
225 get { return this.checkCertRevocationStatus; }
226 set { this.checkCertRevocationStatus = value; }
229 public CipherAlgorithmType CipherAlgorithm
233 if (this.context.HandshakeState == HandshakeState.Finished)
235 return this.context.Current.Cipher.CipherAlgorithmType;
238 return CipherAlgorithmType.None;
242 public int CipherStrength
246 if (this.context.HandshakeState == HandshakeState.Finished)
248 return this.context.Current.Cipher.EffectiveKeyBits;
255 public HashAlgorithmType HashAlgorithm
259 if (this.context.HandshakeState == HandshakeState.Finished)
261 return this.context.Current.Cipher.HashAlgorithmType;
264 return HashAlgorithmType.None;
268 public int HashStrength
272 if (this.context.HandshakeState == HandshakeState.Finished)
274 return this.context.Current.Cipher.HashSize * 8;
281 public int KeyExchangeStrength
285 if (this.context.HandshakeState == HandshakeState.Finished)
287 return this.context.ServerSettings.Certificates[0].RSA.KeySize;
294 public ExchangeAlgorithmType KeyExchangeAlgorithm
298 if (this.context.HandshakeState == HandshakeState.Finished)
300 return this.context.Current.Cipher.ExchangeAlgorithmType;
303 return ExchangeAlgorithmType.None;
307 public SecurityProtocolType SecurityProtocol
311 if (this.context.HandshakeState == HandshakeState.Finished)
313 return this.context.SecurityProtocol;
320 public X509Certificate ServerCertificate
324 if (this.context.HandshakeState == HandshakeState.Finished)
326 if (this.context.ServerSettings.Certificates != null &&
327 this.context.ServerSettings.Certificates.Count > 0)
329 return new X509Certificate(this.context.ServerSettings.Certificates[0].RawData);
337 // this is used by Mono's certmgr tool to download certificates
338 internal Mono.Security.X509.X509CertificateCollection ServerCertificates
340 get { return context.ServerSettings.Certificates; }
345 #region Internal Async Result/State Class
347 private class InternalAsyncResult : IAsyncResult
349 private object locker = new object ();
350 private AsyncCallback _userCallback;
351 private object _userState;
352 private Exception _asyncException;
353 private ManualResetEvent handle;
354 private bool completed;
355 private int _bytesRead;
356 private bool _fromWrite;
357 private bool _proceedAfterHandshake;
359 private byte[] _buffer;
363 public InternalAsyncResult(AsyncCallback userCallback, object userState, byte[] buffer, int offset, int count, bool fromWrite, bool proceedAfterHandshake)
365 _userCallback = userCallback;
366 _userState = userState;
370 _fromWrite = fromWrite;
371 _proceedAfterHandshake = proceedAfterHandshake;
374 public bool ProceedAfterHandshake
376 get { return _proceedAfterHandshake; }
379 public bool FromWrite
381 get { return _fromWrite; }
386 get { return _buffer; }
391 get { return _offset; }
396 get { return _count; }
401 get { return _bytesRead; }
404 public object AsyncState
406 get { return _userState; }
409 public Exception AsyncException
411 get { return _asyncException; }
414 public bool CompletedWithError
417 if (IsCompleted == false)
419 return null != _asyncException;
423 public WaitHandle AsyncWaitHandle
428 handle = new ManualResetEvent (completed);
434 public bool CompletedSynchronously
436 get { return false; }
439 public bool IsCompleted
447 private void SetComplete(Exception ex, int bytesRead)
454 _asyncException = ex;
455 _bytesRead = bytesRead;
459 if (_userCallback != null)
460 _userCallback.BeginInvoke (this, null, null);
463 public void SetComplete(Exception ex)
468 public void SetComplete(int bytesRead)
470 SetComplete(null, bytesRead);
473 public void SetComplete()
475 SetComplete(null, 0);
480 #region Stream Overrides and Async Stream Operations
482 private bool BeginNegotiateHandshake(InternalAsyncResult asyncResult)
486 lock (this.negotiate)
488 if (this.context.HandshakeState == HandshakeState.None)
490 this.OnBeginNegotiateHandshake(new AsyncCallback(AsyncHandshakeCallback), asyncResult);
500 catch (TlsException ex)
502 this.negotiationComplete.Set();
503 this.protocol.SendAlert(ex.Alert);
505 throw new IOException("The authentication or decryption has failed.", ex);
509 this.negotiationComplete.Set();
510 this.protocol.SendAlert(AlertDescription.InternalError);
512 throw new IOException("The authentication or decryption has failed.", ex);
516 private void EndNegotiateHandshake(InternalAsyncResult asyncResult)
518 if (asyncResult.IsCompleted == false)
519 asyncResult.AsyncWaitHandle.WaitOne();
521 if (asyncResult.CompletedWithError)
523 throw asyncResult.AsyncException;
527 public override IAsyncResult BeginRead(
531 AsyncCallback callback,
534 this.checkDisposed();
538 throw new ArgumentNullException("buffer is a null reference.");
542 throw new ArgumentOutOfRangeException("offset is less than 0.");
544 if (offset > buffer.Length)
546 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
550 throw new ArgumentOutOfRangeException("count is less than 0.");
552 if (count > (buffer.Length - offset))
554 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
557 InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, false, true);
559 if (this.MightNeedHandshake)
561 if (! BeginNegotiateHandshake(asyncResult))
563 //we made it down here so the handshake was not started.
564 //another thread must have started it in the mean time.
565 //wait for it to complete and then perform our original operation
566 this.negotiationComplete.WaitOne();
568 InternalBeginRead(asyncResult);
573 InternalBeginRead(asyncResult);
579 // bigger than max record length for SSL/TLS
580 private byte[] recbuf = new byte[16384];
582 private void InternalBeginRead(InternalAsyncResult asyncResult)
590 // If actual buffer is fully read, reset it
591 bool shouldReset = this.inputBuffer.Position == this.inputBuffer.Length && this.inputBuffer.Length > 0;
593 // If the buffer isn't fully read, but does have data, we need to immediately
594 // read the info from the buffer and let the user know that they have more data.
595 bool shouldReadImmediately = (this.inputBuffer.Length > 0) && (asyncResult.Count > 0);
601 else if (shouldReadImmediately)
603 preReadSize = this.inputBuffer.Read(asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
607 // This is explicitly done outside the synclock to avoid
608 // any potential deadlocks in the delegate call.
611 asyncResult.SetComplete(preReadSize);
613 else if (!this.context.ReceivedConnectionEnd)
615 // this will read data from the network until we have (at least) one
616 // record to send back to the caller
617 this.innerStream.BeginRead(recbuf, 0, recbuf.Length,
618 new AsyncCallback(InternalReadCallback), new object[] { recbuf, asyncResult });
622 // We're done with the connection so we need to let the caller know with 0 bytes read
623 asyncResult.SetComplete(0);
626 catch (TlsException ex)
628 this.protocol.SendAlert(ex.Alert);
630 throw new IOException("The authentication or decryption has failed.", ex);
634 throw new IOException("IO exception during read.", ex);
639 private MemoryStream recordStream = new MemoryStream();
641 // read encrypted data until we have enough to decrypt (at least) one
642 // record and return are the records (may be more than one) we have
643 private void InternalReadCallback(IAsyncResult result)
648 object[] state = (object[])result.AsyncState;
649 byte[] recbuf = (byte[])state[0];
650 InternalAsyncResult internalResult = (InternalAsyncResult)state[1];
654 int n = innerStream.EndRead(result);
657 // Add the just received data to the waiting data
658 recordStream.Write(recbuf, 0, n);
662 // 0 length data means this read operation is done (lost connection in the case of a network stream).
663 internalResult.SetComplete(0);
667 bool dataToReturn = false;
668 long pos = recordStream.Position;
670 recordStream.Position = 0;
671 byte[] record = null;
673 // don't try to decode record unless we have at least 5 bytes
674 // i.e. type (1), protocol (2) and length (2)
675 if (recordStream.Length >= 5)
677 record = this.protocol.ReceiveRecord(recordStream);
680 // a record of 0 length is valid (and there may be more record after it)
681 while (record != null)
683 // we probably received more stuff after the record, and we must keep it!
684 long remainder = recordStream.Length - recordStream.Position;
685 byte[] outofrecord = null;
688 outofrecord = new byte[remainder];
689 recordStream.Read(outofrecord, 0, outofrecord.Length);
694 long position = this.inputBuffer.Position;
696 if (record.Length > 0)
698 // Write new data to the inputBuffer
699 this.inputBuffer.Seek(0, SeekOrigin.End);
700 this.inputBuffer.Write(record, 0, record.Length);
702 // Restore buffer position
703 this.inputBuffer.Seek(position, SeekOrigin.Begin);
708 recordStream.SetLength(0);
713 recordStream.Write(outofrecord, 0, outofrecord.Length);
714 // type (1), protocol (2) and length (2)
715 if (recordStream.Length >= 5)
717 // try to see if another record is available
718 recordStream.Position = 0;
719 record = this.protocol.ReceiveRecord(recordStream);
721 pos = recordStream.Length;
730 if (!dataToReturn && (n > 0))
732 if (context.ReceivedConnectionEnd) {
733 internalResult.SetComplete (0);
735 // there is no record to return to caller and (possibly) more data waiting
736 // so continue reading from network (and appending to stream)
737 recordStream.Position = recordStream.Length;
738 this.innerStream.BeginRead(recbuf, 0, recbuf.Length,
739 new AsyncCallback(InternalReadCallback), state);
744 // we have record(s) to return -or- no more available to read from network
745 // reset position for further reading
746 recordStream.Position = pos;
751 bytesRead = this.inputBuffer.Read(internalResult.Buffer, internalResult.Offset, internalResult.Count);
754 internalResult.SetComplete(bytesRead);
759 internalResult.SetComplete(ex);
764 private void InternalBeginWrite(InternalAsyncResult asyncResult)
768 // Send the buffer as a TLS record
772 byte[] record = this.protocol.EncodeRecord(
773 ContentType.ApplicationData, asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
775 this.innerStream.BeginWrite(
776 record, 0, record.Length, new AsyncCallback(InternalWriteCallback), asyncResult);
779 catch (TlsException ex)
781 this.protocol.SendAlert(ex.Alert);
784 throw new IOException("The authentication or decryption has failed.", ex);
788 throw new IOException("IO exception during Write.", ex);
792 private void InternalWriteCallback(IAsyncResult ar)
797 InternalAsyncResult internalResult = (InternalAsyncResult)ar.AsyncState;
801 this.innerStream.EndWrite(ar);
802 internalResult.SetComplete();
806 internalResult.SetComplete(ex);
810 public override IAsyncResult BeginWrite(
814 AsyncCallback callback,
817 this.checkDisposed();
821 throw new ArgumentNullException("buffer is a null reference.");
825 throw new ArgumentOutOfRangeException("offset is less than 0.");
827 if (offset > buffer.Length)
829 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
833 throw new ArgumentOutOfRangeException("count is less than 0.");
835 if (count > (buffer.Length - offset))
837 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
841 InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, true, true);
843 if (this.MightNeedHandshake)
845 if (! BeginNegotiateHandshake(asyncResult))
847 //we made it down here so the handshake was not started.
848 //another thread must have started it in the mean time.
849 //wait for it to complete and then perform our original operation
850 this.negotiationComplete.WaitOne();
852 InternalBeginWrite(asyncResult);
857 InternalBeginWrite(asyncResult);
863 public override int EndRead(IAsyncResult asyncResult)
865 this.checkDisposed();
867 InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
868 if (internalResult == null)
870 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
873 // Always wait until the read is complete
874 if (!asyncResult.IsCompleted)
876 if (!asyncResult.AsyncWaitHandle.WaitOne ())
877 throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndRead");
880 if (internalResult.CompletedWithError)
882 throw internalResult.AsyncException;
885 return internalResult.BytesRead;
888 public override void EndWrite(IAsyncResult asyncResult)
890 this.checkDisposed();
892 InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
893 if (internalResult == null)
895 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginWrite.");
899 if (!asyncResult.IsCompleted)
901 if (!internalResult.AsyncWaitHandle.WaitOne ())
902 throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndWrite");
905 if (internalResult.CompletedWithError)
907 throw internalResult.AsyncException;
911 public override void Close()
916 public override void Flush()
918 this.checkDisposed();
920 this.innerStream.Flush();
923 public int Read(byte[] buffer)
925 return this.Read(buffer, 0, buffer.Length);
928 public override int Read(byte[] buffer, int offset, int count)
930 this.checkDisposed ();
934 throw new ArgumentNullException ("buffer");
938 throw new ArgumentOutOfRangeException("offset is less than 0.");
940 if (offset > buffer.Length)
942 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
946 throw new ArgumentOutOfRangeException("count is less than 0.");
948 if (count > (buffer.Length - offset))
950 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
953 if (this.context.HandshakeState != HandshakeState.Finished)
955 this.NegotiateHandshake (); // Handshake negotiation
960 record_processing.Reset ();
961 // do we already have some decrypted data ?
962 if (this.inputBuffer.Position > 0) {
963 // or maybe we used all the buffer before ?
964 if (this.inputBuffer.Position == this.inputBuffer.Length) {
965 this.inputBuffer.SetLength (0);
967 int n = this.inputBuffer.Read (buffer, offset, count);
969 record_processing.Set ();
975 bool needMoreData = false;
977 // we first try to process the read with the data we already have
978 if ((recordStream.Position == 0) || needMoreData) {
979 needMoreData = false;
980 // if we loop, then it either means we need more data
981 byte[] recbuf = new byte[16384];
984 int value = innerStream.ReadByte ();
986 recbuf[0] = (byte) value;
990 n = innerStream.Read (recbuf, 0, recbuf.Length);
993 // Add the new received data to the waiting data
994 if ((recordStream.Length > 0) && (recordStream.Position != recordStream.Length))
995 recordStream.Seek (0, SeekOrigin.End);
996 recordStream.Write (recbuf, 0, n);
998 // or that the read operation is done (lost connection in the case of a network stream).
999 record_processing.Set ();
1004 bool dataToReturn = false;
1006 recordStream.Position = 0;
1007 byte[] record = null;
1009 // don't try to decode record unless we have at least 5 bytes
1010 // i.e. type (1), protocol (2) and length (2)
1011 if (recordStream.Length >= 5) {
1012 record = this.protocol.ReceiveRecord (recordStream);
1013 needMoreData = (record == null);
1016 // a record of 0 length is valid (and there may be more record after it)
1017 while (record != null) {
1018 // we probably received more stuff after the record, and we must keep it!
1019 long remainder = recordStream.Length - recordStream.Position;
1020 byte[] outofrecord = null;
1021 if (remainder > 0) {
1022 outofrecord = new byte[remainder];
1023 recordStream.Read (outofrecord, 0, outofrecord.Length);
1026 long position = this.inputBuffer.Position;
1028 if (record.Length > 0) {
1029 // Write new data to the inputBuffer
1030 this.inputBuffer.Seek (0, SeekOrigin.End);
1031 this.inputBuffer.Write (record, 0, record.Length);
1033 // Restore buffer position
1034 this.inputBuffer.Seek (position, SeekOrigin.Begin);
1035 dataToReturn = true;
1038 recordStream.SetLength (0);
1041 if (remainder > 0) {
1042 recordStream.Write (outofrecord, 0, outofrecord.Length);
1046 // we have record(s) to return -or- no more available to read from network
1047 // reset position for further reading
1048 int i = inputBuffer.Read (buffer, offset, count);
1049 record_processing.Set ();
1055 catch (TlsException ex)
1057 throw new IOException("The authentication or decryption has failed.", ex);
1059 catch (Exception ex)
1061 throw new IOException("IO exception during read.", ex);
1066 public override long Seek(long offset, SeekOrigin origin)
1068 throw new NotSupportedException();
1071 public override void SetLength(long value)
1073 throw new NotSupportedException();
1076 public void Write(byte[] buffer)
1078 this.Write(buffer, 0, buffer.Length);
1081 public override void Write(byte[] buffer, int offset, int count)
1083 this.checkDisposed ();
1087 throw new ArgumentNullException ("buffer");
1091 throw new ArgumentOutOfRangeException("offset is less than 0.");
1093 if (offset > buffer.Length)
1095 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
1099 throw new ArgumentOutOfRangeException("count is less than 0.");
1101 if (count > (buffer.Length - offset))
1103 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
1106 if (this.context.HandshakeState != HandshakeState.Finished)
1108 this.NegotiateHandshake ();
1115 // Send the buffer as a TLS record
1116 byte[] record = this.protocol.EncodeRecord (ContentType.ApplicationData, buffer, offset, count);
1117 this.innerStream.Write (record, 0, record.Length);
1119 catch (TlsException ex)
1121 this.protocol.SendAlert(ex.Alert);
1123 throw new IOException("The authentication or decryption has failed.", ex);
1125 catch (Exception ex)
1127 throw new IOException("IO exception during Write.", ex);
1132 public override bool CanRead
1134 get { return this.innerStream.CanRead; }
1137 public override bool CanSeek
1139 get { return false; }
1142 public override bool CanWrite
1144 get { return this.innerStream.CanWrite; }
1147 public override long Length
1149 get { throw new NotSupportedException(); }
1152 public override long Position
1156 throw new NotSupportedException();
1160 throw new NotSupportedException();
1165 #region IDisposable Members and Finalizer
1169 this.Dispose(false);
1172 protected override void Dispose (bool disposing)
1178 if (this.innerStream != null)
1180 if (this.context.HandshakeState == HandshakeState.Finished &&
1181 !this.context.SentConnectionEnd)
1183 // Write close notify
1185 this.protocol.SendAlert(AlertDescription.CloseNotify);
1190 if (this.ownsStream)
1192 // Close inner stream
1193 this.innerStream.Close();
1196 this.ownsStream = false;
1197 this.innerStream = null;
1200 this.disposed = true;
1201 base.Dispose (disposing);
1207 #region Misc Methods
1209 private void resetBuffer()
1211 this.inputBuffer.SetLength(0);
1212 this.inputBuffer.Position = 0;
1215 internal void checkDisposed()
1219 throw new ObjectDisposedException("The Stream is closed.");