DtlsRecordLayer.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.IO;
  5. #if !PORTABLE || NETFX_CORE || DOTNET
  6. using System.Net.Sockets;
  7. #endif
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
  9. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  10. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Date;
  11. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Tls
  12. {
  13. internal class DtlsRecordLayer
  14. : DatagramTransport
  15. {
  16. private const int RECORD_HEADER_LENGTH = 13;
  17. private const int MAX_FRAGMENT_LENGTH = 1 << 14;
  18. private const long TCP_MSL = 1000L * 60 * 2;
  19. private const long RETRANSMIT_TIMEOUT = TCP_MSL * 2;
  20. /// <exception cref="IOException"/>
  21. internal static byte[] ReceiveClientHelloRecord(byte[] data, int dataOff, int dataLen)
  22. {
  23. if (dataLen < RECORD_HEADER_LENGTH)
  24. {
  25. return null;
  26. }
  27. short contentType = TlsUtilities.ReadUint8(data, dataOff + 0);
  28. if (ContentType.handshake != contentType)
  29. return null;
  30. ProtocolVersion version = TlsUtilities.ReadVersion(data, dataOff + 1);
  31. if (!ProtocolVersion.DTLSv10.IsEqualOrEarlierVersionOf(version))
  32. return null;
  33. int epoch = TlsUtilities.ReadUint16(data, dataOff + 3);
  34. if (0 != epoch)
  35. return null;
  36. //long sequenceNumber = TlsUtilities.ReadUint48(data, dataOff + 5);
  37. int length = TlsUtilities.ReadUint16(data, dataOff + 11);
  38. if (dataLen < RECORD_HEADER_LENGTH + length)
  39. return null;
  40. if (length > MAX_FRAGMENT_LENGTH)
  41. return null;
  42. // NOTE: We ignore/drop any data after the first record
  43. return TlsUtilities.CopyOfRangeExact(data, dataOff + RECORD_HEADER_LENGTH,
  44. dataOff + RECORD_HEADER_LENGTH + length);
  45. }
  46. /// <exception cref="IOException"/>
  47. internal static void SendHelloVerifyRequestRecord(DatagramSender sender, long recordSeq, byte[] message)
  48. {
  49. TlsUtilities.CheckUint16(message.Length);
  50. byte[] record = new byte[RECORD_HEADER_LENGTH + message.Length];
  51. TlsUtilities.WriteUint8(ContentType.handshake, record, 0);
  52. TlsUtilities.WriteVersion(ProtocolVersion.DTLSv10, record, 1);
  53. TlsUtilities.WriteUint16(0, record, 3);
  54. TlsUtilities.WriteUint48(recordSeq, record, 5);
  55. TlsUtilities.WriteUint16(message.Length, record, 11);
  56. Array.Copy(message, 0, record, RECORD_HEADER_LENGTH, message.Length);
  57. SendDatagram(sender, record, 0, record.Length);
  58. }
  59. /// <exception cref="IOException"/>
  60. private static void SendDatagram(DatagramSender sender, byte[] buf, int off, int len)
  61. {
  62. // TODO[tls-port] Can we support interrupted IO on .NET?
  63. //try
  64. //{
  65. // sender.Send(buf, off, len);
  66. //}
  67. //catch (InterruptedIOException e)
  68. //{
  69. // e.bytesTransferred = 0;
  70. // throw e;
  71. //}
  72. sender.Send(buf, off, len);
  73. }
  74. private readonly TlsContext m_context;
  75. private readonly TlsPeer m_peer;
  76. private readonly DatagramTransport m_transport;
  77. private readonly ByteQueue m_recordQueue = new ByteQueue();
  78. private readonly object m_writeLock = new object();
  79. private volatile bool m_closed = false;
  80. private volatile bool m_failed = false;
  81. // TODO[dtls13] Review the draft/RFC (legacy_record_version) to see if readVersion can be removed
  82. private volatile ProtocolVersion m_readVersion = null, m_writeVersion = null;
  83. private volatile bool m_inConnection;
  84. private volatile bool m_inHandshake;
  85. private volatile int m_plaintextLimit;
  86. private DtlsEpoch m_currentEpoch, m_pendingEpoch;
  87. private DtlsEpoch m_readEpoch, m_writeEpoch;
  88. private DtlsHandshakeRetransmit m_retransmit = null;
  89. private DtlsEpoch m_retransmitEpoch = null;
  90. private Timeout m_retransmitTimeout = null;
  91. private TlsHeartbeat m_heartbeat = null; // If non-null, controls the sending of heartbeat requests
  92. private bool m_heartBeatResponder = false; // Whether we should send heartbeat responses
  93. private HeartbeatMessage m_heartbeatInFlight = null; // The current in-flight heartbeat request, if any
  94. private Timeout m_heartbeatTimeout = null; // Idle timeout (if none in-flight), else expiry timeout for response
  95. private int m_heartbeatResendMillis = -1; // Delay before retransmit of current in-flight heartbeat request
  96. private Timeout m_heartbeatResendTimeout = null; // Timeout for next retransmit of the in-flight heartbeat request
  97. internal DtlsRecordLayer(TlsContext context, TlsPeer peer, DatagramTransport transport)
  98. {
  99. this.m_context = context;
  100. this.m_peer = peer;
  101. this.m_transport = transport;
  102. this.m_inHandshake = true;
  103. this.m_currentEpoch = new DtlsEpoch(0, TlsNullNullCipher.Instance);
  104. this.m_pendingEpoch = null;
  105. this.m_readEpoch = m_currentEpoch;
  106. this.m_writeEpoch = m_currentEpoch;
  107. SetPlaintextLimit(MAX_FRAGMENT_LENGTH);
  108. }
  109. internal virtual bool IsClosed
  110. {
  111. get { return m_closed; }
  112. }
  113. internal virtual void ResetAfterHelloVerifyRequestServer(long recordSeq)
  114. {
  115. this.m_inConnection = true;
  116. m_currentEpoch.SequenceNumber = recordSeq;
  117. m_currentEpoch.ReplayWindow.Reset(recordSeq);
  118. }
  119. internal virtual void SetPlaintextLimit(int plaintextLimit)
  120. {
  121. this.m_plaintextLimit = plaintextLimit;
  122. }
  123. internal virtual int ReadEpoch
  124. {
  125. get { return m_readEpoch.Epoch; }
  126. }
  127. internal virtual ProtocolVersion ReadVersion
  128. {
  129. get { return m_readVersion; }
  130. set { this.m_readVersion = value; }
  131. }
  132. internal virtual void SetWriteVersion(ProtocolVersion writeVersion)
  133. {
  134. this.m_writeVersion = writeVersion;
  135. }
  136. internal virtual void InitPendingEpoch(TlsCipher pendingCipher)
  137. {
  138. if (m_pendingEpoch != null)
  139. throw new InvalidOperationException();
  140. /*
  141. * TODO "In order to ensure that any given sequence/epoch pair is unique, implementations
  142. * MUST NOT allow the same epoch value to be reused within two times the TCP maximum segment
  143. * lifetime."
  144. */
  145. // TODO Check for overflow
  146. this.m_pendingEpoch = new DtlsEpoch(m_writeEpoch.Epoch + 1, pendingCipher);
  147. }
  148. internal virtual void HandshakeSuccessful(DtlsHandshakeRetransmit retransmit)
  149. {
  150. if (m_readEpoch == m_currentEpoch || m_writeEpoch == m_currentEpoch)
  151. {
  152. // TODO
  153. throw new InvalidOperationException();
  154. }
  155. if (null != retransmit)
  156. {
  157. this.m_retransmit = retransmit;
  158. this.m_retransmitEpoch = m_currentEpoch;
  159. this.m_retransmitTimeout = new Timeout(RETRANSMIT_TIMEOUT);
  160. }
  161. this.m_inHandshake = false;
  162. this.m_currentEpoch = m_pendingEpoch;
  163. this.m_pendingEpoch = null;
  164. }
  165. internal virtual void InitHeartbeat(TlsHeartbeat heartbeat, bool heartbeatResponder)
  166. {
  167. if (m_inHandshake)
  168. throw new InvalidOperationException();
  169. this.m_heartbeat = heartbeat;
  170. this.m_heartBeatResponder = heartbeatResponder;
  171. if (null != heartbeat)
  172. {
  173. ResetHeartbeat();
  174. }
  175. }
  176. internal virtual void ResetWriteEpoch()
  177. {
  178. if (null != m_retransmitEpoch)
  179. {
  180. this.m_writeEpoch = m_retransmitEpoch;
  181. }
  182. else
  183. {
  184. this.m_writeEpoch = m_currentEpoch;
  185. }
  186. }
  187. /// <exception cref="IOException"/>
  188. public virtual int GetReceiveLimit()
  189. {
  190. return System.Math.Min(m_plaintextLimit,
  191. m_readEpoch.Cipher.GetPlaintextLimit(m_transport.GetReceiveLimit() - RECORD_HEADER_LENGTH));
  192. }
  193. /// <exception cref="IOException"/>
  194. public virtual int GetSendLimit()
  195. {
  196. return System.Math.Min(m_plaintextLimit,
  197. m_writeEpoch.Cipher.GetPlaintextLimit(m_transport.GetSendLimit() - RECORD_HEADER_LENGTH));
  198. }
  199. /// <exception cref="IOException"/>
  200. public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
  201. {
  202. long currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
  203. Timeout timeout = Timeout.ForWaitMillis(waitMillis, currentTimeMillis);
  204. byte[] record = null;
  205. while (waitMillis >= 0)
  206. {
  207. if (null != m_retransmitTimeout && m_retransmitTimeout.RemainingMillis(currentTimeMillis) < 1)
  208. {
  209. m_retransmit = null;
  210. m_retransmitEpoch = null;
  211. m_retransmitTimeout = null;
  212. }
  213. if (Timeout.HasExpired(m_heartbeatTimeout, currentTimeMillis))
  214. {
  215. if (null != m_heartbeatInFlight)
  216. throw new TlsTimeoutException("Heartbeat timed out");
  217. this.m_heartbeatInFlight = HeartbeatMessage.Create(m_context,
  218. HeartbeatMessageType.heartbeat_request, m_heartbeat.GeneratePayload());
  219. this.m_heartbeatTimeout = new Timeout(m_heartbeat.TimeoutMillis, currentTimeMillis);
  220. this.m_heartbeatResendMillis = DtlsReliableHandshake.INITIAL_RESEND_MILLIS;
  221. this.m_heartbeatResendTimeout = new Timeout(m_heartbeatResendMillis, currentTimeMillis);
  222. SendHeartbeatMessage(m_heartbeatInFlight);
  223. }
  224. else if (Timeout.HasExpired(m_heartbeatResendTimeout, currentTimeMillis))
  225. {
  226. this.m_heartbeatResendMillis = DtlsReliableHandshake.BackOff(m_heartbeatResendMillis);
  227. this.m_heartbeatResendTimeout = new Timeout(m_heartbeatResendMillis, currentTimeMillis);
  228. SendHeartbeatMessage(m_heartbeatInFlight);
  229. }
  230. waitMillis = Timeout.ConstrainWaitMillis(waitMillis, m_heartbeatTimeout, currentTimeMillis);
  231. waitMillis = Timeout.ConstrainWaitMillis(waitMillis, m_heartbeatResendTimeout, currentTimeMillis);
  232. // NOTE: Guard against bad logic giving a negative value
  233. if (waitMillis < 0)
  234. {
  235. waitMillis = 1;
  236. }
  237. int receiveLimit = System.Math.Min(len, GetReceiveLimit()) + RECORD_HEADER_LENGTH;
  238. if (null == record || record.Length < receiveLimit)
  239. {
  240. record = new byte[receiveLimit];
  241. }
  242. int received = ReceiveRecord(record, 0, receiveLimit, waitMillis);
  243. int processed = ProcessRecord(received, record, buf, off);
  244. if (processed >= 0)
  245. {
  246. return processed;
  247. }
  248. currentTimeMillis = DateTimeUtilities.CurrentUnixMs();
  249. waitMillis = Timeout.GetWaitMillis(timeout, currentTimeMillis);
  250. }
  251. return -1;
  252. }
  253. /// <exception cref="IOException"/>
  254. public virtual void Send(byte[] buf, int off, int len)
  255. {
  256. short contentType = ContentType.application_data;
  257. if (m_inHandshake || m_writeEpoch == m_retransmitEpoch)
  258. {
  259. contentType = ContentType.handshake;
  260. short handshakeType = TlsUtilities.ReadUint8(buf, off);
  261. if (handshakeType == HandshakeType.finished)
  262. {
  263. DtlsEpoch nextEpoch = null;
  264. if (m_inHandshake)
  265. {
  266. nextEpoch = m_pendingEpoch;
  267. }
  268. else if (m_writeEpoch == m_retransmitEpoch)
  269. {
  270. nextEpoch = m_currentEpoch;
  271. }
  272. if (nextEpoch == null)
  273. {
  274. // TODO
  275. throw new InvalidOperationException();
  276. }
  277. // Implicitly send change_cipher_spec and change to pending cipher state
  278. // TODO Send change_cipher_spec and finished records in single datagram?
  279. byte[] data = new byte[]{ 1 };
  280. SendRecord(ContentType.change_cipher_spec, data, 0, data.Length);
  281. this.m_writeEpoch = nextEpoch;
  282. }
  283. }
  284. SendRecord(contentType, buf, off, len);
  285. }
  286. /// <exception cref="IOException"/>
  287. public virtual void Close()
  288. {
  289. if (!m_closed)
  290. {
  291. if (m_inHandshake && m_inConnection)
  292. {
  293. Warn(AlertDescription.user_canceled, "User canceled handshake");
  294. }
  295. CloseTransport();
  296. }
  297. }
  298. internal virtual void Fail(short alertDescription)
  299. {
  300. if (!m_closed)
  301. {
  302. if (m_inConnection)
  303. {
  304. try
  305. {
  306. RaiseAlert(AlertLevel.fatal, alertDescription, null, null);
  307. }
  308. catch (Exception)
  309. {
  310. // Ignore
  311. }
  312. }
  313. this.m_failed = true;
  314. CloseTransport();
  315. }
  316. }
  317. internal virtual void Failed()
  318. {
  319. if (!m_closed)
  320. {
  321. this.m_failed = true;
  322. CloseTransport();
  323. }
  324. }
  325. /// <exception cref="IOException"/>
  326. internal virtual void Warn(short alertDescription, String message)
  327. {
  328. RaiseAlert(AlertLevel.warning, alertDescription, message, null);
  329. }
  330. private void CloseTransport()
  331. {
  332. if (!m_closed)
  333. {
  334. /*
  335. * RFC 5246 7.2.1. Unless some other fatal alert has been transmitted, each party is
  336. * required to send a close_notify alert before closing the write side of the
  337. * connection. The other party MUST respond with a close_notify alert of its own and
  338. * close down the connection immediately, discarding any pending writes.
  339. */
  340. try
  341. {
  342. if (!m_failed)
  343. {
  344. Warn(AlertDescription.close_notify, null);
  345. }
  346. m_transport.Close();
  347. }
  348. catch (Exception)
  349. {
  350. // Ignore
  351. }
  352. this.m_closed = true;
  353. }
  354. }
  355. /// <exception cref="IOException"/>
  356. private void RaiseAlert(short alertLevel, short alertDescription, string message, Exception cause)
  357. {
  358. m_peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause);
  359. byte[] error = new byte[2];
  360. error[0] = (byte)alertLevel;
  361. error[1] = (byte)alertDescription;
  362. SendRecord(ContentType.alert, error, 0, 2);
  363. }
  364. /// <exception cref="IOException"/>
  365. private int ReceiveDatagram(byte[] buf, int off, int len, int waitMillis)
  366. {
  367. try
  368. {
  369. return m_transport.Receive(buf, off, len, waitMillis);
  370. }
  371. catch (TlsTimeoutException)
  372. {
  373. return -1;
  374. }
  375. #if !PORTABLE || NETFX_CORE || DOTNET
  376. catch (SocketException e)
  377. {
  378. if (TlsUtilities.IsTimeout(e))
  379. return -1;
  380. throw e;
  381. }
  382. #endif
  383. // TODO[tls-port] Can we support interrupted IO on .NET?
  384. //catch (InterruptedIOException e)
  385. //{
  386. // e.bytesTransferred = 0;
  387. // throw e;
  388. //}
  389. }
  390. // TODO Include 'currentTimeMillis' as an argument, use with Timeout, resetHeartbeat
  391. /// <exception cref="IOException"/>
  392. private int ProcessRecord(int received, byte[] record, byte[] buf, int off)
  393. {
  394. // NOTE: received < 0 (timeout) is covered by this first case
  395. if (received < RECORD_HEADER_LENGTH)
  396. return -1;
  397. int length = TlsUtilities.ReadUint16(record, 11);
  398. if (received != (length + RECORD_HEADER_LENGTH))
  399. return -1;
  400. // TODO[dtls13] Deal with opaque record type for 1.3 AEAD ciphers
  401. short recordType = TlsUtilities.ReadUint8(record, 0);
  402. switch (recordType)
  403. {
  404. case ContentType.alert:
  405. case ContentType.application_data:
  406. case ContentType.change_cipher_spec:
  407. case ContentType.handshake:
  408. case ContentType.heartbeat:
  409. break;
  410. default:
  411. return -1;
  412. }
  413. int epoch = TlsUtilities.ReadUint16(record, 3);
  414. DtlsEpoch recordEpoch = null;
  415. if (epoch == m_readEpoch.Epoch)
  416. {
  417. recordEpoch = m_readEpoch;
  418. }
  419. else if (recordType == ContentType.handshake && null != m_retransmitEpoch
  420. && epoch == m_retransmitEpoch.Epoch)
  421. {
  422. recordEpoch = m_retransmitEpoch;
  423. }
  424. if (null == recordEpoch)
  425. return -1;
  426. long seq = TlsUtilities.ReadUint48(record, 5);
  427. if (recordEpoch.ReplayWindow.ShouldDiscard(seq))
  428. return -1;
  429. ProtocolVersion recordVersion = TlsUtilities.ReadVersion(record, 1);
  430. if (!recordVersion.IsDtls)
  431. return -1;
  432. if (null != m_readVersion && !m_readVersion.Equals(recordVersion))
  433. {
  434. /*
  435. * Special-case handling for retransmitted ClientHello records.
  436. *
  437. * TODO Revisit how 'readVersion' works, since this is quite awkward.
  438. */
  439. bool isClientHelloFragment =
  440. ReadEpoch == 0
  441. && length > 0
  442. && ContentType.handshake == recordType
  443. && HandshakeType.client_hello == TlsUtilities.ReadUint8(record, RECORD_HEADER_LENGTH);
  444. if (!isClientHelloFragment)
  445. return -1;
  446. }
  447. long macSeqNo = GetMacSequenceNumber(recordEpoch.Epoch, seq);
  448. TlsDecodeResult decoded = recordEpoch.Cipher.DecodeCiphertext(macSeqNo, recordType, recordVersion, record,
  449. RECORD_HEADER_LENGTH, length);
  450. recordEpoch.ReplayWindow.ReportAuthenticated(seq);
  451. if (decoded.len > m_plaintextLimit)
  452. return -1;
  453. if (decoded.len < 1 && decoded.contentType != ContentType.application_data)
  454. return -1;
  455. if (null == m_readVersion)
  456. {
  457. bool isHelloVerifyRequest =
  458. ReadEpoch == 0
  459. && length > 0
  460. && ContentType.handshake == recordType
  461. && HandshakeType.hello_verify_request == TlsUtilities.ReadUint8(record, RECORD_HEADER_LENGTH);
  462. if (isHelloVerifyRequest)
  463. {
  464. /*
  465. * RFC 6347 4.2.1 DTLS 1.2 server implementations SHOULD use DTLS version 1.0
  466. * regardless of the version of TLS that is expected to be negotiated. DTLS 1.2 and
  467. * 1.0 clients MUST use the version solely to indicate packet formatting (which is
  468. * the same in both DTLS 1.2 and 1.0) and not as part of version negotiation.
  469. */
  470. if (!ProtocolVersion.DTLSv12.IsEqualOrLaterVersionOf(recordVersion))
  471. return -1;
  472. }
  473. else
  474. {
  475. this.m_readVersion = recordVersion;
  476. }
  477. }
  478. switch (decoded.contentType)
  479. {
  480. case ContentType.alert:
  481. {
  482. if (decoded.len == 2)
  483. {
  484. short alertLevel = TlsUtilities.ReadUint8(decoded.buf, decoded.off);
  485. short alertDescription = TlsUtilities.ReadUint8(decoded.buf, decoded.off + 1);
  486. m_peer.NotifyAlertReceived(alertLevel, alertDescription);
  487. if (alertLevel == AlertLevel.fatal)
  488. {
  489. Failed();
  490. throw new TlsFatalAlert(alertDescription);
  491. }
  492. // TODO Can close_notify be a fatal alert?
  493. if (alertDescription == AlertDescription.close_notify)
  494. {
  495. CloseTransport();
  496. }
  497. }
  498. return -1;
  499. }
  500. case ContentType.application_data:
  501. {
  502. if (m_inHandshake)
  503. {
  504. // TODO Consider buffering application data for new epoch that arrives
  505. // out-of-order with the Finished message
  506. return -1;
  507. }
  508. break;
  509. }
  510. case ContentType.change_cipher_spec:
  511. {
  512. // Implicitly receive change_cipher_spec and change to pending cipher state
  513. for (int i = 0; i < decoded.len; ++i)
  514. {
  515. short message = TlsUtilities.ReadUint8(decoded.buf, decoded.off + i);
  516. if (message != ChangeCipherSpec.change_cipher_spec)
  517. continue;
  518. if (m_pendingEpoch != null)
  519. {
  520. m_readEpoch = m_pendingEpoch;
  521. }
  522. }
  523. return -1;
  524. }
  525. case ContentType.handshake:
  526. {
  527. if (!m_inHandshake)
  528. {
  529. if (null != m_retransmit)
  530. {
  531. m_retransmit.ReceivedHandshakeRecord(epoch, decoded.buf, decoded.off, decoded.len);
  532. }
  533. // TODO Consider support for HelloRequest
  534. return -1;
  535. }
  536. break;
  537. }
  538. case ContentType.heartbeat:
  539. {
  540. if (null != m_heartbeatInFlight || m_heartBeatResponder)
  541. {
  542. try
  543. {
  544. MemoryStream input = new MemoryStream(decoded.buf, decoded.off, decoded.len, false);
  545. HeartbeatMessage heartbeatMessage = HeartbeatMessage.Parse(input);
  546. if (null != heartbeatMessage)
  547. {
  548. switch (heartbeatMessage.Type)
  549. {
  550. case HeartbeatMessageType.heartbeat_request:
  551. {
  552. if (m_heartBeatResponder)
  553. {
  554. HeartbeatMessage response = HeartbeatMessage.Create(m_context,
  555. HeartbeatMessageType.heartbeat_response, heartbeatMessage.Payload);
  556. SendHeartbeatMessage(response);
  557. }
  558. break;
  559. }
  560. case HeartbeatMessageType.heartbeat_response:
  561. {
  562. if (null != m_heartbeatInFlight
  563. && Arrays.AreEqual(heartbeatMessage.Payload, m_heartbeatInFlight.Payload))
  564. {
  565. ResetHeartbeat();
  566. }
  567. break;
  568. }
  569. default:
  570. break;
  571. }
  572. }
  573. }
  574. catch (Exception)
  575. {
  576. // Ignore
  577. }
  578. }
  579. return -1;
  580. }
  581. default:
  582. return -1;
  583. }
  584. /*
  585. * NOTE: If we receive any non-handshake data in the new epoch implies the peer has
  586. * received our final flight.
  587. */
  588. if (!m_inHandshake && null != m_retransmit)
  589. {
  590. this.m_retransmit = null;
  591. this.m_retransmitEpoch = null;
  592. this.m_retransmitTimeout = null;
  593. }
  594. Array.Copy(decoded.buf, decoded.off, buf, off, decoded.len);
  595. return decoded.len;
  596. }
  597. /// <exception cref="IOException"/>
  598. private int ReceiveRecord(byte[] buf, int off, int len, int waitMillis)
  599. {
  600. if (m_recordQueue.Available > 0)
  601. {
  602. int length = 0;
  603. if (m_recordQueue.Available >= RECORD_HEADER_LENGTH)
  604. {
  605. byte[] lengthBytes = new byte[2];
  606. m_recordQueue.Read(lengthBytes, 0, 2, 11);
  607. length = TlsUtilities.ReadUint16(lengthBytes, 0);
  608. }
  609. int received = System.Math.Min(m_recordQueue.Available, RECORD_HEADER_LENGTH + length);
  610. m_recordQueue.RemoveData(buf, off, received, 0);
  611. return received;
  612. }
  613. {
  614. int received = ReceiveDatagram(buf, off, len, waitMillis);
  615. if (received >= RECORD_HEADER_LENGTH)
  616. {
  617. this.m_inConnection = true;
  618. int fragmentLength = TlsUtilities.ReadUint16(buf, off + 11);
  619. int recordLength = RECORD_HEADER_LENGTH + fragmentLength;
  620. if (received > recordLength)
  621. {
  622. m_recordQueue.AddData(buf, off + recordLength, received - recordLength);
  623. received = recordLength;
  624. }
  625. }
  626. return received;
  627. }
  628. }
  629. private void ResetHeartbeat()
  630. {
  631. this.m_heartbeatInFlight = null;
  632. this.m_heartbeatResendMillis = -1;
  633. this.m_heartbeatResendTimeout = null;
  634. this.m_heartbeatTimeout = new Timeout(m_heartbeat.IdleMillis);
  635. }
  636. /// <exception cref="IOException"/>
  637. private void SendHeartbeatMessage(HeartbeatMessage heartbeatMessage)
  638. {
  639. MemoryStream output = new MemoryStream();
  640. heartbeatMessage.Encode(output);
  641. byte[] buf = output.ToArray();
  642. SendRecord(ContentType.heartbeat, buf, 0, buf.Length);
  643. }
  644. /*
  645. * Currently uses synchronization to ensure heartbeat sends and application data sends don't
  646. * interfere with each other. It may be overly cautious; the sequence number allocation is
  647. * atomic, and if we synchronize only on the datagram send instead, then the only effect should
  648. * be possible reordering of records (which might surprise a reliable transport implementation).
  649. */
  650. /// <exception cref="IOException"/>
  651. private void SendRecord(short contentType, byte[] buf, int off, int len)
  652. {
  653. // Never send anything until a valid ClientHello has been received
  654. if (m_writeVersion == null)
  655. return;
  656. if (len > m_plaintextLimit)
  657. throw new TlsFatalAlert(AlertDescription.internal_error);
  658. /*
  659. * RFC 5246 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
  660. * or ChangeCipherSpec content types.
  661. */
  662. if (len < 1 && contentType != ContentType.application_data)
  663. throw new TlsFatalAlert(AlertDescription.internal_error);
  664. lock (m_writeLock)
  665. {
  666. int recordEpoch = m_writeEpoch.Epoch;
  667. long recordSequenceNumber = m_writeEpoch.AllocateSequenceNumber();
  668. long macSequenceNumber = GetMacSequenceNumber(recordEpoch, recordSequenceNumber);
  669. ProtocolVersion recordVersion = m_writeVersion;
  670. TlsEncodeResult encoded = m_writeEpoch.Cipher.EncodePlaintext(macSequenceNumber, contentType,
  671. recordVersion, RECORD_HEADER_LENGTH, buf, off, len);
  672. int ciphertextLength = encoded.len - RECORD_HEADER_LENGTH;
  673. TlsUtilities.CheckUint16(ciphertextLength);
  674. TlsUtilities.WriteUint8(encoded.recordType, encoded.buf, encoded.off + 0);
  675. TlsUtilities.WriteVersion(recordVersion, encoded.buf, encoded.off + 1);
  676. TlsUtilities.WriteUint16(recordEpoch, encoded.buf, encoded.off + 3);
  677. TlsUtilities.WriteUint48(recordSequenceNumber, encoded.buf, encoded.off + 5);
  678. TlsUtilities.WriteUint16(ciphertextLength, encoded.buf, encoded.off + 11);
  679. SendDatagram(m_transport, encoded.buf, encoded.off, encoded.len);
  680. }
  681. }
  682. private static long GetMacSequenceNumber(int epoch, long sequence_number)
  683. {
  684. return ((epoch & 0xFFFFFFFFL) << 48) | sequence_number;
  685. }
  686. }
  687. }
  688. #pragma warning restore
  689. #endif