public Record[] ProcessHandshakeMessages(ProtocolVersion version, HandshakeMessage[] messages, int maxFragmentLength) { List<Record> records = new List<Record>(); // Write all handshake data to memory stream MemoryStream memStream = new MemoryStream(); for (int i = 0; i < messages.Length; i++) { byte[] msgBytes = messages[i].Encode(); memStream.Write(msgBytes, 0, msgBytes.Length); } // Read handshake data from memory stream one at a time memStream.Position = 0; while (memStream.Position < memStream.Length) { long fragmentLength = Math.Min(memStream.Length - memStream.Position, maxFragmentLength); byte[] fragment = new byte[fragmentLength]; memStream.Read(fragment, 0, fragment.Length); Record record = new Record(RecordType.Handshake, version); record.Fragment = fragment; records.Add(record); } return records.ToArray(); }
public async Task SendAsync(Record[] records, CancellationToken token) { if (records == null) { throw new ArgumentNullException(nameof(records)); } // Make sure fragments are not too long foreach (Record record in records) { if (record.Fragment.Length > MAX_RECORD_SIZE) { throw new RecordTooLargeException("Trying to send a too large fragment: " + record.Fragment.Length); } Log.Debug(">> RECORD TX, Type {0}, Len {1}.", record.Type, record.FragmentLength); var buffer = record.GetBytes(); try { await this.innerStream.WriteAsync(buffer, 0, buffer.Length, token); break; } catch (Exception ex) { Log.Error("Send failed: {0}", ex.Message); throw; } } }
public Record[] ProcessOutputData(ProtocolVersion version, byte type, byte[] data, int maxFragmentLength) { List<Record> records = new List<Record>(); // Read handshake data from memory stream one at a time MemoryStream memStream = new MemoryStream(data); while (memStream.Position < memStream.Length) { long fragmentLength = Math.Min(memStream.Length - memStream.Position, maxFragmentLength); byte[] fragment = new byte[fragmentLength]; memStream.Read(fragment, 0, fragment.Length); Record record = new Record((RecordType)type, version); record.Fragment = fragment; records.Add(record); } return records.ToArray(); }
public HandshakeMessage[] ProcessHandshakeRecord(ProtocolVersion version, Record record) { List<HandshakeMessage> ret = new List<HandshakeMessage>(); // Write record to input stream and attempt to parse a handshake message _inputStream.Write(record.Fragment, 0, record.Fragment.Length); while (_inputStream.Length - _inputtedBytes >= 4) { byte[] inputBuffer = _inputStream.GetBuffer(); int dataLength = (inputBuffer[_inputtedBytes + 1] << 16) | (inputBuffer[_inputtedBytes + 2] << 8) | inputBuffer[_inputtedBytes + 3]; // Check that if we have enough data to read message now if (4 + dataLength > _inputStream.Length - _inputtedBytes) break; byte[] msgData = new byte[4 + dataLength]; long writePosition = _inputStream.Position; // Read message data from inputStream _inputStream.Position = _inputtedBytes; _inputStream.Read(msgData, 0, msgData.Length); _inputStream.Position = writePosition; _inputtedBytes += msgData.Length; // Add resulting handshake message to the results ret.Add(HandshakeMessage.GetInstance(version, msgData)); } if (_inputtedBytes == _inputStream.Length) { // Reset stream to save memory _inputStream.Position = 0; _inputStream.SetLength(0); _inputtedBytes = 0; } return ret.ToArray(); }
private void ProcessUnknownRecord(Record record) { throw new InvalidOperationException("Unknown record"); }
private async Task ProcessSendFatalAlert(Alert alert, CancellationToken token) { this.logger?.Warn("Processing fatal alert..."); try { // Create and encrypt the alert record Record record = new Record(RecordType.Alert, this._handshakeSession.NegotiatedVersion, alert.GetBytes()); this._recordHandler.ProcessOutputRecord(record); // Attempt sending the alert record await this._recordStream.SendAsync(new Record[] { record }, token); } catch (Exception ex) { this.logger?.Error("Processing fatal alert failed: {0}", ex.Message); } finally { if (this._recordStream != null) { this.logger?.Info("Closing record stream..."); this._recordStream.Flush(); this._recordStream.Close(); } } }
private void ProcessAlertRecord(Record record) { // TODO: Received an alert, handle correctly Alert alert = new Alert(record.Fragment); this.logger?.Debug("Received an alert: " + alert.Description); if (alert.Level == AlertLevel.Fatal) { // Fatal alerts don't need close notify _recordStream.Close(); throw new Exception("Received a fatal alert"); } }
private async Task SendServerChangeCipherSpec(CancellationToken token) { this.logger?.Debug("Sending change cipher spec to client..."); // Create the change cipher spec protocol packet // NOTE: this has to be before recordHandler.ChangeLocalState since it uses the old state var record = new Record( RecordType.ChangeCipherSpec, this._handshakeSession.NegotiatedVersion, new byte[] { 0x01 }); this._recordHandler.ProcessOutputRecord(record); // NOTE: keep this before recordHandler.ChangeLocalState since it may generate masterSecret this._handshakeSession.LocalChangeCipherSpec(); // Change cipher suite in record handler and handle it in handshake session this._recordHandler.SetCipherSuite(this._handshakeSession.CipherSuite, this._handshakeSession.ConnectionState); this._recordHandler.ChangeLocalState(); // Send the change cipher spec protocol packet await this._recordStream.SendAsync(new[] { record }, token); }
public async Task<Record[]> ReceiveAsync(CancellationToken token) { var records = new List<Record>(); var readBuffer = new byte[MAX_RECORD_SIZE]; var needsMoreData = true; while (!token.IsCancellationRequested) { if (needsMoreData) { // hook to combine token.cancel with stream.close, otherwise ReadAsync never returns using (token.Register(this.innerStream.Close)) { try { var readBytes = await this.innerStream.ReadAsync(readBuffer, 0, readBuffer.Length, token); if (readBytes < 1) { continue; } this.rcvBuffer.AddRange(readBuffer.Take(readBytes)); } catch (Exception e) { if (token.IsCancellationRequested) { throw new OperationCanceledException("Read was cancelled"); } Log.Error("Read failed: ", e.Message); } } } // do not inspect buffer if there is not even a packet header if (this.rcvBuffer.Count < 5) { await Task.Delay(5, token); continue; } // We require the fragment bytes int fragmentLength = (this.rcvBuffer[3] << 8) | this.rcvBuffer[4]; if (fragmentLength > this.rcvBuffer.Count + 5) { // read more data needsMoreData = true; continue; } needsMoreData = false; // Construct the TLSRecord returned as result var recordBuffer = this.rcvBuffer.Take(5 + fragmentLength).ToArray(); var record = new Record(recordBuffer); Log.Debug("<< RECORD RX, Type {0}, Fragment Len {1} Seq {2}.", record.Type, record.FragmentLength, record.SequenceNumber); Buffer.BlockCopy(recordBuffer, 5, record.Fragment, 0, fragmentLength); records.Add(record); // remove record from input buffer this.rcvBuffer.RemoveRange(0, 5 + fragmentLength); if (this.rcvBuffer.Count == 0) { // return records break; } } return records.ToArray(); }