/// <summary>
        /// Returns the number of bytes to read from the stream.
        /// </summary>
        /// <param name="state">Instance of <see cref="T:NavtelecomProtocol.SessionState" />.</param>
        /// <param name="reader"><see cref="T:SharpStructures.BinaryListReader" /> linked to a FLEX message body.</param>
        /// <param name="writer"><see cref="T:SharpStructures.MemoryBufferWriter" /> with data to be sent to the client.</param>
        /// <param name="token">The token to monitor for cancellation requests.</param>
        /// <returns>A task that represents the asynchronous operation. Contains the total number of bytes to read from the socket. Zero bytes to stop reading and send the response.</returns>
        public Task <int> GetPendingBytesAsync(SessionState state, BinaryListReader reader, MemoryBufferWriter writer,
                                               CancellationToken token)
        {
            switch (reader.ByteList.Count)
            {
            case 2:
                return(Task.FromResult(1));

            case 3:
                var messageSize  = FlexMessageTable.GetFlexMessageSize(state);
                var messageCount = reader.ByteList[2];

                writer.Write(Response);
                writer.Write(messageCount);
                writer.Write(BinaryUtilities.GetCrc8(writer.Buffer));

                return(Task.FromResult(messageSize * messageCount + 1));

            default:
                if (reader.ByteList.Last() != BinaryUtilities.GetCrc8(reader.ByteList.Take(reader.ByteList.Count - 1)))
                {
                    throw new ArgumentException("Invalid FLEX message CRC.");
                }

                return(Task.FromResult(0));
            }
        }
        /// <summary>
        /// Processes the body of the message.
        /// </summary>
        /// <param name="state">Instance of <see cref="T:NavtelecomProtocol.SessionState" />.</param>
        /// <param name="reader"><see cref="T:SharpStructures.BinaryListReader" /> linked to an NTCB message body.</param>
        /// <param name="writer"><see cref="T:SharpStructures.MemoryBufferWriter" /> with data to be sent to the client.</param>
        public void ProcessBody(SessionState state, BinaryListReader reader, MemoryBufferWriter writer)
        {
            if (!reader.ReadBytes(MessageIdentifier.Length).Select(x => (char)x).SequenceEqual(MessageIdentifier))
            {
                throw new ArgumentException("NTCB identity message prefix does not match.");
            }

            if (reader.ReadByte() != ProtocolIdentifier)
            {
                throw new ArgumentException("Unknown NTCB protocol identifier.");
            }

            state.ProtocolVersion = reader.ReadByte();
            state.StructVersion   = reader.ReadByte();

            var dataSize = reader.ReadByte();

            state.FieldMask = new bool[dataSize];

            var maskBytes = reader.ReadBytes(BinaryUtilities.GetByteCountFromBitCount(dataSize));

            for (var i = 0; i < dataSize; ++i)
            {
                var targetByte = maskBytes[i >> 3];

                var mask = 1 << (7 - i & 7);

                state.FieldMask[i] = (targetByte & mask) != 0;
            }

            writer.Write(BinaryUtilities.StringToBytes("*<FLEX"));
            writer.Write(ProtocolIdentifier);
            writer.Write(state.ProtocolVersion);
            writer.Write(state.StructVersion);
        }
        /// <summary>
        /// Returns the number of bytes to read from the stream.
        /// </summary>
        /// <param name="state">Instance of <see cref="T:NavtelecomProtocol.SessionState" />.</param>
        /// <param name="reader"><see cref="T:SharpStructures.BinaryListReader" /> linked to a FLEX message body.</param>
        /// <param name="writer"><see cref="T:SharpStructures.MemoryBufferWriter" /> with data to be sent to the client.</param>
        /// <param name="token">The token to monitor for cancellation requests.</param>
        /// <returns>A task that represents the asynchronous operation. Contains the total number of bytes to read from the socket. Zero bytes to stop reading and send the response.</returns>
        public async Task <int> GetPendingBytesAsync(SessionState state, BinaryListReader reader, MemoryBufferWriter writer, CancellationToken token)
        {
            switch (reader.ByteList.Count)
            {
            case 2:
                return(13);

            case 15:
                reader.SetPosition(13);

                return(reader.ReadUInt16() + 1);

            default:
                if (reader.ByteList.Last() != BinaryUtilities.GetCrc8(reader.ByteList.Take(reader.ByteList.Count - 1)))
                {
                    throw new ArgumentException("Invalid FLEX message CRC.");
                }

                reader.SetPosition(4);

                var result    = reader.ReadByte();
                var crashTime = reader.ReadUInt32();
                var offset    = reader.ReadUInt32();
                var sizeRead  = reader.ReadUInt16();

                if (state.CrashInfo != null && result == 0x00 && state.CrashInfo.Timestamp == crashTime)
                {
                    for (var i = 0; i < sizeRead; ++i)
                    {
                        state.CrashInfo.Data[offset + i] = reader.ByteList[15 + i];
                    }

                    state.CrashInfo.Offset = offset + sizeRead;

                    var bytesLeft = state.CrashInfo.Data.Length - state.CrashInfo.Offset;

                    writer.Write(CrashInformation.CrashDataQuery);
                    writer.Write(crashTime);
                    writer.Write(state.CrashInfo.Offset);
                    writer.Write(bytesLeft > ushort.MaxValue ? ushort.MaxValue : (ushort)bytesLeft);
                    writer.Write(BinaryUtilities.GetCrc8(writer.Buffer));

                    if (bytesLeft == 0)
                    {
                        if (_onReadyCrash != null)
                        {
                            await _onReadyCrash(state, token).ConfigureAwait(false);
                        }

                        state.CrashInfo = null;
                    }
                }

                return(0);
            }
        }
        /// <summary>
        /// Processes the body of the message.
        /// </summary>
        /// <param name="state">Instance of <see cref="T:NavtelecomProtocol.SessionState" />.</param>
        /// <param name="reader"><see cref="T:SharpStructures.BinaryListReader" /> linked to an NTCB message body.</param>
        /// <param name="writer"><see cref="T:SharpStructures.MemoryBufferWriter" /> with data to be sent to the client.</param>
        public void ProcessBody(SessionState state, BinaryListReader reader, MemoryBufferWriter writer)
        {
            if (!reader.ReadBytes(Prefix.Length).Select(x => (char)x).SequenceEqual(Prefix))
            {
                throw new ArgumentException("NTCB identity message prefix does not match.");
            }

            state.DeviceIdentifier = new string(reader.ReadBytes(15).Select(x => (char)x).ToArray());

            writer.Write(Response);
        }
Пример #5
0
        /// <summary>
        /// Returns the number of bytes to read from the stream.
        /// </summary>
        /// <param name="state">Instance of <see cref="T:NavtelecomProtocol.SessionState" />.</param>
        /// <param name="reader"><see cref="T:SharpStructures.BinaryListReader" /> linked to a FLEX message body.</param>
        /// <param name="writer"><see cref="T:SharpStructures.MemoryBufferWriter" /> with data to be sent to the client.</param>
        /// <param name="token">The token to monitor for cancellation requests.</param>
        /// <returns>A task that represents the asynchronous operation. Contains the total number of bytes to read from the socket. Zero bytes to stop reading and send the response.</returns>
        public Task <int> GetPendingBytesAsync(SessionState state, BinaryListReader reader, MemoryBufferWriter writer,
                                               CancellationToken token)
        {
            switch (reader.ByteList.Count)
            {
            case 2:
                return(Task.FromResult(12));

            case 14:
                return(Task.FromResult(reader.ByteList[13] + 1));

            default:
                if (reader.ByteList.Last() != BinaryUtilities.GetCrc8(reader.ByteList.Take(reader.ByteList.Count - 1)))
                {
                    throw new ArgumentException("Invalid FLEX message CRC.");
                }

                reader.SetPosition(2);

                reader.ReadByte();
                reader.ReadByte();

                var crashTime   = reader.ReadUInt32();
                var crashLength = reader.ReadUInt32();

                var flags = reader.ReadByte();

                var nameLength = reader.ReadByte();
                var crashName  = new string(reader.ReadBytes(nameLength).Select(x => (char)x).ToArray());

                if (crashTime > 0 && crashLength > 0 && flags == 0xFF)
                {
                    state.CrashInfo = new CrashInformation
                    {
                        Data      = new byte[crashLength],
                        Name      = crashName,
                        Timestamp = crashTime
                    };

                    writer.Write(CrashInformation.CrashDataQuery);
                    writer.Write(crashTime);
                    writer.Write(0U);
                    writer.Write(crashLength > ushort.MaxValue ? ushort.MaxValue : (ushort)crashLength);
                    writer.Write(BinaryUtilities.GetCrc8(writer.Buffer));
                }

                return(Task.FromResult(0));
            }
        }
        /// <summary>
        /// Returns the number of bytes to read from the stream.
        /// </summary>
        /// <param name="sessionState">Instance of <see cref="T:NavtelecomProtocol.SessionState" />.</param>
        /// <param name="receiveBuffer">A read-only collection of bytes received from a stream.</param>
        /// <param name="sendBuffer"><see cref="T:SharpStructures.MemoryBuffer" /> with data to be sent to the client.</param>
        /// <param name="token">The token to monitor for cancellation requests.</param>
        /// <returns>A task that represents the asynchronous operation. Contains the total number of bytes to read from the socket. Zero bytes to stop reading and send the response.</returns>
        public Task <int> GetPendingBytesAsync(SessionState sessionState, IReadOnlyList <byte> receiveBuffer, MemoryBuffer sendBuffer, CancellationToken token)
        {
            switch (receiveBuffer.Count)
            {
            case 1:
                return(Task.FromResult(HeaderLength - 1));

            case HeaderLength:
                var reader = new BinaryListReader(receiveBuffer, true);

                if (!reader.ReadBytes(HeaderPreamble.Length).Select(x => (char)x).SequenceEqual(HeaderPreamble))
                {
                    throw new ArgumentException("NTCB header preamble does not match.");
                }

                sessionState.ReceiverId = reader.ReadUInt32();
                sessionState.SenderId   = reader.ReadUInt32();

                var payloadLength = reader.ReadUInt16();

                reader.ReadByte();

                var headerChecksum = reader.ReadByte();

                if (BinaryUtilities.GetXorSum(receiveBuffer.Take(HeaderLength - 1)) != headerChecksum)
                {
                    throw new ArgumentException("NTCB header checksum does not match.");
                }

                return(Task.FromResult((int)payloadLength));

            default:
                if (BinaryUtilities.GetXorSum(receiveBuffer.Skip(HeaderLength)) != receiveBuffer[14])
                {
                    throw new ArgumentException("NTCB body checksum does not match.");
                }

                var bodyProcessor =
                    _bodyProcessors.GetValueOrDefault(receiveBuffer.Skip(HeaderLength).Select(x => (char)x));

                if (bodyProcessor == null)
                {
                    throw new ArgumentException("Unknown NTCB message type.");
                }

                var bodyReader = new BinaryListReader(receiveBuffer, true);

                bodyReader.SetPosition(HeaderLength);

                sendBuffer.AllocateSpace(HeaderLength);

                var writer = new MemoryBufferWriter(sendBuffer, true);

                bodyProcessor.ProcessBody(sessionState, bodyReader, writer);

                var responseLength = sendBuffer.Position - HeaderLength;

                sendBuffer.SetPosition(0);

                writer.Write(HeaderPreamble.Select(x => (byte)x).ToArray());
                writer.Write(sessionState.SenderId);
                writer.Write(sessionState.ReceiverId);
                writer.Write((ushort)responseLength);
                writer.Write(BinaryUtilities.GetXorSum(sendBuffer.Array.Skip(HeaderLength).Take(responseLength)));
                writer.Write(BinaryUtilities.GetXorSum(sendBuffer.Array.Take(HeaderLength - 1)));

                sendBuffer.SetPosition(responseLength + HeaderLength);

                return(Task.FromResult(0));
            }
        }
        /// <summary>
        /// Processes the established client-server socket connection.
        /// </summary>
        /// <param name="socket">Socket connected to a client device.</param>
        /// <param name="token">The token to monitor for cancellation requests.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public async Task WorkAsync(Socket socket, CancellationToken token)
        {
            using (var stream = new AsyncBufferedSocketStream(socket, token))
            {
                stream.ReadTimeout = stream.WriteTimeout = _readWriteTimeoutMs;

                try
                {
                    var state = new SessionState();

                    for (;;)
                    {
                        stream.ClearReceiveBuffer();

                        await stream.ReadToBufferAsync(1, token).ConfigureAwait(false);

                        var processor = _primaryOperators[stream.ReceiveBuffer[0]];

                        if (processor == null)
                        {
                            throw new ArgumentException($"Unknown message prefix '0x{stream.ReceiveBuffer[0]:X2}'.");
                        }

                        stream.SendBuffer.SetPosition(0);

                        for (;;)
                        {
                            var pendingBytes =
                                await
                                processor.GetPendingBytesAsync(state, stream.ReceiveBuffer, stream.SendBuffer, token)
                                .ConfigureAwait(false);

                            if (pendingBytes == 0)
                            {
                                break;
                            }

                            await stream.ReadToBufferAsync(pendingBytes, token).ConfigureAwait(false);
                        }

                        if (OnReadyMessage != null)
                        {
                            await OnReadyMessage(state, stream.ReceiveBuffer.ToArray(), token).ConfigureAwait(false);
                        }

                        await stream.WriteBufferToSocketAsync(token).ConfigureAwait(false);

                        // Workaround until crashes are implemented correctly by the devices
                        var crashDetected = false;

                        if (processor.MessageTypeIdentifier == 0x40 && stream.ReceiveBuffer.Count >= 22 &&
                            stream.ReceiveBuffer.Skip(16).Take(6).Select(x => (char)x).SequenceEqual("*>FLEX"))
                        {
                            crashDetected = true;
                        }
                        else if (processor.MessageTypeIdentifier == 0x7E && stream.ReceiveBuffer.Count >= 2)
                        {
                            var reader = new BinaryListReader(stream.ReceiveBuffer, true);

                            const int detectEvent = 0xA03B;

                            switch ((char)stream.ReceiveBuffer[1])
                            {
                            case 'T':
                                if (stream.ReceiveBuffer.Count < 12)
                                {
                                    break;
                                }
                                reader.SetPosition(10);
                                var numType = reader.ReadUInt16();
                                if (numType == detectEvent)
                                {
                                    crashDetected = true;
                                }
                                break;

                            case 'A':
                                if (stream.ReceiveBuffer.Count < 4)
                                {
                                    break;
                                }
                                var eventCount = stream.ReceiveBuffer[2];
                                if (eventCount <= 0)
                                {
                                    break;
                                }
                                var eventLength = (stream.ReceiveBuffer.Count - 4) / eventCount;
                                if (eventLength < 6)
                                {
                                    break;
                                }
                                for (var i = 0; i < eventCount; ++i)
                                {
                                    reader.SetPosition(7 + i * eventLength);
                                    var eventType = reader.ReadUInt16();
                                    if (eventType != detectEvent)
                                    {
                                        continue;
                                    }
                                    crashDetected = true;
                                    break;
                                }
                                break;
                            }
                        }

                        if (crashDetected)
                        {
                            stream.SendBuffer.SetPosition(0);

                            new MemoryBufferWriter(stream.SendBuffer, true).Write(CrashInformation.CrashInfoQuery);

                            await stream.WriteBufferToSocketAsync(token).ConfigureAwait(false);
                        }
                    }
                }
                catch (SocketStreamException)
                {
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    string receivedBytes = null;

                    if (await stream.TryReadAvailableBytesAsync().ConfigureAwait(false))
                    {
                        var hex = new StringBuilder(stream.ReceiveBuffer.Count * 2);

                        foreach (var b in stream.ReceiveBuffer)
                        {
                            hex.Append(b.ToString("X2"));
                        }

                        receivedBytes = hex.ToString();
                    }

                    var exceptionMessage = $"Error: {ex.Message}. Receive buffer: {receivedBytes ?? "NA"}.";

                    throw new Exception(exceptionMessage, ex);
                }
            }
        }