Make files more generic.
[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/packet.h>
30 #include <epan/emem.h>
31 #include <epan/dissectors/packet-tcp.h>
32 #include <epan/prefs.h>
33 #include "opcua_transport_layer.h"
34 #include "opcua_security_layer.h"
35 #include "opcua_application_layer.h"
36 #include "opcua_complextypeparser.h"
37 #include "opcua_serviceparser.h"
38 #include "opcua_enumparser.h"
39 #include "opcua_simpletypes.h"
40 #include "opcua_hfindeces.h"
41
42 /* forward reference */
43 void proto_register_opcua (void);
44 void proto_reg_handoff_opcua (void);
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
48 /* declare parse function pointer */
49 typedef void (*FctParse)(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
50
51 static int proto_opcua = -1;
52 /** Official IANA registered port for OPC UA Binary Protocol. */
53 static int global_opcua_port = 4840;
54 static dissector_handle_t opcua_handle;
55
56 /** subtree types */
57 gint ett_opcua_transport = -1;
58 gint ett_opcua_extensionobject = -1;
59 gint ett_opcua_nodeid = -1;
60
61 /** OpcUa Transport Message Types */
62 enum MessageType
63 {
64     MSG_HELLO = 0,
65     MSG_ACKNOWLEDGE,
66     MSG_DISCONNECT,
67     MSG_DATA_LAST_CHUNK,
68     MSG_DATA,
69     MSG_ABORT,
70     MSG_ERROR,
71     MSG_INVALID,
72     MSG_UNKNOWN
73 };
74
75 /** OpcUa Transport Message Type Names */
76 static char* g_szMessageTypes[] =
77 {
78     "Hello message",
79     "Acknowledge message",
80     "Disconnect message",
81     "Data message, last chunk in message.",
82     "Data message, further chunks must follow.",
83     "Abort message",
84     "Error message",
85     "Invalid message",
86     "Unknown message"
87 };
88
89
90 /** Setup protocol subtree array */
91 static gint *ett[] =
92 {
93     &ett_opcua_transport,
94     &ett_opcua_extensionobject,
95     &ett_opcua_nodeid,
96 };
97
98 /** plugin entry functions.
99  * This registers the OpcUa protocol.
100  */
101 void proto_register_opcua(void)
102 {
103     module_t *opcua_module;
104     
105     if (proto_opcua == -1)
106     {
107         proto_opcua = proto_register_protocol(
108             "OpcUa Binary Protocol", /* name */
109             "OpcUa",                 /* short name */
110             "opcua"                  /* abbrev */
111             );
112     }
113     opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua);
114     
115     registerTransportLayerTypes(proto_opcua);
116     registerSecurityLayerTypes(proto_opcua);
117     registerApplicationLayerTypes(proto_opcua);
118     registerSimpleTypes(proto_opcua);
119     registerEnumTypes(proto_opcua);
120     registerComplexTypes();
121     registerServiceTypes();
122     registerFieldTypes(proto_opcua);
123
124     proto_register_subtree_array(ett, array_length(ett));    
125 }
126
127 /** Register sub protocol. 
128   * For TCP port 4840.
129   */
130 void proto_reg_handoff_opcua(void)
131 {
132     static int Initialized=FALSE;
133     
134     if (!Initialized)
135     {
136         opcua_handle = create_dissector_handle(dissect_opcua, proto_opcua);
137         dissector_add("tcp.port", global_opcua_port, opcua_handle);
138     }
139 }
140
141 /** header length that is needed to compute
142   * the pdu length.
143   * @see get_opcua_message_len
144   */
145 #define FRAME_HEADER_LEN 8
146
147 /** returns the length of an OpcUa message.
148   * This function reads the length information from
149   * the transport header.
150   */
151 static guint get_opcua_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
152 {
153     gint32 plen;
154
155     /* the message length starts at offset 4 */
156     plen = tvb_get_letohl(tvb, offset + 4);
157
158     return plen;
159 }
160
161 /** The main OpcUa dissector functions.
162   * It uses tcp_dissect_pdus from packet-tcp.h
163   * to reassemble the TCP data.
164   */
165 static void dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
166 {
167     tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
168       get_opcua_message_len, dissect_opcua_message);
169 }
170
171 /** The OpcUa message dissector.
172   * This method dissects full OpcUa messages.
173   * It gets only called with reassembled data
174   * from tcp_dissect_pdus.
175   */
176 static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
177 {
178     FctParse pfctParse = NULL;
179     enum MessageType msgtype = MSG_INVALID;
180
181     if (check_col(pinfo->cinfo, COL_PROTOCOL))
182     {
183         col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpcUa");
184     }
185
186     /* parse message type */
187     if (tvb->real_data[0] == 'U' && tvb->real_data[1] == 'A')
188     {
189         if (tvb->real_data[2] == 'T')
190         {
191             switch(tvb->real_data[3])
192             {
193             case 'H': msgtype = MSG_HELLO;
194                 pfctParse = parseHello;
195                 break;
196             case 'A': msgtype = MSG_ACKNOWLEDGE;
197                 pfctParse = parseAcknowledge;
198                 break;
199             case 'D': msgtype = MSG_DISCONNECT;
200                 pfctParse = parseDisconnect;
201                 break;
202             default: msgtype = MSG_INVALID;
203                 break;
204             }                
205         }
206         else if (tvb->real_data[2] == 'M')
207         {
208             switch(tvb->real_data[3])
209             {
210             case 'G': msgtype = MSG_DATA_LAST_CHUNK;
211                 pfctParse = parseData;
212                 break;
213             case 'C': msgtype = MSG_DATA;
214                 pfctParse = parseData;
215                 break;
216             case 'A': msgtype = MSG_ABORT;
217                 pfctParse = parseAbort;
218                 break;
219             case 'E': msgtype = MSG_ERROR;
220                 pfctParse = parseError;
221                 break;
222             default: msgtype = MSG_INVALID;
223                 break;
224             }                
225         }
226     }
227     else
228     {
229         msgtype = MSG_UNKNOWN;
230     }
231
232     /* Clear out stuff in the info column */
233     if(check_col(pinfo->cinfo, COL_INFO))
234     {
235         col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
236     }
237
238     if (tree && pfctParse)
239     {
240         gint offset = 0;
241
242         /* we are being asked for details */
243         proto_item *ti = NULL;
244         proto_tree *transport_tree = NULL;
245
246         ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, FALSE);
247         transport_tree = proto_item_add_subtree(ti, ett_opcua_transport);
248
249         /* call the transport message dissector */
250         (*pfctParse)(transport_tree, tvb, &offset);
251
252     }
253 }    
254
255
256
257