Пример #1
0
        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;
        }
Пример #2
0
        /// <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();
                }
            }
        }
Пример #3
0
        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());
            }
        }
Пример #4
0
        /// <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));
            }
        }
Пример #6
0
        /// <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);
        }