From Gerhard Gappmeier:
[obnox/wireshark/wip.git] / plugins / opcua / opcua.c
index 0742b5137e221f4979fb1018c908c3b69ac56aaf..269fe329a57dfa30d2c85a0f76d36c74959b28f5 100644 (file)
@@ -26,6 +26,7 @@
 #endif
 
 #include <glib.h>
+#include <epan/prefs.h>
 #include <epan/packet.h>
 #include <epan/dissectors/packet-tcp.h>
 #include "opcua_transport_layer.h"
 /* forward reference */
 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);
+void proto_reg_handoff_opcua(void);
 
 /* declare parse function pointer */
 typedef void (*FctParse)(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
 
 static int proto_opcua = -1;
+static dissector_handle_t opcua_handle;
+static range_t *global_tcp_ports_opcua;
 /** Official IANA registered port for OPC UA Binary Protocol. */
 #define OPCUA_PORT 4840
 
@@ -58,13 +62,11 @@ enum MessageType
 {
     MSG_HELLO = 0,
     MSG_ACKNOWLEDGE,
-    MSG_DISCONNECT,
-    MSG_DATA_LAST_CHUNK,
-    MSG_DATA,
-    MSG_ABORT,
     MSG_ERROR,
-    MSG_INVALID,
-    MSG_UNKNOWN
+    MSG_MESSAGE,
+    MSG_OPENSECURECHANNEL,
+    MSG_CLOSESECURECHANNEL,
+    MSG_INVALID
 };
 
 /** OpcUa Transport Message Type Names */
@@ -72,13 +74,11 @@ 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"
+    "UA Secure Conversation Message",
+    "OpenSecureChannel message",
+    "CloseSecureChannel message",
+    "Invalid message"
 };
 
 
@@ -95,11 +95,14 @@ static gint *ett[] =
  */
 void proto_register_opcua(void)
 {
+    module_t *opcua_module;
+
     proto_opcua = proto_register_protocol(
         "OpcUa Binary Protocol", /* name */
         "OpcUa",                 /* short name */
         "opcua"                  /* abbrev */
         );
+  
     registerTransportLayerTypes(proto_opcua);
     registerSecurityLayerTypes(proto_opcua);
     registerApplicationLayerTypes(proto_opcua);
@@ -110,16 +113,15 @@ void proto_register_opcua(void)
     registerFieldTypes(proto_opcua);
 
     proto_register_subtree_array(ett, array_length(ett));    
-}
 
-/** Register sub protocol. 
-  * For TCP port 4840.
-  */
-void proto_reg_handoff_opcua(void)
-{
-    dissector_handle_t opcua_handle;
-    opcua_handle = create_dissector_handle(dissect_opcua, proto_opcua);
-    dissector_add("tcp.port", OPCUA_PORT, opcua_handle);
+    range_convert_str(&global_tcp_ports_opcua, ep_strdup_printf("%u", OPCUA_PORT),  65535);
+
+    /* register user preferences */
+    opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua);
+    prefs_register_range_preference(opcua_module, "tcp_ports",
+                                "OPC UA TCP Ports",
+                                "The TCP ports for the OPC UA TCP Binary Protocol",
+                                &global_tcp_ports_opcua, 65535);
 }
 
 /** header length that is needed to compute
@@ -168,51 +170,41 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree
     }
 
     /* parse message type */
-    if (tvb->real_data[0] == 'U' && tvb->real_data[1] == 'A')
+    if (tvb->real_data[0] == 'H' && tvb->real_data[1] == 'E' && tvb->real_data[2] == 'L')
+    {
+        msgtype = MSG_HELLO;
+        pfctParse = parseHello;
+    }
+    else if (tvb->real_data[0] == 'A' && tvb->real_data[1] == 'C' && tvb->real_data[2] == 'K')
+    {
+        msgtype = MSG_ACKNOWLEDGE;
+        pfctParse = parseAcknowledge;
+    }
+    else if (tvb->real_data[0] == 'E' && tvb->real_data[1] == 'R' && tvb->real_data[2] == 'R')
+    {
+        msgtype = MSG_ERROR;
+        pfctParse = parseError;
+    }
+    else if (tvb->real_data[0] == 'M' && tvb->real_data[1] == 'S' && tvb->real_data[2] == 'G')
+    {
+        msgtype = MSG_MESSAGE;
+        pfctParse = parseMessage;
+    }
+    else if (tvb->real_data[0] == 'O' && tvb->real_data[1] == 'P' && tvb->real_data[2] == 'N')
+    {
+        msgtype = MSG_OPENSECURECHANNEL;
+        pfctParse = parseOpenSecureChannel;
+    }
+    else if (tvb->real_data[0] == 'C' && tvb->real_data[1] == 'L' && tvb->real_data[2] == 'O')
     {
-        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;
-            }                
-        }
+        msgtype = MSG_CLOSESECURECHANNEL;
+        pfctParse = parseCloseSecureChannel;
     }
     else
     {
-        msgtype = MSG_UNKNOWN;
+        msgtype = MSG_INVALID;
     }
-
+    
     /* Clear out stuff in the info column */
     if(check_col(pinfo->cinfo, COL_INFO))
     {
@@ -232,10 +224,44 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree
 
         /* call the transport message dissector */
         (*pfctParse)(transport_tree, tvb, &offset);
-
     }
-}    
+}
+
+static void register_tcp_port(guint32 port)
+{
+  if (port != 0)
+    dissector_add("tcp.port", port, opcua_handle);
+}
+
+static void unregister_tcp_port(guint32 port)
+{
+  if (port != 0)
+    dissector_delete("tcp.port", port, opcua_handle);
+}
+
+void proto_reg_handoff_opcua(void)
+{
+  static gboolean opcua_initialized = FALSE;
+  static range_t *tcp_ports_opcua  = NULL;
 
+  if(!opcua_initialized)
+  {
+    opcua_handle = create_dissector_handle(dissect_opcua, proto_opcua);
+    opcua_initialized = TRUE;
+  } 
+  else 
+  {    
+    /* clean up ports and their lists */
+    if (tcp_ports_opcua != NULL)
+    {
+      range_foreach(tcp_ports_opcua, unregister_tcp_port);
+      g_free(tcp_ports_opcua);
+    }
+  }
 
+  /* If we now have a PDU tree, register for the port or ports we have */
+  tcp_ports_opcua = range_copy(global_tcp_ports_opcua);
+  range_foreach(tcp_ports_opcua, register_tcp_port);
+}