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
);
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
);
195 #region Event Methods
197 internal X509Certificate
RaiseLocalCertificateSelection(X509CertificateCollection certificates
,
198 X509Certificate remoteCertificate
,
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
,
219 return OnLocalPrivateKeySelection(certificate
, targetHost
);
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
;
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;
283 public int KeyExchangeStrength
287 if (this.context
.HandshakeState
== HandshakeState
.Finished
)
289 return this.context
.ServerSettings
.Certificates
[0].RSA
.KeySize
;
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
;
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
);
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; }
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
;
365 public InternalAsyncResult(AsyncCallback userCallback
, object userState
, byte[] buffer
, int offset
, int count
, bool fromWrite
, bool proceedAfterHandshake
)
367 _userCallback
= userCallback
;
368 _userState
= userState
;
372 _fromWrite
= fromWrite
;
373 _proceedAfterHandshake
= proceedAfterHandshake
;
376 public bool ProceedAfterHandshake
378 get { return _proceedAfterHandshake; }
381 public bool FromWrite
383 get { return _fromWrite; }
388 get { return _buffer; }
393 get { return _offset; }
398 get { return _count; }
403 get { return _bytesRead; }
406 public object AsyncState
408 get { return _userState; }
411 public Exception AsyncException
413 get { return _asyncException; }
416 public bool CompletedWithError
419 if (IsCompleted
== false)
421 return null != _asyncException
;
425 public WaitHandle AsyncWaitHandle
430 handle
= new ManualResetEvent (completed
);
436 public bool CompletedSynchronously
438 get { return false; }
441 public bool IsCompleted
449 private void SetComplete(Exception ex
, int bytesRead
)
456 _asyncException
= ex
;
457 _bytesRead
= bytesRead
;
461 if (_userCallback
!= null)
462 _userCallback
.BeginInvoke (this, null, null);
465 public void SetComplete(Exception ex
)
470 public void SetComplete(int bytesRead
)
472 SetComplete(null, bytesRead
);
475 public void SetComplete()
477 SetComplete(null, 0);
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
);
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
);
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(
533 AsyncCallback callback
,
536 this.checkDisposed();
540 throw new ArgumentNullException("buffer is a null reference.");
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.");
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
);
575 InternalBeginRead(asyncResult
);
581 // bigger than max record length for SSL/TLS
582 private byte[] recbuf
= new byte[16384];
584 private void InternalBeginRead(InternalAsyncResult asyncResult
)
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);
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.
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 }
);
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
);
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
)
650 object[] state
= (object[])result
.AsyncState
;
651 byte[] recbuf
= (byte[])state
[0];
652 InternalAsyncResult internalResult
= (InternalAsyncResult
)state
[1];
656 int n
= innerStream
.EndRead(result
);
659 // Add the just received data to the waiting data
660 recordStream
.Write(recbuf
, 0, n
);
664 // 0 length data means this read operation is done (lost connection in the case of a network stream).
665 internalResult
.SetComplete(0);
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;
690 outofrecord
= new byte[remainder
];
691 recordStream
.Read(outofrecord
, 0, outofrecord
.Length
);
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
);
710 recordStream
.SetLength(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
);
723 pos
= recordStream
.Length
;
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
);
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
;
749 bytesRead
= this.inputBuffer
.Read(internalResult
.Buffer
, internalResult
.Offset
, internalResult
.Count
);
752 internalResult
.SetComplete(bytesRead
);
757 internalResult
.SetComplete(ex
);
762 private void InternalBeginWrite(InternalAsyncResult asyncResult
)
766 // Send the buffer as a TLS record
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
);
782 throw new IOException("The authentication or decryption has failed.", ex
);
786 throw new IOException("IO exception during Write.", ex
);
790 private void InternalWriteCallback(IAsyncResult ar
)
795 InternalAsyncResult internalResult
= (InternalAsyncResult
)ar
.AsyncState
;
799 this.innerStream
.EndWrite(ar
);
800 internalResult
.SetComplete();
804 internalResult
.SetComplete(ex
);
808 public override IAsyncResult
BeginWrite(
812 AsyncCallback callback
,
815 this.checkDisposed();
819 throw new ArgumentNullException("buffer is a null reference.");
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.");
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
);
855 InternalBeginWrite(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()
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 ();
932 throw new ArgumentNullException ("buffer");
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.");
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
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);
965 int n
= this.inputBuffer
.Read (buffer
, offset
, count
);
967 record_processing
.Set ();
973 bool needMoreData
= false;
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];
982 int value = innerStream
.ReadByte ();
984 recbuf
[0] = (byte) value;
988 n
= innerStream
.Read (recbuf
, 0, recbuf
.Length
);
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
);
996 // or that the read operation is done (lost connection in the case of a network stream).
997 record_processing
.Set ();
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);
1039 if (remainder
> 0) {
1040 recordStream
.Write (outofrecord
, 0, outofrecord
.Length
);
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 ();
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 ();
1085 throw new ArgumentNullException ("buffer");
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.");
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 ();
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
);
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();
1163 #region IDisposable Members and Finalizer
1167 this.Dispose(false);
1170 protected override void Dispose (bool 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
);
1202 #region Misc Methods
1204 private void resetBuffer()
1206 this.inputBuffer
.SetLength(0);
1207 this.inputBuffer
.Position
= 0;
1210 internal void checkDisposed()
1214 throw new ObjectDisposedException("The Stream is closed.");