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 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
;
56 private ManualResetEvent negotiationComplete
;
63 protected SslStreamBase(
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);
88 private void AsyncHandshakeCallback(IAsyncResult asyncResult
)
90 InternalAsyncResult internalResult
= asyncResult
.AsyncState
as InternalAsyncResult
;
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
);
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
);
120 InternalBeginRead(internalResult
);
122 negotiationComplete
.Set();
126 negotiationComplete
.Set();
127 internalResult
.SetComplete();
133 negotiationComplete
.Set();
134 internalResult
.SetComplete(ex
);
138 internal bool MightNeedHandshake
142 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
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();
170 this.EndNegotiateHandshake(ar
);
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
,
185 X509CertificateCollection serverRequestedCertificates
);
187 internal abstract bool OnRemoteCertificateValidation(X509Certificate certificate
, int[] errors
);
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 AsymmetricAlgorithm
RaiseLocalPrivateKeySelection(
209 X509Certificate certificate
,
212 return OnLocalPrivateKeySelection(certificate
, targetHost
);
216 #region Security Properties
218 public bool CheckCertRevocationStatus
220 get { return this.checkCertRevocationStatus; }
221 set { this.checkCertRevocationStatus = value; }
224 public CipherAlgorithmType CipherAlgorithm
228 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
230 return this.context
.Current
.Cipher
.CipherAlgorithmType
;
233 return CipherAlgorithmType
.None
;
237 public int CipherStrength
241 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
243 return this.context
.Current
.Cipher
.EffectiveKeyBits
;
250 public HashAlgorithmType HashAlgorithm
254 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
256 return this.context
.Current
.Cipher
.HashAlgorithmType
;
259 return HashAlgorithmType
.None
;
263 public int HashStrength
267 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
269 return this.context
.Current
.Cipher
.HashSize
* 8;
276 public int KeyExchangeStrength
280 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
282 return this.context
.ServerSettings
.Certificates
[0].RSA
.KeySize
;
289 public ExchangeAlgorithmType KeyExchangeAlgorithm
293 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
295 return this.context
.Current
.Cipher
.ExchangeAlgorithmType
;
298 return ExchangeAlgorithmType
.None
;
302 public SecurityProtocolType SecurityProtocol
306 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
308 return this.context
.SecurityProtocol
;
315 public X509Certificate ServerCertificate
319 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
321 if (this.context
.ServerSettings
.Certificates
!= null &&
322 this.context
.ServerSettings
.Certificates
.Count
> 0)
324 return new X509Certificate(this.context
.ServerSettings
.Certificates
[0].RawData
);
332 // this is used by Mono's certmgr tool to download certificates
333 internal Mono
.Security
.X509
.X509CertificateCollection ServerCertificates
335 get { return context.ServerSettings.Certificates; }
340 #region Internal Async Result/State Class
342 private class InternalAsyncResult
: IAsyncResult
344 private object locker
= new object ();
345 private AsyncCallback _userCallback
;
346 private object _userState
;
347 private Exception _asyncException
;
348 private ManualResetEvent handle
;
349 private bool completed
;
350 private int _bytesRead
;
351 private bool _fromWrite
;
352 private bool _proceedAfterHandshake
;
354 private byte[] _buffer
;
358 public InternalAsyncResult(AsyncCallback userCallback
, object userState
, byte[] buffer
, int offset
, int count
, bool fromWrite
, bool proceedAfterHandshake
)
360 _userCallback
= userCallback
;
361 _userState
= userState
;
365 _fromWrite
= fromWrite
;
366 _proceedAfterHandshake
= proceedAfterHandshake
;
369 public bool ProceedAfterHandshake
371 get { return _proceedAfterHandshake; }
374 public bool FromWrite
376 get { return _fromWrite; }
381 get { return _buffer; }
386 get { return _offset; }
391 get { return _count; }
396 get { return _bytesRead; }
399 public object AsyncState
401 get { return _userState; }
404 public Exception AsyncException
406 get { return _asyncException; }
409 public bool CompletedWithError
412 if (IsCompleted
== false)
414 return null != _asyncException
;
418 public WaitHandle AsyncWaitHandle
423 handle
= new ManualResetEvent (completed
);
429 public bool CompletedSynchronously
431 get { return false; }
434 public bool IsCompleted
442 private void SetComplete(Exception ex
, int bytesRead
)
449 _asyncException
= ex
;
450 _bytesRead
= bytesRead
;
454 if (_userCallback
!= null)
455 _userCallback
.BeginInvoke (this, null, null);
458 public void SetComplete(Exception ex
)
463 public void SetComplete(int bytesRead
)
465 SetComplete(null, bytesRead
);
468 public void SetComplete()
470 SetComplete(null, 0);
475 #region Stream Overrides and Async Stream Operations
477 private bool BeginNegotiateHandshake(InternalAsyncResult asyncResult
)
481 lock (this.negotiate
)
483 if (this.context
.HandshakeState
== HandshakeState
.None
)
485 this.OnBeginNegotiateHandshake(new AsyncCallback(AsyncHandshakeCallback
), asyncResult
);
495 catch (TlsException ex
)
497 this.negotiationComplete
.Set();
498 this.protocol
.SendAlert(ex
.Alert
);
500 throw new IOException("The authentication or decryption has failed.", ex
);
504 this.negotiationComplete
.Set();
505 this.protocol
.SendAlert(AlertDescription
.InternalError
);
507 throw new IOException("The authentication or decryption has failed.", ex
);
511 private void EndNegotiateHandshake(InternalAsyncResult asyncResult
)
513 if (asyncResult
.IsCompleted
== false)
514 asyncResult
.AsyncWaitHandle
.WaitOne();
516 if (asyncResult
.CompletedWithError
)
518 throw asyncResult
.AsyncException
;
522 public override IAsyncResult
BeginRead(
526 AsyncCallback callback
,
529 this.checkDisposed();
533 throw new ArgumentNullException("buffer is a null reference.");
537 throw new ArgumentOutOfRangeException("offset is less than 0.");
539 if (offset
> buffer
.Length
)
541 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
545 throw new ArgumentOutOfRangeException("count is less than 0.");
547 if (count
> (buffer
.Length
- offset
))
549 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
552 InternalAsyncResult asyncResult
= new InternalAsyncResult(callback
, state
, buffer
, offset
, count
, false, true);
554 if (this.MightNeedHandshake
)
556 if (! BeginNegotiateHandshake(asyncResult
))
558 //we made it down here so the handshake was not started.
559 //another thread must have started it in the mean time.
560 //wait for it to complete and then perform our original operation
561 this.negotiationComplete
.WaitOne();
563 InternalBeginRead(asyncResult
);
568 InternalBeginRead(asyncResult
);
574 // bigger than max record length for SSL/TLS
575 private byte[] recbuf
= new byte[16384];
577 private void InternalBeginRead(InternalAsyncResult asyncResult
)
585 // If actual buffer is fully read, reset it
586 bool shouldReset
= this.inputBuffer
.Position
== this.inputBuffer
.Length
&& this.inputBuffer
.Length
> 0;
588 // If the buffer isn't fully read, but does have data, we need to immediately
589 // read the info from the buffer and let the user know that they have more data.
590 bool shouldReadImmediately
= (this.inputBuffer
.Length
> 0) && (asyncResult
.Count
> 0);
596 else if (shouldReadImmediately
)
598 preReadSize
= this.inputBuffer
.Read(asyncResult
.Buffer
, asyncResult
.Offset
, asyncResult
.Count
);
602 // This is explicitly done outside the synclock to avoid
603 // any potential deadlocks in the delegate call.
606 asyncResult
.SetComplete(preReadSize
);
608 else if (!this.context
.ConnectionEnd
)
610 // this will read data from the network until we have (at least) one
611 // record to send back to the caller
612 this.innerStream
.BeginRead(recbuf
, 0, recbuf
.Length
,
613 new AsyncCallback(InternalReadCallback
), new object[] { recbuf, asyncResult }
);
617 // We're done with the connection so we need to let the caller know with 0 bytes read
618 asyncResult
.SetComplete(0);
621 catch (TlsException ex
)
623 this.protocol
.SendAlert(ex
.Alert
);
625 throw new IOException("The authentication or decryption has failed.", ex
);
629 throw new IOException("IO exception during read.", ex
);
634 private MemoryStream recordStream
= new MemoryStream();
636 // read encrypted data until we have enough to decrypt (at least) one
637 // record and return are the records (may be more than one) we have
638 private void InternalReadCallback(IAsyncResult result
)
643 object[] state
= (object[])result
.AsyncState
;
644 byte[] recbuf
= (byte[])state
[0];
645 InternalAsyncResult internalResult
= (InternalAsyncResult
)state
[1];
649 int n
= innerStream
.EndRead(result
);
652 // Add the just received data to the waiting data
653 recordStream
.Write(recbuf
, 0, n
);
657 // 0 length data means this read operation is done (lost connection in the case of a network stream).
658 internalResult
.SetComplete(0);
662 bool dataToReturn
= false;
663 long pos
= recordStream
.Position
;
665 recordStream
.Position
= 0;
666 byte[] record
= null;
668 // don't try to decode record unless we have at least 5 bytes
669 // i.e. type (1), protocol (2) and length (2)
670 if (recordStream
.Length
>= 5)
672 record
= this.protocol
.ReceiveRecord(recordStream
);
675 // a record of 0 length is valid (and there may be more record after it)
676 while (record
!= null)
678 // we probably received more stuff after the record, and we must keep it!
679 long remainder
= recordStream
.Length
- recordStream
.Position
;
680 byte[] outofrecord
= null;
683 outofrecord
= new byte[remainder
];
684 recordStream
.Read(outofrecord
, 0, outofrecord
.Length
);
689 long position
= this.inputBuffer
.Position
;
691 if (record
.Length
> 0)
693 // Write new data to the inputBuffer
694 this.inputBuffer
.Seek(0, SeekOrigin
.End
);
695 this.inputBuffer
.Write(record
, 0, record
.Length
);
697 // Restore buffer position
698 this.inputBuffer
.Seek(position
, SeekOrigin
.Begin
);
703 recordStream
.SetLength(0);
708 recordStream
.Write(outofrecord
, 0, outofrecord
.Length
);
709 // type (1), protocol (2) and length (2)
710 if (recordStream
.Length
>= 5)
712 // try to see if another record is available
713 recordStream
.Position
= 0;
714 record
= this.protocol
.ReceiveRecord(recordStream
);
716 pos
= recordStream
.Length
;
725 if (!dataToReturn
&& (n
> 0))
727 // there is no record to return to caller and (possibly) more data waiting
728 // so continue reading from network (and appending to stream)
729 recordStream
.Position
= recordStream
.Length
;
730 this.innerStream
.BeginRead(recbuf
, 0, recbuf
.Length
,
731 new AsyncCallback(InternalReadCallback
), state
);
735 // we have record(s) to return -or- no more available to read from network
736 // reset position for further reading
737 recordStream
.Position
= pos
;
742 bytesRead
= this.inputBuffer
.Read(internalResult
.Buffer
, internalResult
.Offset
, internalResult
.Count
);
745 internalResult
.SetComplete(bytesRead
);
750 internalResult
.SetComplete(ex
);
755 private void InternalBeginWrite(InternalAsyncResult asyncResult
)
759 // Send the buffer as a TLS record
763 byte[] record
= this.protocol
.EncodeRecord(
764 ContentType
.ApplicationData
, asyncResult
.Buffer
, asyncResult
.Offset
, asyncResult
.Count
);
766 this.innerStream
.BeginWrite(
767 record
, 0, record
.Length
, new AsyncCallback(InternalWriteCallback
), asyncResult
);
770 catch (TlsException ex
)
772 this.protocol
.SendAlert(ex
.Alert
);
775 throw new IOException("The authentication or decryption has failed.", ex
);
779 throw new IOException("IO exception during Write.", ex
);
783 private void InternalWriteCallback(IAsyncResult ar
)
788 InternalAsyncResult internalResult
= (InternalAsyncResult
)ar
.AsyncState
;
792 this.innerStream
.EndWrite(ar
);
793 internalResult
.SetComplete();
797 internalResult
.SetComplete(ex
);
801 public override IAsyncResult
BeginWrite(
805 AsyncCallback callback
,
808 this.checkDisposed();
812 throw new ArgumentNullException("buffer is a null reference.");
816 throw new ArgumentOutOfRangeException("offset is less than 0.");
818 if (offset
> buffer
.Length
)
820 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
824 throw new ArgumentOutOfRangeException("count is less than 0.");
826 if (count
> (buffer
.Length
- offset
))
828 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
832 InternalAsyncResult asyncResult
= new InternalAsyncResult(callback
, state
, buffer
, offset
, count
, true, true);
834 if (this.MightNeedHandshake
)
836 if (! BeginNegotiateHandshake(asyncResult
))
838 //we made it down here so the handshake was not started.
839 //another thread must have started it in the mean time.
840 //wait for it to complete and then perform our original operation
841 this.negotiationComplete
.WaitOne();
843 InternalBeginWrite(asyncResult
);
848 InternalBeginWrite(asyncResult
);
854 public override int EndRead(IAsyncResult asyncResult
)
856 this.checkDisposed();
858 InternalAsyncResult internalResult
= asyncResult
as InternalAsyncResult
;
859 if (internalResult
== null)
861 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
864 // Always wait until the read is complete
865 if (!asyncResult
.IsCompleted
)
867 if (!asyncResult
.AsyncWaitHandle
.WaitOne (WaitTimeOut
, false))
868 throw new TlsException (AlertDescription
.InternalError
, "Couldn't complete EndRead");
871 if (internalResult
.CompletedWithError
)
873 throw internalResult
.AsyncException
;
876 return internalResult
.BytesRead
;
879 public override void EndWrite(IAsyncResult asyncResult
)
881 this.checkDisposed();
883 InternalAsyncResult internalResult
= asyncResult
as InternalAsyncResult
;
884 if (internalResult
== null)
886 throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginWrite.");
890 if (!asyncResult
.IsCompleted
)
892 if (!internalResult
.AsyncWaitHandle
.WaitOne (WaitTimeOut
, false))
893 throw new TlsException (AlertDescription
.InternalError
, "Couldn't complete EndWrite");
896 if (internalResult
.CompletedWithError
)
898 throw internalResult
.AsyncException
;
902 public override void Close()
907 ((IDisposable
)this).Dispose();
911 public override void Flush()
913 this.checkDisposed();
915 this.innerStream
.Flush();
918 public int Read(byte[] buffer
)
920 return this.Read(buffer
, 0, buffer
.Length
);
923 public override int Read(byte[] buffer
, int offset
, int count
)
925 this.checkDisposed ();
929 throw new ArgumentNullException ("buffer");
933 throw new ArgumentOutOfRangeException("offset is less than 0.");
935 if (offset
> buffer
.Length
)
937 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
941 throw new ArgumentOutOfRangeException("count is less than 0.");
943 if (count
> (buffer
.Length
- offset
))
945 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
948 if (this.context
.HandshakeState
!= HandshakeState
.Finished
)
950 this.NegotiateHandshake (); // Handshake negotiation
955 record_processing
.Reset ();
956 // do we already have some decrypted data ?
957 if (this.inputBuffer
.Position
> 0) {
958 // or maybe we used all the buffer before ?
959 if (this.inputBuffer
.Position
== this.inputBuffer
.Length
) {
960 this.inputBuffer
.SetLength (0);
962 int n
= this.inputBuffer
.Read (buffer
, offset
, count
);
964 record_processing
.Set ();
970 bool needMoreData
= false;
972 // we first try to process the read with the data we already have
973 if ((recordStream
.Position
== 0) || needMoreData
) {
974 needMoreData
= false;
975 // if we loop, then it either means we need more data
976 byte[] recbuf
= new byte[16384];
979 int value = innerStream
.ReadByte ();
981 recbuf
[0] = (byte) value;
985 n
= innerStream
.Read (recbuf
, 0, recbuf
.Length
);
988 // Add the new received data to the waiting data
989 if ((recordStream
.Length
> 0) && (recordStream
.Position
!= recordStream
.Length
))
990 recordStream
.Seek (0, SeekOrigin
.End
);
991 recordStream
.Write (recbuf
, 0, n
);
993 // or that the read operation is done (lost connection in the case of a network stream).
994 record_processing
.Set ();
999 bool dataToReturn
= false;
1001 recordStream
.Position
= 0;
1002 byte[] record
= null;
1004 // don't try to decode record unless we have at least 5 bytes
1005 // i.e. type (1), protocol (2) and length (2)
1006 if (recordStream
.Length
>= 5) {
1007 record
= this.protocol
.ReceiveRecord (recordStream
);
1008 needMoreData
= (record
== null);
1011 // a record of 0 length is valid (and there may be more record after it)
1012 while (record
!= null) {
1013 // we probably received more stuff after the record, and we must keep it!
1014 long remainder
= recordStream
.Length
- recordStream
.Position
;
1015 byte[] outofrecord
= null;
1016 if (remainder
> 0) {
1017 outofrecord
= new byte[remainder
];
1018 recordStream
.Read (outofrecord
, 0, outofrecord
.Length
);
1021 long position
= this.inputBuffer
.Position
;
1023 if (record
.Length
> 0) {
1024 // Write new data to the inputBuffer
1025 this.inputBuffer
.Seek (0, SeekOrigin
.End
);
1026 this.inputBuffer
.Write (record
, 0, record
.Length
);
1028 // Restore buffer position
1029 this.inputBuffer
.Seek (position
, SeekOrigin
.Begin
);
1030 dataToReturn
= true;
1033 recordStream
.SetLength (0);
1036 if (remainder
> 0) {
1037 recordStream
.Write (outofrecord
, 0, outofrecord
.Length
);
1041 // we have record(s) to return -or- no more available to read from network
1042 // reset position for further reading
1043 int i
= inputBuffer
.Read (buffer
, offset
, count
);
1044 record_processing
.Set ();
1050 catch (TlsException ex
)
1052 throw new IOException("The authentication or decryption has failed.", ex
);
1054 catch (Exception ex
)
1056 throw new IOException("IO exception during read.", ex
);
1061 public override long Seek(long offset
, SeekOrigin origin
)
1063 throw new NotSupportedException();
1066 public override void SetLength(long value)
1068 throw new NotSupportedException();
1071 public void Write(byte[] buffer
)
1073 this.Write(buffer
, 0, buffer
.Length
);
1076 public override void Write(byte[] buffer
, int offset
, int count
)
1078 this.checkDisposed ();
1082 throw new ArgumentNullException ("buffer");
1086 throw new ArgumentOutOfRangeException("offset is less than 0.");
1088 if (offset
> buffer
.Length
)
1090 throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
1094 throw new ArgumentOutOfRangeException("count is less than 0.");
1096 if (count
> (buffer
.Length
- offset
))
1098 throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
1101 if (this.context
.HandshakeState
!= HandshakeState
.Finished
)
1103 this.NegotiateHandshake ();
1110 // Send the buffer as a TLS record
1111 byte[] record
= this.protocol
.EncodeRecord (ContentType
.ApplicationData
, buffer
, offset
, count
);
1112 this.innerStream
.Write (record
, 0, record
.Length
);
1114 catch (TlsException ex
)
1116 this.protocol
.SendAlert(ex
.Alert
);
1118 throw new IOException("The authentication or decryption has failed.", ex
);
1120 catch (Exception ex
)
1122 throw new IOException("IO exception during Write.", ex
);
1127 public override bool CanRead
1129 get { return this.innerStream.CanRead; }
1132 public override bool CanSeek
1134 get { return false; }
1137 public override bool CanWrite
1139 get { return this.innerStream.CanWrite; }
1142 public override long Length
1144 get { throw new NotSupportedException(); }
1147 public override long Position
1151 throw new NotSupportedException();
1155 throw new NotSupportedException();
1160 #region IDisposable Members and Finalizer
1164 this.Dispose(false);
1168 public void Dispose()
1171 GC
.SuppressFinalize(this);
1174 protected virtual void Dispose (bool disposing
)
1176 protected override void Dispose (bool disposing
)
1183 if (this.innerStream
!= null)
1185 if (this.context
.HandshakeState
== HandshakeState
.Finished
&&
1186 !this.context
.ConnectionEnd
)
1188 // Write close notify
1189 this.protocol
.SendAlert(AlertDescription
.CloseNotify
);
1192 if (this.ownsStream
)
1194 // Close inner stream
1195 this.innerStream
.Close();
1198 this.ownsStream
= false;
1199 this.innerStream
= null;
1202 this.disposed
= true;
1204 base.Dispose (disposing
);
1211 #region Misc Methods
1213 private void resetBuffer()
1215 this.inputBuffer
.SetLength(0);
1216 this.inputBuffer
.Position
= 0;
1219 internal void checkDisposed()
1223 throw new ObjectDisposedException("The Stream is closed.");