private void ProcessBinaryMeasurements(IEnumerable <ISupportBinaryImage> measurements, bool useCompactMeasurementFormat) { MemoryStream data = new MemoryStream(); // Serialize data packet flags into response DataPacketFlags flags = DataPacketFlags.NoFlags; // No flags means bit is cleared, i.e., unsynchronized if (useCompactMeasurementFormat) { flags |= DataPacketFlags.Compact; } data.WriteByte((byte)flags); // No frame level timestamp is serialized into the data packet since all data is unsynchronized and essentially // published upon receipt, however timestamps are optionally included in the serialized measurements. // Serialize total number of measurement values to follow data.Write(EndianOrder.BigEndian.GetBytes(measurements.Count()), 0, 4); // Serialize measurements to data buffer foreach (ISupportBinaryImage measurement in measurements) { measurement.CopyBinaryImageToStream(data); } // Publish data packet to client if (m_parent != null) { m_parent.SendClientResponse(m_clientID, ServerResponse.DataPacket, ServerCommand.Subscribe, data.ToArray()); } // Track last publication time m_lastPublishTime = DateTime.UtcNow.Ticks; }
/// <summary> /// Handles the confirmation message received from the /// subscriber to indicate that a buffer block was received. /// </summary> /// <param name="sequenceNumber">The sequence number of the buffer block.</param> /// <returns>A list of buffer block sequence numbers for blocks that need to be retransmitted.</returns> public void ConfirmBufferBlock(uint sequenceNumber) { int sequenceIndex; int removalCount; // We are still receiving confirmations, // so stop the retransmission timer m_bufferBlockRetransmissionTimer.Stop(); lock (m_bufferBlockCacheLock) { // Find the buffer block's location in the cache sequenceIndex = (int)(sequenceNumber - m_expectedBufferBlockConfirmationNumber); if (sequenceIndex >= 0 && sequenceIndex < m_bufferBlockCache.Count && (object)m_bufferBlockCache[sequenceIndex] != null) { // Remove the confirmed block from the cache m_bufferBlockCache[sequenceIndex] = null; if (sequenceNumber == m_expectedBufferBlockConfirmationNumber) { // Get the number of elements to trim from the start of the cache removalCount = m_bufferBlockCache.TakeWhile(m => (object)m == null).Count(); // Trim the cache m_bufferBlockCache.RemoveRange(0, removalCount); // Increase the expected confirmation number m_expectedBufferBlockConfirmationNumber += (uint)removalCount; } else { // Retransmit if confirmations are received out of order for (int i = 0; i < sequenceIndex; i++) { if ((object)m_bufferBlockCache[i] != null) { m_parent.SendClientResponse(m_clientID, ServerResponse.BufferBlock, ServerCommand.Subscribe, m_bufferBlockCache[i]); OnBufferBlockRetransmission(); } } } } // If there are any objects lingering in the // cache, start the retransmission timer if (m_bufferBlockCache.Count > 0) { m_bufferBlockRetransmissionTimer.Start(); } } }
private void ProcessBinaryMeasurements(IEnumerable <ISupportBinaryImage> measurements, long frameLevelTimestamp, bool useCompactMeasurementFormat) { MemoryStream data = new MemoryStream(); // Serialize data packet flags into response DataPacketFlags flags = DataPacketFlags.Synchronized; if (useCompactMeasurementFormat) { flags |= DataPacketFlags.Compact; } data.WriteByte((byte)flags); // Serialize frame timestamp into data packet - this only occurs in synchronized data packets, // unsynchronized subcriptions always include timestamps in the serialized measurements data.Write(EndianOrder.BigEndian.GetBytes(frameLevelTimestamp), 0, 8); // Serialize total number of measurement values to follow data.Write(EndianOrder.BigEndian.GetBytes(measurements.Count()), 0, 4); // Serialize measurements to data buffer foreach (ISupportBinaryImage measurement in measurements) { measurement.CopyBinaryImageToStream(data); } // Publish data packet to client if (m_parent != null) { m_parent.SendClientResponse(m_clientID, ServerResponse.DataPacket, ServerCommand.Subscribe, data.ToArray()); } }
/// <summary> /// Rotates or initializes the crypto keys for this <see cref="ClientConnection"/>. /// </summary> public bool RotateCipherKeys() { // Make sure at least a second has passed before next key rotation if ((DateTime.UtcNow.Ticks - m_lastCipherKeyUpdateTime).ToMilliseconds() >= 1000.0D) { try { // Since this function cannot be not called more than once per second there // is no real benefit to maintaining these memory streams at a member level using (BlockAllocatedMemoryStream response = new BlockAllocatedMemoryStream()) { byte[] bytes, bufferLen; // Create or update cipher keys and initialization vectors UpdateKeyIVs(); // Add current cipher index to response response.WriteByte((byte)m_cipherIndex); // Serialize new keys using (BlockAllocatedMemoryStream buffer = new BlockAllocatedMemoryStream()) { // Write even key bufferLen = BigEndian.GetBytes(m_keyIVs[EvenKey][KeyIndex].Length); buffer.Write(bufferLen, 0, bufferLen.Length); buffer.Write(m_keyIVs[EvenKey][KeyIndex], 0, m_keyIVs[EvenKey][KeyIndex].Length); // Write even initialization vector bufferLen = BigEndian.GetBytes(m_keyIVs[EvenKey][IVIndex].Length); buffer.Write(bufferLen, 0, bufferLen.Length); buffer.Write(m_keyIVs[EvenKey][IVIndex], 0, m_keyIVs[EvenKey][IVIndex].Length); // Write odd key bufferLen = BigEndian.GetBytes(m_keyIVs[OddKey][KeyIndex].Length); buffer.Write(bufferLen, 0, bufferLen.Length); buffer.Write(m_keyIVs[OddKey][KeyIndex], 0, m_keyIVs[OddKey][KeyIndex].Length); // Write odd initialization vector bufferLen = BigEndian.GetBytes(m_keyIVs[OddKey][IVIndex].Length); buffer.Write(bufferLen, 0, bufferLen.Length); buffer.Write(m_keyIVs[OddKey][IVIndex], 0, m_keyIVs[OddKey][IVIndex].Length); // Get bytes from serialized buffer bytes = buffer.ToArray(); } // Encrypt keys using private keys known only to current client and server if (m_authenticated && !string.IsNullOrWhiteSpace(m_sharedSecret)) { bytes = bytes.Encrypt(m_sharedSecret, CipherStrength.Aes256); } // Add serialized key response response.Write(bytes, 0, bytes.Length); // Send cipher key updates m_parent.SendClientResponse(m_clientID, ServerResponse.UpdateCipherKeys, ServerCommand.Subscribe, response.ToArray()); } // Send success message m_parent.SendClientResponse(m_clientID, ServerResponse.Succeeded, ServerCommand.RotateCipherKeys, "New cipher keys established."); m_parent.OnStatusMessage(MessageLevel.Info, $"{ConnectionID} cipher keys rotated."); return(true); } catch (Exception ex) { // Send failure message m_parent.SendClientResponse(m_clientID, ServerResponse.Failed, ServerCommand.RotateCipherKeys, "Failed to establish new cipher keys: " + ex.Message); m_parent.OnStatusMessage(MessageLevel.Warning, $"Failed to establish new cipher keys for {ConnectionID}: {ex.Message}"); return(false); } } m_parent.SendClientResponse(m_clientID, ServerResponse.Failed, ServerCommand.RotateCipherKeys, "Cipher key rotation skipped, keys were already rotated within last second."); m_parent.OnStatusMessage(MessageLevel.Warning, $"Cipher key rotation skipped for {ConnectionID}, keys were already rotated within last second."); return(false); }
private void ProcessMeasurements(IEnumerable <IMeasurement> measurements) { if (m_usePayloadCompression && m_compressionModes.HasFlag(CompressionModes.TSSC)) { ProcessTSSCMeasurements(measurements); return; } // Includes data packet flags and measurement count const int PacketHeaderSize = DataPublisher.ClientResponseHeaderSize + 5; List <IBinaryMeasurement> packet; int packetSize; bool usePayloadCompression; bool useCompactMeasurementFormat; BufferBlockMeasurement bufferBlockMeasurement; byte[] bufferBlock; ushort bufferBlockSignalIndex; IBinaryMeasurement binaryMeasurement; int binaryLength; try { if (!Enabled) { return; } packet = new List <IBinaryMeasurement>(); packetSize = PacketHeaderSize; usePayloadCompression = m_usePayloadCompression; useCompactMeasurementFormat = m_useCompactMeasurementFormat || usePayloadCompression; foreach (IMeasurement measurement in measurements) { bufferBlockMeasurement = measurement as BufferBlockMeasurement; if ((object)bufferBlockMeasurement != null) { // Still sending buffer block measurements to client; we are expecting // confirmations which will indicate whether retransmission is necessary, // so we will restart the retransmission timer m_bufferBlockRetransmissionTimer.Stop(); // Handle buffer block measurements as a special case - this can be any kind of data, // measurement subscriber will need to know how to interpret buffer bufferBlock = new byte[6 + bufferBlockMeasurement.Length]; // Prepend sequence number BigEndian.CopyBytes(m_bufferBlockSequenceNumber, bufferBlock, 0); m_bufferBlockSequenceNumber++; // Copy signal index into buffer bufferBlockSignalIndex = m_signalIndexCache.GetSignalIndex(bufferBlockMeasurement.Key); BigEndian.CopyBytes(bufferBlockSignalIndex, bufferBlock, 4); // Append measurement data and send Buffer.BlockCopy(bufferBlockMeasurement.Buffer, 0, bufferBlock, 6, bufferBlockMeasurement.Length); m_parent.SendClientResponse(m_clientID, ServerResponse.BufferBlock, ServerCommand.Subscribe, bufferBlock); lock (m_bufferBlockCacheLock) { // Cache buffer block for retransmission m_bufferBlockCache.Add(bufferBlock); } // Start the retransmission timer in case we never receive a confirmation m_bufferBlockRetransmissionTimer.Start(); } else { // Serialize the current measurement. if (useCompactMeasurementFormat) { binaryMeasurement = new CompactMeasurement(measurement, m_signalIndexCache, m_includeTime, m_baseTimeOffsets, m_timeIndex, m_useMillisecondResolution); } else { binaryMeasurement = new SerializableMeasurement(measurement, m_parent.GetClientEncoding(m_clientID)); } // Determine the size of the measurement in bytes. binaryLength = binaryMeasurement.BinaryLength; // If the current measurement will not fit in the packet based on the max // packet size, process the current packet and start a new packet. if (packetSize + binaryLength > DataPublisher.MaxPacketSize) { ProcessBinaryMeasurements(packet, useCompactMeasurementFormat, usePayloadCompression); packet.Clear(); packetSize = PacketHeaderSize; } // Add the current measurement to the packet. packet.Add(binaryMeasurement); packetSize += binaryLength; } } // Process the remaining measurements. if (packet.Count > 0) { ProcessBinaryMeasurements(packet, useCompactMeasurementFormat, usePayloadCompression); } IncrementProcessedMeasurements(measurements.Count()); // Update latency statistics m_parent.UpdateLatencyStatistics(measurements.Select(m => (long)(m_lastPublishTime - m.Timestamp))); } catch (Exception ex) { string message = $"Error processing measurements: {ex.Message}"; OnProcessException(MessageLevel.Info, new InvalidOperationException(message, ex)); } }
/// <summary> /// Rotates or initializes the crypto keys for this <see cref="ClientConnection"/>. /// </summary> public bool RotateCipherKeys() { // Make sure at least a second has passed before next key rotation if ((DateTime.UtcNow.Ticks - m_lastCipherKeyUpdateTime).ToMilliseconds() >= 1000.0D) { try { MemoryStream response = new MemoryStream(); // Create or update cipher keys and initialization vectors UpdateKeyIVs(); // Add current cipher index to response response.WriteByte((byte)m_cipherIndex); // Serialize new keys MemoryStream buffer = new MemoryStream(); byte[] bytes, bufferLen; // Write even key bufferLen = EndianOrder.BigEndian.GetBytes(m_keyIVs[EvenKey][KeyIndex].Length); buffer.Write(bufferLen, 0, bufferLen.Length); buffer.Write(m_keyIVs[EvenKey][KeyIndex], 0, m_keyIVs[EvenKey][KeyIndex].Length); // Write even initialization vector bufferLen = EndianOrder.BigEndian.GetBytes(m_keyIVs[EvenKey][IVIndex].Length); buffer.Write(bufferLen, 0, bufferLen.Length); buffer.Write(m_keyIVs[EvenKey][IVIndex], 0, m_keyIVs[EvenKey][IVIndex].Length); // Write odd key bufferLen = EndianOrder.BigEndian.GetBytes(m_keyIVs[OddKey][KeyIndex].Length); buffer.Write(bufferLen, 0, bufferLen.Length); buffer.Write(m_keyIVs[OddKey][KeyIndex], 0, m_keyIVs[OddKey][KeyIndex].Length); // Write odd initialization vector bufferLen = EndianOrder.BigEndian.GetBytes(m_keyIVs[OddKey][IVIndex].Length); buffer.Write(bufferLen, 0, bufferLen.Length); buffer.Write(m_keyIVs[OddKey][IVIndex], 0, m_keyIVs[OddKey][IVIndex].Length); // Get bytes from serialized buffer bytes = buffer.ToArray(); // // Encrypt keys using private keys known only to current client and server // if (m_authenticated && !m_sharedSecret.IsNullOrWhiteSpace()) // bytes = bytes.Encrypt(m_sharedSecret, CipherStrength.Aes256); // Add serialized key response response.Write(bytes, 0, bytes.Length); // Send cipher key updates m_parent.SendClientResponse(m_clientID, ServerResponse.UpdateCipherKeys, ServerCommand.Subscribe, response.ToArray()); // Send success message m_parent.SendClientResponse(m_clientID, ServerResponse.Succeeded, ServerCommand.RotateCipherKeys, "New cipher keys established."); m_parent.OnStatusMessage(ConnectionID + " cipher keys rotated."); return(true); } catch (Exception ex) { // Send failure message m_parent.SendClientResponse(m_clientID, ServerResponse.Failed, ServerCommand.RotateCipherKeys, "Failed to establish new cipher keys: " + ex.Message); m_parent.OnStatusMessage("Failed to establish new cipher keys for {0}: {1}", ConnectionID, ex.Message); return(false); } } m_parent.SendClientResponse(m_clientID, ServerResponse.Failed, ServerCommand.RotateCipherKeys, "Cipher key rotation skipped, keys were already rotated within last second."); m_parent.OnStatusMessage("WARNING: Cipher key rotation skipped for {0}, keys were already rotated within last second.", ConnectionID); return(false); }