SslStreamBase.cs 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  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 ValidationResult OnRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection);
  163. internal abstract bool HaveRemoteValidation2Callback { get; }
  164. internal abstract AsymmetricAlgorithm OnLocalPrivateKeySelection(X509Certificate certificate, string targetHost);
  165. #endregion
  166. #region Event Methods
  167. internal X509Certificate RaiseLocalCertificateSelection(X509CertificateCollection certificates,
  168. X509Certificate remoteCertificate,
  169. string targetHost,
  170. X509CertificateCollection requestedCertificates)
  171. {
  172. return OnLocalCertificateSelection(certificates, remoteCertificate, targetHost, requestedCertificates);
  173. }
  174. internal bool RaiseRemoteCertificateValidation(X509Certificate certificate, int[] errors)
  175. {
  176. return OnRemoteCertificateValidation(certificate, errors);
  177. }
  178. internal ValidationResult RaiseRemoteCertificateValidation2 (Mono.Security.X509.X509CertificateCollection collection)
  179. {
  180. return OnRemoteCertificateValidation2 (collection);
  181. }
  182. internal AsymmetricAlgorithm RaiseLocalPrivateKeySelection(
  183. X509Certificate certificate,
  184. string targetHost)
  185. {
  186. return OnLocalPrivateKeySelection(certificate, targetHost);
  187. }
  188. #endregion
  189. #region Security Properties
  190. public bool CheckCertRevocationStatus
  191. {
  192. get { return this.checkCertRevocationStatus; }
  193. set { this.checkCertRevocationStatus = value; }
  194. }
  195. public CipherAlgorithmType CipherAlgorithm
  196. {
  197. get
  198. {
  199. if (this.context.HandshakeState == HandshakeState.Finished)
  200. {
  201. return this.context.Current.Cipher.CipherAlgorithmType;
  202. }
  203. return CipherAlgorithmType.None;
  204. }
  205. }
  206. public int CipherStrength
  207. {
  208. get
  209. {
  210. if (this.context.HandshakeState == HandshakeState.Finished)
  211. {
  212. return this.context.Current.Cipher.EffectiveKeyBits;
  213. }
  214. return 0;
  215. }
  216. }
  217. public HashAlgorithmType HashAlgorithm
  218. {
  219. get
  220. {
  221. if (this.context.HandshakeState == HandshakeState.Finished)
  222. {
  223. return this.context.Current.Cipher.HashAlgorithmType;
  224. }
  225. return HashAlgorithmType.None;
  226. }
  227. }
  228. public int HashStrength
  229. {
  230. get
  231. {
  232. if (this.context.HandshakeState == HandshakeState.Finished)
  233. {
  234. return this.context.Current.Cipher.HashSize * 8;
  235. }
  236. return 0;
  237. }
  238. }
  239. public int KeyExchangeStrength
  240. {
  241. get
  242. {
  243. if (this.context.HandshakeState == HandshakeState.Finished)
  244. {
  245. return this.context.ServerSettings.Certificates[0].RSA.KeySize;
  246. }
  247. return 0;
  248. }
  249. }
  250. public ExchangeAlgorithmType KeyExchangeAlgorithm
  251. {
  252. get
  253. {
  254. if (this.context.HandshakeState == HandshakeState.Finished)
  255. {
  256. return this.context.Current.Cipher.ExchangeAlgorithmType;
  257. }
  258. return ExchangeAlgorithmType.None;
  259. }
  260. }
  261. public SecurityProtocolType SecurityProtocol
  262. {
  263. get
  264. {
  265. if (this.context.HandshakeState == HandshakeState.Finished)
  266. {
  267. return this.context.SecurityProtocol;
  268. }
  269. return 0;
  270. }
  271. }
  272. public X509Certificate ServerCertificate
  273. {
  274. get
  275. {
  276. if (this.context.HandshakeState == HandshakeState.Finished)
  277. {
  278. if (this.context.ServerSettings.Certificates != null &&
  279. this.context.ServerSettings.Certificates.Count > 0)
  280. {
  281. return new X509Certificate(this.context.ServerSettings.Certificates[0].RawData);
  282. }
  283. }
  284. return null;
  285. }
  286. }
  287. // this is used by Mono's certmgr tool to download certificates
  288. internal Mono.Security.X509.X509CertificateCollection ServerCertificates
  289. {
  290. get { return context.ServerSettings.Certificates; }
  291. }
  292. #endregion
  293. #region Internal Async Result/State Class
  294. private class InternalAsyncResult : IAsyncResult
  295. {
  296. private object locker = new object ();
  297. private AsyncCallback _userCallback;
  298. private object _userState;
  299. private Exception _asyncException;
  300. private ManualResetEvent handle;
  301. private bool completed;
  302. private int _bytesRead;
  303. private bool _fromWrite;
  304. private bool _proceedAfterHandshake;
  305. private byte[] _buffer;
  306. private int _offset;
  307. private int _count;
  308. public InternalAsyncResult(AsyncCallback userCallback, object userState, byte[] buffer, int offset, int count, bool fromWrite, bool proceedAfterHandshake)
  309. {
  310. _userCallback = userCallback;
  311. _userState = userState;
  312. _buffer = buffer;
  313. _offset = offset;
  314. _count = count;
  315. _fromWrite = fromWrite;
  316. _proceedAfterHandshake = proceedAfterHandshake;
  317. }
  318. public bool ProceedAfterHandshake
  319. {
  320. get { return _proceedAfterHandshake; }
  321. }
  322. public bool FromWrite
  323. {
  324. get { return _fromWrite; }
  325. }
  326. public byte[] Buffer
  327. {
  328. get { return _buffer; }
  329. }
  330. public int Offset
  331. {
  332. get { return _offset; }
  333. }
  334. public int Count
  335. {
  336. get { return _count; }
  337. }
  338. public int BytesRead
  339. {
  340. get { return _bytesRead; }
  341. }
  342. public object AsyncState
  343. {
  344. get { return _userState; }
  345. }
  346. public Exception AsyncException
  347. {
  348. get { return _asyncException; }
  349. }
  350. public bool CompletedWithError
  351. {
  352. get {
  353. if (IsCompleted == false)
  354. return false;
  355. return null != _asyncException;
  356. }
  357. }
  358. public WaitHandle AsyncWaitHandle
  359. {
  360. get {
  361. lock (locker) {
  362. if (handle == null)
  363. handle = new ManualResetEvent (completed);
  364. }
  365. return handle;
  366. }
  367. }
  368. public bool CompletedSynchronously
  369. {
  370. get { return false; }
  371. }
  372. public bool IsCompleted
  373. {
  374. get {
  375. lock (locker)
  376. return completed;
  377. }
  378. }
  379. private void SetComplete(Exception ex, int bytesRead)
  380. {
  381. lock (locker) {
  382. if (completed)
  383. return;
  384. completed = true;
  385. _asyncException = ex;
  386. _bytesRead = bytesRead;
  387. if (handle != null)
  388. handle.Set ();
  389. }
  390. if (_userCallback != null)
  391. _userCallback.BeginInvoke (this, null, null);
  392. }
  393. public void SetComplete(Exception ex)
  394. {
  395. SetComplete(ex, 0);
  396. }
  397. public void SetComplete(int bytesRead)
  398. {
  399. SetComplete(null, bytesRead);
  400. }
  401. public void SetComplete()
  402. {
  403. SetComplete(null, 0);
  404. }
  405. }
  406. #endregion
  407. #region Stream Overrides and Async Stream Operations
  408. private bool BeginNegotiateHandshake(InternalAsyncResult asyncResult)
  409. {
  410. try
  411. {
  412. lock (this.negotiate)
  413. {
  414. if (this.context.HandshakeState == HandshakeState.None)
  415. {
  416. this.OnBeginNegotiateHandshake(new AsyncCallback(AsyncHandshakeCallback), asyncResult);
  417. return true;
  418. }
  419. else
  420. {
  421. return false;
  422. }
  423. }
  424. }
  425. catch (TlsException ex)
  426. {
  427. this.negotiationComplete.Set();
  428. this.protocol.SendAlert(ex.Alert);
  429. throw new IOException("The authentication or decryption has failed.", ex);
  430. }
  431. catch (Exception ex)
  432. {
  433. this.negotiationComplete.Set();
  434. this.protocol.SendAlert(AlertDescription.InternalError);
  435. throw new IOException("The authentication or decryption has failed.", ex);
  436. }
  437. }
  438. private void EndNegotiateHandshake(InternalAsyncResult asyncResult)
  439. {
  440. if (asyncResult.IsCompleted == false)
  441. asyncResult.AsyncWaitHandle.WaitOne();
  442. if (asyncResult.CompletedWithError)
  443. {
  444. throw asyncResult.AsyncException;
  445. }
  446. }
  447. public override IAsyncResult BeginRead(
  448. byte[] buffer,
  449. int offset,
  450. int count,
  451. AsyncCallback callback,
  452. object state)
  453. {
  454. this.checkDisposed();
  455. if (buffer == null)
  456. {
  457. throw new ArgumentNullException("buffer is a null reference.");
  458. }
  459. if (offset < 0)
  460. {
  461. throw new ArgumentOutOfRangeException("offset is less than 0.");
  462. }
  463. if (offset > buffer.Length)
  464. {
  465. throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
  466. }
  467. if (count < 0)
  468. {
  469. throw new ArgumentOutOfRangeException("count is less than 0.");
  470. }
  471. if (count > (buffer.Length - offset))
  472. {
  473. throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
  474. }
  475. InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, false, true);
  476. if (this.MightNeedHandshake)
  477. {
  478. if (! BeginNegotiateHandshake(asyncResult))
  479. {
  480. //we made it down here so the handshake was not started.
  481. //another thread must have started it in the mean time.
  482. //wait for it to complete and then perform our original operation
  483. this.negotiationComplete.WaitOne();
  484. InternalBeginRead(asyncResult);
  485. }
  486. }
  487. else
  488. {
  489. InternalBeginRead(asyncResult);
  490. }
  491. return asyncResult;
  492. }
  493. // bigger than max record length for SSL/TLS
  494. private byte[] recbuf = new byte[16384];
  495. private void InternalBeginRead(InternalAsyncResult asyncResult)
  496. {
  497. try
  498. {
  499. int preReadSize = 0;
  500. lock (this.read)
  501. {
  502. // If actual buffer is fully read, reset it
  503. bool shouldReset = this.inputBuffer.Position == this.inputBuffer.Length && this.inputBuffer.Length > 0;
  504. // If the buffer isn't fully read, but does have data, we need to immediately
  505. // read the info from the buffer and let the user know that they have more data.
  506. bool shouldReadImmediately = (this.inputBuffer.Length > 0) && (asyncResult.Count > 0);
  507. if (shouldReset)
  508. {
  509. this.resetBuffer();
  510. }
  511. else if (shouldReadImmediately)
  512. {
  513. preReadSize = this.inputBuffer.Read(asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
  514. }
  515. }
  516. // This is explicitly done outside the synclock to avoid
  517. // any potential deadlocks in the delegate call.
  518. if (0 < preReadSize)
  519. {
  520. asyncResult.SetComplete(preReadSize);
  521. }
  522. else if (!this.context.ReceivedConnectionEnd)
  523. {
  524. // this will read data from the network until we have (at least) one
  525. // record to send back to the caller
  526. this.innerStream.BeginRead(recbuf, 0, recbuf.Length,
  527. new AsyncCallback(InternalReadCallback), new object[] { recbuf, asyncResult });
  528. }
  529. else
  530. {
  531. // We're done with the connection so we need to let the caller know with 0 bytes read
  532. asyncResult.SetComplete(0);
  533. }
  534. }
  535. catch (TlsException ex)
  536. {
  537. this.protocol.SendAlert(ex.Alert);
  538. throw new IOException("The authentication or decryption has failed.", ex);
  539. }
  540. catch (Exception ex)
  541. {
  542. throw new IOException("IO exception during read.", ex);
  543. }
  544. }
  545. private MemoryStream recordStream = new MemoryStream();
  546. // read encrypted data until we have enough to decrypt (at least) one
  547. // record and return are the records (may be more than one) we have
  548. private void InternalReadCallback(IAsyncResult result)
  549. {
  550. if (this.disposed)
  551. return;
  552. object[] state = (object[])result.AsyncState;
  553. byte[] recbuf = (byte[])state[0];
  554. InternalAsyncResult internalResult = (InternalAsyncResult)state[1];
  555. try
  556. {
  557. int n = innerStream.EndRead(result);
  558. if (n > 0)
  559. {
  560. // Add the just received data to the waiting data
  561. recordStream.Write(recbuf, 0, n);
  562. }
  563. else
  564. {
  565. // 0 length data means this read operation is done (lost connection in the case of a network stream).
  566. internalResult.SetComplete(0);
  567. return;
  568. }
  569. bool dataToReturn = false;
  570. long pos = recordStream.Position;
  571. recordStream.Position = 0;
  572. byte[] record = null;
  573. // don't try to decode record unless we have at least 5 bytes
  574. // i.e. type (1), protocol (2) and length (2)
  575. if (recordStream.Length >= 5)
  576. {
  577. record = this.protocol.ReceiveRecord(recordStream);
  578. }
  579. // a record of 0 length is valid (and there may be more record after it)
  580. while (record != null)
  581. {
  582. // we probably received more stuff after the record, and we must keep it!
  583. long remainder = recordStream.Length - recordStream.Position;
  584. byte[] outofrecord = null;
  585. if (remainder > 0)
  586. {
  587. outofrecord = new byte[remainder];
  588. recordStream.Read(outofrecord, 0, outofrecord.Length);
  589. }
  590. lock (this.read)
  591. {
  592. long position = this.inputBuffer.Position;
  593. if (record.Length > 0)
  594. {
  595. // Write new data to the inputBuffer
  596. this.inputBuffer.Seek(0, SeekOrigin.End);
  597. this.inputBuffer.Write(record, 0, record.Length);
  598. // Restore buffer position
  599. this.inputBuffer.Seek(position, SeekOrigin.Begin);
  600. dataToReturn = true;
  601. }
  602. }
  603. recordStream.SetLength(0);
  604. record = null;
  605. if (remainder > 0)
  606. {
  607. recordStream.Write(outofrecord, 0, outofrecord.Length);
  608. // type (1), protocol (2) and length (2)
  609. if (recordStream.Length >= 5)
  610. {
  611. // try to see if another record is available
  612. recordStream.Position = 0;
  613. record = this.protocol.ReceiveRecord(recordStream);
  614. if (record == null)
  615. pos = recordStream.Length;
  616. }
  617. else
  618. pos = remainder;
  619. }
  620. else
  621. pos = 0;
  622. }
  623. if (!dataToReturn && (n > 0))
  624. {
  625. if (context.ReceivedConnectionEnd) {
  626. internalResult.SetComplete (0);
  627. } else {
  628. // there is no record to return to caller and (possibly) more data waiting
  629. // so continue reading from network (and appending to stream)
  630. recordStream.Position = recordStream.Length;
  631. this.innerStream.BeginRead(recbuf, 0, recbuf.Length,
  632. new AsyncCallback(InternalReadCallback), state);
  633. }
  634. }
  635. else
  636. {
  637. // we have record(s) to return -or- no more available to read from network
  638. // reset position for further reading
  639. recordStream.Position = pos;
  640. int bytesRead = 0;
  641. lock (this.read)
  642. {
  643. bytesRead = this.inputBuffer.Read(internalResult.Buffer, internalResult.Offset, internalResult.Count);
  644. }
  645. internalResult.SetComplete(bytesRead);
  646. }
  647. }
  648. catch (Exception ex)
  649. {
  650. internalResult.SetComplete(ex);
  651. }
  652. }
  653. private void InternalBeginWrite(InternalAsyncResult asyncResult)
  654. {
  655. try
  656. {
  657. // Send the buffer as a TLS record
  658. lock (this.write)
  659. {
  660. byte[] record = this.protocol.EncodeRecord(
  661. ContentType.ApplicationData, asyncResult.Buffer, asyncResult.Offset, asyncResult.Count);
  662. this.innerStream.BeginWrite(
  663. record, 0, record.Length, new AsyncCallback(InternalWriteCallback), asyncResult);
  664. }
  665. }
  666. catch (TlsException ex)
  667. {
  668. this.protocol.SendAlert(ex.Alert);
  669. this.Close();
  670. throw new IOException("The authentication or decryption has failed.", ex);
  671. }
  672. catch (Exception ex)
  673. {
  674. throw new IOException("IO exception during Write.", ex);
  675. }
  676. }
  677. private void InternalWriteCallback(IAsyncResult ar)
  678. {
  679. if (this.disposed)
  680. return;
  681. InternalAsyncResult internalResult = (InternalAsyncResult)ar.AsyncState;
  682. try
  683. {
  684. this.innerStream.EndWrite(ar);
  685. internalResult.SetComplete();
  686. }
  687. catch (Exception ex)
  688. {
  689. internalResult.SetComplete(ex);
  690. }
  691. }
  692. public override IAsyncResult BeginWrite(
  693. byte[] buffer,
  694. int offset,
  695. int count,
  696. AsyncCallback callback,
  697. object state)
  698. {
  699. this.checkDisposed();
  700. if (buffer == null)
  701. {
  702. throw new ArgumentNullException("buffer is a null reference.");
  703. }
  704. if (offset < 0)
  705. {
  706. throw new ArgumentOutOfRangeException("offset is less than 0.");
  707. }
  708. if (offset > buffer.Length)
  709. {
  710. throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
  711. }
  712. if (count < 0)
  713. {
  714. throw new ArgumentOutOfRangeException("count is less than 0.");
  715. }
  716. if (count > (buffer.Length - offset))
  717. {
  718. throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
  719. }
  720. InternalAsyncResult asyncResult = new InternalAsyncResult(callback, state, buffer, offset, count, true, true);
  721. if (this.MightNeedHandshake)
  722. {
  723. if (! BeginNegotiateHandshake(asyncResult))
  724. {
  725. //we made it down here so the handshake was not started.
  726. //another thread must have started it in the mean time.
  727. //wait for it to complete and then perform our original operation
  728. this.negotiationComplete.WaitOne();
  729. InternalBeginWrite(asyncResult);
  730. }
  731. }
  732. else
  733. {
  734. InternalBeginWrite(asyncResult);
  735. }
  736. return asyncResult;
  737. }
  738. public override int EndRead(IAsyncResult asyncResult)
  739. {
  740. this.checkDisposed();
  741. InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
  742. if (internalResult == null)
  743. {
  744. throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginRead.");
  745. }
  746. // Always wait until the read is complete
  747. if (!asyncResult.IsCompleted)
  748. {
  749. if (!asyncResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
  750. throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndRead");
  751. }
  752. if (internalResult.CompletedWithError)
  753. {
  754. throw internalResult.AsyncException;
  755. }
  756. return internalResult.BytesRead;
  757. }
  758. public override void EndWrite(IAsyncResult asyncResult)
  759. {
  760. this.checkDisposed();
  761. InternalAsyncResult internalResult = asyncResult as InternalAsyncResult;
  762. if (internalResult == null)
  763. {
  764. throw new ArgumentNullException("asyncResult is null or was not obtained by calling BeginWrite.");
  765. }
  766. if (!asyncResult.IsCompleted)
  767. {
  768. if (!internalResult.AsyncWaitHandle.WaitOne (WaitTimeOut, false))
  769. throw new TlsException (AlertDescription.InternalError, "Couldn't complete EndWrite");
  770. }
  771. if (internalResult.CompletedWithError)
  772. {
  773. throw internalResult.AsyncException;
  774. }
  775. }
  776. public override void Close()
  777. {
  778. base.Close ();
  779. }
  780. public override void Flush()
  781. {
  782. this.checkDisposed();
  783. this.innerStream.Flush();
  784. }
  785. public int Read(byte[] buffer)
  786. {
  787. return this.Read(buffer, 0, buffer.Length);
  788. }
  789. public override int Read(byte[] buffer, int offset, int count)
  790. {
  791. this.checkDisposed ();
  792. if (buffer == null)
  793. {
  794. throw new ArgumentNullException ("buffer");
  795. }
  796. if (offset < 0)
  797. {
  798. throw new ArgumentOutOfRangeException("offset is less than 0.");
  799. }
  800. if (offset > buffer.Length)
  801. {
  802. throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
  803. }
  804. if (count < 0)
  805. {
  806. throw new ArgumentOutOfRangeException("count is less than 0.");
  807. }
  808. if (count > (buffer.Length - offset))
  809. {
  810. throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
  811. }
  812. if (this.context.HandshakeState != HandshakeState.Finished)
  813. {
  814. this.NegotiateHandshake (); // Handshake negotiation
  815. }
  816. lock (this.read) {
  817. try {
  818. record_processing.Reset ();
  819. // do we already have some decrypted data ?
  820. if (this.inputBuffer.Position > 0) {
  821. // or maybe we used all the buffer before ?
  822. if (this.inputBuffer.Position == this.inputBuffer.Length) {
  823. this.inputBuffer.SetLength (0);
  824. } else {
  825. int n = this.inputBuffer.Read (buffer, offset, count);
  826. if (n > 0) {
  827. record_processing.Set ();
  828. return n;
  829. }
  830. }
  831. }
  832. bool needMoreData = false;
  833. while (true) {
  834. // we first try to process the read with the data we already have
  835. if ((recordStream.Position == 0) || needMoreData) {
  836. needMoreData = false;
  837. // if we loop, then it either means we need more data
  838. byte[] recbuf = new byte[16384];
  839. int n = 0;
  840. if (count == 1) {
  841. int value = innerStream.ReadByte ();
  842. if (value >= 0) {
  843. recbuf[0] = (byte) value;
  844. n = 1;
  845. }
  846. } else {
  847. n = innerStream.Read (recbuf, 0, recbuf.Length);
  848. }
  849. if (n > 0) {
  850. // Add the new received data to the waiting data
  851. if ((recordStream.Length > 0) && (recordStream.Position != recordStream.Length))
  852. recordStream.Seek (0, SeekOrigin.End);
  853. recordStream.Write (recbuf, 0, n);
  854. } else {
  855. // or that the read operation is done (lost connection in the case of a network stream).
  856. record_processing.Set ();
  857. return 0;
  858. }
  859. }
  860. bool dataToReturn = false;
  861. recordStream.Position = 0;
  862. byte[] record = null;
  863. // don't try to decode record unless we have at least 5 bytes
  864. // i.e. type (1), protocol (2) and length (2)
  865. if (recordStream.Length >= 5) {
  866. record = this.protocol.ReceiveRecord (recordStream);
  867. needMoreData = (record == null);
  868. }
  869. // a record of 0 length is valid (and there may be more record after it)
  870. while (record != null) {
  871. // we probably received more stuff after the record, and we must keep it!
  872. long remainder = recordStream.Length - recordStream.Position;
  873. byte[] outofrecord = null;
  874. if (remainder > 0) {
  875. outofrecord = new byte[remainder];
  876. recordStream.Read (outofrecord, 0, outofrecord.Length);
  877. }
  878. long position = this.inputBuffer.Position;
  879. if (record.Length > 0) {
  880. // Write new data to the inputBuffer
  881. this.inputBuffer.Seek (0, SeekOrigin.End);
  882. this.inputBuffer.Write (record, 0, record.Length);
  883. // Restore buffer position
  884. this.inputBuffer.Seek (position, SeekOrigin.Begin);
  885. dataToReturn = true;
  886. }
  887. recordStream.SetLength (0);
  888. record = null;
  889. if (remainder > 0) {
  890. recordStream.Write (outofrecord, 0, outofrecord.Length);
  891. }
  892. if (dataToReturn) {
  893. // we have record(s) to return -or- no more available to read from network
  894. // reset position for further reading
  895. int i = inputBuffer.Read (buffer, offset, count);
  896. record_processing.Set ();
  897. return i;
  898. }
  899. }
  900. }
  901. }
  902. catch (TlsException ex)
  903. {
  904. throw new IOException("The authentication or decryption has failed.", ex);
  905. }
  906. catch (Exception ex)
  907. {
  908. throw new IOException("IO exception during read.", ex);
  909. }
  910. }
  911. }
  912. public override long Seek(long offset, SeekOrigin origin)
  913. {
  914. throw new NotSupportedException();
  915. }
  916. public override void SetLength(long value)
  917. {
  918. throw new NotSupportedException();
  919. }
  920. public void Write(byte[] buffer)
  921. {
  922. this.Write(buffer, 0, buffer.Length);
  923. }
  924. public override void Write(byte[] buffer, int offset, int count)
  925. {
  926. this.checkDisposed ();
  927. if (buffer == null)
  928. {
  929. throw new ArgumentNullException ("buffer");
  930. }
  931. if (offset < 0)
  932. {
  933. throw new ArgumentOutOfRangeException("offset is less than 0.");
  934. }
  935. if (offset > buffer.Length)
  936. {
  937. throw new ArgumentOutOfRangeException("offset is greater than the length of buffer.");
  938. }
  939. if (count < 0)
  940. {
  941. throw new ArgumentOutOfRangeException("count is less than 0.");
  942. }
  943. if (count > (buffer.Length - offset))
  944. {
  945. throw new ArgumentOutOfRangeException("count is less than the length of buffer minus the value of the offset parameter.");
  946. }
  947. if (this.context.HandshakeState != HandshakeState.Finished)
  948. {
  949. this.NegotiateHandshake ();
  950. }
  951. lock (this.write)
  952. {
  953. try
  954. {
  955. // Send the buffer as a TLS record
  956. byte[] record = this.protocol.EncodeRecord (ContentType.ApplicationData, buffer, offset, count);
  957. this.innerStream.Write (record, 0, record.Length);
  958. }
  959. catch (TlsException ex)
  960. {
  961. this.protocol.SendAlert(ex.Alert);
  962. this.Close();
  963. throw new IOException("The authentication or decryption has failed.", ex);
  964. }
  965. catch (Exception ex)
  966. {
  967. throw new IOException("IO exception during Write.", ex);
  968. }
  969. }
  970. }
  971. public override bool CanRead
  972. {
  973. get { return this.innerStream.CanRead; }
  974. }
  975. public override bool CanSeek
  976. {
  977. get { return false; }
  978. }
  979. public override bool CanWrite
  980. {
  981. get { return this.innerStream.CanWrite; }
  982. }
  983. public override long Length
  984. {
  985. get { throw new NotSupportedException(); }
  986. }
  987. public override long Position
  988. {
  989. get
  990. {
  991. throw new NotSupportedException();
  992. }
  993. set
  994. {
  995. throw new NotSupportedException();
  996. }
  997. }
  998. #endregion
  999. #region IDisposable Members and Finalizer
  1000. ~SslStreamBase()
  1001. {
  1002. this.Dispose(false);
  1003. }
  1004. protected override void Dispose (bool disposing)
  1005. {
  1006. if (!this.disposed)
  1007. {
  1008. if (disposing)
  1009. {
  1010. if (this.innerStream != null)
  1011. {
  1012. if (this.context.HandshakeState == HandshakeState.Finished &&
  1013. !this.context.SentConnectionEnd)
  1014. {
  1015. // Write close notify
  1016. try {
  1017. this.protocol.SendAlert(AlertDescription.CloseNotify);
  1018. } catch {
  1019. }
  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. base.Dispose (disposing);
  1032. }
  1033. }
  1034. #endregion
  1035. #region Misc Methods
  1036. private void resetBuffer()
  1037. {
  1038. this.inputBuffer.SetLength(0);
  1039. this.inputBuffer.Position = 0;
  1040. }
  1041. internal void checkDisposed()
  1042. {
  1043. if (this.disposed)
  1044. {
  1045. throw new ObjectDisposedException("The Stream is closed.");
  1046. }
  1047. }
  1048. #endregion
  1049. }
  1050. }