269fe329a57dfa30d2c85a0f76d36c74959b28f5
[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 /* forward reference */
42 static void dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
43 static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
44 void proto_reg_handoff_opcua(void);
45
46 /* declare parse function pointer */
47 typedef void (*FctParse)(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
48
49 static int proto_opcua = -1;
50 static dissector_handle_t opcua_handle;
51 static range_t *global_tcp_ports_opcua;
52 /** Official IANA registered port for OPC UA Binary Protocol. */
53 #define OPCUA_PORT 4840
54
55 /** subtree types */
56 gint ett_opcua_transport = -1;
57 gint ett_opcua_extensionobject = -1;
58 gint ett_opcua_nodeid = -1;
59
60 /** OpcUa Transport Message Types */
61 enum MessageType
62 {
63     MSG_HELLO = 0,
64     MSG_ACKNOWLEDGE,
65     MSG_ERROR,
66     MSG_MESSAGE,
67     MSG_OPENSECURECHANNEL,
68     MSG_CLOSESECURECHANNEL,
69     MSG_INVALID
70 };
71
72 /** OpcUa Transport Message Type Names */
73 static char* g_szMessageTypes[] =
74 {
75     "Hello message",
76     "Acknowledge message",
77     "Error message",
78     "UA Secure Conversation Message",
79     "OpenSecureChannel message",
80     "CloseSecureChannel message",
81     "Invalid message"
82 };
83
84
85 /** Setup protocol subtree array */
86 static gint *ett[] =
87 {
88     &ett_opcua_transport,
89     &ett_opcua_extensionobject,
90     &ett_opcua_nodeid,
91 };
92
93 /** plugin entry functions.
94  * This registers the OpcUa protocol.
95  */
96 void proto_register_opcua(void)
97 {
98     module_t *opcua_module;
99
100     proto_opcua = proto_register_protocol(
101         "OpcUa Binary Protocol", /* name */
102         "OpcUa",                 /* short name */
103         "opcua"                  /* abbrev */
104         );
105   
106     registerTransportLayerTypes(proto_opcua);
107     registerSecurityLayerTypes(proto_opcua);
108     registerApplicationLayerTypes(proto_opcua);
109     registerSimpleTypes(proto_opcua);
110     registerEnumTypes(proto_opcua);
111     registerComplexTypes();
112     registerServiceTypes();
113     registerFieldTypes(proto_opcua);
114
115     proto_register_subtree_array(ett, array_length(ett));    
116
117     range_convert_str(&global_tcp_ports_opcua, ep_strdup_printf("%u", OPCUA_PORT),  65535);
118
119     /* register user preferences */
120     opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua);
121     prefs_register_range_preference(opcua_module, "tcp_ports",
122                                  "OPC UA TCP Ports",
123                                  "The TCP ports for the OPC UA TCP Binary Protocol",
124                                  &global_tcp_ports_opcua, 65535);
125 }
126
127 /** header length that is needed to compute
128   * the pdu length.
129   * @see get_opcua_message_len
130   */
131 #define FRAME_HEADER_LEN 8
132
133 /** returns the length of an OpcUa message.
134   * This function reads the length information from
135   * the transport header.
136   */
137 static guint get_opcua_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
138 {
139     gint32 plen;
140
141     /* the message length starts at offset 4 */
142     plen = tvb_get_letohl(tvb, offset + 4);
143
144     return plen;
145 }
146
147 /** The main OpcUa dissector functions.
148   * It uses tcp_dissect_pdus from packet-tcp.h
149   * to reassemble the TCP data.
150   */
151 static void dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
152 {
153     tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
154       get_opcua_message_len, dissect_opcua_message);
155 }
156
157 /** The OpcUa message dissector.
158   * This method dissects full OpcUa messages.
159   * It gets only called with reassembled data
160   * from tcp_dissect_pdus.
161   */
162 static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
163 {
164     FctParse pfctParse = NULL;
165     enum MessageType msgtype = MSG_INVALID;
166
167     if (check_col(pinfo->cinfo, COL_PROTOCOL))
168     {
169         col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpcUa");
170     }
171
172     /* parse message type */
173     if (tvb->real_data[0] == 'H' && tvb->real_data[1] == 'E' && tvb->real_data[2] == 'L')
174     {
175         msgtype = MSG_HELLO;
176         pfctParse = parseHello;
177     }
178     else if (tvb->real_data[0] == 'A' && tvb->real_data[1] == 'C' && tvb->real_data[2] == 'K')
179     {
180         msgtype = MSG_ACKNOWLEDGE;
181         pfctParse = parseAcknowledge;
182     }
183     else if (tvb->real_data[0] == 'E' && tvb->real_data[1] == 'R' && tvb->real_data[2] == 'R')
184     {
185         msgtype = MSG_ERROR;
186         pfctParse = parseError;
187     }
188     else if (tvb->real_data[0] == 'M' && tvb->real_data[1] == 'S' && tvb->real_data[2] == 'G')
189     {
190         msgtype = MSG_MESSAGE;
191         pfctParse = parseMessage;
192     }
193     else if (tvb->real_data[0] == 'O' && tvb->real_data[1] == 'P' && tvb->real_data[2] == 'N')
194     {
195         msgtype = MSG_OPENSECURECHANNEL;
196         pfctParse = parseOpenSecureChannel;
197     }
198     else if (tvb->real_data[0] == 'C' && tvb->real_data[1] == 'L' && tvb->real_data[2] == 'O')
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     if(check_col(pinfo->cinfo, COL_INFO))
210     {
211         col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
212     }
213
214     if (tree && pfctParse)
215     {
216         gint offset = 0;
217
218         /* we are being asked for details */
219         proto_item *ti = NULL;
220         proto_tree *transport_tree = NULL;
221
222         ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, FALSE);
223         transport_tree = proto_item_add_subtree(ti, ett_opcua_transport);
224
225         /* call the transport message dissector */
226         (*pfctParse)(transport_tree, tvb, &offset);
227     }
228 }
229
230 static void register_tcp_port(guint32 port)
231 {
232   if (port != 0)
233     dissector_add("tcp.port", port, opcua_handle);
234 }
235
236 static void unregister_tcp_port(guint32 port)
237 {
238   if (port != 0)
239     dissector_delete("tcp.port", port, opcua_handle);
240 }
241
242 void proto_reg_handoff_opcua(void)
243 {
244   static gboolean opcua_initialized = FALSE;
245   static range_t *tcp_ports_opcua  = NULL;
246
247   if(!opcua_initialized)
248   {
249     opcua_handle = create_dissector_handle(dissect_opcua, proto_opcua);
250     opcua_initialized = TRUE;
251   } 
252   else 
253   {     
254     /* clean up ports and their lists */
255     if (tcp_ports_opcua != NULL)
256     {
257       range_foreach(tcp_ports_opcua, unregister_tcp_port);
258       g_free(tcp_ports_opcua);
259     }
260   }
261
262   /* If we now have a PDU tree, register for the port or ports we have */
263   tcp_ports_opcua = range_copy(global_tcp_ports_opcua);
264   range_foreach(tcp_ports_opcua, register_tcp_port);
265 }
266
267