/// <summary>
        /// Writes the asymmetric security header to the buffer.
        /// </summary>
        protected void WriteAsymmetricMessageHeader(
            BinaryEncoder encoder,
            uint messageType,
            uint secureChannelId,
            string securityPolicyUri,
            X509Certificate2 senderCertificate,
            X509Certificate2 receiverCertificate)
        {
            int start = encoder.Position;

            encoder.WriteUInt32(null, messageType);
            encoder.WriteUInt32(null, 0);
            encoder.WriteUInt32(null, secureChannelId);
            encoder.WriteString(null, securityPolicyUri);

            if (SecurityMode != MessageSecurityMode.None)
            {
                encoder.WriteByteString(null, senderCertificate.RawData);
                encoder.WriteByteString(null, GetThumbprintBytes(receiverCertificate.Thumbprint));
            }
            else
            {
                encoder.WriteByteString(null, null);
                encoder.WriteByteString(null, null);
            }

            if (encoder.Position - start > SendBufferSize)
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadInternalError,
                          "AsymmetricSecurityHeader is {0} bytes which is too large for the send buffer size of {1} bytes.",
                          encoder.Position - start,
                          SendBufferSize);
            }
        }
        /// <summary>
        /// Writes the asymmetric security header to the buffer.
        /// </summary>
        protected void WriteAsymmetricMessageHeader(
            BinaryEncoder encoder,
            uint messageType,
            uint secureChannelId,
            string securityPolicyUri,
            X509Certificate2 senderCertificate,
            X509Certificate2Collection senderCertificateChain,
            X509Certificate2 receiverCertificate,
            out int senderCertificateSize)
        {
            int start = encoder.Position;

            senderCertificateSize = 0;

            encoder.WriteUInt32(null, messageType);
            encoder.WriteUInt32(null, 0);
            encoder.WriteUInt32(null, secureChannelId);
            encoder.WriteString(null, securityPolicyUri);

            if (SecurityMode != MessageSecurityMode.None)
            {
                if (senderCertificateChain != null && senderCertificateChain.Count > 0)
                {
                    X509Certificate2 currentCertificate  = senderCertificateChain[0];
                    int         maxSenderCertificateSize = GetMaxSenderCertificateSize(currentCertificate, securityPolicyUri);
                    List <byte> senderCertificateList    = new List <byte>(currentCertificate.RawData);
                    senderCertificateSize = currentCertificate.RawData.Length;

                    for (int i = 1; i < senderCertificateChain.Count; i++)
                    {
                        currentCertificate     = senderCertificateChain[i];
                        senderCertificateSize += currentCertificate.RawData.Length;

                        if (senderCertificateSize < maxSenderCertificateSize)
                        {
                            senderCertificateList.AddRange(currentCertificate.RawData);
                        }
                        else
                        {
                            senderCertificateSize -= currentCertificate.RawData.Length;
                            break;
                        }
                    }

                    encoder.WriteByteString(null, senderCertificateList.ToArray());
                }
                else
                {
                    encoder.WriteByteString(null, senderCertificate.RawData);
                }

                encoder.WriteByteString(null, GetThumbprintBytes(receiverCertificate.Thumbprint));
            }
            else
            {
                encoder.WriteByteString(null, null);
                encoder.WriteByteString(null, null);
            }

            if (encoder.Position - start > SendBufferSize)
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadInternalError,
                          "AsymmetricSecurityHeader is {0} bytes which is too large for the send buffer size of {1} bytes.",
                          encoder.Position - start,
                          SendBufferSize);
            }
        }
Ejemplo n.º 3
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");
                }
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Encodes field value as RawData
        /// </summary>
        /// <param name="binaryEncoder"></param>
        /// <param name="field"></param>
        private void EncodeFieldAsRawData(BinaryEncoder binaryEncoder, Field field)
        {
            try
            {
                // 01 RawData Field Encoding
                var variant = field.Value.WrappedValue;

                if (variant.TypeInfo == null || variant.TypeInfo.BuiltInType == BuiltInType.Null)
                {
                    return;
                }
                object valueToEncode = variant.Value;

                if (field.FieldMetaData.ValueRank == ValueRanks.Scalar)
                {
                    switch ((BuiltInType)field.FieldMetaData.BuiltInType)
                    {
                    case BuiltInType.Boolean:
                        binaryEncoder.WriteBoolean("Bool", Convert.ToBoolean(valueToEncode));
                        break;

                    case BuiltInType.SByte:
                        binaryEncoder.WriteSByte("SByte", Convert.ToSByte(valueToEncode));
                        break;

                    case BuiltInType.Byte:
                        binaryEncoder.WriteByte("Byte", Convert.ToByte(valueToEncode));
                        break;

                    case BuiltInType.Int16:
                        binaryEncoder.WriteInt16("Int16", Convert.ToInt16(valueToEncode));
                        break;

                    case BuiltInType.UInt16:
                        binaryEncoder.WriteUInt16("UInt16", Convert.ToUInt16(valueToEncode));
                        break;

                    case BuiltInType.Int32:
                        binaryEncoder.WriteInt32("Int32", Convert.ToInt32(valueToEncode));
                        break;

                    case BuiltInType.UInt32:
                        binaryEncoder.WriteUInt32("UInt32", Convert.ToUInt32(valueToEncode));
                        break;

                    case BuiltInType.Int64:
                        binaryEncoder.WriteInt64("Int64", Convert.ToInt64(valueToEncode));
                        break;

                    case BuiltInType.UInt64:
                        binaryEncoder.WriteUInt64("UInt64", Convert.ToUInt64(valueToEncode));
                        break;

                    case BuiltInType.Float:
                        binaryEncoder.WriteFloat("Float", Convert.ToSingle(valueToEncode));
                        break;

                    case BuiltInType.Double:
                        binaryEncoder.WriteDouble("Double", Convert.ToDouble(valueToEncode));
                        break;

                    case BuiltInType.DateTime:
                        binaryEncoder.WriteDateTime("DateTime", Convert.ToDateTime(valueToEncode));
                        break;

                    case BuiltInType.Guid:
                        binaryEncoder.WriteGuid("GUID", (Uuid)valueToEncode);
                        break;

                    case BuiltInType.String:
                        binaryEncoder.WriteString("String", valueToEncode as string);
                        break;

                    case BuiltInType.ByteString:
                        binaryEncoder.WriteByteString("ByteString", (byte[])valueToEncode);
                        break;

                    case BuiltInType.QualifiedName:
                        binaryEncoder.WriteQualifiedName("QualifiedName", valueToEncode as QualifiedName);
                        break;

                    case BuiltInType.LocalizedText:
                        binaryEncoder.WriteLocalizedText("LocalizedText", valueToEncode as LocalizedText);
                        break;

                    case BuiltInType.NodeId:
                        binaryEncoder.WriteNodeId("NodeId", valueToEncode as NodeId);
                        break;

                    case BuiltInType.ExpandedNodeId:
                        binaryEncoder.WriteExpandedNodeId("ExpandedNodeId", valueToEncode as ExpandedNodeId);
                        break;

                    case BuiltInType.StatusCode:
                        binaryEncoder.WriteStatusCode("StatusCode", (StatusCode)valueToEncode);
                        break;

                    case BuiltInType.XmlElement:
                        binaryEncoder.WriteXmlElement("XmlElement", valueToEncode as XmlElement);
                        break;

                    case BuiltInType.Enumeration:
                        binaryEncoder.WriteInt32("Enumeration", Convert.ToInt32(valueToEncode));
                        break;

                    case BuiltInType.ExtensionObject:
                        binaryEncoder.WriteExtensionObject("ExtensionObject", valueToEncode as ExtensionObject);
                        break;
                    }
                }
                else if (field.FieldMetaData.ValueRank >= ValueRanks.OneDimension)
                {
                    binaryEncoder.WriteArray(null, valueToEncode, field.FieldMetaData.ValueRank, (BuiltInType)field.FieldMetaData.BuiltInType);
                }
            }
            catch (Exception ex)
            {
                Utils.Trace(ex, "Error encoding field {0}.", field.FieldMetaData.Name);
            }
        }
        /// <summary>
        /// Writes the asymmetric security header to the buffer.
        /// </summary>
        protected void WriteAsymmetricMessageHeader(
            BinaryEncoder      encoder,
            uint               messageType,
            uint               secureChannelId,
            string             securityPolicyUri,
            X509Certificate2   senderCertificate, 
            X509Certificate2   receiverCertificate)
        {
            int start = encoder.Position;

            encoder.WriteUInt32(null, messageType);
            encoder.WriteUInt32(null, 0);
            encoder.WriteUInt32(null, secureChannelId);
            encoder.WriteString(null, securityPolicyUri);

            if (SecurityMode != MessageSecurityMode.None)
            {
                encoder.WriteByteString(null, senderCertificate.RawData);
                encoder.WriteByteString(null, GetThumbprintBytes(receiverCertificate.Thumbprint));
            }
            else
            {
                encoder.WriteByteString(null, null);
                encoder.WriteByteString(null, null);
            }
            
            if (encoder.Position - start > SendBufferSize)
            {
                throw ServiceResultException.Create(
                    StatusCodes.BadInternalError, 
                    "AsymmetricSecurityHeader is {0} bytes which is too large for the send buffer size of {1} bytes.",
                    encoder.Position - start,
                    SendBufferSize);
            }
        }
Ejemplo n.º 6
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");
                    }
                }
            }
        private async Task SendOpenSecureChannelRequestAsync(OpenSecureChannelRequest request, CancellationToken token)
        {
            var bodyStream = SerializableBytes.CreateWritableStream();
            var bodyEncoder = new BinaryEncoder(bodyStream, this);
            try
            {
                bodyEncoder.WriteNodeId(null, OpenSecureChannelRequestNodeId);
                request.Encode(bodyEncoder);
                bodyStream.Position = 0;
                if (bodyStream.Length > this.RemoteMaxMessageSize)
                {
                    throw new ServiceResultException(StatusCodes.BadEncodingLimitsExceeded);
                }

                // write chunks
                int chunkCount = 0;
                int bodyCount = (int)(bodyStream.Length - bodyStream.Position);
                while (bodyCount > 0)
                {
                    chunkCount++;
                    if (this.RemoteMaxChunkCount > 0 && chunkCount > this.RemoteMaxChunkCount)
                    {
                        throw new ServiceResultException(StatusCodes.BadEncodingLimitsExceeded);
                    }

                    var stream = new MemoryStream(this.sendBuffer, 0, (int)this.RemoteReceiveBufferSize, true, true);
                    var encoder = new BinaryEncoder(stream, this);
                    try
                    {
                        // header
                        encoder.WriteUInt32(null, UaTcpMessageTypes.OPNF);
                        encoder.WriteUInt32(null, 0u);
                        encoder.WriteUInt32(null, this.ChannelId);

                        // asymmetric security header
                        encoder.WriteString(null, this.RemoteEndpoint.SecurityPolicyUri);
                        if (this.RemoteEndpoint.SecurityMode != MessageSecurityMode.None)
                        {
                            encoder.WriteByteString(null, this.localCertificateBlob);
                            encoder.WriteByteString(null, this.RemoteCertificate.GetCertHash());
                        }
                        else
                        {
                            encoder.WriteByteString(null, null);
                            encoder.WriteByteString(null, null);
                        }

                        int plainHeaderSize = encoder.Position;

                        // sequence header
                        encoder.WriteUInt32(null, this.GetNextSequenceNumber());
                        encoder.WriteUInt32(null, request.RequestHeader.RequestHandle);

                        // body
                        int paddingHeaderSize;
                        int maxBodySize;
                        int bodySize;
                        int paddingSize;
                        int chunkSize;
                        if (this.asymIsEncrypted)
                        {
                            paddingHeaderSize = this.asymRemoteCipherTextBlockSize > 256 ? 2 : 1;
                            maxBodySize = ((((int)this.RemoteReceiveBufferSize - plainHeaderSize - this.asymLocalSignatureSize - paddingHeaderSize) / this.asymRemoteCipherTextBlockSize) * this.asymRemotePlainTextBlockSize) - SequenceHeaderSize;
                            if (bodyCount < maxBodySize)
                            {
                                bodySize = bodyCount;
                                paddingSize = (this.asymRemotePlainTextBlockSize - ((SequenceHeaderSize + bodySize + paddingHeaderSize + this.asymLocalSignatureSize) % this.asymRemotePlainTextBlockSize)) % this.asymRemotePlainTextBlockSize;
                            }
                            else
                            {
                                bodySize = maxBodySize;
                                paddingSize = 0;
                            }

                            chunkSize = plainHeaderSize + (((SequenceHeaderSize + bodySize + paddingSize + paddingHeaderSize + this.asymLocalSignatureSize) / this.asymRemotePlainTextBlockSize) * this.asymRemoteCipherTextBlockSize);
                        }
                        else
                        {
                            paddingHeaderSize = 0;
                            paddingSize = 0;
                            maxBodySize = (int)this.RemoteReceiveBufferSize - plainHeaderSize - this.asymLocalSignatureSize - SequenceHeaderSize;
                            if (bodyCount < maxBodySize)
                            {
                                bodySize = bodyCount;
                            }
                            else
                            {
                                bodySize = maxBodySize;
                            }

                            chunkSize = plainHeaderSize + SequenceHeaderSize + bodySize + this.asymLocalSignatureSize;
                        }

                        bodyStream.Read(this.sendBuffer, encoder.Position, bodySize);
                        encoder.Position += bodySize;
                        bodyCount -= bodySize;

                        // padding
                        if (this.asymIsEncrypted)
                        {
                            byte paddingByte = (byte)(paddingSize & 0xFF);
                            encoder.WriteByte(null, paddingByte);
                            for (int i = 0; i < paddingSize; i++)
                            {
                                encoder.WriteByte(null, paddingByte);
                            }

                            if (paddingHeaderSize == 2)
                            {
                                byte extraPaddingByte = (byte)((paddingSize >> 8) & 0xFF);
                                encoder.WriteByte(null, extraPaddingByte);
                            }
                        }

                        // update message type and (encrypted) length
                        var position = encoder.Position;
                        encoder.Position = 3;
                        encoder.WriteByte(null, bodyCount > 0 ? (byte)'C' : (byte)'F');
                        encoder.WriteUInt32(null, (uint)chunkSize);
                        encoder.Position = position;

                        // sign
                        if (this.asymIsSigned)
                        {
                            // sign with local private key.
                            byte[] signature = this.LocalPrivateKey.SignData(this.sendBuffer, 0, position, this.asymSignatureHashAlgorithmName, RSASignaturePadding.Pkcs1);
                            Debug.Assert(signature.Length == this.asymLocalSignatureSize, nameof(this.asymLocalSignatureSize));
                            encoder.Write(signature, 0, this.asymLocalSignatureSize);
                        }

                        // encrypt
                        if (this.asymIsEncrypted)
                        {
                            position = encoder.Position;
                            Buffer.BlockCopy(this.sendBuffer, 0, this.encryptionBuffer, 0, plainHeaderSize);
                            byte[] plainText = new byte[this.asymRemotePlainTextBlockSize];
                            int jj = plainHeaderSize;
                            for (int ii = plainHeaderSize; ii < position; ii += this.asymRemotePlainTextBlockSize)
                            {
                                Buffer.BlockCopy(this.sendBuffer, ii, plainText, 0, this.asymRemotePlainTextBlockSize);

                                // encrypt with remote public key.
                                byte[] cipherText = this.RemotePublicKey.Encrypt(plainText, this.asymEncryptionPadding);
                                Debug.Assert(cipherText.Length == this.asymRemoteCipherTextBlockSize, nameof(this.asymRemoteCipherTextBlockSize));
                                Buffer.BlockCopy(cipherText, 0, this.encryptionBuffer, jj, this.asymRemoteCipherTextBlockSize);
                                jj += this.asymRemoteCipherTextBlockSize;
                            }

                            await this.SendAsync(this.encryptionBuffer, 0, jj, token).ConfigureAwait(false);
                            return;
                        }

                        // pass buffer to transport
                        await this.SendAsync(this.sendBuffer, 0, encoder.Position, token).ConfigureAwait(false);
                    }
                    finally
                    {
                        encoder.Dispose();
                    }
                }
            }
            finally
            {
                bodyEncoder.Dispose();
            }
        }
        /// <summary>
        /// Encodes field value as RawData
        /// </summary>
        /// <param name="binaryEncoder"></param>
        /// <param name="field"></param>
        private void EncodeFieldAsRawData(BinaryEncoder binaryEncoder, Field field)
        {
            try
            {
                // 01 RawData Field Encoding (TODO: StructuredValue)
                var variant = field.Value.WrappedValue;

                if (variant.TypeInfo == null || variant.TypeInfo.BuiltInType == BuiltInType.Null)
                {
                    return;
                }

                if (field.FieldMetaData.ValueRank == ValueRanks.Scalar)
                {
                    switch ((BuiltInType)field.FieldMetaData.BuiltInType)
                    {
                    case BuiltInType.Boolean:
                        binaryEncoder.WriteBoolean("Bool", Convert.ToBoolean(variant.Value));
                        break;

                    case BuiltInType.SByte:
                        binaryEncoder.WriteSByte("SByte", Convert.ToSByte(variant.Value));
                        break;

                    case BuiltInType.Byte:
                        binaryEncoder.WriteByte("Byte", Convert.ToByte(variant.Value));
                        break;

                    case BuiltInType.Int16:
                        binaryEncoder.WriteInt16("Int16", Convert.ToInt16(variant.Value));
                        break;

                    case BuiltInType.UInt16:
                        binaryEncoder.WriteUInt16("UInt16", Convert.ToUInt16(variant.Value));
                        break;

                    case BuiltInType.Int32:
                        binaryEncoder.WriteInt32("Int32", Convert.ToInt32(variant.Value));
                        break;

                    case BuiltInType.UInt32:
                        binaryEncoder.WriteUInt32("UInt32", Convert.ToUInt32(variant.Value));
                        break;

                    case BuiltInType.Int64:
                        binaryEncoder.WriteInt64("Int64", Convert.ToInt64(variant.Value));
                        break;

                    case BuiltInType.UInt64:
                        binaryEncoder.WriteUInt64("UInt64", Convert.ToUInt64(variant.Value));
                        break;

                    case BuiltInType.Float:
                        binaryEncoder.WriteFloat("Float", Convert.ToSingle(variant.Value));
                        break;

                    case BuiltInType.Double:
                        binaryEncoder.WriteDouble("Double", Convert.ToDouble(variant.Value));
                        break;

                    case BuiltInType.DateTime:
                        binaryEncoder.WriteDateTime("DateTime", Convert.ToDateTime(variant.Value));
                        break;

                    case BuiltInType.Guid:
                        binaryEncoder.WriteGuid("GUID", (Uuid)variant.Value);
                        break;

                    case BuiltInType.String:
                        binaryEncoder.WriteString("String", variant.Value as string);
                        break;

                    case BuiltInType.ByteString:
                        binaryEncoder.WriteByteString("ByteString", (byte[])variant.Value);
                        break;

                    case BuiltInType.QualifiedName:
                        binaryEncoder.WriteQualifiedName("QualifiedName", variant.Value as QualifiedName);
                        break;

                    case BuiltInType.LocalizedText:
                        binaryEncoder.WriteLocalizedText("LocalizedText", variant.Value as LocalizedText);
                        break;

                    case BuiltInType.NodeId:
                        binaryEncoder.WriteNodeId("NodeId", variant.Value as NodeId);
                        break;

                    case BuiltInType.ExpandedNodeId:
                        binaryEncoder.WriteExpandedNodeId("ExpandedNodeId", variant.Value as ExpandedNodeId);
                        break;

                    case BuiltInType.StatusCode:
                        binaryEncoder.WriteStatusCode("StatusCode", (StatusCode)variant.Value);
                        break;

                    case BuiltInType.XmlElement:
                        binaryEncoder.WriteXmlElement("XmlElement", variant.Value as XmlElement);
                        break;

                    case BuiltInType.Enumeration:
                        binaryEncoder.WriteInt32("Enumeration", Convert.ToInt32(variant.Value));
                        break;

                    case BuiltInType.ExtensionObject:
                        binaryEncoder.WriteExtensionObject("ExtensionObject", variant.Value as ExtensionObject);
                        break;
                    }
                }
                else
                {
                    switch ((BuiltInType)field.FieldMetaData.BuiltInType)
                    {
                    case BuiltInType.Boolean:
                        binaryEncoder.WriteBooleanArray("BooleanArray", (bool[])variant.Value);
                        break;

                    case BuiltInType.SByte:
                        binaryEncoder.WriteSByteArray("SByteArray", (sbyte[])variant.Value);
                        break;

                    case BuiltInType.Byte:
                        binaryEncoder.WriteByteArray("ByteArray", (byte[])variant.Value);
                        break;

                    case BuiltInType.Int16:
                        binaryEncoder.WriteInt16Array("ByteArray", (short[])variant.Value);
                        break;

                    case BuiltInType.UInt16:
                        binaryEncoder.WriteUInt16Array("UInt16Array", (ushort[])variant.Value);
                        break;

                    case BuiltInType.Int32:
                        binaryEncoder.WriteInt32Array("Int32Array", (int[])variant.Value);
                        break;

                    case BuiltInType.UInt32:
                        binaryEncoder.WriteUInt32Array("UInt32Array", (uint[])variant.Value);
                        break;

                    case BuiltInType.Int64:
                        binaryEncoder.WriteInt64Array("Int64Array", (long[])variant.Value);
                        break;

                    case BuiltInType.UInt64:
                        binaryEncoder.WriteUInt64Array("UInt64Array", (ulong[])variant.Value);
                        break;

                    case BuiltInType.Float:
                        binaryEncoder.WriteFloatArray("FloatArray", (float[])variant.Value);
                        break;

                    case BuiltInType.Double:
                        binaryEncoder.WriteDoubleArray("DoubleArray", (double[])variant.Value);
                        break;

                    case BuiltInType.DateTime:
                        binaryEncoder.WriteDateTimeArray("DateTimeArray", (DateTime[])variant.Value);
                        break;

                    case BuiltInType.Guid:
                        binaryEncoder.WriteGuidArray("GuidArray", (Uuid[])variant.Value);
                        break;

                    case BuiltInType.String:
                        binaryEncoder.WriteStringArray("StringArray", (string[])variant.Value);
                        break;

                    case BuiltInType.ByteString:
                        binaryEncoder.WriteByteStringArray("StringArray", (byte[][])variant.Value);
                        break;

                    case BuiltInType.QualifiedName:
                        binaryEncoder.WriteQualifiedNameArray("QualifiedNameArray", (QualifiedName[])variant.Value);
                        break;

                    case BuiltInType.LocalizedText:
                        binaryEncoder.WriteLocalizedTextArray("LocalizedTextArray", (LocalizedText[])variant.Value);
                        break;

                    case BuiltInType.NodeId:
                        binaryEncoder.WriteNodeIdArray("NodeIdArray", (NodeId[])variant.Value);
                        break;

                    case BuiltInType.ExpandedNodeId:
                        binaryEncoder.WriteExpandedNodeIdArray("ExpandedNodeIdArray", (ExpandedNodeId[])variant.Value);
                        break;

                    case BuiltInType.StatusCode:
                        binaryEncoder.WriteStatusCodeArray("StatusCodeArray", (StatusCode[])variant.Value);
                        break;

                    case BuiltInType.XmlElement:
                        binaryEncoder.WriteXmlElementArray("XmlElementArray", (System.Xml.XmlElement[])variant.Value);
                        break;

                    case BuiltInType.Variant:
                        binaryEncoder.WriteVariantArray("VariantArray", (Variant[])variant.Value);
                        break;

                    case BuiltInType.Enumeration:
                        //TODO make this work
                        //binaryEncoder.WriteInt32Array("EnumerationArray", Convert.ToInt32(variant.Value));
                        binaryEncoder.WriteVariantArray("EnumerationArray", (Variant[])variant.Value);
                        break;

                    case BuiltInType.ExtensionObject:
                        binaryEncoder.WriteExtensionObjectArray("ExtensionObjectArray", (ExtensionObject[])variant.Value);
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Utils.Trace("Error encoding field {0} - {1}", field.FieldMetaData.Name, ex);
            }
        }