DtlsRecordLayer.cs 36 KB

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