SslStreamBase.cs 29 KB

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