1 /******************************************************************************
4 ** Copyright (C) 2006-2007 ascolab GmbH. All Rights Reserved.
5 ** Web: http://www.ascolab.com
7 ** This program is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU General Public License
9 ** as published by the Free Software Foundation; either version 2
10 ** of the License, or (at your option) any later version.
12 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
13 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
15 ** Project: OpcUa Wireshark Plugin
17 ** Description: Implementation of OpcUa built-in type parsers.
18 ** This contains all the simple types and some complex types.
20 ** Author: Gerhard Gappmeier <gerhard.gappmeier@ascolab.com>
21 ** Last change by: $Author: gergap $
23 ******************************************************************************/
30 #include <epan/packet.h>
31 #include <epan/dissectors/packet-windows-common.h>
32 #include "opcua_simpletypes.h"
33 #include "opcua_hfindeces.h"
35 #include <epan/emem.h>
38 #define MAX_BUFFER 256
40 #define DIAGNOSTICINFO_ENCODINGMASK_SYMBOLICID_FLAG 0x01
41 #define DIAGNOSTICINFO_ENCODINGMASK_NAMESPACE_FLAG 0x02
42 #define DIAGNOSTICINFO_ENCODINGMASK_LOCALIZEDTEXT_FLAG 0x04
43 #define DIAGNOSTICINFO_ENCODINGMASK_ADDITIONALINFO_FLAG 0x08
44 #define DIAGNOSTICINFO_ENCODINGMASK_INNERSTATUSCODE_FLAG 0x10
45 #define DIAGNOSTICINFO_ENCODINGMASK_INNERDIAGNOSTICINFO_FLAG 0x20
46 #define LOCALIZEDTEXT_ENCODINGBYTE_LOCALE 0x01
47 #define LOCALIZEDTEXT_ENCODINGBYTE_TEXT 0x02
48 #define NODEID_URIMASK 0x80
49 #define NODEID_SERVERINDEXFLAG 0x40
50 #define DATAVALUE_ENCODINGBYTE_VALUE 0x01
51 #define DATAVALUE_ENCODINGBYTE_STATUSCODE 0x02
52 #define DATAVALUE_ENCODINGBYTE_SOURCETIMESTAMP 0x04
53 #define DATAVALUE_ENCODINGBYTE_SERVERTIMESTAMP 0x08
54 #define DATAVALUE_ENCODINGBYTE_SOURCEPICOSECONDS 0x10
55 #define DATAVALUE_ENCODINGBYTE_SERVERPICOSECONDS 0x20
56 #define EXTOBJ_ENCODINGMASK_BINBODY_FLAG 0x01
57 #define EXTOBJ_ENCODINGMASK_XMLBODY_FLAG 0x02
59 static int hf_opcua_diag_mask_symbolicflag = -1;
60 static int hf_opcua_diag_mask_namespaceflag = -1;
61 static int hf_opcua_diag_mask_localizedtextflag = -1;
62 static int hf_opcua_diag_mask_additionalinfoflag = -1;
63 static int hf_opcua_diag_mask_innerstatuscodeflag = -1;
64 static int hf_opcua_diag_mask_innerdiaginfoflag = -1;
65 static int hf_opcua_loctext_mask_localeflag = -1;
66 static int hf_opcua_loctext_mask_textflag = -1;
67 static int hf_opcua_datavalue_mask_valueflag = -1;
68 static int hf_opcua_datavalue_mask_statuscodeflag = -1;
69 static int hf_opcua_datavalue_mask_sourcetimestampflag = -1;
70 static int hf_opcua_datavalue_mask_servertimestampflag = -1;
71 static int hf_opcua_datavalue_mask_sourcepicoseconds = -1;
72 static int hf_opcua_datavalue_mask_serverpicoseconds = -1;
73 static int hf_opcua_nodeid_encodingmask = -1;
74 static int hf_opcua_variant_encodingmask = -1;
75 static int hf_opcua_nodeid_nsid = -1;
76 static int hf_opcua_nodeid_numeric = -1;
77 static int hf_opcua_localizedtext_locale = -1;
78 static int hf_opcua_localizedtext_text = -1;
79 static int hf_opcua_qualifiedname_id = -1;
80 static int hf_opcua_qualifiedname_name = -1;
81 static int hf_opcua_SourceTimestamp = -1;
82 static int hf_opcua_SourcePicoseconds = -1;
83 static int hf_opcua_ServerTimestamp = -1;
84 static int hf_opcua_ServerPicoseconds = -1;
85 static int hf_opcua_diag_symbolicid = -1;
86 static int hf_opcua_diag_namespace = -1;
87 static int hf_opcua_diag_localizedtext = -1;
88 static int hf_opcua_diag_additionalinfo = -1;
89 static int hf_opcua_diag_innerstatuscode = -1;
90 static int hf_opcua_extobj_mask_binbodyflag = -1;
91 static int hf_opcua_extobj_mask_xmlbodyflag = -1;
92 static int hf_opcua_ArraySize = -1;
93 static int hf_opcua_Uri = -1;
94 static int hf_opcua_ServerIndex = -1;
96 /** NodeId encoding mask table */
97 static const value_string g_nodeidmasks[] = {
98 { 0, "Two byte encoded Numeric" },
99 { 1, "Four byte encoded Numeric" },
100 { 2, "Numeric of arbitrary length" },
109 /** UA Variant Type enum */
110 typedef enum _OpcUa_BuiltInType
113 OpcUaType_Boolean = 1,
117 OpcUaType_UInt16 = 5,
119 OpcUaType_UInt32 = 7,
121 OpcUaType_UInt64 = 9,
122 OpcUaType_Float = 10,
123 OpcUaType_Double = 11,
124 OpcUaType_String = 12,
125 OpcUaType_DateTime = 13,
127 OpcUaType_ByteString = 15,
128 OpcUaType_XmlElement = 16,
129 OpcUaType_NodeId = 17,
130 OpcUaType_ExpandedNodeId = 18,
131 OpcUaType_StatusCode = 19,
132 OpcUaType_QualifiedName = 20,
133 OpcUaType_LocalizedText = 21,
134 OpcUaType_ExtensionObject = 22,
135 OpcUaType_DataValue = 23,
136 OpcUaType_Variant = 24,
137 OpcUaType_DiagnosticInfo = 25
141 /** Variant encoding mask table */
142 static const value_string g_VariantTypes[] = {
158 { 15, "ByteString" },
159 { 16, "XmlElement" },
161 { 18, "ExpandedNodeId" },
162 { 19, "StatusCode" },
163 { 20, "DiagnosticInfo" },
164 { 21, "QualifiedName" },
165 { 22, "LocalizedText" },
166 { 23, "ExtensionObject" },
169 { 0x80, "Array of Null" },
170 { 0x80+1, "Array of Boolean" },
171 { 0x80+2, "Array of SByte" },
172 { 0x80+3, "Array of Byte" },
173 { 0x80+4, "Array of Int16" },
174 { 0x80+5, "Array of UInt16" },
175 { 0x80+6, "Array of Int32" },
176 { 0x80+7, "Array of UInt32" },
177 { 0x80+8, "Array of Int64" },
178 { 0x80+9, "Array of UInt64" },
179 { 0x80+10, "Array of Float" },
180 { 0x80+11, "Array of Double" },
181 { 0x80+12, "Array of String" },
182 { 0x80+13, "Array of DateTime" },
183 { 0x80+14, "Array of Guid" },
184 { 0x80+15, "Array of ByteString" },
185 { 0x80+16, "Array of XmlElement" },
186 { 0x80+17, "Array of NodeId" },
187 { 0x80+18, "Array of ExpandedNodeId" },
188 { 0x80+19, "Array of StatusCode" },
189 { 0x80+20, "Array of DiagnosticInfo" },
190 { 0x80+21, "Array of QualifiedName" },
191 { 0x80+22, "Array of LocalizedText" },
192 { 0x80+23, "Array of ExtensionObject" },
193 { 0x80+24, "Array of DataValue" },
194 { 0x80+25, "Array of Variant" },
197 #define VARIANT_ARRAYDIMENSIONS 0x40
198 #define VARIANT_ARRAYMASK 0x80
201 static gint ett_opcua_array = -1;
202 static gint ett_opcua_diagnosticinfo = -1;
203 static gint ett_opcua_nodeid = -1;
204 static gint ett_opcua_localeid = -1;
205 static gint ett_opcua_localizedtext = -1;
206 static gint ett_opcua_qualifiedname = -1;
207 static gint ett_opcua_datavalue = -1;
208 static gint ett_opcua_variant = -1;
209 static gint ett_opcua_extensionobject = -1;
210 static gint ett_opcua_extobj_encodingmask = -1;
214 &ett_opcua_diagnosticinfo,
217 &ett_opcua_localizedtext,
218 &ett_opcua_qualifiedname,
219 &ett_opcua_datavalue,
221 &ett_opcua_extensionobject,
222 &ett_opcua_extobj_encodingmask
226 static hf_register_info hf[] =
228 /* full name , abbreviation , type , display , strings, bitmask, blurb, id, parent, ref_count, bitshift */
229 { &hf_opcua_diag_mask_symbolicflag,
230 { "has symbolic id", "", FT_BOOLEAN, 8, NULL, DIAGNOSTICINFO_ENCODINGMASK_SYMBOLICID_FLAG, "", HFILL }
232 { &hf_opcua_diag_mask_namespaceflag,
233 { "has namespace", "", FT_BOOLEAN, 8, NULL, DIAGNOSTICINFO_ENCODINGMASK_NAMESPACE_FLAG, "", HFILL }
235 { &hf_opcua_diag_mask_localizedtextflag,
236 { "has localizedtext", "", FT_BOOLEAN, 8, NULL, DIAGNOSTICINFO_ENCODINGMASK_LOCALIZEDTEXT_FLAG, "", HFILL }
238 { &hf_opcua_diag_mask_additionalinfoflag,
239 { "has additional info", "", FT_BOOLEAN, 8, NULL, DIAGNOSTICINFO_ENCODINGMASK_ADDITIONALINFO_FLAG, "", HFILL }
241 { &hf_opcua_diag_mask_innerstatuscodeflag,
242 { "has inner statuscode", "", FT_BOOLEAN, 8, NULL, DIAGNOSTICINFO_ENCODINGMASK_INNERSTATUSCODE_FLAG, "", HFILL }
244 { &hf_opcua_diag_mask_innerdiaginfoflag,
245 { "has inner diagnostic info", "", FT_BOOLEAN, 8, NULL, DIAGNOSTICINFO_ENCODINGMASK_INNERDIAGNOSTICINFO_FLAG, "", HFILL }
247 { &hf_opcua_loctext_mask_localeflag,
248 { "has locale information", "", FT_BOOLEAN, 8, NULL, LOCALIZEDTEXT_ENCODINGBYTE_LOCALE, "", HFILL }
250 { &hf_opcua_loctext_mask_textflag,
251 { "has text", "", FT_BOOLEAN, 8, NULL, LOCALIZEDTEXT_ENCODINGBYTE_TEXT, "", HFILL }
253 { &hf_opcua_nodeid_encodingmask,
254 { "NodeId EncodingMask", "application.nodeid.encodingmask", FT_UINT8, BASE_HEX, VALS(g_nodeidmasks), 0x0, "", HFILL }
256 { &hf_opcua_nodeid_nsid,
257 { "NodeId Namespace Id", "application.nodeid.nsid", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }
259 { &hf_opcua_nodeid_numeric,
260 { "NodeId Identifier Numeric", "application.nodeid.numeric", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }
262 { &hf_opcua_localizedtext_locale, { "Locale", "", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } },
263 { &hf_opcua_localizedtext_text, { "Text", "", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } },
264 { &hf_opcua_qualifiedname_id, { "Id", "", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } },
265 { &hf_opcua_qualifiedname_name, { "Name", "", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } },
266 { &hf_opcua_datavalue_mask_valueflag, { "has value", "", FT_BOOLEAN, 8, NULL, DATAVALUE_ENCODINGBYTE_VALUE, "", HFILL } },
267 { &hf_opcua_datavalue_mask_statuscodeflag, { "has statuscode", "", FT_BOOLEAN, 8, NULL, DATAVALUE_ENCODINGBYTE_STATUSCODE, "", HFILL } },
268 { &hf_opcua_datavalue_mask_sourcetimestampflag, { "has source timestamp", "", FT_BOOLEAN, 8, NULL, DATAVALUE_ENCODINGBYTE_SOURCETIMESTAMP, "", HFILL } },
269 { &hf_opcua_datavalue_mask_servertimestampflag, { "has server timestamp", "", FT_BOOLEAN, 8, NULL, DATAVALUE_ENCODINGBYTE_SERVERTIMESTAMP, "", HFILL } },
270 { &hf_opcua_datavalue_mask_sourcepicoseconds, { "has source picoseconds", "", FT_BOOLEAN, 8, NULL, DATAVALUE_ENCODINGBYTE_SOURCEPICOSECONDS, "", HFILL } },
271 { &hf_opcua_datavalue_mask_serverpicoseconds, { "has server picoseconds", "", FT_BOOLEAN, 8, NULL, DATAVALUE_ENCODINGBYTE_SERVERPICOSECONDS, "", HFILL } },
272 { &hf_opcua_variant_encodingmask, { "Variant Type", "", FT_UINT8, BASE_HEX, VALS(g_VariantTypes), 0x0, "", HFILL } },
273 { &hf_opcua_SourceTimestamp, { "SourceTimestamp", "", FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0, "", HFILL } },
274 { &hf_opcua_SourcePicoseconds, { "SourcePicoseconds", "", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } },
275 { &hf_opcua_ServerTimestamp, { "ServerTimestamp", "", FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0, "", HFILL } },
276 { &hf_opcua_ServerPicoseconds, { "ServerPicoseconds", "", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } },
277 { &hf_opcua_diag_symbolicid, { "SymbolicId", "", FT_INT32, BASE_DEC, NULL, 0x0, "", HFILL } },
278 { &hf_opcua_diag_namespace, { "Namespace", "", FT_INT32, BASE_DEC, NULL, 0x0, "", HFILL } },
279 { &hf_opcua_diag_localizedtext, { "LocaliezdText", "", FT_INT32, BASE_DEC, NULL, 0x0, "", HFILL } },
280 { &hf_opcua_diag_additionalinfo, { "AdditionalInfo", "", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } },
281 { &hf_opcua_diag_innerstatuscode, { "InnerStatusCode", "", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL } },
282 { &hf_opcua_extobj_mask_binbodyflag, { "has binary body", "", FT_BOOLEAN, 8, NULL, EXTOBJ_ENCODINGMASK_BINBODY_FLAG, "", HFILL } },
283 { &hf_opcua_extobj_mask_xmlbodyflag, { "has xml body", "", FT_BOOLEAN, 8, NULL, EXTOBJ_ENCODINGMASK_XMLBODY_FLAG, "", HFILL } },
284 { &hf_opcua_ArraySize, { "ArraySize", "", FT_INT32, BASE_DEC, NULL, 0x0, "", HFILL } },
285 { &hf_opcua_Uri, { "Uri", "", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL } },
286 { &hf_opcua_ServerIndex, { "ServerIndex", "", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL } }
289 void registerSimpleTypes(int proto)
291 proto_register_field_array(proto, hf, array_length(hf));
292 proto_register_subtree_array(ett, array_length(ett));
295 void parseBoolean(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
297 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 1, TRUE); *pOffset+=1;
300 void parseByte(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
302 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 1, TRUE); *pOffset+=1;
305 void parseSByte(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
307 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 1, TRUE); *pOffset+=1;
310 void parseUInt16(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
312 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 2, TRUE); *pOffset+=2;
315 void parseInt16(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
317 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 2, TRUE); *pOffset+=2;
320 void parseUInt32(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
322 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 4, TRUE); *pOffset+=4;
325 void parseInt32(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
327 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 4, TRUE); *pOffset+=4;
330 void parseUInt64(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
332 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 8, TRUE); *pOffset+=8;
335 void parseInt64(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
337 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 8, TRUE); *pOffset+=8;
340 void parseString(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
342 char *szValue = ep_alloc(MAX_BUFFER);
343 gint iOffset = *pOffset;
344 gint32 iLen = tvb_get_letohl(tvb, *pOffset);
351 g_snprintf(szValue, MAX_BUFFER, "[OpcUa Null String]");
356 if (iStrLen > (MAX_BUFFER-1)) iStrLen = MAX_BUFFER - 1;
357 /* copy non null terminated string of length iStrlen */
358 strncpy(szValue, (char*)&tvb->real_data[iOffset], iStrLen);
359 /* set null terminator */
360 szValue[iStrLen] = 0;
361 iOffset += iLen; /* eat the whole string */
365 g_snprintf(szValue, MAX_BUFFER, "[Invalid String] Ups, something is wrong with this message.");
368 proto_tree_add_string(tree, hfIndex, tvb, *pOffset, (iOffset - *pOffset), szValue);
373 void parseStatusCode(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
375 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, 4, TRUE);
379 void parseLocalizedText(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, char *szFieldName)
381 gint iOffset = *pOffset;
383 proto_tree *mask_tree;
387 ti = proto_tree_add_text(tree, tvb, 0, -1, "%s: LocalizedText", szFieldName);
388 subtree = proto_item_add_subtree(ti, ett_opcua_localizedtext);
390 /* parse encoding mask */
391 EncodingMask = tvb_get_guint8(tvb, iOffset);
392 ti = proto_tree_add_text(subtree, tvb, 0, -1, "EncodingMask");
393 mask_tree = proto_item_add_subtree(ti, ett_opcua_localizedtext);
394 proto_tree_add_item(mask_tree, hf_opcua_loctext_mask_localeflag, tvb, iOffset, 1, TRUE);
395 proto_tree_add_item(mask_tree, hf_opcua_loctext_mask_textflag, tvb, iOffset, 1, TRUE);
398 if (EncodingMask & LOCALIZEDTEXT_ENCODINGBYTE_LOCALE)
400 parseString(subtree, tvb, &iOffset, hf_opcua_localizedtext_locale);
403 if (EncodingMask & LOCALIZEDTEXT_ENCODINGBYTE_TEXT)
405 parseString(subtree, tvb, &iOffset, hf_opcua_localizedtext_text);
411 void parseGuid(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
413 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, GUID_LEN, TRUE); *pOffset+=GUID_LEN;
416 void parseByteString(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
418 int iOffset = *pOffset;
419 gint32 iLen = tvb_get_letohl(tvb, iOffset);
429 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, (iOffset - *pOffset), TRUE);
433 void parseXmlElement(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
435 parseByteString(tree, tvb, pOffset, hfIndex);
438 void parseFloat(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
440 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, sizeof(gfloat), TRUE);
441 *pOffset += sizeof(gfloat);
444 void parseDouble(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
446 proto_tree_add_item(tree, hfIndex, tvb, *pOffset, sizeof(gdouble), TRUE);
447 *pOffset += sizeof(gdouble);
450 void parseDateTime(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex)
452 *pOffset = dissect_nt_64bit_time(tvb, tree, *pOffset, hfIndex);
455 void parseDiagnosticInfo(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, char *szFieldName)
457 gint iOffset = *pOffset;
459 proto_tree *mask_tree;
463 ti = proto_tree_add_text(tree, tvb, 0, -1, "%s: DiagnosticInfo", szFieldName);
464 subtree = proto_item_add_subtree(ti, ett_opcua_diagnosticinfo);
466 /* parse encoding mask */
467 EncodingMask = tvb_get_guint8(tvb, iOffset);
468 ti = proto_tree_add_text(subtree, tvb, 0, -1, "EncodingMask");
469 mask_tree = proto_item_add_subtree(ti, ett_opcua_diagnosticinfo);
470 proto_tree_add_item(mask_tree, hf_opcua_diag_mask_symbolicflag, tvb, iOffset, 1, TRUE);
471 proto_tree_add_item(mask_tree, hf_opcua_diag_mask_namespaceflag, tvb, iOffset, 1, TRUE);
472 proto_tree_add_item(mask_tree, hf_opcua_diag_mask_localizedtextflag, tvb, iOffset, 1, TRUE);
473 proto_tree_add_item(mask_tree, hf_opcua_diag_mask_additionalinfoflag, tvb, iOffset, 1, TRUE);
474 proto_tree_add_item(mask_tree, hf_opcua_diag_mask_innerstatuscodeflag, tvb, iOffset, 1, TRUE);
475 proto_tree_add_item(mask_tree, hf_opcua_diag_mask_innerdiaginfoflag, tvb, iOffset, 1, TRUE);
478 if (EncodingMask & DIAGNOSTICINFO_ENCODINGMASK_SYMBOLICID_FLAG)
480 parseInt32(subtree, tvb, &iOffset, hf_opcua_diag_symbolicid);
482 if (EncodingMask & DIAGNOSTICINFO_ENCODINGMASK_NAMESPACE_FLAG)
484 parseInt32(subtree, tvb, &iOffset, hf_opcua_diag_namespace);
486 if (EncodingMask & DIAGNOSTICINFO_ENCODINGMASK_LOCALIZEDTEXT_FLAG)
488 parseInt32(subtree, tvb, &iOffset, hf_opcua_diag_localizedtext);
490 if (EncodingMask & DIAGNOSTICINFO_ENCODINGMASK_ADDITIONALINFO_FLAG)
492 parseString(subtree, tvb, &iOffset, hf_opcua_diag_additionalinfo);
494 if (EncodingMask & DIAGNOSTICINFO_ENCODINGMASK_INNERSTATUSCODE_FLAG)
496 parseStatusCode(subtree, tvb, &iOffset, hf_opcua_diag_innerstatuscode);
498 if (EncodingMask & DIAGNOSTICINFO_ENCODINGMASK_INNERDIAGNOSTICINFO_FLAG)
500 parseDiagnosticInfo(subtree, tvb, &iOffset, "Inner DiagnosticInfo");
506 void parseQualifiedName(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, char *szFieldName)
508 proto_item *ti = proto_tree_add_text(tree, tvb, 0, -1, "%s: QualifiedName", szFieldName);
509 proto_tree *subtree = proto_item_add_subtree(ti, ett_opcua_qualifiedname);
511 parseUInt16(subtree, tvb, pOffset, hf_opcua_qualifiedname_id);
512 parseString(subtree, tvb, pOffset, hf_opcua_qualifiedname_name);
515 void parseDataValue(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, char *szFieldName)
517 proto_item *ti = proto_tree_add_text(tree, tvb, 0, -1, "%s: DataValue", szFieldName);
518 proto_tree *subtree = proto_item_add_subtree(ti, ett_opcua_datavalue);
519 proto_tree *mask_tree;
520 gint iOffset = *pOffset;
523 EncodingMask = tvb_get_guint8(tvb, iOffset);
524 ti = proto_tree_add_text(subtree, tvb, 0, -1, "EncodingMask");
525 mask_tree = proto_item_add_subtree(ti, ett_opcua_datavalue);
526 proto_tree_add_item(mask_tree, hf_opcua_datavalue_mask_valueflag, tvb, iOffset, 1, TRUE);
527 proto_tree_add_item(mask_tree, hf_opcua_datavalue_mask_statuscodeflag, tvb, iOffset, 1, TRUE);
528 proto_tree_add_item(mask_tree, hf_opcua_datavalue_mask_sourcetimestampflag, tvb, iOffset, 1, TRUE);
529 proto_tree_add_item(mask_tree, hf_opcua_datavalue_mask_servertimestampflag, tvb, iOffset, 1, TRUE);
530 proto_tree_add_item(mask_tree, hf_opcua_datavalue_mask_sourcepicoseconds, tvb, iOffset, 1, TRUE);
531 proto_tree_add_item(mask_tree, hf_opcua_datavalue_mask_serverpicoseconds, tvb, iOffset, 1, TRUE);
534 if (EncodingMask & DATAVALUE_ENCODINGBYTE_VALUE)
536 parseVariant(subtree, tvb, &iOffset, "Value");
538 if (EncodingMask & DATAVALUE_ENCODINGBYTE_STATUSCODE)
540 parseStatusCode(subtree, tvb, &iOffset, hf_opcua_StatusCode);
542 if (EncodingMask & DATAVALUE_ENCODINGBYTE_SOURCETIMESTAMP)
544 parseDateTime(subtree, tvb, &iOffset, hf_opcua_SourceTimestamp);
546 if (EncodingMask & DATAVALUE_ENCODINGBYTE_SOURCEPICOSECONDS)
548 parseUInt16(subtree, tvb, &iOffset, hf_opcua_SourcePicoseconds);
550 if (EncodingMask & DATAVALUE_ENCODINGBYTE_SERVERTIMESTAMP)
552 parseDateTime(subtree, tvb, &iOffset, hf_opcua_ServerTimestamp);
554 if (EncodingMask & DATAVALUE_ENCODINGBYTE_SERVERPICOSECONDS)
556 parseUInt16(subtree, tvb, &iOffset, hf_opcua_ServerPicoseconds);
562 void parseVariant(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, char *szFieldName)
564 proto_item *ti = proto_tree_add_text(tree, tvb, 0, -1, "%s: Variant", szFieldName);
565 proto_tree *subtree = proto_item_add_subtree(ti, ett_opcua_variant);
566 gint iOffset = *pOffset;
570 EncodingMask = tvb_get_guint8(tvb, iOffset);
571 proto_tree_add_item(subtree, hf_opcua_variant_encodingmask, tvb, iOffset, 1, TRUE);
573 ArrayLength = tvb_get_letohl(tvb, iOffset);
575 if (EncodingMask & VARIANT_ARRAYMASK)
577 /* type is encoded in bits 0-5 */
578 switch(EncodingMask & 0x3f)
580 case OpcUaType_Null: break;
581 case OpcUaType_Boolean: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_Boolean, parseBoolean); break;
582 case OpcUaType_SByte: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_SByte, parseSByte); break;
583 case OpcUaType_Byte: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_Byte, parseByte); break;
584 case OpcUaType_Int16: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_Int16, parseInt16); break;
585 case OpcUaType_UInt16: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_UInt16, parseUInt16); break;
586 case OpcUaType_Int32: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_Int32, parseInt32); break;
587 case OpcUaType_UInt32: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_UInt32, parseUInt32); break;
588 case OpcUaType_Int64: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_Int64, parseInt64); break;
589 case OpcUaType_UInt64: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_UInt64, parseUInt64); break;
590 case OpcUaType_Float: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_Float, parseFloat); break;
591 case OpcUaType_Double: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_Double, parseDouble); break;
592 case OpcUaType_String: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_String, parseString); break;
593 case OpcUaType_DateTime: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_DateTime, parseDateTime); break;
594 case OpcUaType_Guid: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_Guid, parseGuid); break;
595 case OpcUaType_ByteString: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_ByteString, parseByteString); break;
596 case OpcUaType_XmlElement: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_XmlElement, parseXmlElement); break;
597 case OpcUaType_NodeId: parseArrayComplex(subtree, tvb, &iOffset, "NodeId", parseNodeId); break;
598 case OpcUaType_ExpandedNodeId: parseArrayComplex(subtree, tvb, &iOffset, "ExpandedNodeId", parseExpandedNodeId); break;
599 case OpcUaType_StatusCode: parseArraySimple(subtree, tvb, &iOffset, hf_opcua_StatusCode, parseStatusCode); break;
600 case OpcUaType_DiagnosticInfo: parseArrayComplex(subtree, tvb, &iOffset, "DiagnosticInfo", parseDiagnosticInfo); break;
601 case OpcUaType_QualifiedName: parseArrayComplex(subtree, tvb, &iOffset, "QualifiedName", parseQualifiedName); break;
602 case OpcUaType_LocalizedText: parseArrayComplex(subtree, tvb, &iOffset, "LocalizedText", parseLocalizedText); break;
603 case OpcUaType_ExtensionObject: parseArrayComplex(subtree, tvb, &iOffset, "ExtensionObject", parseExtensionObject); break;
604 case OpcUaType_DataValue: parseArrayComplex(subtree, tvb, &iOffset, "DataValue", parseDataValue); break;
605 case OpcUaType_Variant: parseArrayComplex(subtree, tvb, &iOffset, "Variant", parseVariant); break;
610 /* type is encoded in bits 0-5 */
611 switch(EncodingMask & 0x3f)
613 case OpcUaType_Null: break;
614 case OpcUaType_Boolean: parseBoolean(subtree, tvb, &iOffset, hf_opcua_Boolean); break;
615 case OpcUaType_SByte: parseSByte(subtree, tvb, &iOffset, hf_opcua_SByte); break;
616 case OpcUaType_Byte: parseByte(subtree, tvb, &iOffset, hf_opcua_Byte); break;
617 case OpcUaType_Int16: parseInt16(subtree, tvb, &iOffset, hf_opcua_Int16); break;
618 case OpcUaType_UInt16: parseUInt16(subtree, tvb, &iOffset, hf_opcua_UInt16); break;
619 case OpcUaType_Int32: parseInt32(subtree, tvb, &iOffset, hf_opcua_Int32); break;
620 case OpcUaType_UInt32: parseUInt32(subtree, tvb, &iOffset, hf_opcua_UInt32); break;
621 case OpcUaType_Int64: parseInt64(subtree, tvb, &iOffset, hf_opcua_Int64); break;
622 case OpcUaType_UInt64: parseUInt64(subtree, tvb, &iOffset, hf_opcua_UInt64); break;
623 case OpcUaType_Float: parseFloat(subtree, tvb, &iOffset, hf_opcua_Float); break;
624 case OpcUaType_Double: parseDouble(subtree, tvb, &iOffset, hf_opcua_Double); break;
625 case OpcUaType_String: parseString(subtree, tvb, &iOffset, hf_opcua_String); break;
626 case OpcUaType_DateTime: parseDateTime(subtree, tvb, &iOffset, hf_opcua_DateTime); break;
627 case OpcUaType_Guid: parseGuid(subtree, tvb, &iOffset, hf_opcua_Guid); break;
628 case OpcUaType_ByteString: parseByteString(subtree, tvb, &iOffset, hf_opcua_ByteString); break;
629 case OpcUaType_XmlElement: parseXmlElement(subtree, tvb, &iOffset, hf_opcua_XmlElement); break;
630 case OpcUaType_NodeId: parseNodeId(subtree, tvb, &iOffset, "Value"); break;
631 case OpcUaType_ExpandedNodeId: parseExpandedNodeId(subtree, tvb, &iOffset, "Value"); break;
632 case OpcUaType_StatusCode: parseStatusCode(subtree, tvb, &iOffset, hf_opcua_StatusCode); break;
633 case OpcUaType_DiagnosticInfo: parseDiagnosticInfo(subtree, tvb, &iOffset, "Value"); break;
634 case OpcUaType_QualifiedName: parseQualifiedName(subtree, tvb, &iOffset, "Value"); break;
635 case OpcUaType_LocalizedText: parseLocalizedText(subtree, tvb, &iOffset, "Value"); break;
636 case OpcUaType_ExtensionObject: parseExtensionObject(subtree, tvb, &iOffset, "Value"); break;
637 case OpcUaType_DataValue: parseDataValue(subtree, tvb, &iOffset, "Value"); break;
638 case OpcUaType_Variant: parseVariant(subtree, tvb, &iOffset, "Value"); break;
642 if (EncodingMask & VARIANT_ARRAYDIMENSIONS)
644 proto_item *ti = proto_tree_add_text(tree, tvb, 0, -1, "Array Dimensions");
645 proto_tree *subtree = proto_item_add_subtree(ti, ett_opcua_array);
648 for (i=0; i<ArrayLength; i++)
650 parseInt32(subtree, tvb, pOffset, hf_opcua_Int32);
657 /** General parsing function for arrays of simple types.
658 * All arrays have one 4 byte signed integer length information,
659 * followed by n data elements.
661 void parseArraySimple(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, int hfIndex, fctSimpleTypeParser pParserFunction)
663 char szFieldName[] = "Array of Simple Type";
664 proto_item *ti = proto_tree_add_text(tree, tvb, 0, -1, "%s", szFieldName);
665 proto_tree *subtree = proto_item_add_subtree(ti, ett_opcua_array);
669 /* read array length */
670 iLen = tvb_get_letohl(tvb, *pOffset);
671 proto_tree_add_item(subtree, hf_opcua_ArraySize, tvb, *pOffset, 4, TRUE);
674 if (iLen == -1) return; /* no array */
675 if (iLen == 0) return; /* array with zero elements*/
677 for (i=0; i<iLen; i++)
679 (*pParserFunction)(subtree, tvb, pOffset, hfIndex);
683 /** General parsing function for arrays of enums.
684 * All arrays have one 4 byte signed integer length information,
685 * followed by n data elements.
687 void parseArrayEnum(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, fctEnumParser pParserFunction)
689 char szFieldName[] = "Array of Enum Type";
690 proto_item *ti = proto_tree_add_text(tree, tvb, 0, -1, "%s", szFieldName);
691 proto_tree *subtree = proto_item_add_subtree(ti, ett_opcua_array);
695 /* read array length */
696 iLen = tvb_get_letohl(tvb, *pOffset);
697 proto_tree_add_item(subtree, hf_opcua_ArraySize, tvb, *pOffset, 4, TRUE);
700 if (iLen == -1) return; /* no array */
701 if (iLen == 0) return; /* array with zero elements*/
703 for (i=0; i<iLen; i++)
705 (*pParserFunction)(subtree, tvb, pOffset);
709 /** General parsing function for arrays of complex types.
710 * All arrays have one 4 byte signed integer length information,
711 * followed by n data elements.
713 void parseArrayComplex(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, char *szFieldName, fctComplexTypeParser pParserFunction)
715 proto_item *ti = proto_tree_add_text(tree, tvb, 0, -1, "Array of %s", szFieldName);
716 proto_tree *subtree = proto_item_add_subtree(ti, ett_opcua_array);
720 /* read array length */
721 iLen = tvb_get_letohl(tvb, *pOffset);
722 proto_tree_add_item(subtree, hf_opcua_ArraySize, tvb, *pOffset, 4, TRUE);
725 if (iLen == -1) return; /* no array */
726 if (iLen == 0) return; /* array with zero elements*/
728 for (i=0; i<iLen; i++)
731 g_snprintf(szNum, 20, "[%i]", i);
732 (*pParserFunction)(subtree, tvb, pOffset, szNum);
736 void parseNodeId(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, char *szFieldName)
738 proto_item *ti = proto_tree_add_text(tree, tvb, 0, -1, "%s: NodeId", szFieldName);
739 proto_tree *subtree = proto_item_add_subtree(ti, ett_opcua_nodeid);
740 gint iOffset = *pOffset;
743 EncodingMask = tvb_get_guint8(tvb, iOffset);
744 proto_tree_add_item(subtree, hf_opcua_nodeid_encodingmask, tvb, iOffset, 1, TRUE);
749 case 0x00: /* two byte node id */
750 proto_tree_add_item(subtree, hf_opcua_nodeid_numeric, tvb, iOffset, 1, TRUE);
753 case 0x01: /* four byte node id */
754 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 1, TRUE);
756 proto_tree_add_item(subtree, hf_opcua_nodeid_numeric, tvb, iOffset, 2, TRUE);
759 case 0x02: /* numeric, that does not fit into four bytes */
760 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 2, TRUE);
762 proto_tree_add_item(subtree, hf_opcua_nodeid_numeric, tvb, iOffset, 4, TRUE);
765 case 0x03: /* string */
766 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 2, TRUE);
768 parseString(subtree, tvb, &iOffset, hf_opcua_String);
770 case 0x04: /* guid */
771 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 2, TRUE);
773 parseGuid(subtree, tvb, &iOffset, hf_opcua_Guid);
775 case 0x05: /* byte string */
776 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 4, TRUE);
778 parseByteString(subtree, tvb, &iOffset, hf_opcua_ByteString);
785 void parseExtensionObject(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, char *szFieldName)
787 gint iOffset = *pOffset;
789 proto_tree *extobj_tree;
790 proto_tree *mask_tree;
793 /* add extension object subtree */
794 ti = proto_tree_add_text(tree, tvb, 0, -1, "%s : ExtensionObject", szFieldName);
795 extobj_tree = proto_item_add_subtree(ti, ett_opcua_extensionobject);
797 /* add nodeid subtree */
798 parseExpandedNodeId(extobj_tree, tvb, &iOffset, "TypeId");
800 /* parse encoding mask */
801 EncodingMask = tvb_get_guint8(tvb, iOffset);
802 ti = proto_tree_add_text(extobj_tree, tvb, 0, -1, "EncodingMask");
803 mask_tree = proto_item_add_subtree(ti, ett_opcua_extobj_encodingmask);
804 proto_tree_add_item(mask_tree, hf_opcua_extobj_mask_binbodyflag, tvb, iOffset, 1, TRUE);
805 proto_tree_add_item(mask_tree, hf_opcua_extobj_mask_xmlbodyflag, tvb, iOffset, 1, TRUE);
808 if (EncodingMask & EXTOBJ_ENCODINGMASK_BINBODY_FLAG) /* has binary body ? */
810 parseByteString(extobj_tree, tvb, &iOffset, hf_opcua_ByteString);
816 void parseExpandedNodeId(proto_tree *tree, tvbuff_t *tvb, gint *pOffset, char *szFieldName)
818 proto_item *ti = proto_tree_add_text(tree, tvb, 0, -1, "%s: ExpandedNodeId", szFieldName);
819 proto_tree *subtree = proto_item_add_subtree(ti, ett_opcua_nodeid);
820 gint iOffset = *pOffset;
823 EncodingMask = tvb_get_guint8(tvb, iOffset);
824 proto_tree_add_item(subtree, hf_opcua_nodeid_encodingmask, tvb, iOffset, 1, TRUE);
829 case 0x00: /* two byte node id */
830 proto_tree_add_item(subtree, hf_opcua_nodeid_numeric, tvb, iOffset, 1, TRUE);
833 case 0x01: /* four byte node id */
834 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 1, TRUE);
836 proto_tree_add_item(subtree, hf_opcua_nodeid_numeric, tvb, iOffset, 2, TRUE);
839 case 0x02: /* numeric, that does not fit into four bytes */
840 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 2, TRUE);
842 proto_tree_add_item(subtree, hf_opcua_nodeid_numeric, tvb, iOffset, 4, TRUE);
845 case 0x03: /* string */
846 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 2, TRUE);
848 parseString(subtree, tvb, &iOffset, hf_opcua_String);
850 case 0x04: /* guid */
851 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 2, TRUE);
853 parseGuid(subtree, tvb, &iOffset, hf_opcua_Guid);
855 case 0x05: /* byte string */
856 proto_tree_add_item(subtree, hf_opcua_nodeid_nsid, tvb, iOffset, 2, TRUE);
858 parseByteString(subtree, tvb, &iOffset, hf_opcua_ByteString);
862 if (EncodingMask & NODEID_URIMASK)
864 parseString(subtree, tvb, &iOffset, hf_opcua_Uri);
866 if (EncodingMask & NODEID_SERVERINDEXFLAG)
868 parseUInt32(subtree, tvb, &iOffset, hf_opcua_ServerIndex);