wireshark/plugins/opcua/opcua.c

258 lines
6.9 KiB
C

/******************************************************************************
** $Id$
**
** Copyright (C) 2006-2007 ascolab GmbH. All Rights Reserved.
** Web: http://www.ascolab.com
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** Project: OpcUa Wireshark Plugin
**
** Description: OpcUa Protocol Decoder.
**
** Author: Gerhard Gappmeier <gerhard.gappmeier@ascolab.com>
** Last change by: $Author: gergap $
**
******************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <glib.h>
#include <epan/packet.h>
#include <epan/emem.h>
#include <epan/dissectors/packet-tcp.h>
#include <epan/prefs.h>
#include "opcua_transport_layer.h"
#include "opcua_security_layer.h"
#include "opcua_application_layer.h"
#include "opcua_complextypeparser.h"
#include "opcua_serviceparser.h"
#include "opcua_enumparser.h"
#include "opcua_simpletypes.h"
#include "opcua_hfindeces.h"
/* forward reference */
void proto_register_opcua (void);
void proto_reg_handoff_opcua (void);
static void dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
/* declare parse function pointer */
typedef void (*FctParse)(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
static int proto_opcua = -1;
/** Official IANA registered port for OPC UA Binary Protocol. */
static int global_opcua_port = 4840;
static dissector_handle_t opcua_handle;
/** subtree types */
gint ett_opcua_transport = -1;
gint ett_opcua_extensionobject = -1;
gint ett_opcua_nodeid = -1;
/** OpcUa Transport Message Types */
enum MessageType
{
MSG_HELLO = 0,
MSG_ACKNOWLEDGE,
MSG_DISCONNECT,
MSG_DATA_LAST_CHUNK,
MSG_DATA,
MSG_ABORT,
MSG_ERROR,
MSG_INVALID,
MSG_UNKNOWN
};
/** OpcUa Transport Message Type Names */
static char* g_szMessageTypes[] =
{
"Hello message",
"Acknowledge message",
"Disconnect message",
"Data message, last chunk in message.",
"Data message, further chunks must follow.",
"Abort message",
"Error message",
"Invalid message",
"Unknown message"
};
/** Setup protocol subtree array */
static gint *ett[] =
{
&ett_opcua_transport,
&ett_opcua_extensionobject,
&ett_opcua_nodeid,
};
/** plugin entry functions.
* This registers the OpcUa protocol.
*/
void proto_register_opcua(void)
{
module_t *opcua_module;
if (proto_opcua == -1)
{
proto_opcua = proto_register_protocol(
"OpcUa Binary Protocol", /* name */
"OpcUa", /* short name */
"opcua" /* abbrev */
);
}
opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua);
registerTransportLayerTypes(proto_opcua);
registerSecurityLayerTypes(proto_opcua);
registerApplicationLayerTypes(proto_opcua);
registerSimpleTypes(proto_opcua);
registerEnumTypes(proto_opcua);
registerComplexTypes();
registerServiceTypes();
registerFieldTypes(proto_opcua);
proto_register_subtree_array(ett, array_length(ett));
}
/** Register sub protocol.
* For TCP port 4840.
*/
void proto_reg_handoff_opcua(void)
{
static int Initialized=FALSE;
if (!Initialized)
{
opcua_handle = create_dissector_handle(dissect_opcua, proto_opcua);
dissector_add("tcp.port", global_opcua_port, opcua_handle);
}
}
/** header length that is needed to compute
* the pdu length.
* @see get_opcua_message_len
*/
#define FRAME_HEADER_LEN 8
/** returns the length of an OpcUa message.
* This function reads the length information from
* the transport header.
*/
static guint get_opcua_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
{
gint32 plen;
/* the message length starts at offset 4 */
plen = tvb_get_letohl(tvb, offset + 4);
return plen;
}
/** The main OpcUa dissector functions.
* It uses tcp_dissect_pdus from packet-tcp.h
* to reassemble the TCP data.
*/
static void dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
get_opcua_message_len, dissect_opcua_message);
}
/** The OpcUa message dissector.
* This method dissects full OpcUa messages.
* It gets only called with reassembled data
* from tcp_dissect_pdus.
*/
static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
FctParse pfctParse = NULL;
enum MessageType msgtype = MSG_INVALID;
if (check_col(pinfo->cinfo, COL_PROTOCOL))
{
col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpcUa");
}
/* parse message type */
if (tvb->real_data[0] == 'U' && tvb->real_data[1] == 'A')
{
if (tvb->real_data[2] == 'T')
{
switch(tvb->real_data[3])
{
case 'H': msgtype = MSG_HELLO;
pfctParse = parseHello;
break;
case 'A': msgtype = MSG_ACKNOWLEDGE;
pfctParse = parseAcknowledge;
break;
case 'D': msgtype = MSG_DISCONNECT;
pfctParse = parseDisconnect;
break;
default: msgtype = MSG_INVALID;
break;
}
}
else if (tvb->real_data[2] == 'M')
{
switch(tvb->real_data[3])
{
case 'G': msgtype = MSG_DATA_LAST_CHUNK;
pfctParse = parseData;
break;
case 'C': msgtype = MSG_DATA;
pfctParse = parseData;
break;
case 'A': msgtype = MSG_ABORT;
pfctParse = parseAbort;
break;
case 'E': msgtype = MSG_ERROR;
pfctParse = parseError;
break;
default: msgtype = MSG_INVALID;
break;
}
}
}
else
{
msgtype = MSG_UNKNOWN;
}
/* Clear out stuff in the info column */
if(check_col(pinfo->cinfo, COL_INFO))
{
col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
}
if (tree && pfctParse)
{
gint offset = 0;
/* we are being asked for details */
proto_item *ti = NULL;
proto_tree *transport_tree = NULL;
ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, FALSE);
transport_tree = proto_item_add_subtree(ti, ett_opcua_transport);
/* call the transport message dissector */
(*pfctParse)(transport_tree, tvb, &offset);
}
}