/// <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); } }
/// <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> /// 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> /// 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); } }