summaryrefslogtreecommitdiff
path: root/epan/dissectors
diff options
context:
space:
mode:
authorAnders Broman <anders.broman@ericsson.com>2008-07-25 13:36:58 +0000
committerAnders Broman <anders.broman@ericsson.com>2008-07-25 13:36:58 +0000
commite9a6106af594105bb88eef364ff8f17408aaab8a (patch)
treee2eadf97ab6e45e7963ffcf3448ce2b3e321f396 /epan/dissectors
parent4a233c45a73bbc9a0965fe6e353b968fb2084e9b (diff)
downloadwireshark-e9a6106af594105bb88eef364ff8f17408aaab8a.tar.gz
From David Aggeler:
multiple PDV per PDU support - Support multiple PDVs per PDU - Better summary, in PDV, PDU header and in INFO Column, e.g. show commands like C-STORE - Fixed Association Reject (was working before my changes) - Fixed PDV Continuation with very small packets. Reduced minimum packet length from 10 to 2 Bytes for PDU Type 4 - Fixed PDV Continuation. Last packet was not found correctly. - Fixed complilation warning (build 56 on solaris) - Fixed tree expansion (hf_dcm_xxx) - Added expert_add_info() for Assoctiation Reject - Added expert_add_info() for Assoctiation Abort - Added expert_add_info() for short PDVs (i.e. last fragment, but PDV is not completed yet) - Clarified and grouped data structures and its related code (dcmItem, dcmState) to have consistent _new() & _get() functions and to be be according to coding conventions - Added more function declaration to be more consistent - All dissect_dcm_xx now have (almost) the same parameter order - Removed DISSECTOR_ASSERT() for packet data errors. Not designed to handle this. - Handle multiple DICOM Associations in a capture correctly, i.e. if presentation contexts are different. svn path=/trunk/; revision=25824
Diffstat (limited to 'epan/dissectors')
-rw-r--r--epan/dissectors/packet-dcm.c1857
-rw-r--r--epan/dissectors/packet-dcm.h10
2 files changed, 1369 insertions, 498 deletions
diff --git a/epan/dissectors/packet-dcm.c b/epan/dissectors/packet-dcm.c
index dd2b875ecd..5cf6926f59 100644
--- a/epan/dissectors/packet-dcm.c
+++ b/epan/dissectors/packet-dcm.c
@@ -4,7 +4,7 @@
* Copyright 2008, David Aggeler <david_aggeler@hispeed.ch>
*
* DICOM communication protocol
- * http://medical.nema.org/dicom/2003.html
+ * http://medical.nema.org/dicom/2008
* DICOM Part 8: Network Communication Support for Message Exchange
*
* (NOTE: you need to turn on 'Allow subdissector to desegment TCP streams'
@@ -58,11 +58,11 @@
*
* - The 'value to string' routines should probably be hash lookups.
*
- * 9 Nov 2004
+ * 9 Nov 2004, Rich Coe
* - Fixed the heuristic code -- sometimes a conversation already exists
* - Fixed the dissect code to display all the tags in the pdu
*
- * 28 Apr 2005
+ * 28 Apr 2005, Rich Coe
* - fix memory leak when Assoc packet is processed repeatedly in wireshark
*
* - removed unused partial packet flag
@@ -86,10 +86,8 @@
* - added framework for reporting DICOM async negotiation (not finished)
* (I'm not aware of an implementation which currently supports this)
*
- * - still need to fix display of continuation packets
*
- *
- * 23 May 2008 David Aggeler
+ * May 23 2008, David Aggeler
*
* - Added Class UID lookup, both in the association and in the transfer
* - Better hierarchy for items in Association request/response and therefore better overview
@@ -113,19 +111,44 @@
* already be seen in the packet and decimal is easier for length calculation
* in respect to TCP
*
- * - Still to do
- * Support multiple PDV per PDU
- * Support almost all tags (prepared)
+ * Jun 17 2008, David Aggeler
+ *
+ * - Support multiple PDVs per PDU
+ * - Better summary, in PDV, PDU header and in INFO Column, e.g. show commands like C-STORE
+ * - Fixed Association Reject (was working before my changes)
+ * - Fixed PDV Continuation with very small packets. Reduced minimum packet length
+ * from 10 to 2 Bytes for PDU Type 4
+ * - Fixed PDV Continuation. Last packet was not found correctly.
+ * - Fixed complilation warning (build 56 on solaris)
+ * - Fixed tree expansion (hf_dcm_xxx)
+ * - Added expert_add_info() for Assoctiation Reject
+ * - Added expert_add_info() for Assoctiation Abort
+ * - Added expert_add_info() for short PDVs (i.e. last fragment, but PDV is not completed yet)
+ * - Clarified and grouped data structures and its related code (dcmItem, dcmState) to have
+ * consistent _new() & _get() functions and to be be according to coding conventions
+ * - Added more function declaration to be more consistent
+ * - All dissect_dcm_xx now have (almost) the same parameter order
+ * - Removed DISSECTOR_ASSERT() for packet data errors. Not designed to handle this.
+ * - Handle multiple DICOM Associations in a capture correctly, i.e. if presentation contexts are different.
+ *
+ * Jul 17 2008, David Aggeler
+ *
+ * - Export objects as part 10 compliant DICOM file. Finally, this major milestone has beed reached.
+ * - PDVs are now a child of the PCTX rather than the ASSOC object.
+ * - Fixed PDV continuation for unknown tags (e.g. RT Structure Set)
+ * - Replaced proprietary trim() with g_strstrip()
+ * - Fixed strings that are displayed with /000 (padding of odd length)
+ * - Added expert_add_info() for invalid flags and presentation context IDs
+ *
+ * ****************************************************************************************
+ * - Still ToDo
+ * Decent error handlung for expert_add_info(), i.e. return value handling and info column text
+ * Support almost all tags
+ * Show tags as subtree
+ * Show Association Headers as individual items
* Cleanup types of offset & position
- * Cleanup hf_dcm_xx to make tree expansion more logical
- * Cleanup data structures
* Create subtrees for sequences
* Support item 56-59 in Accociation Request
- * More length checks
- * Handle multiple dicom associations in a capture correctly, if presentation contexts are different.
- * Save content as dicom file
- * Fix string that are displayed with /000
- * Add Command (C-STRORE) ... to Info Column
*/
#include <stdio.h>
@@ -142,6 +165,8 @@
#include <epan/emem.h>
#include <epan/strutil.h>
#include <epan/conversation.h>
+#include <epan/expert.h>
+#include <epan/tap.h>
#include "packet-tcp.h"
@@ -149,13 +174,23 @@
#define DICOM_DEFAULT_RANGE "104"
+/* Many thanks to http://medicalconnections.co.uk/ for the GUID */
+#define WIRESHARK_IMPLEMENTATION_UID "1.2.826.0.1.3680043.8.427.10"
+#define WIRESHARK_MEDIA_STORAGE_SOP_CLASS_UID "1.2.826.0.1.3680043.8.427.11.1"
+#define WIRESHARK_MEDIA_STORAGE_SOP_INSTANCE_UID_PREFIX "1.2.826.0.1.3680043.8.427.11.2"
+#define WIRESHARK_IMPLEMENTATION_VERSION "WIRESHARK"
+
static range_t *global_dcm_tcp_range = NULL;
static range_t *global_dcm_tcp_range_backup = NULL; /* needed to deregister */
static gboolean global_dcm_heuristic = FALSE;
+static gboolean global_dcm_header = TRUE;
/* Initialize the protocol and registered fields */
static int proto_dcm = -1;
+
+static int dicom_eo_tap = -1;
+
static int hf_dcm_pdu = -1,
hf_dcm_pdu_len = -1,
hf_dcm_pdu_type = -1,
@@ -175,12 +210,19 @@ static int hf_dcm_pdu = -1,
hf_dcm_data_tag = -1;
/* Initialize the subtree pointers */
-static gint ett_dcm = -1,
+static gint
+ ett_dcm = -1,
ett_assoc = -1,
- ett_assoc_item = -1,
+ ett_assoc_actx = -1,
ett_assoc_pctx = -1,
- ett_assoc_userinfo = -1,
- ett_dcm_data = -1;
+ ett_assoc_pctx_abss = -1,
+ ett_assoc_pctx_xfer = -1,
+ ett_assoc_info = -1,
+ ett_assoc_info_uid = -1,
+ ett_assoc_info_version = -1,
+ ett_dcm_data = -1,
+ ett_dcm_data_pdv = -1,
+ ett_dcm_data_tag = -1;
static dissector_handle_t dcm_handle;
@@ -206,25 +248,55 @@ static const value_string dcm_assoc_item_type[] = {
{ 0x50, "User Info" },
{ 0x51, "Max Length" },
{ 0x52, "Implementation Class UID" },
+ { 0x55, "Implementation Version" },
{ 0, NULL }
};
-static const value_string dcm_pctx_result[] = {
- { 0, "Accept" },
- { 1, "User Reject" },
- { 2, "No Reason" },
- { 3, "Abstract Syntax Unsupported" },
- { 4, "Transfer Syntax Unsupported" }
- { 0, NULL }
-};
+/*
+ Per Data PDV store data needed, to allow decoding of tags longer than a PDV
+*/
+typedef struct dcm_state_pdv {
+
+ struct dcm_state_pdv *next, *prev;
+
+ gboolean initalized; /* define, wheter open_tag_len, open_tag_rlen and open_tag_desc have been set */
+ guint32 packet_no; /* Wireshark packet number, where pdv starts */
+ guint32 offset; /* Offset in packet, where PDV header starts */
+
+ gchar *desc; /* PDV description. se_alloc() */
+
+ guint8 pctx_id; /* Reference to used Presentation Context */
+
+ /* Used and filled for Export Object only */
+ gpointer data; /* Copy of PDV data without any PDU/PDV header */
+ guint32 data_len; /* Length of this PDV buffer. If >0, memory has been alocated */
+ gchar *sop_class_uid; /* SOP Class UID. Set in 1st PDV of a DICOM object. se_alloc() */
+ gchar *sop_instance_uid; /* SOP Instance UID. Set in 1st PDV of a DICOM object. se_alloc() */
+ /* End Export use */
+
+ gboolean is_storage; /* Ture, if the Data PDV is on the context of a storage SOP Class */
+ gboolean is_flagvalid; /* The following two flags are initalized correctly (TBD if needed) */
+ gboolean is_command; /* This PDV is a command rather than a data package */
+ gboolean is_last_fragment; /* Last Fragment bit was set, i.e. termination of an object
+ This flag delimits different dicom object in the same
+ association */
+ gboolean is_corrupt; /* Early termination of long PDVs */
+
+ /* Used to reassemble PDVs */
+ guint32 open_tag_len; /* Tag lenght of 'oversized' tags. Used for display */
+ guint32 open_tag_rlen; /* Remining tag bytes to 'decoded' as binary data after this PDV */
+ gchar *open_tag_desc; /* last decoded description */
+
+} dcm_state_pdv_t;
/*
Per Presentation Context in an association store data needed, for subsequent decoding
*/
-struct dcmItem {
- struct dcmItem *next, *prev;
- int valid;
+typedef struct dcm_state_pctx {
+
+ struct dcm_state_pctx *next, *prev;
+
guint8 id; /* 0x20 Presentation Context ID */
guchar *abss_uid; /* 0x30 Abstract syntax */
guchar *abss_desc; /* 0x30 Abstract syntax decoded*/
@@ -235,28 +307,19 @@ struct dcmItem {
#define DCM_EBE 0x02 /* explicit, big endian */
#define DCM_ELE 0x03 /* explicit, little endian */
#define DCM_UNK 0xf0
-};
-typedef struct dcmItem dcmItem_t;
-/*
- Per Presentation Data PDU store data needed, to allow decoding of tags longer than a PDU
-*/
-struct dcm_pdu_state {
- struct dcm_pdu_state *next, *prev;
- gboolean valid;
- guint32 packet_no; /* Wireshark packet number */
- guint32 offset; /* Offset in packet, where PDU header starts */
- guint32 tag_rlen; /* remining tag bytes to 'decoded' as binary data after this PDU */
- guchar *tag_desc; /* last decoded description */
-};
-typedef struct dcm_pdu_state dcm_pdu_state_t;
+ dcm_state_pdv_t *first_pdv, *last_pdv; /* List of PDV objects */
+} dcm_state_pctx_t;
-struct dcmState {
- dcmItem_t *first_pctx, *last_pctx; /* List of Presentation context objects */
- dcm_pdu_state_t *first_pdu, *last_pdu; /* List of PDU objects */
- gboolean valid; /* this conversation is a DICOM conversation */
+typedef struct dcm_state_assoc {
+
+ struct dcm_state_assoc *next, *prev;
+
+ dcm_state_pctx_t *first_pctx, *last_pctx; /* List of Presentation context objects */
+
+ guint32 packet_no; /* Wireshark packet number, where association starts */
#define AEEND 16
guchar ae_called[1+AEEND]; /* Called AE tilte in A-ASSOCIATE RQ */
@@ -264,11 +327,20 @@ struct dcmState {
guchar ae_called_resp[1+AEEND]; /* Called AE tilte in A-ASSOCIATE RP */
guchar ae_calling_resp[1+AEEND]; /* Calling AE tilte in A-ASSOCIATE RP */
guint8 source, result, reason;
-};
-typedef struct dcmState dcmState_t;
+} dcm_state_assoc_t;
+
+typedef struct dcm_state {
+
+ struct dcm_state_assoc *first_assoc, *last_assoc;
-struct dcmTag {
- int tag;
+ gboolean valid; /* this conversation is a DICOM conversation */
+
+} dcm_state_t;
+
+
+/* Following defines around tags have a potential to be merged */
+typedef struct dcmTag {
+ guint32 tag;
int dtype;
const char *desc;
#define DCM_TSTR 1
@@ -276,13 +348,51 @@ struct dcmTag {
#define DCM_TINT4 3
#define DCM_TFLT 4
#define DCM_TDBL 5
-#define DCM_TSTAT 6 /* call dcm_rsp2str() on TINT2 */
+#define DCM_TSTAT 6 /* call dcm_rsp2str() on TINT2 */
#define DCM_TRET 7
#define DCM_TCMD 8
#define DCM_SQ 9 /* sequence */
#define DCM_OTH 10 /* other */
+} dcmTag_t;
+
+
+#define DCM_VR_AE 1 /* Application Entity */
+#define DCM_VR_AS 2 /* Age String */
+#define DCM_VR_AT 3 /* Attribute Tag */
+#define DCM_VR_CS 4 /* Code String */
+#define DCM_VR_DA 5 /* Date */
+#define DCM_VR_DS 6 /* Decimal String */
+#define DCM_VR_DT 7 /* Date Time */
+#define DCM_VR_FL 8 /* Floating Point Single */
+#define DCM_VR_FD 9 /* Floating Point Double */
+#define DCM_VR_IS 10 /* Integer String */
+#define DCM_VR_LO 11 /* Long String */
+#define DCM_VR_LT 12 /* Long Text */
+#define DCM_VR_OB 13 /* Other Byte String */
+#define DCM_VR_OF 14 /* Other Float String */
+#define DCM_VR_OW 15 /* Other Word String */
+#define DCM_VR_PN 16 /* Person Name */
+#define DCM_VR_SH 17 /* Short String */
+#define DCM_VR_SL 18 /* Signed Long */
+#define DCM_VR_SQ 19 /* Sequence of Items */
+#define DCM_VR_SS 20 /* Signed Short */
+#define DCM_VR_ST 21 /* Short Text */
+#define DCM_VR_TM 22 /* Time */
+#define DCM_VR_UI 23 /* Unique Identifier (UID) */
+#define DCM_VR_UL 24 /* Unsigned Long */
+#define DCM_VR_UN 25 /* Unknown */
+#define DCM_VR_US 26 /* Unsigned Short */
+#define DCM_VR_UT 27 /* Unlimited Text */
+
+/* Following must be in the same order as the defintions above */
+static const guchar* dcm_tag_lookup[] = {
+ " ",
+ "AE","AS","AT","CS","DA","DS","DT","FL",
+ "FD","IS","LO","LT","OB","OF","OW","PN",
+ "SH","SL","SQ","SS","ST","TM","UI","UL",
+ "UN","US","UT"
};
-typedef struct dcmTag dcmTag_t;
+
static GHashTable *dcm_tagTable = NULL;
@@ -355,35 +465,45 @@ static dcmTag_t tagData[] = {
{ 0xfffee0dd, DCM_TRET, "Sequence End" },
};
-/* following definitions are used to call */
+/* following definitions are used to call dissect_dcm_assoc_item() */
#define DCM_ITEM_VALUE_TYPE_UID 1
#define DCM_ITEM_VALUE_TYPE_STRING 2
#define DCM_ITEM_VALUE_TYPE_UINT32 3
/* A few function declataions */
+/* Per object, a xxx_new() and a xxx_get() function. The _get() will create one if specified. */
-static dcmItem_t * lookupCtx(dcmState_t *dd, guint8 ctx);
+static dcm_state_t* dcm_state_new(void);
+static dcm_state_t* dcm_state_get(packet_info *pinfo, gboolean create);
-static dcm_pdu_state_t* dcm_pdu_state_get_or_create(dcmState_t *dcm_data, guint32 packet_no, guint32 offset);
+static dcm_state_assoc_t* dcm_state_assoc_new (dcm_state_t *dcm_data, guint32 packet_no);
+static dcm_state_assoc_t* dcm_state_assoc_get (dcm_state_t *dcm_data, guint32 packet_no, gboolean create);
+static dcm_state_pctx_t* dcm_state_pctx_new (dcm_state_assoc_t *assoc, guint8 pctx_id);
+static dcm_state_pctx_t* dcm_state_pctx_get (dcm_state_assoc_t *assoc, guint8 pctx_id, gboolean create);
+static dcm_state_pdv_t* dcm_state_pdv_new (dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset);
+static dcm_state_pdv_t* dcm_state_pdv_get (dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset, gboolean create);
static int dissect_dcm_static (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
static int dissect_dcm_heuristic (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
static int dissect_dcm_main (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean require_assoc_req);
+static int dissect_dcm_pdu (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset);
-static int dissect_dcm_pdu (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset);
+static int dissect_dcm_assoc (tvbuff_t *tvb, packet_info *pinfo, proto_item *ti, dcm_state_assoc_t *assoc, int offset, int len);
+static void dissect_dcm_pctx (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix, gboolean request);
+static void dissect_dcm_assoc_item (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, guchar *pitem_prefix, int item_value_type, guchar **item_value, guchar **item_description, int *hf_type, int *hf_len, int *hf_value, int ett_subtree);
+static void dissect_dcm_userinfo (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix);
-static int dissect_dcm_assoc (dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offset, int len);
-static void dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvbuff_t *tvb, int offset, int len, guchar *pitem_prefix, gboolean request);
-static void dissect_dcm_assoc_item (proto_tree *dcm_tree, tvbuff_t *tvb, int offset, guchar *pitem_prefix, int item_value_type, guchar **item_value, guchar **item_description, int *hf_type, int *hf_len, int *hf_value);
-static void dissect_dcm_userinfo (dcmState_t *dcm_data, proto_tree *dcm_tree, tvbuff_t *tvb, int offset, int len, guchar *pitem_prefix);
+static int dissect_dcm_data (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, guint32 pdu_len, guchar **pdu_description);
+static int dissect_dcm_pdv (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, guint32 pdv_len, guchar **pdv_description);
+static int dissect_dcm_pdv_header (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, guint8 *syntax, dcm_state_pdv_t **pdv);
-static int dissect_dcm_data (dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, packet_info *pinfo, int offset, int len);
+static void dcm_set_syntax (dcm_state_pctx_t *pctx, guchar *xfer_uid, guchar *xfer_desc);
+static void dcm_export_create_object (packet_info *pinfo, dcm_state_assoc_t *assoc, dcm_state_pdv_t *pdv);
static void
dcm_init(void)
{
- guchar *test=NULL;
if (NULL == dcm_tagTable) {
unsigned int i;
@@ -403,85 +523,278 @@ dcm_init(void)
}
-static dcmState_t *
-mkds(void)
+static dcm_state_t *
+dcm_state_new(void)
{
- dcmState_t *ds;
+ /* Not much fun. Just create very simple root structure */
+
+ dcm_state_t *ds=NULL;
- if (NULL == (ds = (dcmState_t *) g_malloc(sizeof(dcmState_t)))) {
- return NULL;
+ ds = (dcm_state_t *) se_alloc(sizeof(dcm_state_t));
+ if (ds) {
+ ds->first_assoc=NULL;
+ ds->last_assoc=NULL;
}
+ return ds;
+}
+
+static dcm_state_t *
+dcm_state_get(packet_info *pinfo, gboolean create)
+{
+
+ /* Get or create converstation and DICOM data structure if desired
+ Return new or existing dicom struture, which is used to store context IDs and xfer Syntax
+ Return NULL in case of the structure couldn't be created
+ */
+
+ conversation_t *conv=NULL;
+ dcm_state_t *dcm_data=NULL;
- ds->first_pctx = NULL; /* List of Presentation context objects */
- ds->last_pctx = NULL;
+ conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
+ pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
- ds->first_pdu = NULL; /* List of PDU objects */
- ds->last_pdu = NULL;
+ if (conv == NULL) {
+ /* Conversation does not exist, create one.
+ Usually set for the first packet already. Probably by dissect-tcp
+ */
+ conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ }
+ else { /* conversation exists, try to get data already filled */
+ dcm_data = conversation_get_proto_data(conv, proto_dcm);
+ }
- ds->valid = TRUE;
+ if (dcm_data == NULL && create) {
- memset(ds->ae_called, 0, sizeof(ds->ae_called));
- memset(ds->ae_calling, 0, sizeof(ds->ae_calling));
- memset(ds->ae_called_resp, 0, sizeof(ds->ae_called_resp));
- memset(ds->ae_calling_resp, 0, sizeof(ds->ae_calling_resp));
+ dcm_data = dcm_state_new();
+ if (dcm_data != NULL) {
+ conversation_add_proto_data(conv, proto_dcm, dcm_data);
+ }
- return ds;
+ /* Mark it as DICOM conversation. Needed for the heuristic mode,
+ to prevent stealing subsequent packets by other dissectors
+ */
+ conversation_set_dissector(conv, dcm_handle);
+ }
+
+ return dcm_data;
}
-static dcm_pdu_state_t*
-dcm_pdu_state_new(dcmState_t *dcm_data, guint32 packet_no, guint32 offset)
+
+static dcm_state_assoc_t *
+dcm_state_assoc_new(dcm_state_t *dcm_data, guint32 packet_no)
{
- dcm_pdu_state_t *pdu=NULL;
+ /* Create new accociation object and initalize the members */
- pdu = (dcm_pdu_state_t *) se_alloc(sizeof(dcm_pdu_state_t));
- if (pdu != NULL) {
+ dcm_state_assoc_t *assoc;
- pdu->prev=NULL;
- pdu->next=NULL;
+ assoc = (dcm_state_assoc_t *) g_malloc(sizeof(dcm_state_assoc_t));
+ if (assoc) {
- pdu->packet_no=packet_no;
- pdu->offset=offset;
- pdu->valid=FALSE;
- pdu->tag_desc=NULL;
- pdu->tag_rlen=0;
+ assoc->next = NULL;
+ assoc->prev = NULL;
+ assoc->packet_no = packet_no; /* Identifier */
- /* add to the end of the list list */
- if (dcm_data->last_pdu) {
- dcm_data->last_pdu->next = pdu;
- pdu->prev = dcm_data->last_pdu;
+ assoc->first_pctx = NULL; /* List of Presentation context objects */
+ assoc->last_pctx = NULL;
+
+ memset(assoc->ae_called, 0, sizeof(assoc->ae_called));
+ memset(assoc->ae_calling, 0, sizeof(assoc->ae_calling));
+ memset(assoc->ae_called_resp, 0, sizeof(assoc->ae_called_resp));
+ memset(assoc->ae_calling_resp, 0, sizeof(assoc->ae_calling_resp));
+
+ /* add to the end of the list */
+ if (dcm_data->last_assoc) {
+ dcm_data->last_assoc->next = assoc;
+ assoc->prev = dcm_data->last_assoc;
+ }
+ else {
+ dcm_data->first_assoc = assoc;
+ }
+ dcm_data->last_assoc = assoc;
+ }
+ return assoc;
+}
+
+static dcm_state_assoc_t *
+dcm_state_assoc_get(dcm_state_t *dcm_data, guint32 packet_no, gboolean create)
+{
+ /* Find or create Association object.
+ Return NULL, if Association was not found, based on packet number
+ */
+
+ dcm_state_assoc_t *assoc = NULL;
+
+ assoc=dcm_data->first_assoc;
+
+ while (assoc) {
+
+ if (assoc->next) {
+ /* we have more associations in the same stream */
+ if ((assoc->packet_no <= packet_no) && (packet_no < assoc->next->packet_no))
+ break;
}
else {
- dcm_data->first_pdu = pdu;
+ /* last or only associations in the same stream */
+ if (assoc->packet_no <= packet_no)
+ break;
+ }
+ assoc = assoc->next;
+ }
+
+ if (assoc == NULL && create) {
+ assoc = dcm_state_assoc_new(dcm_data, packet_no);
+ }
+ return assoc;
+}
+
+static dcm_state_pctx_t *
+dcm_state_pctx_new(dcm_state_assoc_t *assoc, guint8 pctx_id)
+{
+ /* Create new presentation context object and initalize the members */
+
+ dcm_state_pctx_t *pctx=NULL;
+
+ pctx = se_alloc(sizeof(dcm_state_pctx_t));
+ if (pctx) {
+
+ pctx->next = NULL;
+ pctx->prev = NULL;
+
+ pctx->id = pctx_id;
+ pctx->abss_uid = NULL;
+ pctx->abss_desc = NULL;
+ pctx->xfer_uid = NULL;
+ pctx->xfer_desc = NULL;
+ pctx->syntax = DCM_UNK;
+
+ pctx->first_pdv = NULL; /* List of PDV objects */
+ pctx->last_pdv = NULL;
+
+ /* add to the end of the list list */
+ if (assoc->last_pctx) {
+ assoc->last_pctx->next = pctx;
+ pctx->prev = assoc->last_pctx;
+ }
+ else {
+ assoc->first_pctx = pctx;
}
- dcm_data->last_pdu = pdu;
+ assoc->last_pctx = pctx;
+ }
+
+ return pctx;
+}
+
+static dcm_state_pctx_t *
+dcm_state_pctx_get(dcm_state_assoc_t *assoc, guint8 pctx_id, gboolean create)
+{
+ /* Find or create presentation context object. Return NULL, if Context ID was not found */
+
+ dcm_state_pctx_t *pctx =NULL;
+
+ pctx = assoc->first_pctx;
+ /*
+ static char notfound[] = "not found - click on ASSOC Request";
+ static dcm_state_pctx_t dunk = { NULL, NULL, FALSE, 0, notfound, notfound, notfound, notfound, DCM_UNK };
+ */
+ while (pctx) {
+ if (pctx->id == pctx_id)
+ break;
+ pctx = pctx->next;
+ }
+ if (pctx == NULL && create) {
+ pctx = dcm_state_pctx_new(assoc, pctx_id);
}
- return pdu;
+ return pctx;
+}
+
+
+static dcm_state_pdv_t*
+dcm_state_pdv_new(dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset)
+{
+ /* Create new PDV object and initalize the members */
+
+ dcm_state_pdv_t *pdv=NULL;
+
+ pdv = (dcm_state_pdv_t *) se_alloc(sizeof(dcm_state_pdv_t));
+ if (pdv != NULL) {
+
+ pdv->prev = NULL;
+ pdv->next = NULL;
+ pdv->data = NULL;
+ pdv->data_len = 0;
+ pdv->pctx_id = 0;
+
+ pdv->desc = NULL;
+
+ pdv->sop_class_uid = NULL;
+ pdv->sop_instance_uid = NULL;
+
+ pdv->is_storage = FALSE;
+ pdv->is_flagvalid = FALSE;
+ pdv->is_command = FALSE;
+ pdv->is_last_fragment = TRUE; /* Continuation PDVs are more tricky */
+ pdv->is_corrupt = FALSE;
+
+ pdv->packet_no = packet_no;
+ pdv->offset = offset;
+ pdv->initalized = FALSE;
+ pdv->open_tag_desc = NULL;
+ pdv->open_tag_len = 0;
+ pdv->open_tag_rlen = 0;
+
+ /* add to the end of the list list */
+ if (pctx->last_pdv) {
+ pctx->last_pdv->next = pdv;
+ pdv->prev = pctx->last_pdv;
+ }
+ else {
+ pctx->first_pdv = pdv;
+ }
+ pctx->last_pdv = pdv;
+ }
+ return pdv;
}
-static dcm_pdu_state_t*
-dcm_pdu_state_get_or_create(dcmState_t *dcm_data, guint32 packet_no, guint32 offset)
+
+static dcm_state_pdv_t*
+dcm_state_pdv_get(dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset, gboolean create)
{
+ /* Find or create PDV object. Return NULL, if PDV was not found, based on packet number and offset */
- dcm_pdu_state_t *pdu = NULL;
+ dcm_state_pdv_t *pdv = NULL;
- pdu=dcm_data->first_pdu;
+ pdv=pctx->first_pdv;
- while (pdu) {
- if ((pdu->packet_no == packet_no) && (pdu->offset == offset))
+ while (pdv) {
+ if ((pdv->packet_no == packet_no) && (pdv->offset == offset))
break;
- pdu = pdu->next;
+ pdv = pdv->next;
}
- if (pdu == NULL) {
- pdu = dcm_pdu_state_new(dcm_data, packet_no, offset);
+ if (pdv == NULL && create) {
+ pdv = dcm_state_pdv_new(pctx, packet_no, offset);
}
- return pdu;
+ return pdv;
}
+static dcm_state_pdv_t*
+dcm_state_pdv_get_obj_start(dcm_state_pdv_t *pdv_curr)
+{
+
+ dcm_state_pdv_t *pdv_first=pdv_curr;
+
+ /* Get First PDV of the DICOM Object */
+ while (pdv_first->prev && !pdv_first->prev->is_last_fragment) {
+ pdv_first = pdv_first->prev;
+ }
+
+ return pdv_first;
+}
static const char *
dcm_pdu2str(guint8 item)
@@ -585,19 +898,6 @@ dcm_PCresult2str(guint8 result)
return s;
}
-static const char *
-dcm_flags2str(guint8 flags)
-{
- const char *s = "";
- switch (flags) {
- case 0: s = "Data, more Fragments"; break; /* 00 */
- case 1: s = "Command, more Fragments"; break; /* 01 */
- case 2: s = "Data, last Fragment"; break; /* 10 */
- case 3: s = "Command, last Fragment"; break; /* 11 */
- default: break;
- }
- return s;
-}
static const char *
dcm_cmd2str(guint16 us)
@@ -661,51 +961,58 @@ dcm_uid_or_desc(guchar *dcm_uid, guchar *dcm_desc)
}
static void
-dcm_setSyntax(dcmItem_t *di, guchar *xfer_uid, guchar *xfer_desc)
+dcm_set_syntax(dcm_state_pctx_t *pctx, guchar *xfer_uid, guchar *xfer_desc)
{
- if (NULL == di) return;
- if (di->xfer_uid != NULL)
- g_free(di->xfer_uid); /* free prev allocated xfer */
- if (di->xfer_desc != NULL)
- g_free(di->xfer_desc); /* free prev allocated xfer */
+ if (pctx == NULL)
+ return;
- di->syntax = 0;
- di->xfer_uid = g_strdup(xfer_uid);
- di->xfer_desc = g_strdup(xfer_desc);
+ if (pctx->xfer_uid != NULL)
+ g_free(pctx->xfer_uid); /* free prev allocated xfer */
+ if (pctx->xfer_desc != NULL)
+ g_free(pctx->xfer_desc); /* free prev allocated xfer */
+
+ pctx->syntax = 0;
+ pctx->xfer_uid = g_strdup(xfer_uid);
+ pctx->xfer_desc = g_strdup(xfer_desc);
if (*xfer_uid == NULL) return;
/* this would be faster to skip the common parts, and have a FSA to
* find the syntax.
* Absent of coding that, this is in descending order of probability */
if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2"))
- di->syntax = DCM_ILE; /* implicit little endian */
+ pctx->syntax = DCM_ILE; /* implicit little endian */
else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.1"))
- di->syntax = DCM_ELE; /* explicit little endian */
+ pctx->syntax = DCM_ELE; /* explicit little endian */
else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.2"))
- di->syntax = DCM_EBE; /* explicit big endian */
+ pctx->syntax = DCM_EBE; /* explicit big endian */
else if (0 == strcmp(xfer_uid, "1.2.840.113619.5.2"))
- di->syntax = DCM_ILE; /* implicit little endian, big endian pixels, GE private */
+ pctx->syntax = DCM_ILE; /* implicit little endian, big endian pixels, GE private */
else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.4.70"))
- di->syntax = DCM_ELE; /* explicit little endian, jpeg */
+ pctx->syntax = DCM_ELE; /* explicit little endian, jpeg */
else if (0 == strncmp(xfer_uid, "1.2.840.10008.1.2.4", 18))
- di->syntax = DCM_ELE; /* explicit little endian, jpeg */
+ pctx->syntax = DCM_ELE; /* explicit little endian, jpeg */
else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.1.99"))
- di->syntax = DCM_ELE; /* explicit little endian, deflated */
+ pctx->syntax = DCM_ELE; /* explicit little endian, deflated */
}
static char *
-dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, guint32 len, int vr, int tr)
+dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, guint32 len, int vr, int tr, guchar **tag_value)
{
- char *buf;
- const guint8 *vval;
- char *p;
- guint32 tag, val32;
- guint16 val16;
+ guchar *buf;
+ const guchar *vval;
+ guchar *p;
+
+ guint32 tag, val32=0;
+ guint16 val16=0;
+ guint8 val8=0;
dcmTag_t *dtag;
static dcmTag_t utag = { 0, 0, "(unknown)" };
#define MAX_BUF_LEN 1024
+
buf=ep_alloc(MAX_BUF_LEN);
+ *tag_value = se_alloc(MAX_BUF_LEN);
+
*buf = 0;
if (0 == elm) {
if (DCM_ILE & syntax)
@@ -731,9 +1038,20 @@ dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset,
switch (tr > 0 ? tr : dtag->dtype) {
case DCM_TSTR:
default: /* try ascii */
- vval = tvb_format_text(tvb, offset, len);
+
+ val8 = tvb_get_guint8(tvb, offset+len-1);
+ if (val8 == 0x00) {
+ /* Last byte of string is 0x00, i.e. padded */
+ vval = tvb_format_text(tvb, offset, len-1);
+ }
+ else {
+ vval = tvb_format_text(tvb, offset, len);
+ }
p+=MIN(MAX_BUF_LEN-(p-buf),
g_snprintf(p, MAX_BUF_LEN-(p-buf), " %s", vval));
+
+ g_snprintf(*tag_value, MAX_BUF_LEN, "%s", vval);
+
break;
case DCM_TINT2:
if (DCM_ILE & syntax)
@@ -778,6 +1096,8 @@ dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset,
else val16 = tvb_get_ntohs(tvb, offset);
p+=MIN(MAX_BUF_LEN-(p-buf),
g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x '%s'", val16, dcm_cmd2str(val16)));
+
+ g_snprintf(*tag_value, MAX_BUF_LEN, "%s", dcm_cmd2str(val16));
break;
case DCM_SQ: /* Sequence */
case DCM_OTH: /* Other BYTE, WORD, ... */
@@ -788,10 +1108,309 @@ dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset,
}
static void
-dissect_dcm_assoc_item(proto_tree *dcm_tree, tvbuff_t *tvb, int offset,
+dcm_guin16_to_le(guint8 *buffer, guint16 value)
+{
+
+ buffer[0]=(guint8) (value & 0x00FF);
+ buffer[1]=(guint8)((value & 0xFF00) >> 8);
+}
+
+static void
+dcm_guin32_to_le(guint8 *buffer, guint32 value)
+{
+
+ buffer[0]=(guint8) (value & 0x000000FF);
+ buffer[1]=(guint8)((value & 0x0000FF00) >> 8);
+ buffer[2]=(guint8)((value & 0x00FF0000) >> 16);
+ buffer[3]=(guint8)((value & 0xFF000000) >> 24);
+
+}
+
+static guint32
+dcm_export_create_tag_base(guint8 *buffer, guint32 bufflen, guint32 offset,
+ guint16 grp, guint16 elm, guint16 vr,
+ guint8 *value_buffer, guint32 value_len)
+
+ /* Only Explict Littele Endian is needed to create Metafile Header
+ Generic function to write a TAG, VR, LEN & VALUE to a combined buffer
+ The value (buffer, len) must be preprocessed by a VR specific function
+ */
+{
+ guint8 *pos=NULL;
+
+ pos=buffer+offset;
+
+ dcm_guin16_to_le(pos, grp);
+ pos+=2;
+ dcm_guin16_to_le(pos, elm);
+ pos+=2;
+
+ memmove(pos, dcm_tag_lookup[vr], 2);
+ pos+=2;
+
+ switch (vr) {
+ case DCM_VR_OB:
+ case DCM_VR_OW:
+ case DCM_VR_OF:
+ case DCM_VR_SQ:
+ case DCM_VR_UT:
+ case DCM_VR_UN:
+ /* DICOM likes it complicated. Special handling for these types */
+
+ /* Add two reserved 0x00 bytes */
+ dcm_guin16_to_le(pos, 0);
+ pos+=2;
+
+ /* Length is a 4 byte field */
+ dcm_guin32_to_le(pos, (guint32)value_len);
+ pos+=4;
+ break;
+
+ default:
+ /* Length is a 2 byte field */
+ dcm_guin16_to_le(pos, (guint16)value_len);
+ pos+=2;
+ }
+
+ memmove(pos, value_buffer, value_len);
+ pos+=value_len;
+
+ return pos-buffer;
+}
+
+static guint32
+dcm_export_create_tag_guint16(guint8 *buffer, guint32 bufflen, guint32 offset,
+ guint16 grp, guint16 elm, guint16 vr, guint16 value)
+{
+
+ return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr, (guint8*)&value, 2);
+}
+
+static guint32
+dcm_export_create_tag_guint32(guint8 *buffer, guint32 bufflen, guint32 offset,
+ guint16 grp, guint16 elm, guint16 vr, guint32 value)
+{
+
+ return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr, (guint8*)&value, 4);
+}
+
+static guint32
+dcm_export_create_tag_str(guint8 *buffer, guint32 bufflen, guint32 offset,
+ guint16 grp, guint16 elm, guint16 vr, guchar *value)
+{
+ guint16 len;
+
+ if (!value) {
+ /* NULL object. E.g. happens if UID was not found/set. Don't create element*/
+ return offset;
+ }
+
+ len=strlen(value);
+
+ if ((len & 0x01) == 1) {
+ /* Odd length: since buffer is 0 initalized, pad with a 0x00 */
+ len += 1;
+ }
+
+ return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr,
+ (guint8*)value, len);
+}
+
+
+static guint8*
+dcm_export_create_header(guint32 *dcm_header_len, gchar *sop_class_uid, gchar *sop_instance_uid, gchar *xfer_uid)
+{
+ guint8 *dcm_header=NULL;
+ guint32 offset=0;
+ guint32 offset_header_len=0;
+
+#define DCM_HEADER_MAX 512
+
+ dcm_header=ep_alloc(DCM_HEADER_MAX); /* Slightly longer than needed */
+
+ memset(dcm_header, 0, DCM_HEADER_MAX); /* The subsequent functions rely on a 0 intitalized buffer */
+ offset=128;
+
+ memmove(dcm_header+offset, "DICM", 4);
+ offset+=4;
+
+ offset_header_len=offset; /* remember for later */
+
+ offset+=12;
+
+ /*
+ (0002,0000) File Meta Information Group Length UL
+ (0002,0001) File Meta Information Version OB
+ (0002,0002) Media Storage SOP Class UID UI
+ (0002,0003) Media Storage SOP Instance UID UI
+ (0002,0010) Transfer Syntax UID UI
+ (0002,0012) Implementation Class UID UI
+ (0002,0013) Implementation Version Name SH
+ */
+
+ offset=dcm_export_create_tag_guint16(dcm_header, DCM_HEADER_MAX, offset,
+ 0x0002, 0x0001, DCM_VR_OB, 0x0100); /* will result on 00 01 since it is little endian */
+
+ offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
+ 0x0002, 0x0002, DCM_VR_UI, sop_class_uid);
+
+ offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
+ 0x0002, 0x0003, DCM_VR_UI, sop_instance_uid);
+
+ offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
+ 0x0002, 0x0010, DCM_VR_UI, xfer_uid);
+
+ offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
+ 0x0002, 0x0012, DCM_VR_UI, WIRESHARK_IMPLEMENTATION_UID);
+
+ offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
+ 0x0002, 0x0013, DCM_VR_SH, WIRESHARK_IMPLEMENTATION_VERSION);
+
+ /* Finally write the meta header lenght */
+ dcm_export_create_tag_guint32(dcm_header, DCM_HEADER_MAX, offset_header_len,
+ 0x0002, 0x0000, DCM_VR_UL, offset-offset_header_len-12);
+
+ *dcm_header_len=offset;
+
+ return dcm_header;
+
+}
+
+static void
+dcm_export_create_object(packet_info *pinfo, dcm_state_assoc_t *assoc, dcm_state_pdv_t *pdv)
+{
+
+ /* Concat different PDVs into one buffer and add it to export object list
+ This function caused quite a few crashes, with all the string pointers
+ */
+
+ dicom_eo_t *eo_info = NULL;
+
+ dcm_state_pdv_t *pdv_curr = NULL;
+ dcm_state_pdv_t *pdv_same_pkt = NULL;
+ dcm_state_pctx_t *pctx = NULL;
+
+ guint8 *pdv_combined = NULL;
+ guint8 *pdv_combined_curr = NULL;
+ guint8 *dcm_header = NULL;
+ guint32 pdv_combined_len = 0;
+ guint32 dcm_header_len = 0;
+ guint16 cnt_same_pkt = 1;
+ gchar *filename = NULL;
+ gchar *hostname = NULL;
+
+ gchar *sop_class_uid = NULL;
+ gchar *sop_instance_uid = NULL;
+
+ /* Calculate total PDV lenghth, i.e. all packets until last PDV without continuation */
+ pdv_curr = pdv;
+ pdv_same_pkt = pdv;
+ pdv_combined_len=pdv_curr->data_len;
+
+ while (pdv_curr->prev && !pdv_curr->prev->is_last_fragment) {
+ pdv_curr = pdv_curr->prev;
+ pdv_combined_len += pdv_curr->data_len;
+ }
+
+ /* Count number of PDVs with the same Packet Number */
+ while (pdv_same_pkt->prev && (pdv_same_pkt->prev->packet_no == pdv_same_pkt->packet_no)) {
+ pdv_same_pkt = pdv_same_pkt->prev;
+ cnt_same_pkt += 1;
+ }
+
+ pctx=dcm_state_pctx_get(assoc, pdv_curr->pctx_id, FALSE);
+
+ sop_class_uid = ep_alloc(MAX_BUF_LEN);
+ sop_instance_uid = ep_alloc(MAX_BUF_LEN);
+
+ hostname = ep_alloc(MAX_BUF_LEN);
+ filename = ep_alloc(MAX_BUF_LEN);
+
+ if (assoc->ae_calling && strlen(assoc->ae_calling)>0 &&
+ assoc->ae_called && strlen(assoc->ae_called)>0 ) {
+ g_snprintf(hostname, MAX_BUF_LEN, "%s <-> %s", assoc->ae_calling, assoc->ae_called);
+ }
+ else {
+ g_snprintf(hostname, MAX_BUF_LEN, "AE title(s) unknown");
+ }
+
+ if (pdv->is_storage &&
+ pdv_curr->sop_class_uid && strlen(pdv_curr->sop_class_uid)>0 &&
+ pdv_curr->sop_instance_uid && strlen(pdv_curr->sop_instance_uid)>0) {
+
+ g_snprintf(sop_class_uid, MAX_BUF_LEN, "%s", pdv_curr->sop_class_uid);
+ g_snprintf(sop_instance_uid, MAX_BUF_LEN, "%s", pdv_curr->sop_instance_uid);
+
+ /* Make sure filename does not contain invalid character. Rather conservative.
+ Eventhough this should be a valid DICOM UID, apply the same filter rules
+ in case of bogus data.
+ */
+ g_snprintf(filename, MAX_BUF_LEN, "%06d-%d-%s.dcm", pinfo->fd->num, cnt_same_pkt,
+ g_strcanon(pdv_curr->sop_instance_uid, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-.", '-'));
+ }
+ else {
+ /* No SOP Instance or SOP Class UID found in PDV. Use wireshark ones */
+
+ g_snprintf(sop_class_uid, MAX_BUF_LEN, "%s", WIRESHARK_MEDIA_STORAGE_SOP_CLASS_UID);
+
+ g_snprintf(sop_instance_uid, MAX_BUF_LEN, "%s.%d.%d",
+ WIRESHARK_MEDIA_STORAGE_SOP_INSTANCE_UID_PREFIX, pinfo->fd->num, cnt_same_pkt);
+
+ /* Make sure filename does not contain invalid character. Rather conservative.*/
+ g_snprintf(filename, MAX_BUF_LEN, "%06d-%d-%s.dcm", pinfo->fd->num, cnt_same_pkt,
+ g_strcanon(pdv->desc, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-.", '-'));
+
+ }
+
+ if (global_dcm_header) {
+ if (pctx && pctx->xfer_uid && strlen(pctx->xfer_uid)>0) {
+ dcm_header=dcm_export_create_header(&dcm_header_len, sop_class_uid, sop_instance_uid, pctx->xfer_uid);
+ }
+ else {
+ /* We are running blind, i.e. no presentation context found. Don't invent one
+ The meta header will miss this tag (even tough it is mandatory)
+ */
+ dcm_header=dcm_export_create_header(&dcm_header_len, sop_class_uid, sop_instance_uid, NULL);
+ }
+ }
+
+
+ /* Allocate the final size */
+ pdv_combined = ep_alloc(dcm_header_len+pdv_combined_len);
+ pdv_combined_curr = pdv_combined;
+
+ memmove(pdv_combined, dcm_header, dcm_header_len);
+ pdv_combined_curr += dcm_header_len;
+
+ /* Copy PDV per PDV to target buffer */
+ while (!pdv_curr->is_last_fragment) {
+ memmove(pdv_combined_curr, pdv_curr->data, pdv_curr->data_len); /* this is a copy not move */
+ pdv_combined_curr += pdv_curr->data_len;
+ pdv_curr = pdv_curr->next;
+ }
+
+ /* Last packet */
+ g_memmove(pdv_combined_curr, pdv->data, pdv->data_len); /* this is a copy not move */
+
+ /* Add to list */
+ eo_info = ep_alloc(sizeof(dicom_eo_t));
+ eo_info->hostname = hostname;
+ eo_info->filename = filename;
+ eo_info->content_type = pdv->desc;
+
+ eo_info->payload_data = pdv_combined;
+ eo_info->payload_len = dcm_header_len+pdv_combined_len;
+
+ tap_queue_packet(dicom_eo_tap, pinfo, eo_info);
+}
+
+
+static void
+dissect_dcm_assoc_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ dcm_state_assoc_t *assoc, int offset,
guchar *pitem_prefix, int item_value_type,
guchar **item_value, guchar **item_description,
- int *hf_type, int *hf_len, int *hf_value)
+ int *hf_type, int *hf_len, int *hf_value, int ett_subtree)
{
/*
* Decode one item in a association request or response. Lookup UIDs if requested
@@ -807,6 +1426,8 @@ dissect_dcm_assoc_item(proto_tree *dcm_tree, tvbuff_t *tvb, int offset,
proto_tree *assoc_item_ptree = NULL; /* Tree for item details */
proto_item *assoc_item_pitem = NULL;
+ guint32 item_number;
+
guint8 item_type;
guint16 item_len;
@@ -823,8 +1444,8 @@ dissect_dcm_assoc_item(proto_tree *dcm_tree, tvbuff_t *tvb, int offset,
item_type = tvb_get_guint8(tvb, offset);
item_len = tvb_get_ntohs(tvb, offset+2);
- assoc_item_pitem = proto_tree_add_text(dcm_tree, tvb, offset, item_len+4, pitem_prefix);
- assoc_item_ptree = proto_item_add_subtree(assoc_item_pitem, ett_assoc_item);
+ assoc_item_pitem = proto_tree_add_text(tree, tvb, offset, item_len+4, pitem_prefix);
+ assoc_item_ptree = proto_item_add_subtree(assoc_item_pitem, ett_subtree);
proto_tree_add_uint(assoc_item_ptree, *hf_type, tvb, offset, 1, item_type);
proto_tree_add_uint(assoc_item_ptree, *hf_len, tvb, offset+2, 2, item_len);
@@ -852,10 +1473,16 @@ dissect_dcm_assoc_item(proto_tree *dcm_tree, tvbuff_t *tvb, int offset,
proto_tree_add_string(assoc_item_ptree, *hf_value, tvb, offset+4, item_len, *item_value);
break;
- /*
+
case DCM_ITEM_VALUE_TYPE_UINT32:
- *item_value = tvb_get_ntohl(tvb, offset+4);
- break;*/
+ item_number = tvb_get_ntohl(tvb, offset+4);
+ *item_value = se_alloc(MAX_BUFFER);
+ g_snprintf(*item_value, MAX_BUFFER, "%d", item_number, item_number);
+
+ proto_item_append_text(assoc_item_pitem, "%s", *item_value);
+ proto_tree_add_item(assoc_item_ptree, *hf_value, tvb, offset+4, 4, FALSE);
+
+ break;
default:
break;
@@ -864,8 +1491,8 @@ dissect_dcm_assoc_item(proto_tree *dcm_tree, tvbuff_t *tvb, int offset,
static void
-dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvbuff_t *tvb,
- int offset, int len, guchar *pitem_prefix, gboolean request)
+dissect_dcm_pctx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix, gboolean is_assoc_request)
{
/*
Decode a presentation context item in a Association Request or Response
@@ -875,13 +1502,13 @@ dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvb
proto_tree *pctx_ptree = NULL; /* Tree for presentation context details */
proto_item *pctx_pitem = NULL;
- dcmItem_t *di = NULL;
+ dcm_state_pctx_t *pctx = NULL;
- guint8 item_type;
- guint16 item_len;
+ guint8 item_type=0;
+ guint16 item_len=0;
- guint8 pctx_id; /* Presentation Context ID */
- guint8 pctx_result;
+ guint8 pctx_id=0; /* Presentation Context ID */
+ guint8 pctx_result=0;
guchar *pctx_abss_uid=NULL; /* Abstract Syntax UID alias SOP Class UID */
guchar *pctx_abss_desc=NULL; /* Description of UID */
@@ -890,7 +1517,10 @@ dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvb
guchar *pctx_xfer_desc=NULL; /* Description of UID */
guchar *buf_desc=NULL; /* Used in infor mode for item text */
- int endpos;
+ int endpos=0;
+
+ int cnt_abbs=0; /* Number of Abstract Syntax Items */
+ int cnt_xfer=0; /* Number of Trasfer Syntax Items */
#define MAX_BUFFER 1024
@@ -902,45 +1532,24 @@ dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvb
item_type = tvb_get_guint8(tvb, offset-4);
item_len = tvb_get_ntohs(tvb, offset-2);
- pctx_pitem = proto_tree_add_text(dcm_tree, tvb, offset-4, item_len+4, pitem_prefix);
+ pctx_pitem = proto_tree_add_text(tree, tvb, offset-4, item_len+4, pitem_prefix);
pctx_ptree = proto_item_add_subtree(pctx_pitem, ett_assoc_pctx);
pctx_id = tvb_get_guint8(tvb, offset);
pctx_result = tvb_get_guint8(tvb, 2 + offset); /* only set in responses, otherwise reserved and 0x00 */
/* Find or create dicom context object */
- di = lookupCtx(dcm_data, pctx_id);
- if (!di->valid) {
- di = se_alloc(sizeof(struct dcmItem));
- di->id = pctx_id;
- di->valid = 1;
-
- if (request) {
- di->abss_uid = NULL;
- }
- else {
- di->abss_uid = "Missing A-ASSOCIATE request";
- }
- di->abss_desc = NULL;
-
- di->xfer_uid = NULL;
- di->xfer_desc = NULL;
- di->syntax = DCM_UNK;
- di->next = di->prev = NULL;
- if (dcm_data->last_pctx) {
- dcm_data->last_pctx->next = di;
- di->prev = dcm_data->last_pctx;
- dcm_data->last_pctx = di;
- } else
- dcm_data->first_pctx = dcm_data->last_pctx = di;
+ pctx = dcm_state_pctx_get(assoc, pctx_id, TRUE);
+ if (pctx == NULL) { /* Internal error. Failed to create data structre */
+ return;
}
proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_type, tvb, offset-4, 2, item_type);
- proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_len, tvb, offset-2, 2, item_len);
+ proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_len, tvb, offset-2, 2, item_len);
proto_tree_add_uint_format(pctx_ptree, hf_dcm_pctx_id, tvb, offset, 1, pctx_id, "Context ID: 0x%02x", pctx_id);
- if (!request) {
+ if (!is_assoc_request) {
/* Accociation response. */
proto_tree_add_uint_format(pctx_ptree, hf_dcm_pctx_result, tvb, offset+2, 1, pctx_result, "Result: %s (0x%x)", dcm_PCresult2str(pctx_result), pctx_result);
}
@@ -955,35 +1564,31 @@ dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvb
switch (item_type) {
case 0x30: /* Abstract syntax */
- if (request) {
-
- /* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
- dissect_dcm_assoc_item(pctx_ptree, tvb, offset-4,
- "Abstract Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_abss_uid, &pctx_abss_desc,
- &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_abss_syntax);
- }
+ /* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
+ dissect_dcm_assoc_item(tvb, pinfo, pctx_ptree, assoc, offset-4,
+ "Abstract Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_abss_uid, &pctx_abss_desc,
+ &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_abss_syntax, ett_assoc_pctx_abss);
+ cnt_abbs += 1;
offset += item_len;
break;
case 0x40: /* Transfer syntax */
- dissect_dcm_assoc_item(pctx_ptree, tvb, offset-4,
+ dissect_dcm_assoc_item(tvb, pinfo, pctx_ptree, assoc, offset-4,
"Transfer Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_xfer_uid, &pctx_xfer_desc,
- &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_xfer_syntax);
+ &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_xfer_syntax, ett_assoc_pctx_xfer);
/*
In a correct Association Response, only one Transfer syntax shall be present.
Therefore, pctx_xfer_uid, pctx_xfer_desc are used for the accept scenario in the info mode
*/
- if (!request && pctx_result == 0) {
- /* Association Response, Context Accepted*/
-
- if (di && di->valid) {
- dcm_setSyntax(di, pctx_xfer_uid, pctx_xfer_desc);
- }
+ if (!is_assoc_request && pctx_result == 0) {
+ /* Association Response, Context Accepted */
+ dcm_set_syntax(pctx, pctx_xfer_uid, pctx_xfer_desc);
}
+ cnt_xfer += 1;
offset += item_len;
break;
@@ -993,12 +1598,46 @@ dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvb
}
}
- if (di->abss_uid==NULL) {
- /* Permanent copy information into structure */
- di->abss_uid =g_strdup(pctx_abss_uid);
- di->abss_desc=g_strdup(pctx_abss_desc);
+ if (is_assoc_request) {
+
+ if (cnt_abbs<1) {
+ expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
+ "No Abstract Syntax provided for this Presentation Context");
+ return;
+ }
+ else if (cnt_abbs>1) {
+ expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
+ "More than one Abstract Syntax provided for this Presentation Context");
+ return;
+ }
+
+ if (cnt_xfer==0) {
+ expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
+ "No Transfer Syntax provided for this Presentation Context");
+ return;
+ }
+
+ if (pctx_abss_uid==NULL) {
+ expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
+ "No Abstract Syntax UID found for this Presentation Context");
+ return;
+ }
+
+ }
+ else {
+
+ if (cnt_xfer>1) {
+ expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
+ "Only one Transfer Syntax allowed in a Association Response");
+ return;
+ }
}
+ if (pctx->abss_uid==NULL) {
+ /* Permanent copy information into structure */
+ pctx->abss_uid =g_strdup(pctx_abss_uid);
+ pctx->abss_desc=g_strdup(pctx_abss_desc);
+ }
/*
Copy to buffer first, because proto_item_append_text()
@@ -1006,7 +1645,7 @@ dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvb
and in my opinion correctly set parameters.
*/
- if (request) {
+ if (is_assoc_request) {
if (pctx_abss_desc == NULL) {
g_snprintf(buf_desc, MAX_BUFFER, "%s", pctx_abss_uid);
}
@@ -1022,14 +1661,14 @@ dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvb
/* Accepted */
g_snprintf(buf_desc, MAX_BUFFER, "ID 0x%02x, %s, %s, %s",
pctx_id, dcm_PCresult2str(pctx_result),
- dcm_uid_or_desc(di->xfer_uid, di->xfer_desc),
- dcm_uid_or_desc(di->abss_uid, di->abss_desc));
+ dcm_uid_or_desc(pctx->xfer_uid, pctx->xfer_desc),
+ dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
}
else {
/* Rejected */
g_snprintf(buf_desc, MAX_BUFFER, "ID 0x%02x, %s, %s",
pctx_id, dcm_PCresult2str(pctx_result),
- dcm_uid_or_desc(di->abss_uid, di->abss_desc));
+ dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
}
}
proto_item_append_text(pctx_pitem, "%s", buf_desc);
@@ -1037,8 +1676,8 @@ dissect_dcm_presentation_context(dcmState_t *dcm_data, proto_tree *dcm_tree, tvb
}
static void
-dissect_dcm_userinfo(dcmState_t *dcm_data, proto_tree *dcm_tree, tvbuff_t *tvb, int offset, int len,
- guchar *pitem_prefix)
+dissect_dcm_userinfo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix)
{
/*
Decode the user info item in a Association Request or Response
@@ -1052,9 +1691,11 @@ dissect_dcm_userinfo(dcmState_t *dcm_data, proto_tree *dcm_tree, tvbuff_t *tvb,
guint32 mlen=0;
- gboolean first_item=-1;
+ gboolean first_item=TRUE;
guchar *buf_desc=NULL; /* Used in infor mode for item text */
+
+ guchar *info_max_pdu=NULL;
guchar *info_impl_uid=NULL;
guchar *info_impl_version=NULL;
guchar *dummy=NULL;
@@ -1066,8 +1707,8 @@ dissect_dcm_userinfo(dcmState_t *dcm_data, proto_tree *dcm_tree, tvbuff_t *tvb,
item_type = tvb_get_guint8(tvb, offset-4);
item_len = tvb_get_ntohs(tvb, offset-2);
- userinfo_pitem = proto_tree_add_text(dcm_tree, tvb, offset-4, item_len+4, pitem_prefix);
- userinfo_ptree = proto_item_add_subtree(userinfo_pitem, ett_assoc_userinfo);
+ userinfo_pitem = proto_tree_add_text(tree, tvb, offset-4, item_len+4, pitem_prefix);
+ userinfo_ptree = proto_item_add_subtree(userinfo_pitem, ett_assoc_info);
proto_tree_add_uint(userinfo_ptree, hf_dcm_assoc_item_type, tvb, offset-4, 2, item_type);
proto_tree_add_uint(userinfo_ptree, hf_dcm_assoc_item_len, tvb, offset-2, 2, item_len);
@@ -1080,14 +1721,16 @@ dissect_dcm_userinfo(dcmState_t *dcm_data, proto_tree *dcm_tree, tvbuff_t *tvb,
offset += 4;
switch (item_type) {
case 0x51: /* Max length */
- mlen = tvb_get_ntohl(tvb, offset);
- proto_tree_add_item(userinfo_ptree, hf_dcm_pdu_maxlen, tvb, offset, 4, FALSE);
+
+ dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, assoc, offset-4,
+ "Max PDU Length: ", DCM_ITEM_VALUE_TYPE_UINT32, &info_max_pdu, &dummy,
+ &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pdu_maxlen, ett_assoc_info_uid);
if (!first_item) {
proto_item_append_text(userinfo_pitem, ", ");
}
- proto_item_append_text(userinfo_pitem, "Max PDU LENGHT 0x%x", mlen);
- first_item=0;
+ proto_item_append_text(userinfo_pitem, "Max PDU Length %s", info_max_pdu);
+ first_item=FALSE;
offset += item_len;
break;
@@ -1095,30 +1738,30 @@ dissect_dcm_userinfo(dcmState_t *dcm_data, proto_tree *dcm_tree, tvbuff_t *tvb,
case 0x52: /* UID */
/* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
- dissect_dcm_assoc_item(userinfo_ptree, tvb, offset-4,
+ dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, assoc, offset-4,
"Implementation UID: ", DCM_ITEM_VALUE_TYPE_STRING, &info_impl_uid, &dummy,
- &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_uid);
+ &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_uid, ett_assoc_info_uid);
if (!first_item) {
proto_item_append_text(userinfo_pitem, ", ");
}
proto_item_append_text(userinfo_pitem, "Implementation UID %s", info_impl_uid);
-
- first_item=0;
+ first_item=FALSE;
offset += item_len;
break;
+
case 0x55: /* version */
- dissect_dcm_assoc_item(userinfo_ptree, tvb, offset-4,
+ dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, assoc, offset-4,
"Implementation Version: ", DCM_ITEM_VALUE_TYPE_STRING, &info_impl_version, &dummy,
- &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_version);
+ &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_version, ett_assoc_info_version);
if (!first_item) {
proto_item_append_text(userinfo_pitem, ", ");
}
proto_item_append_text(userinfo_pitem, "Version %s", info_impl_version);
- first_item=0;
+ first_item=FALSE;
offset += item_len;
break;
@@ -1137,9 +1780,10 @@ dissect_dcm_userinfo(dcmState_t *dcm_data, proto_tree *dcm_tree, tvbuff_t *tvb,
static int
-dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offset, int len)
+dissect_dcm_assoc(tvbuff_t *tvb, packet_info *pinfo, proto_item *ti,
+ dcm_state_assoc_t *assoc, int offset, int len)
{
- proto_tree *dcm_tree = NULL; /* Tree for PDU details */
+ proto_tree *assoc_tree = NULL; /* Tree for PDU details */
proto_item *item_pctx = NULL;
guint8 item_type;
@@ -1155,7 +1799,7 @@ dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offse
endpos = offset+len;
if (ti) {
- dcm_tree = proto_item_add_subtree(ti, ett_assoc);
+ assoc_tree = proto_item_add_subtree(ti, ett_assoc);
while (-1 < offset && offset < endpos) {
item_type = tvb_get_guint8(tvb, offset);
@@ -1167,27 +1811,27 @@ dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offse
switch (item_type) {
case 0x10: /* Application context */
- dissect_dcm_assoc_item(dcm_tree, tvb, offset-4,
+ dissect_dcm_assoc_item(tvb, pinfo, assoc_tree, assoc, offset-4,
"Application Context: ", DCM_ITEM_VALUE_TYPE_UID, &item_value, &item_description,
- &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_actx);
+ &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_actx, ett_assoc_actx);
offset += item_len;
break;
case 0x20: /* Presentation context request */
- dissect_dcm_presentation_context(dcm_data, dcm_tree, tvb, offset, item_len,
- "Presentation Context: ", -1);
+ dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len,
+ "Presentation Context: ", TRUE);
offset += item_len;
break;
case 0x21: /* Presentation context reply */
- dissect_dcm_presentation_context(dcm_data, dcm_tree, tvb, offset, item_len,
- "Presentation Context: ", 0);
+ dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len,
+ "Presentation Context: ", FALSE);
offset += item_len;
break;
case 0x50: /* User Info */
- dissect_dcm_userinfo(dcm_data, dcm_tree, tvb, offset, item_len, "User Info: ");
+ dissect_dcm_userinfo(tvb, pinfo, assoc_tree, assoc, offset, item_len, "User Info: ");
offset += item_len;
break;
@@ -1201,192 +1845,305 @@ dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offse
}
-static dcmItem_t *
-lookupCtx(dcmState_t *dd, guint8 ctx)
+static int
+dissect_dcm_pdv_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ dcm_state_assoc_t *assoc, int offset, guint8 *syntax,
+ dcm_state_pdv_t **pdv)
{
- dcmItem_t *di = dd->first_pctx;
- static char notfound[] = "not found - click on ASSOC Request";
- static dcmItem_t dunk = { NULL, NULL, 0, -1, notfound, notfound, notfound, notfound, DCM_UNK };
- while (di) {
- if (ctx == di->id)
- break;
- di = di->next;
- }
- return di ? di : &dunk;
-}
+ /* Dissect Context and Flags of a PDV and create new PDV stucture */
-/*
-04 P-DATA-TF
- 1 1 reserved
- 2 4 length
- - (1+) presentation data value (PDV) items
- 6 4 length
-10 1 Presentation Context ID (odd ints 1 - 255)
- - PDV
-11 1 header
- 0x01 if set, contains Message Command info, else Message Data
- 0x02 if set, contains last fragment
- */
-#define D_HEADER 1
-#define D_TAG 2
-#define D_VR 3
-#define D_LEN2 4
-#define D_LEN4 5
-#define D_VALUE 6
+ proto_item *pdv_ctx_pitem = NULL;
+ proto_item *pdv_flags_pitem = NULL;
-char *trim(char *str)
-{
- char *ibuf = str, *obuf = str;
- int i = 0, cnt = 0;
+ dcm_state_pctx_t *pctx = NULL;
+ dcm_state_pdv_t *pdv_first_data = NULL;
- if (str)
- {
- /* Remove leading spaces */
+ const gchar *desc_flag = NULL; /* Flag Description in tree */
+ gchar *desc_header = NULL; /* Used for PDV description */
- for (ibuf = str; *ibuf && isspace(*ibuf); ++ibuf)
- ;
+ guint8 flags = 0;
+ guint8 pctx_id = 0;
- if (str != ibuf)
- memmove(str, ibuf, ibuf - str);
+ *syntax = DCM_UNK;
- while (*ibuf)
- {
- obuf[i++] = *ibuf++;
- }
- obuf[i] = 0x0;
+ /* 1 Byte Context */
+ pctx_id = tvb_get_guint8(tvb, offset);
+ pctx = dcm_state_pctx_get(assoc, pctx_id, FALSE);
- /* Remove trailing spaces */
+ if (pctx && pctx->xfer_uid) {
+ proto_tree_add_uint_format(tree, hf_dcm_pdv_ctx, tvb, offset, 1,
+ pctx_id, "Context: 0x%02x (%s, %s)", pctx_id,
+ dcm_uid_or_desc(pctx->xfer_uid, pctx->xfer_desc),
+ dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
+ }
+ else {
+ pdv_ctx_pitem=proto_tree_add_uint_format(tree, hf_dcm_pdv_ctx, tvb, offset, 1,
+ pctx_id, "Context: 0x%02x not found. A-ASSOCIATE request not found in capture.", pctx_id);
+
+ expert_add_info_format(pinfo, pdv_ctx_pitem, PI_MALFORMED, PI_ERROR, "Invalid Presentation Context ID");
- while (--i >= 0)
- {
- if (!isspace(obuf[i]))
- break;
- }
- obuf[++i] = 0x0;
+ /* Create fake PCTX and guess Syntax ILE, ELE, EBE */
+ pctx = dcm_state_pctx_new(assoc, pctx_id);
+
+ /* To be done: Guess Syntax */
+ pctx->syntax = DCM_UNK;
}
- return str;
-}
+ offset +=1;
-static int
-dissect_dcm_data(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, packet_info *pinfo, int offset, int datalen)
-{
+ *syntax = pctx->syntax;
- int toffset, state, vr = 0, tr = 0;
- proto_tree *dcm_tree;
- dcmItem_t *di;
- dcm_pdu_state_t *pdu=NULL;
+ /* Create PDV structure:
- guint32 tag_value_fragment_len; /* used for values that span multiple PDUs */
+ Since we can have multiple PDV per packet (offset) and
+ multiple merged packets per PDV (tvb->raw_offset)
+ we need both values to uniquely identify a PDV
+ */
- guint8 ctx = DCM_UNK;
- guint8 syntax = DCM_UNK;
- guint8 flags;
+ *pdv=dcm_state_pdv_get(pctx, pinfo->fd->num, tvb->raw_offset+offset, TRUE);
+ if (*pdv==NULL) {
+ return 0; /* Failed to allocate memory */
+ }
- guint16 grp = 0, elm = 0;
- guint32 tlen = 0;
- guint32 nlen = 0; /* Length of next sub item */
+ /* 1 Byte Flag */
+ flags = tvb_get_guint8(tvb, offset);
- guint32 tag_rlen = 0;
+ (*pdv)->pctx_id = pctx_id; /* TBD: Required for export */
+
+ desc_header=se_alloc(MAX_BUFFER); /* Valid for this capture, since we return this buffer */
+ memset(desc_header, 0, MAX_BUFFER);
+
+ switch (flags) {
+ case 0: /* 00 */
+ desc_flag = "Data, More Fragments";
+
+ (*pdv)->is_flagvalid = TRUE;
+ (*pdv)->is_command = FALSE;
+ (*pdv)->is_last_fragment = FALSE;
+ break;
+
+ case 2: /* 10 */
+ desc_flag = "Data, Last Fragment";
+
+ (*pdv)->is_flagvalid = TRUE;
+ (*pdv)->is_command = FALSE;
+ (*pdv)->is_last_fragment = TRUE;
+ break;
+
+ case 1: /* 01 */
+ desc_flag = "Command, More Fragments";
+ g_snprintf(desc_header, MAX_BUFFER, "Command"); /* Will be overwritten with real command tag */
+
+ *syntax = DCM_ILE; /* Command tags are always little endian*/
+
+ (*pdv)->is_flagvalid = TRUE;
+ (*pdv)->is_command = TRUE;
+ (*pdv)->is_last_fragment = FALSE;
+ break;
- int endpos = offset + datalen;
+ case 3: /* 11 */
+ desc_flag = "Command, Last Fragment";
+ g_snprintf(desc_header, MAX_BUFFER, "Command");
- offset += 6; /* Skip PDU Header */
+ *syntax = DCM_ILE; /* Command tags are always little endian*/
- /* There should be a loop per PDV. Currently only one is supported */
+ (*pdv)->is_flagvalid = TRUE;
+ (*pdv)->is_command = TRUE;
+ (*pdv)->is_last_fragment = TRUE;
+ break;
- dcm_tree = proto_item_add_subtree(ti, ett_dcm_data);
- proto_tree_add_item(dcm_tree, hf_dcm_pdv_len, tvb, offset, 4, FALSE);
- offset +=4;
+ default:
+ desc_flag = "Invalid Flags";
+ g_snprintf(desc_header, MAX_BUFFER, "Invalid Flags");
- ctx = tvb_get_guint8(tvb, offset);
- di = lookupCtx(dcm_data, ctx);
+ *syntax = DCM_UNK;
- if (di->xfer_uid) {
- proto_tree_add_uint_format(dcm_tree, hf_dcm_pdv_ctx, tvb, offset, 1,
- ctx, "Context: 0x%x (%s, %s)", ctx,
- dcm_uid_or_desc(di->xfer_uid, di->xfer_desc),
- dcm_uid_or_desc(di->abss_uid, di->abss_desc));
+ (*pdv)->is_flagvalid = FALSE;
+ (*pdv)->is_command = FALSE;
+ (*pdv)->is_last_fragment = FALSE;
}
- else {
- proto_tree_add_uint_format(dcm_tree, hf_dcm_pdv_ctx, tvb, offset, 1,
- ctx, "Context: 0x%x not found. A-ASSOCIATE request not found in capture.", ctx);
+
+ if (flags == 0 || flags == 2) {
+ /* Data PDV */
+ pdv_first_data = dcm_state_pdv_get_obj_start(*pdv);
+
+ if (pdv_first_data->prev && pdv_first_data->prev->is_command) {
+ /* Every Data PDV sequence should be preceeded by a Command PDV,
+ so we should always hit this for a correct capture
+ */
+
+ if (pctx && pctx->abss_desc && g_str_has_suffix(pctx->abss_desc, "Storage")) {
+ /* Should be done far more intelligent, e.g. does not catch the (Retired) ones */
+ if (flags == 0) {
+ g_snprintf(desc_header, MAX_BUFFER, "%s (more fragments)", pctx->abss_desc);
+ }
+ else {
+ g_snprintf(desc_header, MAX_BUFFER, "%s", pctx->abss_desc);
+ }
+ (*pdv)->is_storage = TRUE;
+ }
+ else {
+ /* Use previous command and append DATA*/
+ g_snprintf(desc_header, MAX_BUFFER, "%s-DATA", pdv_first_data->prev->desc);
+ }
+ }
+ else {
+ g_snprintf(desc_header, MAX_BUFFER, "DATA");
+ }
}
- offset +=1;
- flags = tvb_get_guint8(tvb, offset);
- proto_tree_add_uint_format(dcm_tree, hf_dcm_pdv_flags, tvb, offset, 1,
- flags, "Flags: 0x%x (%s)", flags, dcm_flags2str(flags));
+ (*pdv)->desc = desc_header;
+ pdv_flags_pitem = proto_tree_add_uint_format(tree, hf_dcm_pdv_flags, tvb, offset, 1,
+ flags, "Flags: 0x%02x (%s)", flags, desc_flag);
+
+ if (flags>3) {
+ expert_add_info_format(pinfo, pdv_flags_pitem, PI_MALFORMED, PI_ERROR, "Invalid Flags");
+ }
offset +=1;
- if (0x1 & flags)
- syntax = DCM_ILE;
- else if (di->syntax == DCM_UNK) {
- const guint8 *val;
- tlen = datalen - offset;
- val = tvb_get_ptr(tvb, offset, tlen+8);
- proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
- offset, tlen, val, "(%04x,%04x) %-8x Unparsed data", 0, 0, tlen);
- offset = datalen;
- } else
- syntax = di->syntax;
+ return offset;
+}
- /* Since we can have multiple PDU per packet (offset) and
- multiple merged packets per PDU (tvb->raw_offset)
- we need both to uniquely identify a PDU
- */
- pdu=dcm_pdu_state_get_or_create(dcm_data, pinfo->fd->num, tvb->raw_offset+offset);
+static int
+dissect_dcm_pdv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ dcm_state_assoc_t *assoc, int offset, guint32 pdv_len, guchar **pdv_description)
+{
+ /* Handle one PDV inside a data PDU */
- if (pdu==NULL) {
- return 0; /* Failed to allocate memory */
+#define D_HEADER 1
+#define D_TAG 2
+#define D_VR 3
+#define D_LEN2 4
+#define D_LEN4 5
+#define D_VALUE 6
+
+ dcm_state_pdv_t *pdv = NULL;
+
+ proto_item *pitem = NULL;
+
+ int toffset, state, vr = 0, tr = 0;
+ guchar *flag_description=NULL;
+
+ guint32 tag_value_fragment_len; /* used for values that span multiple PDVs */
+
+ guint16 grp = 0, elm = 0;
+ guint32 tlen = 0;
+ guint32 nlen = 0; /* Length of next sub item */
+
+ guint32 tag_rlen = 0;
+
+ guchar *tag_value=NULL; /* used for commands only so far */
+
+ guint8 syntax;
+
+ const guint8 *val=NULL;
+
+ int endpos = offset + pdv_len;
+
+ /* Dissect Context ID, Command/Data flag and More Fragments flag */
+ offset = dissect_dcm_pdv_header(tvb, pinfo, tree, assoc, offset, &syntax, &pdv);
+
+ if (have_tap_listener(dicom_eo_tap) && pdv->data_len==0) {
+ /* If not yet done, copy pure dicom data to buffer when running in export object mode */
+ pdv->data_len = endpos-offset;
+ pdv->data = se_alloc(pdv->data_len);
+ if (pdv->data) {
+ g_memmove(pdv->data, tvb_get_ptr(tvb, offset, pdv->data_len), pdv->data_len);
+ }
+ else {
+ pdv->data_len = 0; /* Failed to allocate memory. Don't copy anything */
+ }
}
- else if (pdu != dcm_data->first_pdu) {
- /* Not frist PDU in association (Those don't have remaining data to parse :-) */
- if (pdu->prev->tag_rlen>0) {
- /* previous PDU has left overs, i.e. this is a continuation PDU */
+ if (pdv->prev) {
+ /* Not frist PDV in the give presentation context (Those don't have remaining data to parse :-) */
- const guint8 *val;
+ if (pdv->prev->open_tag_rlen > 0) {
+ /* previous PDV has left overs, i.e. this is a continuation PDV */
- if (datalen - offset>=(int)pdu->prev->tag_rlen) {
+ if (endpos - offset >= (int)pdv->prev->open_tag_rlen) {
/*
* Remaining bytes are equal or more than we expect for the open tag
* Finally reach the end of this tag
*/
+ tag_value_fragment_len = pdv->prev->open_tag_rlen;
- val = tvb_get_ptr(tvb, offset, pdu->prev->tag_rlen);
+ pdv->open_tag_len = 0;
+ pdv->open_tag_rlen = 0;
+ pdv->open_tag_desc = NULL;
+ pdv->initalized = TRUE;
- proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
- offset, pdu->prev->tag_rlen, val, "%s [end]", pdu->prev->tag_desc);
+ pdv->is_corrupt = FALSE;
+ }
+ else if (pdv->is_flagvalid && pdv->is_last_fragment) {
+ /*
+ * The tag is not yet complete, however, the flag indicates that it should be
+ * Therefore end this tag and issue an expert_add_info
+ */
- offset+=pdu->prev->tag_rlen;
+ tag_value_fragment_len = endpos - offset;
+ pdv->open_tag_len = 0;
+ pdv->open_tag_rlen = 0;
+ pdv->open_tag_desc = NULL;
+ pdv->initalized = TRUE;
+ pdv->is_corrupt = TRUE;
}
else {
/*
* More to do for this tag
*/
- tag_value_fragment_len = datalen - offset;
-
- val = tvb_get_ptr(tvb, offset, tag_value_fragment_len);
+ tag_value_fragment_len = endpos - offset;
+
+ /* Set data in current PDV structure */
+ pdv->open_tag_len = pdv->prev->open_tag_len;
+ pdv->open_tag_rlen = pdv->prev->open_tag_rlen - tag_value_fragment_len;
+ pdv->open_tag_desc = pdv->prev->open_tag_desc;
+ pdv->initalized = TRUE;
+ pdv->is_corrupt = FALSE;
+ }
- proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
- offset, tag_value_fragment_len, val, "%s [contiuation]", pdu->prev->tag_desc);
+ val = tvb_get_ptr(tvb, offset, tag_value_fragment_len);
- offset+=tag_value_fragment_len;
+ if (pdv->is_corrupt) {
+ pitem = proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
+ offset, tag_value_fragment_len, val, "%s [incomplete]",
+ pdv->prev->open_tag_desc);
- /* Update data in PDU structure */
- pdu->tag_rlen=pdu->prev->tag_rlen-tag_value_fragment_len;
- pdu->tag_desc=pdu->prev->tag_desc;
- pdu->valid = TRUE;
+ expert_add_info_format(pinfo, pitem, PI_MALFORMED, PI_ERROR,
+ "Early termination of tag. Data is missing");
}
+ else {
+ proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
+ offset, tag_value_fragment_len, val, "%s Bytes %d - %d [%s]",
+ pdv->prev->open_tag_desc,
+ pdv->prev->open_tag_len - pdv->prev->open_tag_rlen + 1,
+ pdv->prev->open_tag_len - pdv->open_tag_rlen,
+ (pdv->open_tag_rlen > 0 ? "continuation" : "end") );
+ }
+
+ offset += tag_value_fragment_len;
}
}
- toffset = offset;
+ if (syntax == DCM_UNK) {
+ const guint8 *val;
+ tlen = endpos - offset;
+ val = tvb_get_ptr(tvb, offset, tlen); /* Verify */
+ proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
+ offset, tlen, val, "(%04x,%04x) %-8x Unparsed data", 0, 0, tlen);
+ offset = pdv_len;
+ }
+ /* Command Tags
+ 2 Group
+ 2 Element
+ 4 unsigned Length n
+ n Value, always in Implicit Little VR
+ */
+ toffset = offset;
state = D_TAG;
nlen = 4;
while (offset + nlen <= (guint32) endpos) {
@@ -1477,20 +2234,35 @@ dissect_dcm_data(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, packet_inf
int totlen = (offset - toffset);
if (0xffffffff == tlen || 0xfffe == grp) {
val = tvb_get_ptr(tvb, toffset, totlen);
- proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
+ proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
toffset, totlen, val,
"(%04x,%04x) %-8x %s", grp, elm, tlen,
- dcm_tag2str(grp, elm, syntax, tvb, offset, 0, vr, tr));
+ dcm_tag2str(grp, elm, syntax, tvb, offset, 0, vr, tr, &tag_value));
+
tlen = 0;
/* } else if (0xfffe == grp) { */ /* need to make a sub-tree here */
} else {
totlen += tlen;
val = tvb_get_ptr(tvb, toffset, totlen);
- proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
+ proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
toffset, totlen, val,
"(%04x,%04x) %-8x %s", grp, elm, tlen,
- dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, tr));
+ dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, tr, &tag_value));
+ }
+
+
+ /* Store SOP Class and Instance UID in first PDV of this object */
+ if (grp == 0x0008 && elm == 0x0016) {
+ dcm_state_pdv_get_obj_start(pdv)->sop_class_uid = g_strdup(tag_value);
}
+ else if (grp == 0x0008 && elm == 0x0018) {
+ dcm_state_pdv_get_obj_start(pdv)->sop_instance_uid = g_strdup(tag_value);
+ }
+ else if (grp == 0x0000 && elm == 0x0100) {
+ /* This is the command tag -> overwrite existing PDV description */
+ pdv->desc = g_strdup(tag_value);
+ }
+
offset += tlen;
state = D_TAG;
nlen = 4;
@@ -1504,13 +2276,14 @@ dissect_dcm_data(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, packet_inf
But if we are in a not detected continutation we may also get 'stuck' in state D_VALUE
*/
-
if (D_VALUE == state) {
const guint8 *val;
guchar *buf;
- tag_value_fragment_len = datalen - offset;
+ tag_value_fragment_len = pdv_len - offset + 10; /* The 10 is a result of debugging :-((
+ Fix once the Tag parisng has been structured
+ */
val = tvb_get_ptr(tvb, offset, tag_value_fragment_len);
buf=ep_alloc(2048); /* Longer than what dcm_tag2str() returns */
@@ -1518,83 +2291,118 @@ dissect_dcm_data(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, packet_inf
g_snprintf(buf, 2048, "(%04x,%04x) %-8x %s",
grp, elm, tlen,
- dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, tr));
+ dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, DCM_OTH, &tag_value));
- proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
- offset, tag_value_fragment_len, val, "%s [start]", buf);
+ proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
+ offset, tag_value_fragment_len, val, "%s Bytes %10d - %10d [start]", buf, 1, tag_value_fragment_len);
- if (!pdu->valid) {
- /* First time parsing of this PDU.
- Save the needed data for reuse, i.e. when called just to open a particular packet
+ if (!pdv->initalized) {
+ /* First time parsing of this PDV.
+ Save the needed data for reuse, i.e. when being called just to open a particular packet
*/
- pdu->tag_rlen=tlen-tag_value_fragment_len;
- pdu->tag_desc=buf;
- pdu->valid = TRUE;
+ pdv->open_tag_len = tlen;
+ pdv->open_tag_rlen = tlen-tag_value_fragment_len;
+ pdv->open_tag_desc = g_strdup(buf); /* EP memory will be freeded. Therefore copy */
+ pdv->initalized = TRUE;
}
}
else {
- if (!pdu->valid) {
- /* First time parsing of this PDU.
+ if (!pdv->initalized) {
+ /* First time parsing of this PDV.
Save the needed data for reuse, i.e. when called just to open a particular packet
*/
- pdu->tag_rlen=0;
- pdu->tag_desc=NULL;
- pdu->valid = TRUE;
+ pdv->open_tag_len = 0;
+ pdv->open_tag_rlen = 0;
+ pdv->open_tag_desc = NULL;
+ pdv->initalized = TRUE;
+ }
+ }
+
+ if (have_tap_listener(dicom_eo_tap) && pdv->data_len>0) {
+ if (pdv->is_last_fragment) {
+ dcm_export_create_object(pinfo, assoc, pdv);
}
}
+ *pdv_description = pdv->desc;
+
return endpos;
}
-static dcmState_t *
-dcm_state_get_or_create(packet_info *pinfo, gboolean create_dcm_data)
+static int
+dissect_dcm_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ dcm_state_assoc_t *assoc, int offset, guint32 pdu_len, guchar **pdu_description)
{
- /* Get or create converstation and DICOM data structure if desired
- Return new or existing dicom struture, which is used to store context IDs and xfer Syntax
- Return NULL in case of the structure couldn't be created
+ /* 04 P-DATA-TF
+ 1 1 reserved
+ 2 4 length
+ - (1+) presentation data value (PDV) items
+ 6 4 length
+ 10 1 Presentation Context ID (odd ints 1 - 255)
+ - PDV
+ 11 1 header
+ 0x01 if set, contains Message Command info, else Message Data
+ 0x02 if set, contains last fragment
*/
- conversation_t *conv=NULL;
- dcmState_t *dcm_data=NULL;
+ proto_tree *pdv_ptree = NULL; /* Tree for item details */
+ proto_item *pdv_pitem = NULL;
- conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
- pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+ guchar *buf_desc=NULL; /* PDU description */
+ guchar *pdv_description=NULL;
- if (conv == NULL) { /* conversation does not exist, create one */
- conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
- pinfo->srcport, pinfo->destport, 0);
- }
- else { /* conversation exists, try to get data already filled */
- dcm_data = conversation_get_proto_data(conv, proto_dcm);
- }
+ gboolean first_pdv=TRUE;
- if (dcm_data == NULL && create_dcm_data) {
+ int endpos = offset + pdu_len;
+ int pdv_len=0;
- dcm_data = mkds();
- if (dcm_data != NULL) {
- conversation_add_proto_data(conv, proto_dcm, dcm_data);
+ buf_desc=se_alloc(MAX_BUFFER); /* Valid for this capture, since we return this buffer */
+ buf_desc[0]=0;
+
+ /* Loop thorugh multiple PDVs */
+ while (offset < endpos) {
+ pdv_len = tvb_get_ntohl(tvb, offset);
+ DISSECTOR_ASSERT(pdv_len > 0);
+
+ pdv_pitem = proto_tree_add_text(tree, tvb, offset, pdv_len+4, "PDV");
+ pdv_ptree = proto_item_add_subtree(pdv_pitem, ett_dcm_data_pdv);
+
+ proto_tree_add_item(pdv_ptree, hf_dcm_pdv_len, tvb, offset, 4, FALSE);
+ offset +=4;
+
+ offset = dissect_dcm_pdv(tvb, pinfo, pdv_ptree, assoc, offset, pdv_len, &pdv_description);
+
+ if (first_pdv) {
+ g_snprintf(buf_desc, MAX_BUFFER, "%s", pdv_description);
+ }
+ else {
+ g_snprintf(buf_desc, MAX_BUFFER, "%s, %s", buf_desc, pdv_description);
}
- /* Mark it as DICOM conversation. Needed for the heuristic mode,
- to prevent stealing subsequent packets by other dissectors
- */
- conversation_set_dissector(conv, dcm_handle);
+ proto_item_append_text(pdv_pitem, ", %s", pdv_description);
+ first_pdv=FALSE;
+
+
+ /* offset should be advanced by pdv_len */
}
- return dcm_data;
+ *pdu_description=buf_desc;
+ return offset;
}
-/* Code to actually dissect the packets */
+
static int
dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean require_assoc_req)
{
- guint8 pdu_type;
- guint32 pdu_start;
- guint32 pdu_len;
- guint16 vers;
- guint32 tlen;
+ /* Code to actually dissect the packets */
+
+ guint8 pdu_type=0;
+ guint32 pdu_start=0;
+ guint32 pdu_len=0;
+ guint16 vers=0;
+ guint32 tlen=0;
int offset=0;
@@ -1608,9 +2416,7 @@ dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean r
traffic.
Therfore do the byte checking as early as possible
-
- TBD: for the heurisitc hook, check for a converstation
-
+ The heurisitc hook, checks for an association request
DICOM PDU are nice, but need to be managed
@@ -1626,22 +2432,40 @@ dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean r
per Tag, and PDU recombinaion here would
a) need to eliminate PDU/PDV/Ctx header (12 bytes)
b) not show the true DICOM logic in transfer
+
+ The Lenght check is tricky. If not a PDV continuation, 10 Bytes are required. For PDV continuation
+ anything seems to be possible, depending on the buffer alignment of the sending process
+
+ I have seen a 4 Byte PDU 'Header' just at the end of a TCP packet, which will come in here
+ as tlen with 4 bytes.
*/
tlen = tvb_reported_length(tvb);
- if (tlen<10) /* Not long enough */
- return 0; /* No bytes taken from the stack */
pdu_type = tvb_get_guint8(tvb, 0);
- if (pdu_type==0 || pdu_type>7) /* Wrong PDU type. Or is slightly more efficient than and here */
+ if (pdu_type==0 || pdu_type>7) /* Wrong PDU type. 'Or' is slightly more efficient than 'and' */
+ return 0; /* No bytes taken from the stack */
+
+ if (pdu_type==4) {
+ if (tlen<2) {
+ /* Hopefully we don't have 1 Byte PDUs in PDV continuations, otherwise reduce to 1 */
+ return 0;
+ }
+ else if (tlen<6) {
+ /* we need 6 bytes at least to get PDU length */
+ pinfo->desegment_offset = offset;
+ pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
+ return TRUE;
+ }
+ }
+ else if (tlen<10) {
return 0;
+ }
pdu_len = tvb_get_ntohl(tvb, 2);
- if (pdu_len<6) /* Not long enough */
+ if (pdu_len<4) /* The smallest PDUs are ASSOC Rejects & Release Msgs */
return 0;
- vers = tvb_get_ntohs(tvb, 6);
-
if (require_assoc_req) {
/* find_conversation() seems to return a converstation, even if we never saw
@@ -1650,10 +2474,12 @@ dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean r
Therefore also check, if we already stored configuration data for converstation
*/
- if (dcm_state_get_or_create(pinfo, FALSE)==NULL) {
-
+ if (dcm_state_get(pinfo, FALSE)==NULL) {
+
/* config data does not exist, check for association request */
+ vers = tvb_get_ntohs(tvb, 6);
+
if (!(pdu_type == 1 && vers == 1)) { /* Not PDU type 0x01 or not Version 1 */
return 0;
}
@@ -1662,7 +2488,7 @@ dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean r
ok. for PRESENTATION_DATA, questionable for ASSOCIATION requests
*/
if (pdu_len+6 < tlen)
- return 0;
+ return 0;
}
}
@@ -1671,10 +2497,6 @@ dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean r
col_clear(pinfo->cinfo, COL_PROTOCOL);
}
-
- pdu_start = 0;
- pdu_len = tvb_get_ntohl(tvb, pdu_start+2);
-
/* Process all PDUs in the buffer */
while (pdu_start < tlen) {
@@ -1687,10 +2509,10 @@ dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean r
pinfo->desegment_offset = offset;
pinfo->desegment_len = (pdu_len+6) - (tlen-offset);
- /* Why return a boolean for a deliberate int function?
- No better working example found.
+ /* Why return a boolean for a deliberate int function? No clue, but
+ no better working example found.
*/
- return TRUE;
+ return TRUE;
}
/* Process a whole PDU */
@@ -1698,16 +2520,16 @@ dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean r
/* Next PDU */
pdu_start = pdu_start + pdu_len + 6;
- if (pdu_start < tlen) {
- pdu_len = tvb_get_ntohl(tvb, pdu_start+2);
+
+ if (pdu_start < tlen - 6) {
+ /* we got at least 6 bytes of the next PDU still in the buffer */
+ pdu_len = tvb_get_ntohl(tvb, pdu_start+2);
}
else {
pdu_len = 0;
}
}
-
return offset;
-
}
/* Call back functions used to register */
@@ -1729,23 +2551,29 @@ dissect_dcm_heuristic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
static int
dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
{
- proto_item *ti;
- dcmState_t *dcm_data;
- proto_tree *dcm_tree;
+ proto_item *ti=NULL;
+ proto_item *ti_pdu_type=NULL;
+
+ dcm_state_t *dcm_data=NULL;
+ dcm_state_assoc_t *assoc=NULL;
+
+ proto_tree *dcm_tree=NULL;
- guint8 pdu_type;
- guint32 pdu_len;
+ guint8 pdu_type=0;
+ guint32 pdu_len=0;
+
+ guchar *pdu_description=NULL;
int assoc_header=0;
gboolean valid_pdutype=TRUE;
- char *buf;
- const char *info_str = NULL;
+ guchar *buf=NULL;
+ guchar *info_str = NULL;
/* Get or create converstation. Used to store context IDs and xfer Syntax */
- dcm_data = dcm_state_get_or_create(pinfo, TRUE);
+ dcm_data = dcm_state_get(pinfo, TRUE);
if (dcm_data == NULL) { /* internal error. Failed to create main dicom data structre */
return 0;
}
@@ -1762,47 +2590,56 @@ dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
if (check_col(pinfo->cinfo, COL_INFO))
col_clear(pinfo->cinfo, COL_INFO);
+ if (pdu_type==1) {
+ /* 'Force' new association object */
+ assoc=dcm_state_assoc_new(dcm_data, pinfo->fd->num);
+ }
+ else {
+ /* Create new association object, if needed, i.e. if we association request is not in capture */
+ assoc=dcm_state_assoc_get(dcm_data, pinfo->fd->num, TRUE);
+ }
+
+ if (assoc == NULL) { /* internal error. Failed to association structre */
+ return 0;
+ }
+
+ info_str=ep_alloc(MAX_BUFFER);
+ info_str[0]=0;
+
pdu_type = tvb_get_guint8(tvb, offset);
pdu_len = tvb_get_ntohl(tvb, offset + 2);
switch (pdu_type) {
case 1: /* ASSOC Request */
- tvb_memcpy(tvb, dcm_data->ae_called, 10, 16);
- tvb_memcpy(tvb, dcm_data->ae_calling, 26, 16);
- dcm_data->ae_called[AEEND] = 0;
- dcm_data->ae_calling[AEEND] = 0;
- buf = ep_alloc(128);
- g_snprintf(buf, 128, "A-ASSOCIATE request %s --> %s",
- trim(dcm_data->ae_calling), trim(dcm_data->ae_called));
- info_str = buf;
+ tvb_memcpy(tvb, assoc->ae_called, 10, 16);
+ tvb_memcpy(tvb, assoc->ae_calling, 26, 16);
+ assoc->ae_called[AEEND] = 0;
+ assoc->ae_calling[AEEND] = 0;
+ g_snprintf(info_str, 128, "A-ASSOCIATE request %s --> %s",
+ g_strstrip(assoc->ae_calling), g_strstrip(assoc->ae_called));
assoc_header = 74;
break;
case 2: /* ASSOC Accept */
- tvb_memcpy(tvb, dcm_data->ae_called_resp, 10, 16);
- tvb_memcpy(tvb, dcm_data->ae_calling_resp, 26, 16);
- dcm_data->ae_called_resp[AEEND] = 0;
- dcm_data->ae_calling_resp[AEEND] = 0;
- buf = ep_alloc(128);
- g_snprintf(buf, 128, "A-ASSOCIATE accept %s <-- %s",
- trim(dcm_data->ae_calling_resp), trim(dcm_data->ae_called_resp));
- info_str = buf;
+ tvb_memcpy(tvb, assoc->ae_called_resp, 10, 16);
+ tvb_memcpy(tvb, assoc->ae_calling_resp, 26, 16);
+ assoc->ae_called_resp[AEEND] = 0;
+ assoc->ae_calling_resp[AEEND] = 0;
+ g_snprintf(info_str, MAX_BUFFER, "A-ASSOCIATE accept %s <-- %s",
+ g_strstrip(assoc->ae_calling_resp), g_strstrip(assoc->ae_called_resp));
assoc_header = 74;
break;
case 3: /* ASSOC Reject */
- dcm_data->result = tvb_get_guint8(tvb, 7);
- dcm_data->source = tvb_get_guint8(tvb, 8);
- dcm_data->reason = tvb_get_guint8(tvb, 9);
- buf = ep_alloc(128);
- g_snprintf(buf, 128, "A-ASSOCIATE reject %s <-- %s %s %s %s",
- trim(dcm_data->ae_calling_resp), trim(dcm_data->ae_called_resp),
- dcm_result2str(dcm_data->result),
- dcm_source2str(dcm_data->source),
- dcm_reason2str(dcm_data->source, dcm_data->reason));
- info_str = buf;
- offset += pdu_len+6;
+ assoc->result = tvb_get_guint8(tvb, 7);
+ assoc->source = tvb_get_guint8(tvb, 8);
+ assoc->reason = tvb_get_guint8(tvb, 9);
+ g_snprintf(info_str, 128, "A-ASSOCIATE reject %s <-- %s %s %s %s",
+ g_strstrip(assoc->ae_calling), g_strstrip(assoc->ae_called),
+ dcm_result2str(assoc->result),
+ dcm_source2str(assoc->source),
+ dcm_reason2str(assoc->source, assoc->reason));
break;
case 4: /* DATA */
- info_str="PRESENTATION-DATA";
+ info_str="P-DATA";
break;
case 5: /* RELEASE Request */
info_str="A-RELEASE request";
@@ -1811,15 +2648,13 @@ dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
info_str="A-RELEASE response";
break;
case 7: /* ABORT */
- dcm_data->source = tvb_get_guint8(tvb, 8);
- dcm_data->reason = tvb_get_guint8(tvb, 9);
- buf = ep_alloc(128);
- g_snprintf(buf, 128, "ABORT %s <-- %s %s %s",
- dcm_data->ae_called, dcm_data->ae_calling,
- (dcm_data->source == 1) ? "USER" :
- (dcm_data->source == 2) ? "PROVIDER" : "",
- dcm_data->source == 1 ? dcm_abort2str(dcm_data->reason) : "");
- info_str = buf;
+ assoc->source = tvb_get_guint8(tvb, 8);
+ assoc->reason = tvb_get_guint8(tvb, 9);
+ g_snprintf(info_str, 128, "ABORT %s <-- %s %s %s",
+ assoc->ae_called, assoc->ae_calling,
+ (assoc->source == 1) ? "USER" :
+ (assoc->source == 2) ? "PROVIDER" : "",
+ assoc->source == 1 ? dcm_abort2str(assoc->reason) : "");
break;
default:
info_str="Continuation or non-DICOM traffic";
@@ -1832,7 +2667,7 @@ dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
if (valid_pdutype) {
- if (tree) {
+ if (tree || have_tap_listener(dicom_eo_tap)) {
/* In the interest of speed, if "tree" is NULL, don't do any work not
necessary to generate protocol tree items.
*/
@@ -1840,19 +2675,31 @@ dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
proto_item *tf;
ti = proto_tree_add_item(tree, proto_dcm, tvb, offset, -1, FALSE);
dcm_tree = proto_item_add_subtree(ti, ett_dcm);
- proto_tree_add_uint_format(dcm_tree, hf_dcm_pdu, tvb, offset, pdu_len+6,
+ ti_pdu_type = proto_tree_add_uint_format(dcm_tree, hf_dcm_pdu, tvb, offset, pdu_len+6,
pdu_type, "PDU Type 0x%x (%s)", pdu_type, dcm_pdu2str(pdu_type));
proto_tree_add_item(dcm_tree, hf_dcm_pdu_len, tvb, offset+2, 4, FALSE);
+ if (pdu_type==3) {
+ expert_add_info_format(pinfo, ti_pdu_type, PI_RESPONSE_CODE, PI_WARN, "Asscociation rejected");
+ }
+ else if (pdu_type==7) {
+ expert_add_info_format(pinfo, ti_pdu_type, PI_RESPONSE_CODE, PI_WARN, "Asscociation aborted");
+ }
+
switch (pdu_type) {
case 1: /* ASSOC Request */
case 2: /* ASSOC Accept */
tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, offset, pdu_len+6, info_str);
- offset = dissect_dcm_assoc(dcm_data, tf, tvb, offset+assoc_header, pdu_len+6-assoc_header);
+ offset = dissect_dcm_assoc(tvb, pinfo, tf, assoc, offset+assoc_header, pdu_len+6-assoc_header);
break;
+
case 4: /* DATA */
- tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, offset, pdu_len+6, info_str);
- offset = dissect_dcm_data(dcm_data, tf, tvb, pinfo, offset, pdu_len+6);
+ offset = dissect_dcm_data(tvb, pinfo, dcm_tree, assoc, offset+6, pdu_len, &pdu_description);
+ proto_item_append_text(ti, ", %s", pdu_description);
+
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", pdu_description);
+
break;
case 3: /* ASSOC Reject */
@@ -1861,7 +2708,6 @@ dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
case 7: /* ABORT */
/* Info string decoding only at this point */
offset += pdu_len+6;
-
break;
default:
@@ -1873,7 +2719,7 @@ dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
/* Always dissect Association request and response in order
to set the data strucures needed for the PDU Data packets
*/
- offset = dissect_dcm_assoc(dcm_data, NULL, tvb, offset+assoc_header, pdu_len+6-assoc_header);
+ offset = dissect_dcm_assoc(tvb, pinfo, NULL, assoc, offset+assoc_header, pdu_len+6-assoc_header);
}
}
@@ -1903,7 +2749,7 @@ static void dcm_apply_settings(void) {
/* deregister first */
range_foreach(global_dcm_tcp_range_backup, range_delete_dcm_tcp_callback);
g_free(global_dcm_tcp_range_backup);
-
+
heur_dissector_delete("tcp", dissect_dcm_heuristic, proto_dcm);
/* Register 'static' tcp port range specified in properties
@@ -1919,7 +2765,7 @@ static void dcm_apply_settings(void) {
/* Add heuristic search, if user selected it */
- if (global_dcm_heuristic)
+ if (global_dcm_heuristic)
heur_dissector_add("tcp", dissect_dcm_heuristic, proto_dcm);
}
@@ -1936,7 +2782,7 @@ proto_register_dcm(void)
FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
{ &hf_dcm_assoc_item_type, { "Item Type", "dicom.assoc.item.type",
FT_UINT8, BASE_HEX, VALS(dcm_assoc_item_type), 0, "", HFILL } },
- { &hf_dcm_assoc_item_len, { "Item Len", "dicom.assoc.item.len",
+ { &hf_dcm_assoc_item_len, { "Item Length", "dicom.assoc.item.len",
FT_UINT16, BASE_DEC, NULL, 0, "", HFILL } },
{ &hf_dcm_actx, { "Application Context", "dicom.actx",
FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
@@ -1952,7 +2798,7 @@ proto_register_dcm(void)
FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
{ &hf_dcm_info_version, { "Implementation Version", "dicom.userinfo.version",
FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
- { &hf_dcm_pdu_maxlen, { "MAX PDU LENGTH", "dicom.max_pdu_len",
+ { &hf_dcm_pdu_maxlen, { "Max PDU Length", "dicom.max_pdu_len",
FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
{ &hf_dcm_pdv_len, { "PDV Length", "dicom.pdv.len",
FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
@@ -1972,10 +2818,16 @@ proto_register_dcm(void)
static gint *ett[] = {
&ett_dcm,
&ett_assoc,
- &ett_assoc_item,
+ &ett_assoc_actx,
&ett_assoc_pctx,
- &ett_assoc_userinfo,
- &ett_dcm_data
+ &ett_assoc_pctx_abss,
+ &ett_assoc_pctx_xfer,
+ &ett_assoc_info,
+ &ett_assoc_info_uid,
+ &ett_assoc_info_version,
+ &ett_dcm_data,
+ &ett_dcm_data_pdv,
+ &ett_dcm_data_tag
};
module_t *dcm_module;
@@ -2001,6 +2853,15 @@ proto_register_dcm(void)
"Disabled by default, to preserve resources for the non DICOM community.",
&global_dcm_heuristic);
+ prefs_register_bool_preference(dcm_module, "header",
+ "Create Meta Header on Export",
+ "Create DICOM File Meta Header according to PS 3.10 on export for PDUs. "
+ "If the cpatured PDV does not contain a SOP Class UID and SOP Instance UID "
+ "(e.g. for command PDVs), wireshark spefic ones will be created.",
+ &global_dcm_header);
+
+ dicom_eo_tap = register_tap("dicom_eo"); /* DICOM Export Object tap */
+
register_init_routine(&dcm_init);
}
diff --git a/epan/dissectors/packet-dcm.h b/epan/dissectors/packet-dcm.h
index b107f7f33e..246d8049cb 100644
--- a/epan/dissectors/packet-dcm.h
+++ b/epan/dissectors/packet-dcm.h
@@ -27,6 +27,16 @@
#ifndef PACKET_DCM_H
#define PACKET_DCM_H
+/* Used for DICOM Export Object feature */
+typedef struct _dicom_eo_t {
+ guint32 pkt_num;
+ gchar *hostname;
+ gchar *filename;
+ gchar *content_type;
+ guint32 payload_len;
+ const guint8 *payload_data;
+} dicom_eo_t;
+
/* ---------------------------------------------------------------------
* DICOM UID Definitions