From Gerhard Gappmeier via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5429 :
[obnox/wireshark/wip.git] / plugins / opcua / opcua.c
1 /******************************************************************************
2 ** $Id$
3 **
4 ** Copyright (C) 2006-2007 ascolab GmbH. All Rights Reserved.
5 ** Web: http://www.ascolab.com
6 ** 
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.
11 ** 
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.
14 ** 
15 ** Project: OpcUa Wireshark Plugin
16 **
17 ** Description: OpcUa Protocol Decoder.
18 **
19 ** Author: Gerhard Gappmeier <gerhard.gappmeier@ascolab.com>
20 ** Last change by: $Author: gergap $
21 **
22 ******************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <glib.h>
29 #include <epan/prefs.h>
30 #include <epan/packet.h>
31 #include <epan/dissectors/packet-tcp.h>
32 #include "opcua_transport_layer.h"
33 #include "opcua_security_layer.h"
34 #include "opcua_application_layer.h"
35 #include "opcua_complextypeparser.h"
36 #include "opcua_serviceparser.h"
37 #include "opcua_enumparser.h"
38 #include "opcua_simpletypes.h"
39 #include "opcua_hfindeces.h"
40
41 extern const value_string g_requesttypes[];
42 extern const int g_NumServices;
43
44 /* forward reference */
45 static void dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
46 static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
47 void proto_reg_handoff_opcua(void);
48
49 /* declare parse function pointer */
50 typedef int (*FctParse)(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
51
52 static int proto_opcua = -1;
53 static dissector_handle_t opcua_handle;
54 static range_t *global_tcp_ports_opcua;
55 /** Official IANA registered port for OPC UA Binary Protocol. */
56 #define OPCUA_PORT 4840
57
58 /** subtree types */
59 gint ett_opcua_transport = -1;
60 gint ett_opcua_extensionobject = -1;
61 gint ett_opcua_nodeid = -1;
62
63 /** OpcUa Transport Message Types */
64 enum MessageType
65 {
66     MSG_HELLO = 0,
67     MSG_ACKNOWLEDGE,
68     MSG_ERROR,
69     MSG_MESSAGE,
70     MSG_OPENSECURECHANNEL,
71     MSG_CLOSESECURECHANNEL,
72     MSG_INVALID
73 };
74
75 /** OpcUa Transport Message Type Names */
76 static char* g_szMessageTypes[] =
77 {
78     "Hello message",
79     "Acknowledge message",
80     "Error message",
81     "UA Secure Conversation Message",
82     "OpenSecureChannel message",
83     "CloseSecureChannel message",
84     "Invalid message"
85 };
86
87
88 /** Setup protocol subtree array */
89 static gint *ett[] =
90 {
91     &ett_opcua_transport,
92     &ett_opcua_extensionobject,
93     &ett_opcua_nodeid,
94 };
95
96 /** plugin entry functions.
97  * This registers the OpcUa protocol.
98  */
99 void proto_register_opcua(void)
100 {
101     module_t *opcua_module;
102
103     proto_opcua = proto_register_protocol(
104         "OpcUa Binary Protocol", /* name */
105         "OpcUa",                 /* short name */
106         "opcua"                  /* abbrev */
107         );
108   
109     registerTransportLayerTypes(proto_opcua);
110     registerSecurityLayerTypes(proto_opcua);
111     registerApplicationLayerTypes(proto_opcua);
112     registerSimpleTypes(proto_opcua);
113     registerEnumTypes(proto_opcua);
114     registerComplexTypes();
115     registerServiceTypes();
116     registerFieldTypes(proto_opcua);
117
118     proto_register_subtree_array(ett, array_length(ett));    
119
120     range_convert_str(&global_tcp_ports_opcua, ep_strdup_printf("%u", OPCUA_PORT),  65535);
121
122     /* register user preferences */
123     opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua);
124     prefs_register_range_preference(opcua_module, "tcp_ports",
125                                  "OPC UA TCP Ports",
126                                  "The TCP ports for the OPC UA TCP Binary Protocol",
127                                  &global_tcp_ports_opcua, 65535);
128 }
129
130 /** header length that is needed to compute
131   * the pdu length.
132   * @see get_opcua_message_len
133   */
134 #define FRAME_HEADER_LEN 8
135
136 /** returns the length of an OpcUa message.
137   * This function reads the length information from
138   * the transport header.
139   */
140 static guint get_opcua_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
141 {
142     gint32 plen;
143
144     /* the message length starts at offset 4 */
145     plen = tvb_get_letohl(tvb, offset + 4);
146
147     return plen;
148 }
149
150 /** The main OpcUa dissector functions.
151   * It uses tcp_dissect_pdus from packet-tcp.h
152   * to reassemble the TCP data.
153   */
154 static void dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
155 {
156     tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
157       get_opcua_message_len, dissect_opcua_message);
158 }
159
160 /** The OpcUa message dissector.
161   * This method dissects full OpcUa messages.
162   * It gets only called with reassembled data
163   * from tcp_dissect_pdus.
164   */
165 static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
166 {
167     FctParse pfctParse = NULL;
168     enum MessageType msgtype = MSG_INVALID;
169
170     col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpcUa");
171
172     /* parse message type */
173     if (tvb_memeql(tvb, 0, "HEL", 3) == 0)
174     {
175         msgtype = MSG_HELLO;
176         pfctParse = parseHello;
177     }
178     else if (tvb_memeql(tvb, 0, "ACK", 3) == 0)
179     {
180         msgtype = MSG_ACKNOWLEDGE;
181         pfctParse = parseAcknowledge;
182     }
183     else if (tvb_memeql(tvb, 0, "ERR", 3) == 0)
184     {
185         msgtype = MSG_ERROR;
186         pfctParse = parseError;
187     }
188     else if (tvb_memeql(tvb, 0, "MSG", 3) == 0)
189     {
190         msgtype = MSG_MESSAGE;
191         pfctParse = parseMessage;
192     }
193     else if (tvb_memeql(tvb, 0, "OPN", 3) == 0)
194     {
195         msgtype = MSG_OPENSECURECHANNEL;
196         pfctParse = parseOpenSecureChannel;
197     }
198     else if (tvb_memeql(tvb, 0, "CLO", 3) == 0)
199     {
200         msgtype = MSG_CLOSESECURECHANNEL;
201         pfctParse = parseCloseSecureChannel;
202     }
203     else
204     {
205         msgtype = MSG_INVALID;
206     }
207     
208     /* Clear out stuff in the info column */
209     col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
210
211     if (tree && pfctParse)
212     {
213         gint offset = 0;
214         int iServiceId = -1;
215
216         /* we are being asked for details */
217         proto_item *ti = NULL;
218         proto_tree *transport_tree = NULL;
219
220         ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, FALSE);
221         transport_tree = proto_item_add_subtree(ti, ett_opcua_transport);
222
223         /* call the transport message dissector */
224         iServiceId = (*pfctParse)(transport_tree, tvb, &offset);
225
226         /* display the service type in addition to the message type */
227         if (iServiceId != -1)
228         {
229             int index = 0;
230             while (index < g_NumServices)
231             {
232                 if (g_requesttypes[index].value == (guint32)iServiceId)
233                 {
234                     col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", g_szMessageTypes[msgtype], g_requesttypes[index].strptr);
235                     break;
236                 }
237                 index++;
238             }
239         }
240     }
241 }
242
243 static void register_tcp_port(guint32 port)
244 {
245   if (port != 0)
246     dissector_add("tcp.port", port, opcua_handle);
247 }
248
249 static void unregister_tcp_port(guint32 port)
250 {
251   if (port != 0)
252     dissector_delete("tcp.port", port, opcua_handle);
253 }
254
255 void proto_reg_handoff_opcua(void)
256 {
257   static gboolean opcua_initialized = FALSE;
258   static range_t *tcp_ports_opcua  = NULL;
259
260   if(!opcua_initialized)
261   {
262     opcua_handle = create_dissector_handle(dissect_opcua, proto_opcua);
263     opcua_initialized = TRUE;
264   } 
265   else 
266   {     
267     /* clean up ports and their lists */
268     if (tcp_ports_opcua != NULL)
269     {
270       range_foreach(tcp_ports_opcua, unregister_tcp_port);
271       g_free(tcp_ports_opcua);
272     }
273   }
274
275   /* If we now have a PDU tree, register for the port or ports we have */
276   tcp_ports_opcua = range_copy(global_tcp_ports_opcua);
277   range_foreach(tcp_ports_opcua, register_tcp_port);
278 }
279
280