/// <summary>
        /// Compare encoded/decoded dataset messages
        /// </summary>
        /// <param name="uadpDataSetMessage"></param>
        /// <returns></returns>
        private void CompareEncodeDecode(UadpDataSetMessage uadpDataSetMessage)
        {
            IServiceMessageContext messageContextEncode = new ServiceMessageContext();

            byte[] bytes;
            var    memoryStream = new MemoryStream();

            using (BinaryEncoder encoder = new BinaryEncoder(memoryStream, messageContextEncode, true))
            {
                uadpDataSetMessage.Encode(encoder);
                _     = encoder.Close();
                bytes = ReadBytes(memoryStream);
            }

            UadpDataSetMessage uaDataSetMessageDecoded = new UadpDataSetMessage();
            BinaryDecoder      decoder = new BinaryDecoder(bytes, messageContextEncode);

            // workaround
            uaDataSetMessageDecoded.DataSetWriterId = TestDataSetWriterId;
            uaDataSetMessageDecoded.DecodePossibleDataSetReader(decoder, m_firstDataSetReaderType);
            decoder.Dispose();

            // compare uadpDataSetMessage with uaDataSetMessageDecoded
            CompareUadpDataSetMessages(uadpDataSetMessage, uaDataSetMessageDecoded);
        }
示例#2
0
        /// <summary>
        /// Sends an error message over the socket.
        /// </summary>
        protected void SendErrorMessage(ServiceResult error)
        {
            Utils.Trace("Channel {0}: SendErrorMessage()", ChannelId);

            byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "SendErrorMessage");

            try
            {
                BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext);

                encoder.WriteUInt32(null, TcpMessageType.Error);
                encoder.WriteUInt32(null, 0);

                WriteErrorMessageBody(encoder, error);

                int size = encoder.Close();
                UpdateMessageSize(buffer, 0, size);

                BeginWriteMessage(new ArraySegment <byte>(buffer, 0, size), null);
                buffer = null;
            }
            finally
            {
                if (buffer != null)
                {
                    BufferManager.ReturnBuffer(buffer, "SendErrorMessage");
                }
            }
        }
        public void ValidateMajorVersionEqMinorVersionEq(
            [Values(DataSetFieldContentMask.None, DataSetFieldContentMask.RawData, // list here all possible DataSetFieldContentMask
                    DataSetFieldContentMask.ServerPicoSeconds, DataSetFieldContentMask.ServerTimestamp, DataSetFieldContentMask.SourcePicoSeconds,
                    DataSetFieldContentMask.SourceTimestamp, DataSetFieldContentMask.StatusCode,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.ServerTimestamp,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.SourcePicoSeconds,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.SourceTimestamp,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.StatusCode,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.ServerTimestamp | DataSetFieldContentMask.SourcePicoSeconds,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.ServerTimestamp | DataSetFieldContentMask.SourceTimestamp,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.ServerTimestamp | DataSetFieldContentMask.StatusCode,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.ServerTimestamp | DataSetFieldContentMask.SourcePicoSeconds | DataSetFieldContentMask.SourceTimestamp,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.ServerTimestamp | DataSetFieldContentMask.SourcePicoSeconds | DataSetFieldContentMask.StatusCode,
                    DataSetFieldContentMask.ServerPicoSeconds | DataSetFieldContentMask.ServerTimestamp | DataSetFieldContentMask.SourcePicoSeconds | DataSetFieldContentMask.SourceTimestamp | DataSetFieldContentMask.StatusCode
                    )]
            DataSetFieldContentMask dataSetFieldContentMask)
        {
            const int VersionValue = 2;

            // Arrange
            UadpDataSetMessage uadpDataSetMessage = GetFirstDataSetMessage(dataSetFieldContentMask);

            // Act
            uadpDataSetMessage.SetMessageContentMask(UadpDataSetMessageContentMask.MajorVersion | UadpDataSetMessageContentMask.MinorVersion);
            uadpDataSetMessage.MetaDataVersion.MajorVersion = VersionValue;
            uadpDataSetMessage.MetaDataVersion.MinorVersion = VersionValue * 10;

            IServiceMessageContext messageContextEncode = new ServiceMessageContext();

            byte[] bytes;
            var    memoryStream = new MemoryStream();

            using (BinaryEncoder encoder = new BinaryEncoder(memoryStream, messageContextEncode, true))
            {
                uadpDataSetMessage.Encode(encoder);
                _     = encoder.Close();
                bytes = ReadBytes(memoryStream);
            }

            UadpDataSetMessage uaDataSetMessageDecoded = new UadpDataSetMessage();
            BinaryDecoder      decoder = new BinaryDecoder(bytes, messageContextEncode);

            // Make sure the reader MajorVersion and MinorVersion are the same with the ones on the dataset message
            DataSetReaderDataType reader = (DataSetReaderDataType)m_firstDataSetReaderType.MemberwiseClone();

            reader.DataSetMetaData.ConfigurationVersion.MajorVersion = VersionValue;
            reader.DataSetMetaData.ConfigurationVersion.MinorVersion = VersionValue * 10;

            // workaround
            uaDataSetMessageDecoded.DataSetWriterId = TestDataSetWriterId;
            uaDataSetMessageDecoded.DecodePossibleDataSetReader(decoder, reader);
            decoder.Dispose();

            // Assert
            Assert.AreEqual(DataSetDecodeErrorReason.NoError, uaDataSetMessageDecoded.DecodeErrorReason);
            Assert.AreEqual(false, uaDataSetMessageDecoded.IsMetadataMajorVersionChange);
            Assert.AreNotEqual(null, uaDataSetMessageDecoded.DataSet);
            // compare uadpDataSetMessage with uaDataSetMessageDecoded
            CompareUadpDataSetMessages(uadpDataSetMessage, uaDataSetMessageDecoded);
        }
        /// <summary>
        /// Reverse client is connected, send reverse hello message.
        /// </summary>
        private void OnReverseConnectComplete(object sender, IMessageSocketAsyncEventArgs result)
        {
            var ar = (ReverseConnectAsyncResult)result.UserToken;

            if (ar == null || m_pendingReverseHello != null)
            {
                return;
            }

            if (result.IsSocketError)
            {
                ar.Exception = new ServiceResultException(StatusCodes.BadNotConnected, result.SocketErrorString);
                ar.OperationCompleted();
                return;
            }

            byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "OnReverseConnectConnectComplete");

            try
            {
                // start reading messages.
                ar.Socket.ReadNextMessage();

                // send reverse hello message.
                BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext);
                encoder.WriteUInt32(null, TcpMessageType.ReverseHello);
                encoder.WriteUInt32(null, 0);
                encoder.WriteString(null, EndpointDescription.Server.ApplicationUri);
                encoder.WriteString(null, EndpointDescription.EndpointUrl);
                int size = encoder.Close();
                UpdateMessageSize(buffer, 0, size);

                // set state to waiting for hello.
                State = TcpChannelState.Connecting;
                m_pendingReverseHello = ar;

                BeginWriteMessage(new ArraySegment <byte>(buffer, 0, size), null);
                buffer = null;
            }
            catch (Exception e)
            {
                ar.Exception = e;
                ar.OperationCompleted();
            }
            finally
            {
                if (buffer != null)
                {
                    BufferManager.ReturnBuffer(buffer, "OnReverseConnectComplete");
                }
            }
        }
        /// <summary>
        /// Writes the collection to a binary stream. The stream is closed by this method.
        /// </summary>
        public void SaveAsBinary(ISystemContext context, Stream ostrm)
        {
            ServiceMessageContext messageContext = new ServiceMessageContext();

            messageContext.NamespaceUris = context.NamespaceUris;
            messageContext.ServerUris    = context.ServerUris;
            messageContext.Factory       = context.EncodeableFactory;

            BinaryEncoder encoder = new BinaryEncoder(ostrm, messageContext);

            encoder.SaveStringTable(context.NamespaceUris);
            encoder.SaveStringTable(context.ServerUris);

            encoder.WriteInt32(null, this.Count);

            for (int ii = 0; ii < this.Count; ii++)
            {
                NodeState state = this[ii];
                state.SaveAsBinary(context, encoder);
            }

            encoder.Close();
        }
示例#6
0
        /// <summary>
        /// Secures the message using the security token.
        /// </summary>
        protected BufferCollection WriteSymmetricMessage(
            uint messageType,
            uint requestId,
            ChannelToken token,
            object messageBody,
            bool isRequest,
            out bool limitsExceeded)
        {
            limitsExceeded = false;
            bool             success         = false;
            BufferCollection chunksToProcess = null;

            try
            {
                // calculate chunk sizes.
                int maxCipherTextSize = SendBufferSize - TcpMessageLimits.SymmetricHeaderSize;
                int maxCipherBlocks   = maxCipherTextSize / EncryptionBlockSize;
                int maxPlainTextSize  = maxCipherBlocks * EncryptionBlockSize;
                int maxPayloadSize    = maxPlainTextSize - SymmetricSignatureSize - 1 - TcpMessageLimits.SequenceHeaderSize;
                int headerSize        = TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize;

                // write the body to stream.
                ArraySegmentStream ostrm = new ArraySegmentStream(
                    BufferManager,
                    SendBufferSize,
                    headerSize,
                    maxPayloadSize);

                // check for encodeable body.
                IEncodeable encodeable = messageBody as IEncodeable;

                if (encodeable != null)
                {
                    // debug code used to verify that message aborts are handled correctly.
                    // int maxMessageSize = Quotas.MessageContext.MaxMessageSize;
                    // Quotas.MessageContext.MaxMessageSize = Int32.MaxValue;

                    BinaryEncoder.EncodeMessage(encodeable, ostrm, Quotas.MessageContext);

                    // Quotas.MessageContext.MaxMessageSize = maxMessageSize;
                }

                // check for raw bytes.
                ArraySegment <byte>?rawBytes = messageBody as ArraySegment <byte>?;

                if (rawBytes != null)
                {
                    BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext);
                    encoder.WriteRawBytes(rawBytes.Value.Array, rawBytes.Value.Offset, rawBytes.Value.Count);
                    encoder.Close();
                }

                chunksToProcess = ostrm.GetBuffers("WriteSymmetricMessage");

                // ensure there is at least one chunk.
                if (chunksToProcess.Count == 0)
                {
                    byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteSymmetricMessage");
                    chunksToProcess.Add(new ArraySegment <byte>(buffer, 0, 0));
                }

                BufferCollection chunksToSend = new BufferCollection(chunksToProcess.Capacity);

                int messageSize = 0;

                for (int ii = 0; ii < chunksToProcess.Count; ii++)
                {
                    ArraySegment <byte> chunkToProcess = chunksToProcess[ii];

                    // nothing more to do if limits exceeded.
                    if (limitsExceeded)
                    {
                        BufferManager.ReturnBuffer(chunkToProcess.Array, "WriteSymmetricMessage");
                        continue;
                    }

                    MemoryStream  strm    = new MemoryStream(chunkToProcess.Array, 0, SendBufferSize);
                    BinaryEncoder encoder = new BinaryEncoder(strm, Quotas.MessageContext);

                    // check if the message needs to be aborted.
                    if (MessageLimitsExceeded(isRequest, messageSize + chunkToProcess.Count - headerSize, ii + 1))
                    {
                        encoder.WriteUInt32(null, messageType | TcpMessageType.Abort);

                        // replace the body in the chunk with an error message.
                        BinaryEncoder errorEncoder = new BinaryEncoder(
                            chunkToProcess.Array,
                            chunkToProcess.Offset,
                            chunkToProcess.Count,
                            Quotas.MessageContext);

                        WriteErrorMessageBody(errorEncoder, (isRequest) ? StatusCodes.BadRequestTooLarge : StatusCodes.BadResponseTooLarge);

                        int size = errorEncoder.Close();
                        chunkToProcess = new ArraySegment <byte>(chunkToProcess.Array, chunkToProcess.Offset, size);

                        limitsExceeded = true;
                    }

                    // check if the message is complete.
                    else if (ii == chunksToProcess.Count - 1)
                    {
                        encoder.WriteUInt32(null, messageType | TcpMessageType.Final);
                    }

                    // more chunks to follow.
                    else
                    {
                        encoder.WriteUInt32(null, messageType | TcpMessageType.Intermediate);
                    }

                    int count = 0;

                    count += TcpMessageLimits.SequenceHeaderSize;
                    count += chunkToProcess.Count;
                    count += SymmetricSignatureSize;

                    // calculate the padding.
                    int padding = 0;

                    if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
                    {
                        // reserve one byte for the padding size.
                        count++;

                        if (count % EncryptionBlockSize != 0)
                        {
                            padding = EncryptionBlockSize - (count % EncryptionBlockSize);
                        }

                        count += padding;
                    }

                    count += TcpMessageLimits.SymmetricHeaderSize;

                    encoder.WriteUInt32(null, (uint)count);
                    encoder.WriteUInt32(null, ChannelId);
                    encoder.WriteUInt32(null, token.TokenId);

                    uint sequenceNumber = GetNewSequenceNumber();
                    encoder.WriteUInt32(null, sequenceNumber);

                    encoder.WriteUInt32(null, requestId);

                    // skip body.
                    strm.Seek(chunkToProcess.Count, SeekOrigin.Current);

                    // update message size count.
                    messageSize += chunkToProcess.Count;

                    // write padding.
                    if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
                    {
                        for (int jj = 0; jj <= padding; jj++)
                        {
                            encoder.WriteByte(null, (byte)padding);
                        }
                    }

                    if (SecurityMode != MessageSecurityMode.None)
                    {
                        // calculate and write signature.
                        byte[] signature = Sign(token, new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position), isRequest);

                        if (signature != null)
                        {
                            encoder.WriteRawBytes(signature, 0, signature.Length);
                        }
                    }

                    if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
                    {
                        // encrypt the data.
                        ArraySegment <byte> dataToEncrypt = new ArraySegment <byte>(chunkToProcess.Array, TcpMessageLimits.SymmetricHeaderSize, encoder.Position - TcpMessageLimits.SymmetricHeaderSize);
                        Encrypt(token, dataToEncrypt, isRequest);
                    }

                    // add the header into chunk.
                    chunksToSend.Add(new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position));
                }

                // ensure the buffers don't get cleaned up on exit.
                success = true;
                return(chunksToSend);
            }
            finally
            {
                if (!success)
                {
                    if (chunksToProcess != null)
                    {
                        chunksToProcess.Release(BufferManager, "WriteSymmetricMessage");
                    }
                }
            }
        }
        /// <summary>
        /// Sends a OpenSecureChannel request.
        /// </summary>
        protected BufferCollection WriteAsymmetricMessage(
            uint messageType,
            uint requestId,
            X509Certificate2 senderCertificate,
            X509Certificate2Collection senderCertificateChain,
            X509Certificate2 receiverCertificate,
            ArraySegment <byte> messageBody)
        {
            bool             success      = false;
            BufferCollection chunksToSend = new BufferCollection();

            byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteAsymmetricMessage");

            try
            {
                BinaryEncoder encoder    = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext);
                int           headerSize = 0;

                if (senderCertificateChain != null && senderCertificateChain.Count > 0)
                {
                    int senderCertificateSize = 0;

                    WriteAsymmetricMessageHeader(
                        encoder,
                        messageType | TcpMessageType.Intermediate,
                        ChannelId,
                        SecurityPolicyUri,
                        senderCertificate,
                        senderCertificateChain,
                        receiverCertificate,
                        out senderCertificateSize);

                    headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate, senderCertificateSize);
                }
                else
                {
                    WriteAsymmetricMessageHeader(
                        encoder,
                        messageType | TcpMessageType.Intermediate,
                        ChannelId,
                        SecurityPolicyUri,
                        senderCertificate,
                        receiverCertificate);

                    headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate);
                }

                int signatureSize = GetAsymmetricSignatureSize(senderCertificate);

                // save the header.
                ArraySegment <byte> header = new ArraySegment <byte>(buffer, 0, headerSize);

                // calculate the space available.
                int plainTextBlockSize  = GetPlainTextBlockSize(receiverCertificate);
                int cipherTextBlockSize = GetCipherTextBlockSize(receiverCertificate);
                int maxCipherTextSize   = SendBufferSize - headerSize;
                int maxCipherBlocks     = maxCipherTextSize / cipherTextBlockSize;
                int maxPlainTextSize    = maxCipherBlocks * plainTextBlockSize;
                int maxPayloadSize      = maxPlainTextSize - signatureSize - 1 - TcpMessageLimits.SequenceHeaderSize;

                int bytesToWrite = messageBody.Count;
                int startOfBytes = messageBody.Offset;

                while (bytesToWrite > 0)
                {
                    encoder.WriteUInt32(null, GetNewSequenceNumber());
                    encoder.WriteUInt32(null, requestId);

                    int payloadSize = bytesToWrite;

                    if (payloadSize > maxPayloadSize)
                    {
                        payloadSize = maxPayloadSize;
                    }
                    else
                    {
                        UpdateMessageType(buffer, 0, messageType | TcpMessageType.Final);
                    }

                    // write the message body.
                    encoder.WriteRawBytes(messageBody.Array, messageBody.Offset + startOfBytes, payloadSize);

                    // calculate the amount of plain text to encrypt.
                    int plainTextSize = encoder.Position - headerSize + signatureSize;

                    // calculate the padding.
                    int padding = 0;

                    if (SecurityMode != MessageSecurityMode.None)
                    {
                        if (CertificateFactory.GetRSAPublicKeySize(receiverCertificate) <= TcpMessageLimits.KeySizeExtraPadding)
                        {
                            // need to reserve one byte for the padding.
                            plainTextSize++;

                            if (plainTextSize % plainTextBlockSize != 0)
                            {
                                padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize);
                            }

                            encoder.WriteByte(null, (byte)padding);
                            for (int ii = 0; ii < padding; ii++)
                            {
                                encoder.WriteByte(null, (byte)padding);
                            }
                        }
                        else
                        {
                            // need to reserve one byte for the padding.
                            plainTextSize++;
                            // need to reserve one byte for the extrapadding.
                            plainTextSize++;

                            if (plainTextSize % plainTextBlockSize != 0)
                            {
                                padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize);
                            }

                            byte paddingSize      = (byte)(padding & 0xff);
                            byte extraPaddingByte = (byte)((padding >> 8) & 0xff);

                            encoder.WriteByte(null, paddingSize);
                            for (int ii = 0; ii < padding; ii++)
                            {
                                encoder.WriteByte(null, (byte)paddingSize);
                            }
                            encoder.WriteByte(null, extraPaddingByte);
                        }

                        // update the plaintext size with the padding size.
                        plainTextSize += padding;
                    }

                    // calculate the number of block to encrypt.
                    int encryptedBlocks = plainTextSize / plainTextBlockSize;

                    // calculate the size of the encrypted data.
                    int cipherTextSize = encryptedBlocks * cipherTextBlockSize;

                    // put the message size after encryption into the header.
                    UpdateMessageSize(buffer, 0, cipherTextSize + headerSize);

                    // write the signature.
                    byte[] signature = Sign(new ArraySegment <byte>(buffer, 0, encoder.Position), senderCertificate);

                    if (signature != null)
                    {
                        encoder.WriteRawBytes(signature, 0, signature.Length);
                    }

                    int messageSize = encoder.Close();

                    // encrypt the data.
                    ArraySegment <byte> encryptedBuffer = Encrypt(
                        new ArraySegment <byte>(buffer, headerSize, messageSize - headerSize),
                        header,
                        receiverCertificate);

                    // check for math errors due to code bugs.
                    if (encryptedBuffer.Count != cipherTextSize + headerSize)
                    {
                        throw new InvalidDataException("Actual message size is not the same as the predicted message size.");
                    }

                    // save chunk.
                    chunksToSend.Add(encryptedBuffer);

                    bytesToWrite -= payloadSize;
                    startOfBytes += payloadSize;

                    // reset the encoder to write the plaintext for the next chunk into the same buffer.
                    if (bytesToWrite > 0)
                    {
                        MemoryStream ostrm = new MemoryStream(buffer, 0, SendBufferSize);
                        ostrm.Seek(header.Count, SeekOrigin.Current);
                        encoder = new BinaryEncoder(ostrm, Quotas.MessageContext);
                    }
                }

                // ensure the buffers don't get clean up on exit.
                success = true;
                return(chunksToSend);
            }
            catch (Exception ex)
            {
                throw new Exception("Could not write async message", ex);
            }
            finally
            {
                BufferManager.ReturnBuffer(buffer, "WriteAsymmetricMessage");

                if (!success)
                {
                    chunksToSend.Release(BufferManager, "WriteAsymmetricMessage");
                }
            }
        }
        private bool ProcessHelloMessage(uint messageType, ArraySegment<byte> messageChunk)
        {
            // validate the channel state.            
            if (State != TcpChannelState.Connecting)
            {
                ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected Hello message.");
                return false;
            }
         
            try
            {
                MemoryStream istrm = new MemoryStream(messageChunk.Array, messageChunk.Offset, messageChunk.Count, false);
                BinaryDecoder decoder = new BinaryDecoder(istrm, Quotas.MessageContext);
                istrm.Seek(TcpMessageLimits.MessageTypeAndSize, SeekOrigin.Current);

                // read requested buffer sizes.
                uint protocolVersion = decoder.ReadUInt32(null);  
                uint receiveBufferSize = decoder.ReadUInt32(null); 
                uint sendBufferSize = decoder.ReadUInt32(null); 
                uint maxMessageSize = decoder.ReadUInt32(null); 
                uint maxChunkCount = decoder.ReadUInt32(null); 
                
                // read the endpoint url.
                int length = decoder.ReadInt32(null);

                if (length > 0)
                {
                    if (length > TcpMessageLimits.MaxEndpointUrlLength)
                    {
                        ForceChannelFault(StatusCodes.BadTcpEndpointUrlInvalid);
                        return false;
                    }

                    byte[] endpointUrl = new byte[length];

                    for (int ii = 0; ii < endpointUrl.Length; ii++)
                    {
                        endpointUrl[ii] = decoder.ReadByte(null);
                    }

                    if (!SetEndpointUrl(new UTF8Encoding().GetString(endpointUrl)))
                    {
                        ForceChannelFault(StatusCodes.BadTcpEndpointUrlInvalid);
                        return false;
                    }
                }
                
                decoder.Close();

                // update receive buffer size.
                if (receiveBufferSize < ReceiveBufferSize)
                {
                    ReceiveBufferSize = (int)receiveBufferSize;
                }

                if (ReceiveBufferSize < TcpMessageLimits.MinBufferSize)
                {
                    ReceiveBufferSize = TcpMessageLimits.MinBufferSize;
                }
                
                // update send buffer size.
                if (sendBufferSize < SendBufferSize)
                {
                    SendBufferSize = (int)sendBufferSize;
                }

                if (SendBufferSize < TcpMessageLimits.MinBufferSize)
                {
                    SendBufferSize = TcpMessageLimits.MinBufferSize;
                }            
                
                // update the max message size.
                if (maxMessageSize > 0 && maxMessageSize < MaxResponseMessageSize)
                {
                    MaxResponseMessageSize = (int)maxMessageSize;
                }
                
                if (MaxResponseMessageSize < SendBufferSize)
                {
                    MaxResponseMessageSize = SendBufferSize;
                }

                // update the max chunk count.
                if (maxChunkCount > 0 && maxChunkCount < MaxResponseChunkCount)
                {
                    MaxResponseChunkCount = (int)maxChunkCount;
                }
                
                // send acknowledge.
                byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "ProcessHelloMessage");
                
                try
                {
                    MemoryStream ostrm = new MemoryStream(buffer, 0, SendBufferSize);
                    BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext);
                    
                    encoder.WriteUInt32(null, TcpMessageType.Acknowledge);
                    encoder.WriteUInt32(null, 0);
                    encoder.WriteUInt32(null, 0); // ProtocolVersion
                    encoder.WriteUInt32(null, (uint)ReceiveBufferSize);
                    encoder.WriteUInt32(null, (uint)SendBufferSize);
                    encoder.WriteUInt32(null, (uint)MaxRequestMessageSize);
                    encoder.WriteUInt32(null, (uint)MaxRequestChunkCount);
                    
                    int size = encoder.Close();
                    UpdateMessageSize(buffer, 0, size);
                                                
                    // now ready for the open or bind request.
                    State = TcpChannelState.Opening;

                    BeginWriteMessage(new ArraySegment<byte>(buffer, 0, size), Int32.MaxValue, null);
                    buffer = null;
                }
                finally
                {
                    if (buffer != null)
                    {
                        BufferManager.ReturnBuffer(buffer, "ProcessHelloMessage");
                    }
                }
            }
            catch (Exception e)
            {
                ForceChannelFault(e, StatusCodes.BadTcpInternalError, "Unexpected error while processing a Hello message.");
            }

            return false;
        }
        /// <summary>
        /// Sends an error message over the socket.
        /// </summary>
        protected void SendErrorMessage(ServiceResult error)
        {
            // Utils.Trace("Channel {0}: SendErrorMessage()", ChannelId);

            byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "SendErrorMessage");

            try
            {
                BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext);

                encoder.WriteUInt32(null, TcpMessageType.Error);
                encoder.WriteUInt32(null, 0);
                
                WriteErrorMessageBody(encoder, error);
                                
                int size = encoder.Close();
                UpdateMessageSize(buffer, 0, size);
                
                BeginWriteMessage(new ArraySegment<byte>(buffer, 0, size), Int32.MaxValue, null);
                buffer = null;
            }
            finally 
            {
                if (buffer != null)
                {
                    BufferManager.ReturnBuffer(buffer, "SendErrorMessage");
                }
            }
        }
        private bool ProcessHelloMessage(ArraySegment <byte> messageChunk)
        {
            // validate the channel state.
            if (State != TcpChannelState.Connecting)
            {
                ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected Hello message.");
                return(false);
            }

            try
            {
                MemoryStream  istrm   = new MemoryStream(messageChunk.Array, messageChunk.Offset, messageChunk.Count, false);
                BinaryDecoder decoder = new BinaryDecoder(istrm, Quotas.MessageContext);
                istrm.Seek(TcpMessageLimits.MessageTypeAndSize, SeekOrigin.Current);

                // read requested buffer sizes.
                uint protocolVersion   = decoder.ReadUInt32(null);
                uint receiveBufferSize = decoder.ReadUInt32(null);
                uint sendBufferSize    = decoder.ReadUInt32(null);
                uint maxMessageSize    = decoder.ReadUInt32(null);
                uint maxChunkCount     = decoder.ReadUInt32(null);

                // read the endpoint url.
                int length = decoder.ReadInt32(null);

                if (length > 0)
                {
                    if (length > TcpMessageLimits.MaxEndpointUrlLength)
                    {
                        ForceChannelFault(StatusCodes.BadTcpEndpointUrlInvalid);
                        return(false);
                    }

                    byte[] endpointUrl = new byte[length];

                    for (int ii = 0; ii < endpointUrl.Length; ii++)
                    {
                        endpointUrl[ii] = decoder.ReadByte(null);
                    }

                    if (!SetEndpointUrl(new UTF8Encoding().GetString(endpointUrl, 0, endpointUrl.Length)))
                    {
                        ForceChannelFault(StatusCodes.BadTcpEndpointUrlInvalid);
                        return(false);
                    }
                }

                decoder.Close();

                // update receive buffer size.
                if (receiveBufferSize < ReceiveBufferSize)
                {
                    ReceiveBufferSize = (int)receiveBufferSize;
                }

                if (ReceiveBufferSize < TcpMessageLimits.MinBufferSize)
                {
                    ReceiveBufferSize = TcpMessageLimits.MinBufferSize;
                }

                // update send buffer size.
                if (sendBufferSize < SendBufferSize)
                {
                    SendBufferSize = (int)sendBufferSize;
                }

                if (SendBufferSize < TcpMessageLimits.MinBufferSize)
                {
                    SendBufferSize = TcpMessageLimits.MinBufferSize;
                }

                // update the max message size.
                if (maxMessageSize > 0 && maxMessageSize < MaxResponseMessageSize)
                {
                    MaxResponseMessageSize = (int)maxMessageSize;
                }

                if (MaxResponseMessageSize < SendBufferSize)
                {
                    MaxResponseMessageSize = SendBufferSize;
                }

                // update the max chunk count.
                if (maxChunkCount > 0 && maxChunkCount < MaxResponseChunkCount)
                {
                    MaxResponseChunkCount = (int)maxChunkCount;
                }

                // send acknowledge.
                byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "ProcessHelloMessage");

                try
                {
                    MemoryStream  ostrm   = new MemoryStream(buffer, 0, SendBufferSize);
                    BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext);

                    encoder.WriteUInt32(null, TcpMessageType.Acknowledge);
                    encoder.WriteUInt32(null, 0);
                    encoder.WriteUInt32(null, 0); // ProtocolVersion
                    encoder.WriteUInt32(null, (uint)ReceiveBufferSize);
                    encoder.WriteUInt32(null, (uint)SendBufferSize);
                    encoder.WriteUInt32(null, (uint)MaxRequestMessageSize);
                    encoder.WriteUInt32(null, (uint)MaxRequestChunkCount);

                    int size = encoder.Close();
                    UpdateMessageSize(buffer, 0, size);

                    // now ready for the open or bind request.
                    State = TcpChannelState.Opening;

                    BeginWriteMessage(new ArraySegment <byte>(buffer, 0, size), null);
                    buffer = null;
                }
                finally
                {
                    if (buffer != null)
                    {
                        BufferManager.ReturnBuffer(buffer, "ProcessHelloMessage");
                    }
                }
            }
            catch (Exception e)
            {
                ForceChannelFault(e, StatusCodes.BadTcpInternalError, "Unexpected error while processing a Hello message.");
            }

            return(false);
        }
示例#11
0
        /// <summary>
        /// Secures the message using the security token.
        /// </summary>
        protected BufferCollection WriteSymmetricMessage(
            uint            messageType,
            uint            requestId, 
            TcpChannelToken token,
            object          messageBody,
            bool            isRequest,
            out bool        limitsExceeded)
        {   
            limitsExceeded = false;
            bool success = false;
            BufferCollection chunksToProcess = null;

            try
            {
                // calculate chunk sizes.
                int maxCipherTextSize = SendBufferSize - TcpMessageLimits.SymmetricHeaderSize;
                int maxCipherBlocks   = maxCipherTextSize/EncryptionBlockSize;
                int maxPlainTextSize  = maxCipherBlocks*EncryptionBlockSize;
                int maxPayloadSize    = maxPlainTextSize - SymmetricSignatureSize - 1 - TcpMessageLimits.SequenceHeaderSize;        
                int headerSize        = TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize;

                // write the body to stream.
                ArraySegmentStream ostrm = new ArraySegmentStream(
                    BufferManager, 
                    SendBufferSize, 
                    headerSize,
                    maxPayloadSize);
                
                // check for encodeable body.
                IEncodeable encodeable = messageBody as IEncodeable;

                if (encodeable != null)
                {                    
                    // debug code used to verify that message aborts are handled correctly.
                    // int maxMessageSize = Quotas.MessageContext.MaxMessageSize;
                    // Quotas.MessageContext.MaxMessageSize = Int32.MaxValue;
                    
                    BinaryEncoder.EncodeMessage(encodeable, ostrm, Quotas.MessageContext);

                    // Quotas.MessageContext.MaxMessageSize = maxMessageSize;
                }

                // check for raw bytes.
                ArraySegment<byte>? rawBytes = messageBody as ArraySegment<byte>?;
                
                if (rawBytes != null)
                {
                    BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext);
                    encoder.WriteRawBytes(rawBytes.Value.Array, rawBytes.Value.Offset, rawBytes.Value.Count);
                    encoder.Close();
                }

                chunksToProcess = ostrm.GetBuffers("WriteSymmetricMessage");   
             
                // ensure there is at least one chunk.
                if (chunksToProcess.Count == 0)
                {
                    byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteSymmetricMessage");
                    chunksToProcess.Add(new ArraySegment<byte>(buffer, 0, 0));
                }

                BufferCollection chunksToSend = new BufferCollection(chunksToProcess.Capacity);

                int messageSize = 0;

                for (int ii = 0; ii < chunksToProcess.Count; ii++)
                {
                    ArraySegment<byte> chunkToProcess = chunksToProcess[ii];
                    
                    // nothing more to do if limits exceeded.
                    if (limitsExceeded)
                    {
                        BufferManager.ReturnBuffer(chunkToProcess.Array, "WriteSymmetricMessage");
                        continue;
                    }

                    MemoryStream strm = new MemoryStream(chunkToProcess.Array, 0, SendBufferSize);
                    BinaryEncoder encoder = new BinaryEncoder(strm, Quotas.MessageContext);
                    
                    // check if the message needs to be aborted.
                    if (MessageLimitsExceeded(isRequest, messageSize + chunkToProcess.Count - headerSize, ii+1))
                    {
                        encoder.WriteUInt32(null, messageType | TcpMessageType.Abort);
                        
                        // replace the body in the chunk with an error message.
                        BinaryEncoder errorEncoder = new BinaryEncoder(
                            chunkToProcess.Array, 
                            chunkToProcess.Offset, 
                            chunkToProcess.Count, 
                            Quotas.MessageContext);
                        
                        WriteErrorMessageBody(errorEncoder, (isRequest)?StatusCodes.BadRequestTooLarge:StatusCodes.BadResponseTooLarge);
                                        
                        int size = errorEncoder.Close();
                        chunkToProcess = new ArraySegment<byte>(chunkToProcess.Array, chunkToProcess.Offset, size);

                        limitsExceeded = true;
                    }

                    // check if the message is complete.
                    else if (ii == chunksToProcess.Count-1)
                    {
                        encoder.WriteUInt32(null, messageType | TcpMessageType.Final);
                    }

                    // more chunks to follow.
                    else
                    {
                        encoder.WriteUInt32(null, messageType | TcpMessageType.Intermediate);
                    }
                    
                    int count = 0;
                    
                    count += TcpMessageLimits.SequenceHeaderSize;
                    count += chunkToProcess.Count;
                    count += SymmetricSignatureSize;
                    
                    // calculate the padding.
                    int padding = 0;                    
                    
                    if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
                    {
                        // reserve one byte for the padding size.
                        count++;

                        if (count%EncryptionBlockSize != 0)
                        {
                            padding = EncryptionBlockSize - (count%EncryptionBlockSize);
                        }
                        
                        count += padding;
                    }

                    count += TcpMessageLimits.SymmetricHeaderSize;

                    encoder.WriteUInt32(null, (uint)count);
                    encoder.WriteUInt32(null, ChannelId);
                    encoder.WriteUInt32(null, token.TokenId);

                    uint sequenceNumber = GetNewSequenceNumber();
                    encoder.WriteUInt32(null, sequenceNumber);
                    
                    encoder.WriteUInt32(null, requestId);

                    // skip body.
                    strm.Seek(chunkToProcess.Count, SeekOrigin.Current);
                        
                    // update message size count.
                    messageSize += chunkToProcess.Count;

                    // write padding.
                    if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
                    {
                        for (int jj = 0; jj <= padding; jj++)
                        {
                            encoder.WriteByte(null, (byte)padding);
                        }
                    }

                    if (SecurityMode != MessageSecurityMode.None)
                    {
                        // calculate and write signature.
                        byte[] signature = Sign(token, new ArraySegment<byte>(chunkToProcess.Array, 0, encoder.Position), isRequest);

                        if (signature != null)
                        {
                            encoder.WriteRawBytes(signature, 0, signature.Length);
                        }
                    }
                    
                    if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
                    {
                        // encrypt the data.
                        ArraySegment<byte> dataToEncrypt = new ArraySegment<byte>(chunkToProcess.Array, TcpMessageLimits.SymmetricHeaderSize, encoder.Position-TcpMessageLimits.SymmetricHeaderSize);
                        Encrypt(token, dataToEncrypt, isRequest);
                    }

                    // add the header into chunk.
                    chunksToSend.Add(new ArraySegment<byte>(chunkToProcess.Array, 0, encoder.Position));
                }
           
                // ensure the buffers don't get cleaned up on exit.
                success = true;
                return chunksToSend;
            }
            finally
            {
                if (!success)
                {
                    if (chunksToProcess != null)
                    {
                        chunksToProcess.Release(BufferManager, "WriteSymmetricMessage");
                    }
                }
            }
        }      
示例#12
0
        /// <summary>
        /// Sends a Hello message.
        /// </summary>
        private void SendHelloMessage(WriteOperation operation)
        {            
            // Utils.Trace("Channel {0}: SendHelloMessage()", ChannelId);

            byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "SendHelloMessage");
            
            try
            {
                MemoryStream ostrm = new MemoryStream(buffer, 0, SendBufferSize);
                BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext);
                
                encoder.WriteUInt32(null, TcpMessageType.Hello);
                encoder.WriteUInt32(null, 0);
                encoder.WriteUInt32(null, 0); // ProtocolVersion
                encoder.WriteUInt32(null, (uint)ReceiveBufferSize);
                encoder.WriteUInt32(null, (uint)SendBufferSize);
                encoder.WriteUInt32(null, (uint)MaxResponseMessageSize);
                encoder.WriteUInt32(null, (uint)MaxResponseChunkCount);
                            
                byte[] endpointUrl = new UTF8Encoding().GetBytes(m_url.ToString());

                if (endpointUrl.Length > TcpMessageLimits.MaxEndpointUrlLength)
                {
                    byte[] truncatedUrl = new byte[TcpMessageLimits.MaxEndpointUrlLength];
                    Array.Copy(endpointUrl, truncatedUrl, TcpMessageLimits.MaxEndpointUrlLength);
                    endpointUrl = truncatedUrl;
                }

                encoder.WriteByteString(null, endpointUrl);
                                
                int size = encoder.Close();
                UpdateMessageSize(buffer, 0, size);

                BeginWriteMessage(new ArraySegment<byte>(buffer, 0, size), Int32.MaxValue, operation);
                buffer = null;
            }
            finally
            {
                if (buffer != null)
                {
                    BufferManager.ReturnBuffer(buffer, "SendHelloMessage");
                }
            }
        }
        /// <summary>
        /// Sends a OpenSecureChannel response.
        /// </summary>
        protected BufferCollection WriteAsymmetricMessage(
            uint               messageType,
            uint               requestId, 
            X509Certificate2   senderCertificate,
            X509Certificate2   receiverCertificate,
            ArraySegment<byte> messageBody)
        {                
            bool success = false;
            BufferCollection chunksToSend = new BufferCollection();

            byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteAsymmetricMessage");

            try
            {
                int headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate);
                int signatureSize = GetAsymmetricSignatureSize(senderCertificate);
                                    
                BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext);

                WriteAsymmetricMessageHeader(
                    encoder,
                    messageType | TcpMessageType.Intermediate,
                    ChannelId,
                    SecurityPolicyUri,
                    senderCertificate,
                    receiverCertificate);
                
                // save the header.
                ArraySegment<byte> header = new ArraySegment<byte>(buffer, 0, headerSize);
                
                // calculate the space available.
                int plainTextBlockSize = GetPlainTextBlockSize(receiverCertificate);
                int cipherTextBlockSize = GetCipherTextBlockSize(receiverCertificate);
                int maxCipherTextSize = SendBufferSize - headerSize;
                int maxCipherBlocks = maxCipherTextSize/cipherTextBlockSize; 
                int maxPlainTextSize = maxCipherBlocks*plainTextBlockSize;
                int maxPayloadSize = maxPlainTextSize - signatureSize - 1 - TcpMessageLimits.SequenceHeaderSize;

                int bytesToWrite = messageBody.Count;
                int startOfBytes = messageBody.Offset;

                while (bytesToWrite > 0)
                {
                    encoder.WriteUInt32(null, GetNewSequenceNumber());
                    encoder.WriteUInt32(null, requestId);

                    int payloadSize = bytesToWrite;

                    if (payloadSize > maxPayloadSize)
                    {
                        payloadSize = maxPayloadSize;
                    }
                    else
                    {
                        UpdateMessageType(buffer, 0, messageType | TcpMessageType.Final);
                    }

                    // write the message body.
                    encoder.WriteRawBytes(messageBody.Array, messageBody.Offset+startOfBytes, payloadSize);

                    // calculate the amount of plain text to encrypt.
                    int plainTextSize = encoder.Position - headerSize + signatureSize;
                                    
                    // calculate the padding.
                    int padding = 0;         
           
                    if (SecurityMode != MessageSecurityMode.None)
                    {
                        if (receiverCertificate.PublicKey.Key.KeySize <= TcpMessageLimits.KeySizeExtraPadding)
                        {
                            // need to reserve one byte for the padding.
                            plainTextSize++;

                            if (plainTextSize % plainTextBlockSize != 0)
                            {
                                padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize);
                            }

                            encoder.WriteByte(null, (byte)padding);
                            for (int ii = 0; ii < padding; ii++)
                            {
                                encoder.WriteByte(null, (byte)padding);
                            }
                        }
                        else
                        {
                            // need to reserve one byte for the padding.
                            plainTextSize++;
                            // need to reserve one byte for the extrapadding.
                            plainTextSize++;

                            if (plainTextSize % plainTextBlockSize != 0)
                            {
                                padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize);
                            }

                            byte paddingSize = (byte)(padding & 0xff);
                            byte extraPaddingByte = (byte)((padding >> 8) & 0xff);

                            encoder.WriteByte(null, paddingSize);
                            for (int ii = 0; ii < padding; ii++)
                            {
                                encoder.WriteByte(null, (byte)paddingSize);
                            }
                            encoder.WriteByte(null, extraPaddingByte);
                        }

                        // update the plaintext size with the padding size.
                        plainTextSize += padding;
                    }
                      
                    // calculate the number of block to encrypt.
                    int encryptedBlocks = plainTextSize/plainTextBlockSize;

                    // calculate the size of the encrypted data.
                    int cipherTextSize = encryptedBlocks*cipherTextBlockSize;
                    
                    // put the message size after encryption into the header.
                    UpdateMessageSize(buffer, 0, cipherTextSize + headerSize);

                    // write the signature.
                    byte[] signature = Sign(new ArraySegment<byte>(buffer, 0, encoder.Position), senderCertificate);
                                   
                    if (signature != null)
                    {
                        encoder.WriteRawBytes(signature, 0, signature.Length);
                    }
                    
                    int messageSize = encoder.Close();

                    // encrypt the data.
                    ArraySegment<byte> encryptedBuffer = Encrypt(
                        new ArraySegment<byte>(buffer, headerSize, messageSize-headerSize), 
                        header, 
                        receiverCertificate);

                    // check for math errors due to code bugs.
                    if (encryptedBuffer.Count != cipherTextSize + headerSize)
                    {
                        throw new InvalidDataException("Actual message size is not the same as the predicted message size.");
                    }

                    // save chunk.
                    chunksToSend.Add(encryptedBuffer);

                    bytesToWrite -= payloadSize;
                    startOfBytes += payloadSize;

                    // reset the encoder to write the plaintext for the next chunk into the same buffer.
                    if (bytesToWrite > 0)
                    {
                        MemoryStream ostrm = new MemoryStream(buffer, 0, SendBufferSize);
                        ostrm.Seek(header.Count, SeekOrigin.Current);
                        encoder = new BinaryEncoder(ostrm, Quotas.MessageContext);
                    }
                }

                // ensure the buffers don't get clean up on exit.
                success = true;
                return chunksToSend;
            }
            finally
            {
                BufferManager.ReturnBuffer(buffer, "WriteAsymmetricMessage");  

                if (!success)
                {
                    chunksToSend.Release(BufferManager, "WriteAsymmetricMessage");
                }
            }
        }
示例#14
0
            /// <summary>
            /// Called whenever socket operation completes
            /// </summary>
            /// <param name="arg"></param>
            /// <param name="ok"></param>
            /// <param name="index"></param>
            /// <param name="timeout"></param>
            /// <returns>true if completed, false to be called again</returns>
            public bool CompleteAsync(int index, SocketAsyncEventArgs arg,
                                      out bool ok, out int timeout)
            {
                ok      = false;
                timeout = _timeout;
                if (arg.SocketError != SocketError.Success)
                {
                    _logger.Debug("Probe {index} : {remoteEp} found no opc server. {error}",
                                  index, _socket?.RemoteEndPoint, arg.SocketError);
                    _state = State.BeginProbe;
                    return(true);
                }
                while (true)
                {
                    switch (_state)
                    {
                    case State.BeginProbe:
                        if (arg.ConnectSocket == null)
                        {
                            _logger.Error("Probe {index} : Called without connected socket!",
                                          index);
                            return(true);
                        }
                        _socket = arg.ConnectSocket;
                        var ep = _socket.RemoteEndPoint.TryResolve();
                        using (var ostrm = new MemoryStream(_buffer, 0, _buffer.Length))
                            using (var encoder = new BinaryEncoder(ostrm,
                                                                   ServiceMessageContext.GlobalContext)) {
                                encoder.WriteUInt32(null, TcpMessageType.Hello);
                                encoder.WriteUInt32(null, 0);
                                encoder.WriteUInt32(null, 0); // ProtocolVersion
                                encoder.WriteUInt32(null, TcpMessageLimits.DefaultMaxMessageSize);
                                encoder.WriteUInt32(null, TcpMessageLimits.DefaultMaxMessageSize);
                                encoder.WriteUInt32(null, TcpMessageLimits.DefaultMaxMessageSize);
                                encoder.WriteUInt32(null, TcpMessageLimits.DefaultMaxMessageSize);
                                encoder.WriteByteString(null, Encoding.UTF8.GetBytes("opc.tcp://" + ep));
                                _size = encoder.Close();
                            }
                        _buffer[4] = (byte)(_size & 0x000000FF);
                        _buffer[5] = (byte)((_size & 0x0000FF00) >> 8);
                        _buffer[6] = (byte)((_size & 0x00FF0000) >> 16);
                        _buffer[7] = (byte)((_size & 0xFF000000) >> 24);
                        arg.SetBuffer(_buffer, 0, _size);
                        _len = 0;
                        _logger.Debug("Probe {index} : {ep} ({remoteEp})...", index, "opc.tcp://" + ep,
                                      _socket.RemoteEndPoint);
                        _state = State.SendHello;
                        if (!_socket.SendAsync(arg))
                        {
                            break;
                        }
                        return(false);

                    case State.SendHello:
                        _len += arg.Count;
                        if (_len >= _size)
                        {
                            _len   = 0;
                            _size  = TcpMessageLimits.MessageTypeAndSize;
                            _state = State.ReceiveSize;
                            arg.SetBuffer(0, _size);
                            // Start read size
                            if (!_socket.ReceiveAsync(arg))
                            {
                                break;
                            }
                            return(false);
                        }
                        // Continue to send reset
                        arg.SetBuffer(_len, _size - _len);
                        if (!_socket.SendAsync(arg))
                        {
                            break;
                        }
                        return(false);

                    case State.ReceiveSize:
                        _len += arg.Count;
                        if (_len >= _size)
                        {
                            var type = BitConverter.ToUInt32(_buffer, 0);
                            if (type != TcpMessageType.Acknowledge)
                            {
                                if (TcpMessageType.IsValid(type))
                                {
                                    _logger.Debug("Probe {index} : {remoteEp} " +
                                                  "returned message type {type} != Ack.",
                                                  index, _socket.RemoteEndPoint, type);
                                }
                                else
                                {
                                    _logger.Verbose("Probe {index} : {remoteEp} " +
                                                    "returned invalid message type {type}.",
                                                    index, _socket.RemoteEndPoint, type);
                                }
                                _state = State.BeginProbe;
                                return(true);
                            }
                            _size = (int)BitConverter.ToUInt32(_buffer, 4);
                            if (_size > _buffer.Length)
                            {
                                _logger.Debug("Probe {index} : {remoteEp} " +
                                              "returned invalid message length {size}.",
                                              index, _socket.RemoteEndPoint, _size);
                                _state = State.BeginProbe;
                                return(true);
                            }
                            _len = 0;
                            // Start receive message
                            _state = State.ReceiveAck;
                        }
                        // Continue to read rest of type and size
                        arg.SetBuffer(_len, _size - _len);
                        if (!_socket.ReceiveAsync(arg))
                        {
                            break;
                        }
                        return(false);

                    case State.ReceiveAck:
                        _len += arg.Count;
                        if (_len >= _size)
                        {
                            _state = State.BeginProbe;
                            // Validate message
                            using (var istrm = new MemoryStream(_buffer, 0, _size))
                                using (var decoder = new BinaryDecoder(istrm,
                                                                       ServiceMessageContext.GlobalContext)) {
                                    var protocolVersion   = decoder.ReadUInt32(null);
                                    var sendBufferSize    = (int)decoder.ReadUInt32(null);
                                    var receiveBufferSize = (int)decoder.ReadUInt32(null);
                                    var maxMessageSize    = (int)decoder.ReadUInt32(null);
                                    var maxChunkCount     = (int)decoder.ReadUInt32(null);

                                    _logger.Information("Probe {index} : found OPC UA " +
                                                        "server at {remoteEp} (protocol:{protocolVersion}) ...",
                                                        index, _socket.RemoteEndPoint, protocolVersion);

                                    if (sendBufferSize < TcpMessageLimits.MinBufferSize ||
                                        receiveBufferSize < TcpMessageLimits.MinBufferSize)
                                    {
                                        _logger.Warning("Probe {index} : Bad size value read " +
                                                        "{sendBufferSize} or {receiveBufferSize} from opc " +
                                                        "server at {_socket.RemoteEndPoint}.", index,
                                                        sendBufferSize, receiveBufferSize, _socket.RemoteEndPoint);
                                    }
                                }
                            ok = true;
                            return(true);
                        }
                        // Continue to read rest
                        arg.SetBuffer(_len, _size - _len);
                        if (!_socket.ReceiveAsync(arg))
                        {
                            break;
                        }
                        return(false);

                    default:
                        throw new SystemException("Bad state");
                    }
                }
            }