2 * Routines for DICOM dissection
3 * Copyright 2003, Rich Coe <Richard.Coe@med.ge.com>
4 * Copyright 2008, David Aggeler <david_aggeler@hispeed.ch>
6 * DICOM communication protocol
7 * http://medical.nema.org/dicom/2008
8 * DICOM Part 8: Network Communication Support for Message Exchange
10 * (NOTE: you need to turn on 'Allow subdissector to desegment TCP streams'
11 * in Preferences/Protocols/TCP Option menu, in order to view
12 * DICOM packets correctly.
13 * Also, you might have to turn off tcp.check_checksum if tcp
14 * detects that the checksum is bad - for example, if you're
15 * capturing on a network interface that does TCP checksum
16 * offloading and you're capturing outgoing packets.
17 * This should probably be documented somewhere besides here.)
21 * Wireshark - Network traffic analyzer
22 * By Gerald Combs <gerald@wireshark.org>
23 * Copyright 1998 Gerald Combs
25 * This program is free software; you can redistribute it and/or
26 * modify it under the terms of the GNU General Public License
27 * as published by the Free Software Foundation; either version 2
28 * of the License, or (at your option) any later version.
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
35 * You should have received a copy of the GNU General Public License
36 * along with this program; if not, write to the Free Software
37 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
45 * This is my first pass at a Wireshark dissector to display
46 * DICOM (Digital Imaging and Communications in Medicine) packets.
48 * - It currently displays most of the DICOM packets.
50 * - I've used it to debug Query/Retrieve, Storage, and Echo protocols.
52 * - Not all DICOM tags are currently displayed symbolically.
53 * Unknown tags are displayed as '(unknown)'
54 * More known tags might be added in the future.
55 * If the tag data contains a string, it will be displayed.
56 * Even if the tag contains Explicit VR, it is not currently used to
57 * symbolically display the data. Consider this a future enhancement.
59 * - The 'value to string' routines should probably be hash lookups.
61 * 9 Nov 2004, Rich Coe
62 * - Fixed the heuristic code -- sometimes a conversation already exists
63 * - Fixed the dissect code to display all the tags in the pdu
65 * 28 Apr 2005, Rich Coe
66 * - fix memory leak when Assoc packet is processed repeatedly in wireshark
68 * - removed unused partial packet flag
70 * - added better support for DICOM VR
72 * - report actual VR in packet display, if supplied by xfer syntax
73 * - show that we are not displaying entire tag string with '[...]',
74 * some tags can hold up to 2^32-1 chars
76 * - remove my goofy attempt at trying to get access to the fragmented packets
77 * (anyone have an idea on how to fix this ???)
79 * - process all the data in the Assoc packet even if display is off
81 * - limit display of data in Assoc packet to defined size of the data even
82 * if reported size is larger
84 * - show the last tag in a packet as [incomplete] if we don't have all the data
86 * - added framework for reporting DICOM async negotiation (not finished)
87 * (I'm not aware of an implementation which currently supports this)
90 * May 23 2008, David Aggeler
92 * - Added Class UID lookup, both in the association and in the transfer
93 * - Better hierarchy for items in Association request/response and therefore better overview
94 * This was a major rework. Abstract Syntax & Transfer Syntax are now children
95 * of a presentation context and therefore grouped. User Info is now grouped.
96 * - Re-assemble PDVs that span multiple PDUs, i.e fix continuation packets
97 * This caused significant changes to the data structures
98 * - Added preference with dicom tcp ports, to prevent 'stealing' the converstation
99 * i.e. don't just rely on heuristic
100 * - Use pinfo->desegment_len instead of tcp_dissect_pdus()
101 * - Returns number of bytes parsed
102 * - For non DICOM packets, do not allocate any memory anymore,
103 * - Added one DISSECTOR_ASSERT() to prevent loop with len==0. More to come
104 * - Heuristic search is optional to save resources for non DICOM users
106 * - Output naming closer to DICOM Standard
107 * - Variable names closer to Standard
108 * - Protocol in now called DICOM not dcm anymore.
109 * - Fixed type of a few variables to guchar instead of guint8
110 * - Changed some of the length displays to decimal, because the hex value can
111 * already be seen in the packet and decimal is easier for length calculation
114 * Jun 17 2008, David Aggeler
116 * - Support multiple PDVs per PDU
117 * - Better summary, in PDV, PDU header and in INFO Column, e.g. show commands like C-STORE
118 * - Fixed Association Reject (was working before my changes)
119 * - Fixed PDV Continuation with very small packets. Reduced minimum packet length
120 * from 10 to 2 Bytes for PDU Type 4
121 * - Fixed PDV Continuation. Last packet was not found correctly.
122 * - Fixed complilation warning (build 56 on solaris)
123 * - Fixed tree expansion (hf_dcm_xxx)
124 * - Added expert_add_info() for Assoctiation Reject
125 * - Added expert_add_info() for Assoctiation Abort
126 * - Added expert_add_info() for short PDVs (i.e. last fragment, but PDV is not completed yet)
127 * - Clarified and grouped data structures and its related code (dcmItem, dcmState) to have
128 * consistent _new() & _get() functions and to be be according to coding conventions
129 * - Added more function declaration to be more consistent
130 * - All dissect_dcm_xx now have (almost) the same parameter order
131 * - Removed DISSECTOR_ASSERT() for packet data errors. Not designed to handle this.
132 * - Handle multiple DICOM Associations in a capture correctly, i.e. if presentation contexts are different.
134 * Jul 17 2008, David Aggeler
136 * - Export objects as part 10 compliant DICOM file. Finally, this major milestone has beed reached.
137 * - PDVs are now a child of the PCTX rather than the ASSOC object.
138 * - Fixed PDV continuation for unknown tags (e.g. RT Structure Set)
139 * - Replaced proprietary trim() with g_strstrip()
140 * - Fixed strings that are displayed with /000 (padding of odd length)
141 * - Added expert_add_info() for invalid flags and presentation context IDs
143 * ****************************************************************************************
145 * Decent error handlung for expert_add_info(), i.e. return value handling and info column text
146 * Support almost all tags
147 * Show tags as subtree
148 * Show Association Headers as individual items
149 * Cleanup types of offset & position
150 * Create subtrees for sequences
151 * Support item 56-59 in Accociation Request
163 #include <epan/prefs.h>
164 #include <epan/packet.h>
165 #include <epan/emem.h>
166 #include <epan/strutil.h>
167 #include <epan/conversation.h>
168 #include <epan/expert.h>
169 #include <epan/tap.h>
171 #include "packet-tcp.h"
173 #include "packet-dcm.h"
175 #define DICOM_DEFAULT_RANGE "104"
177 /* Many thanks to http://medicalconnections.co.uk/ for the GUID */
178 #define WIRESHARK_IMPLEMENTATION_UID "1.2.826.0.1.3680043.8.427.10"
179 #define WIRESHARK_MEDIA_STORAGE_SOP_CLASS_UID "1.2.826.0.1.3680043.8.427.11.1"
180 #define WIRESHARK_MEDIA_STORAGE_SOP_INSTANCE_UID_PREFIX "1.2.826.0.1.3680043.8.427.11.2"
181 #define WIRESHARK_IMPLEMENTATION_VERSION "WIRESHARK"
183 static range_t *global_dcm_tcp_range = NULL;
184 static range_t *global_dcm_tcp_range_backup = NULL; /* needed to deregister */
186 static gboolean global_dcm_heuristic = FALSE;
187 static gboolean global_dcm_header = TRUE;
189 /* Initialize the protocol and registered fields */
190 static int proto_dcm = -1;
192 static int dicom_eo_tap = -1;
194 static int hf_dcm_pdu = -1,
196 hf_dcm_pdu_type = -1,
197 hf_dcm_assoc_item_type = -1,
198 hf_dcm_assoc_item_len = -1,
201 hf_dcm_pctx_result = -1,
202 hf_dcm_pctx_abss_syntax = -1,
203 hf_dcm_pctx_xfer_syntax = -1,
204 hf_dcm_info_uid = -1,
205 hf_dcm_info_version = -1,
206 hf_dcm_pdu_maxlen = -1,
209 hf_dcm_pdv_flags = -1,
210 hf_dcm_data_tag = -1;
212 /* Initialize the subtree pointers */
218 ett_assoc_pctx_abss = -1,
219 ett_assoc_pctx_xfer = -1,
221 ett_assoc_info_uid = -1,
222 ett_assoc_info_version = -1,
224 ett_dcm_data_pdv = -1,
225 ett_dcm_data_tag = -1;
227 static dissector_handle_t dcm_handle;
229 static const value_string dcm_pdu_ids[] = {
230 { 1, "ASSOC Request" },
231 { 2, "ASSOC Accept" },
232 { 3, "ASSOC Reject" },
234 { 5, "RELEASE Request" },
235 { 6, "RELEASE Response" },
240 static const value_string dcm_assoc_item_type[] = {
241 { 0x10, "Application Context" },
242 { 0x20, "Presentation Context" },
243 { 0x21, "Presentation Context Reply" },
244 { 0x30, "Abstract Syntax" },
245 { 0x40, "Transfer Syntax" },
246 { 0x50, "User Info" },
247 { 0x51, "Max Length" },
248 { 0x52, "Implementation Class UID" },
249 { 0x55, "Implementation Version" },
254 Per Data PDV store data needed, to allow decoding of tags longer than a PDV
256 typedef struct dcm_state_pdv {
258 struct dcm_state_pdv *next, *prev;
260 gboolean initalized; /* define, wheter open_tag_len, open_tag_rlen and open_tag_desc have been set */
261 guint32 packet_no; /* Wireshark packet number, where pdv starts */
262 guint32 offset; /* Offset in packet, where PDV header starts */
264 gchar *desc; /* PDV description. se_alloc() */
266 guint8 pctx_id; /* Reference to used Presentation Context */
268 /* Used and filled for Export Object only */
269 gpointer data; /* Copy of PDV data without any PDU/PDV header */
270 guint32 data_len; /* Length of this PDV buffer. If >0, memory has been alocated */
272 gchar *sop_class_uid; /* SOP Class UID. Set in 1st PDV of a DICOM object. se_alloc() */
273 gchar *sop_instance_uid; /* SOP Instance UID. Set in 1st PDV of a DICOM object. se_alloc() */
276 gboolean is_storage; /* Ture, if the Data PDV is on the context of a storage SOP Class */
277 gboolean is_flagvalid; /* The following two flags are initalized correctly (TBD if needed) */
278 gboolean is_command; /* This PDV is a command rather than a data package */
279 gboolean is_last_fragment; /* Last Fragment bit was set, i.e. termination of an object
280 This flag delimits different dicom object in the same
282 gboolean is_corrupt; /* Early termination of long PDVs */
284 /* Used to reassemble PDVs */
285 guint32 open_tag_len; /* Tag lenght of 'oversized' tags. Used for display */
286 guint32 open_tag_rlen; /* Remining tag bytes to 'decoded' as binary data after this PDV */
287 gchar *open_tag_desc; /* last decoded description */
292 Per Presentation Context in an association store data needed, for subsequent decoding
294 typedef struct dcm_state_pctx {
296 struct dcm_state_pctx *next, *prev;
298 guint8 id; /* 0x20 Presentation Context ID */
299 guchar *abss_uid; /* 0x30 Abstract syntax */
300 guchar *abss_desc; /* 0x30 Abstract syntax decoded*/
301 guchar *xfer_uid; /* 0x40 Acepted Transfer syntax */
302 guchar *xfer_desc; /* 0x40 Acepted Transfer syntax decoded*/
303 guint8 syntax; /* Decoded transfer syntax */
304 #define DCM_ILE 0x01 /* implicit, little endian */
305 #define DCM_EBE 0x02 /* explicit, big endian */
306 #define DCM_ELE 0x03 /* explicit, little endian */
309 dcm_state_pdv_t *first_pdv, *last_pdv; /* List of PDV objects */
314 typedef struct dcm_state_assoc {
316 struct dcm_state_assoc *next, *prev;
318 dcm_state_pctx_t *first_pctx, *last_pctx; /* List of Presentation context objects */
320 guint32 packet_no; /* Wireshark packet number, where association starts */
323 guchar ae_called[1+AEEND]; /* Called AE tilte in A-ASSOCIATE RQ */
324 guchar ae_calling[1+AEEND]; /* Calling AE tilte in A-ASSOCIATE RQ */
325 guchar ae_called_resp[1+AEEND]; /* Called AE tilte in A-ASSOCIATE RP */
326 guchar ae_calling_resp[1+AEEND]; /* Calling AE tilte in A-ASSOCIATE RP */
327 guint8 source, result, reason;
330 typedef struct dcm_state {
332 struct dcm_state_assoc *first_assoc, *last_assoc;
334 gboolean valid; /* this conversation is a DICOM conversation */
339 /* Following defines around tags have a potential to be merged */
340 typedef struct dcmTag {
349 #define DCM_TSTAT 6 /* call dcm_rsp2str() on TINT2 */
352 #define DCM_SQ 9 /* sequence */
353 #define DCM_OTH 10 /* other */
357 #define DCM_VR_AE 1 /* Application Entity */
358 #define DCM_VR_AS 2 /* Age String */
359 #define DCM_VR_AT 3 /* Attribute Tag */
360 #define DCM_VR_CS 4 /* Code String */
361 #define DCM_VR_DA 5 /* Date */
362 #define DCM_VR_DS 6 /* Decimal String */
363 #define DCM_VR_DT 7 /* Date Time */
364 #define DCM_VR_FL 8 /* Floating Point Single */
365 #define DCM_VR_FD 9 /* Floating Point Double */
366 #define DCM_VR_IS 10 /* Integer String */
367 #define DCM_VR_LO 11 /* Long String */
368 #define DCM_VR_LT 12 /* Long Text */
369 #define DCM_VR_OB 13 /* Other Byte String */
370 #define DCM_VR_OF 14 /* Other Float String */
371 #define DCM_VR_OW 15 /* Other Word String */
372 #define DCM_VR_PN 16 /* Person Name */
373 #define DCM_VR_SH 17 /* Short String */
374 #define DCM_VR_SL 18 /* Signed Long */
375 #define DCM_VR_SQ 19 /* Sequence of Items */
376 #define DCM_VR_SS 20 /* Signed Short */
377 #define DCM_VR_ST 21 /* Short Text */
378 #define DCM_VR_TM 22 /* Time */
379 #define DCM_VR_UI 23 /* Unique Identifier (UID) */
380 #define DCM_VR_UL 24 /* Unsigned Long */
381 #define DCM_VR_UN 25 /* Unknown */
382 #define DCM_VR_US 26 /* Unsigned Short */
383 #define DCM_VR_UT 27 /* Unlimited Text */
385 /* Following must be in the same order as the defintions above */
386 static const guchar* dcm_tag_lookup[] = {
388 "AE","AS","AT","CS","DA","DS","DT","FL",
389 "FD","IS","LO","LT","OB","OF","OW","PN",
390 "SH","SL","SQ","SS","ST","TM","UI","UL",
395 static GHashTable *dcm_tagTable = NULL;
397 static dcmTag_t tagData[] = {
398 { 0x1, DCM_TRET, "(Ret) Length to End" },
399 { 0x2, DCM_TSTR, "Affected Class" },
400 { 0x3, DCM_TSTR, "Requested Class" },
401 { 0x0010, DCM_TRET, "(Ret) Recognition Code" },
402 { 0x0100, DCM_TCMD, "Command Field" },
403 { 0x0110, DCM_TINT2, "Message ID" },
404 { 0x0120, DCM_TINT2, "Resp Message ID" },
405 { 0x0200, DCM_TRET, "(Ret) Initiator" },
406 { 0x0300, DCM_TRET, "(Ret) Reciever" },
407 { 0x0400, DCM_TRET, "(Ret) Find Location" },
408 { 0x0600, DCM_TSTR, "Dest AE" },
409 { 0x0700, DCM_TINT2, "Priority" },
410 { 0x0800, DCM_TINT2, "Data Set (0x0101 means no data set present)" },
411 { 0x0850, DCM_TRET, "(Ret) Num Matches" },
412 { 0x0860, DCM_TRET, "(Ret) Resp Seq Num" },
413 { 0x0900, DCM_TSTAT, "Status" },
414 { 0x0901, DCM_TSTR, "Offending elm(s)" },
415 { 0x0902, DCM_TSTR, "Error Comment" },
416 { 0x0903, DCM_TINT2, "Error Id" },
417 { 0x1000, DCM_TSTR, "Affected Instance UID" },
418 { 0x1001, DCM_TSTR, "Requested Instance UID" },
419 { 0x1002, DCM_TINT2, "Event Type Id" },
420 { 0x1005, DCM_TSTR, "Attr Id List" },
421 { 0x1008, DCM_TINT2, "Action Type Id" },
422 { 0x1020, DCM_TINT2, "Num Remaining Ops" },
423 { 0x1021, DCM_TINT2, "Num Completed Ops" },
424 { 0x1022, DCM_TINT2, "Num Failed Ops" },
425 { 0x1023, DCM_TINT2, "Num Warning Ops" },
426 { 0x1030, DCM_TSTR, "Move ae_called AE" },
427 { 0x1031, DCM_TINT2, "Move ae_called Id" },
428 { 0x4000, DCM_TRET, "(Ret) DIALOG Recv'r" },
429 { 0x4010, DCM_TRET, "(Ret) Terminal Type" },
430 { 0x5010, DCM_TRET, "(Ret) Msg Set ID" },
431 { 0x5020, DCM_TRET, "(Ret) End Msg ID" },
432 { 0x5110, DCM_TRET, "(Ret) Display Fmt" },
433 { 0x5120, DCM_TRET, "(Ret) Page Position ID" },
434 { 0x5130, DCM_TRET, "(Ret) Text Fmt ID" },
435 { 0x5140, DCM_TRET, "(Ret) Nor/Rev" },
436 { 0x5150, DCM_TRET, "(Ret) Add Gray Scale" },
437 { 0x5160, DCM_TRET, "(Ret) Borders" },
438 { 0x5170, DCM_TRET, "(Ret) Copies" },
439 { 0x5180, DCM_TRET, "(Ret) Mag Type" },
440 { 0x5190, DCM_TRET, "(Ret) Erase" },
441 { 0x51a0, DCM_TRET, "(Ret) Print" },
442 { 0x080018, DCM_TSTR, "Image UID" },
443 { 0x080020, DCM_TSTR, "Study Date" },
444 { 0x080030, DCM_TSTR, "Study Time" },
445 { 0x080050, DCM_TSTR, "Acc Num" },
446 { 0x080052, DCM_TSTR, "Q/R Level" },
447 { 0x080054, DCM_TSTR, "Retrieve AE" },
448 { 0x080060, DCM_TSTR, "Modality" },
449 { 0x080070, DCM_TSTR, "Manuf" },
450 { 0x081030, DCM_TSTR, "Study Desc" },
451 { 0x08103e, DCM_TSTR, "Series Desc" },
452 { 0x100010, DCM_TSTR, "Patient Name" },
453 { 0x100020, DCM_TSTR, "Patient Id" },
454 { 0x20000d, DCM_TSTR, "Study UID" },
455 { 0x20000e, DCM_TSTR, "Series UID" },
456 { 0x200010, DCM_TSTR, "Study Num" },
457 { 0x200011, DCM_TSTR, "Series Num" },
458 { 0x200012, DCM_TSTR, "Acq Num" },
459 { 0x200013, DCM_TSTR, "Image Num" },
460 { 0x7fe00010, DCM_OTH, "Pixels" },
461 { 0xfffee000, DCM_TRET, "Item Begin" },
462 { 0xfffee00d, DCM_TRET, "Item End" },
463 { 0xfffee0dd, DCM_TRET, "Sequence End" },
466 /* following definitions are used to call dissect_dcm_assoc_item() */
467 #define DCM_ITEM_VALUE_TYPE_UID 1
468 #define DCM_ITEM_VALUE_TYPE_STRING 2
469 #define DCM_ITEM_VALUE_TYPE_UINT32 3
471 /* A few function declataions */
473 /* Per object, a xxx_new() and a xxx_get() function. The _get() will create one if specified. */
475 static dcm_state_t* dcm_state_new(void);
476 static dcm_state_t* dcm_state_get(packet_info *pinfo, gboolean create);
478 static dcm_state_assoc_t* dcm_state_assoc_new (dcm_state_t *dcm_data, guint32 packet_no);
479 static dcm_state_assoc_t* dcm_state_assoc_get (dcm_state_t *dcm_data, guint32 packet_no, gboolean create);
480 static dcm_state_pctx_t* dcm_state_pctx_new (dcm_state_assoc_t *assoc, guint8 pctx_id);
481 static dcm_state_pctx_t* dcm_state_pctx_get (dcm_state_assoc_t *assoc, guint8 pctx_id, gboolean create);
482 static dcm_state_pdv_t* dcm_state_pdv_new (dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset);
483 static dcm_state_pdv_t* dcm_state_pdv_get (dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset, gboolean create);
485 static int dissect_dcm_static (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
486 static int dissect_dcm_heuristic (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
487 static int dissect_dcm_main (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean require_assoc_req);
488 static int dissect_dcm_pdu (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset);
490 static int dissect_dcm_assoc (tvbuff_t *tvb, packet_info *pinfo, proto_item *ti, dcm_state_assoc_t *assoc, int offset, int len);
491 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);
492 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);
493 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);
495 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);
496 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);
497 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);
499 static void dcm_set_syntax (dcm_state_pctx_t *pctx, guchar *xfer_uid, guchar *xfer_desc);
500 static void dcm_export_create_object (packet_info *pinfo, dcm_state_assoc_t *assoc, dcm_state_pdv_t *pdv);
506 if (NULL == dcm_tagTable) {
508 dcm_tagTable = g_hash_table_new(NULL, NULL);
509 for (i = 0; i < sizeof(tagData) / sizeof(dcmTag_t); i++)
510 g_hash_table_insert(dcm_tagTable, GINT_TO_POINTER(tagData[i].tag),
511 (gpointer) (tagData+i));
514 if (NULL == dcm_uid_table) {
516 dcm_uid_table = g_hash_table_new(g_str_hash, g_str_equal);
517 for (i = 0; i < sizeof(dcm_uid_data) / sizeof(dcm_uid_t); i++)
518 g_hash_table_insert(dcm_uid_table, (gpointer) dcm_uid_data[i].value, (gpointer) dcm_uid_data[i].name);
527 /* Not much fun. Just create very simple root structure */
529 dcm_state_t *ds=NULL;
531 ds = (dcm_state_t *) se_alloc(sizeof(dcm_state_t));
533 ds->first_assoc=NULL;
540 dcm_state_get(packet_info *pinfo, gboolean create)
543 /* Get or create converstation and DICOM data structure if desired
544 Return new or existing dicom struture, which is used to store context IDs and xfer Syntax
545 Return NULL in case of the structure couldn't be created
548 conversation_t *conv=NULL;
549 dcm_state_t *dcm_data=NULL;
551 conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
552 pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
555 /* Conversation does not exist, create one.
556 Usually set for the first packet already. Probably by dissect-tcp
558 conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
559 pinfo->srcport, pinfo->destport, 0);
561 else { /* conversation exists, try to get data already filled */
562 dcm_data = conversation_get_proto_data(conv, proto_dcm);
565 if (dcm_data == NULL && create) {
567 dcm_data = dcm_state_new();
568 if (dcm_data != NULL) {
569 conversation_add_proto_data(conv, proto_dcm, dcm_data);
572 /* Mark it as DICOM conversation. Needed for the heuristic mode,
573 to prevent stealing subsequent packets by other dissectors
575 conversation_set_dissector(conv, dcm_handle);
582 static dcm_state_assoc_t *
583 dcm_state_assoc_new(dcm_state_t *dcm_data, guint32 packet_no)
585 /* Create new accociation object and initalize the members */
587 dcm_state_assoc_t *assoc;
589 assoc = (dcm_state_assoc_t *) g_malloc(sizeof(dcm_state_assoc_t));
594 assoc->packet_no = packet_no; /* Identifier */
596 assoc->first_pctx = NULL; /* List of Presentation context objects */
597 assoc->last_pctx = NULL;
599 memset(assoc->ae_called, 0, sizeof(assoc->ae_called));
600 memset(assoc->ae_calling, 0, sizeof(assoc->ae_calling));
601 memset(assoc->ae_called_resp, 0, sizeof(assoc->ae_called_resp));
602 memset(assoc->ae_calling_resp, 0, sizeof(assoc->ae_calling_resp));
604 /* add to the end of the list */
605 if (dcm_data->last_assoc) {
606 dcm_data->last_assoc->next = assoc;
607 assoc->prev = dcm_data->last_assoc;
610 dcm_data->first_assoc = assoc;
612 dcm_data->last_assoc = assoc;
617 static dcm_state_assoc_t *
618 dcm_state_assoc_get(dcm_state_t *dcm_data, guint32 packet_no, gboolean create)
620 /* Find or create Association object.
621 Return NULL, if Association was not found, based on packet number
624 dcm_state_assoc_t *assoc = NULL;
626 assoc=dcm_data->first_assoc;
631 /* we have more associations in the same stream */
632 if ((assoc->packet_no <= packet_no) && (packet_no < assoc->next->packet_no))
636 /* last or only associations in the same stream */
637 if (assoc->packet_no <= packet_no)
643 if (assoc == NULL && create) {
644 assoc = dcm_state_assoc_new(dcm_data, packet_no);
649 static dcm_state_pctx_t *
650 dcm_state_pctx_new(dcm_state_assoc_t *assoc, guint8 pctx_id)
652 /* Create new presentation context object and initalize the members */
654 dcm_state_pctx_t *pctx=NULL;
656 pctx = se_alloc(sizeof(dcm_state_pctx_t));
664 pctx->abss_uid = NULL;
665 pctx->abss_desc = NULL;
666 pctx->xfer_uid = NULL;
667 pctx->xfer_desc = NULL;
668 pctx->syntax = DCM_UNK;
670 pctx->first_pdv = NULL; /* List of PDV objects */
671 pctx->last_pdv = NULL;
673 /* add to the end of the list list */
674 if (assoc->last_pctx) {
675 assoc->last_pctx->next = pctx;
676 pctx->prev = assoc->last_pctx;
679 assoc->first_pctx = pctx;
681 assoc->last_pctx = pctx;
687 static dcm_state_pctx_t *
688 dcm_state_pctx_get(dcm_state_assoc_t *assoc, guint8 pctx_id, gboolean create)
690 /* Find or create presentation context object. Return NULL, if Context ID was not found */
692 dcm_state_pctx_t *pctx =NULL;
694 pctx = assoc->first_pctx;
696 static char notfound[] = "not found - click on ASSOC Request";
697 static dcm_state_pctx_t dunk = { NULL, NULL, FALSE, 0, notfound, notfound, notfound, notfound, DCM_UNK };
700 if (pctx->id == pctx_id)
705 if (pctx == NULL && create) {
706 pctx = dcm_state_pctx_new(assoc, pctx_id);
713 static dcm_state_pdv_t*
714 dcm_state_pdv_new(dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset)
716 /* Create new PDV object and initalize the members */
718 dcm_state_pdv_t *pdv=NULL;
720 pdv = (dcm_state_pdv_t *) se_alloc(sizeof(dcm_state_pdv_t));
732 pdv->sop_class_uid = NULL;
733 pdv->sop_instance_uid = NULL;
735 pdv->is_storage = FALSE;
736 pdv->is_flagvalid = FALSE;
737 pdv->is_command = FALSE;
738 pdv->is_last_fragment = TRUE; /* Continuation PDVs are more tricky */
739 pdv->is_corrupt = FALSE;
741 pdv->packet_no = packet_no;
742 pdv->offset = offset;
743 pdv->initalized = FALSE;
744 pdv->open_tag_desc = NULL;
745 pdv->open_tag_len = 0;
746 pdv->open_tag_rlen = 0;
748 /* add to the end of the list list */
749 if (pctx->last_pdv) {
750 pctx->last_pdv->next = pdv;
751 pdv->prev = pctx->last_pdv;
754 pctx->first_pdv = pdv;
756 pctx->last_pdv = pdv;
762 static dcm_state_pdv_t*
763 dcm_state_pdv_get(dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset, gboolean create)
765 /* Find or create PDV object. Return NULL, if PDV was not found, based on packet number and offset */
767 dcm_state_pdv_t *pdv = NULL;
772 if ((pdv->packet_no == packet_no) && (pdv->offset == offset))
777 if (pdv == NULL && create) {
778 pdv = dcm_state_pdv_new(pctx, packet_no, offset);
783 static dcm_state_pdv_t*
784 dcm_state_pdv_get_obj_start(dcm_state_pdv_t *pdv_curr)
787 dcm_state_pdv_t *pdv_first=pdv_curr;
789 /* Get First PDV of the DICOM Object */
790 while (pdv_first->prev && !pdv_first->prev->is_last_fragment) {
791 pdv_first = pdv_first->prev;
798 dcm_pdu2str(guint8 item)
802 case 1: s = "ASSOC Request"; break;
803 case 2: s = "ASSOC Accept"; break;
804 case 3: s = "ASSOC Reject"; break;
805 case 4: s = "Data"; break;
806 case 5: s = "RELEASE Request"; break;
807 case 6: s = "RELEASE Response"; break;
808 case 7: s = "ABORT"; break;
809 case 0x10: s = "Application Context"; break;
810 case 0x20: s = "Presentation Context"; break;
811 case 0x21: s = "Presentation Context Reply"; break;
812 case 0x30: s = "Abstract syntax"; break;
813 case 0x40: s = "Transfer syntax"; break;
814 case 0x50: s = "User Info"; break;
815 case 0x51: s = "Max Length"; break;
822 dcm_result2str(guint8 result)
826 case 1: s = "Reject Permanent"; break;
827 case 2: s = "Reject Transient"; break;
834 dcm_source2str(guint8 source)
838 case 1: s = "User"; break;
839 case 2: s = "Provider (ACSE)"; break;
840 case 3: s = "Provider (Presentation)"; break;
847 dcm_reason2str(guint8 source, guint8 reason)
850 if (1 == source) switch (reason) {
851 case 1: s = "No reason"; break;
852 case 2: s = "App Name not supported"; break;
853 case 3: s = "calling AET not recognized"; break;
854 case 7: s = "called AET not recognized"; break;
856 } else if (2 == source) switch (reason) {
857 case 1: s = "No reason"; break;
858 case 2: s = "protocol unsupported"; break;
860 } else if (3 == source) switch (reason) {
861 case 1: s = "temporary congestion"; break;
862 case 2: s = "local limit exceeded"; break;
869 dcm_abort2str(guint8 reason)
873 case 0: s = "not specified"; break;
874 case 1: s = "unrecognized"; break;
875 case 2: s = "unexpected"; break;
876 case 4: s = "unrecognized parameter"; break;
877 case 5: s = "unexpected parameter"; break;
878 case 6: s = "invalid parameter"; break;
885 dcm_PCresult2str(guint8 result)
889 case 0: s = "Accept"; break;
890 case 1: s = "User Reject"; break;
891 case 2: s = "No Reason"; break;
892 case 3: s = "Abstract Syntax Unsupported"; break;
893 case 4: s = "Transfer Syntax Unsupported"; break;
901 dcm_cmd2str(guint16 us)
904 /* there should be a better way to do this */
906 case 0x0001: s = "C-STORE-RQ"; break;
907 case 0x8001: s = "C-STORE-RSP"; break;
908 case 0x0010: s = "C-GET-RQ"; break;
909 case 0x8010: s = "C-GET-RSP"; break;
910 case 0x0020: s = "C-FIND-RQ"; break;
911 case 0x8020: s = "C-FIND-RSP"; break;
912 case 0x0021: s = "C-MOVE-RQ"; break;
913 case 0x8021: s = "C-MOVE-RSP"; break;
914 case 0x0030: s = "C-ECHO-RQ"; break;
915 case 0x8030: s = "C-ECHO-RSP"; break;
916 case 0x0100: s = "N-EVENT-REPORT-RQ"; break;
917 case 0x8100: s = "N-EVENT-REPORT-RSP"; break;
918 case 0x0110: s = "N-GET-RQ"; break;
919 case 0x8110: s = "N-GET-RSP"; break;
920 case 0x0120: s = "N-SET-RQ"; break;
921 case 0x8120: s = "N-SET-RSP"; break;
922 case 0x0130: s = "N-ACTION-RQ"; break;
923 case 0x8130: s = "N-ACTION-RSP"; break;
924 case 0x0140: s = "N-CREATE-RQ"; break;
925 case 0x8140: s = "N-CREATE-RSP"; break;
926 case 0x0150: s = "N-DELETE-RQ"; break;
927 case 0x8150: s = "N-DELETE-RSP"; break;
928 case 0x0fff: s = "C-CANCEL-RQ"; break;
935 dcm_rsp2str(guint16 us)
939 case 0x0000: s = "Success"; break;
941 case 0xa702: s = "Refused: Out of Resources"; break;
942 case 0xa801: s = "Refused: Move Destination unknown"; break;
943 case 0xa900: s = "Failed: Id does not match Class"; break;
944 case 0xb000: s = "Warning: operations complete -- One or more Failures"; break;
945 case 0xfe00: s = "Cancel: operations terminated by Cancel"; break;
946 case 0xff00: s = "Pending: operations are continuing"; break;
949 if (0xC000 == (0xF000 & us)) s = "Failed: Unable to Process";
954 dcm_uid_or_desc(guchar *dcm_uid, guchar *dcm_desc)
956 /* Return Description, UID or error */
958 return (dcm_desc == NULL ? (dcm_uid == NULL ? (guchar *)"Malformed Packet" : dcm_uid) : dcm_desc);
962 dcm_set_syntax(dcm_state_pctx_t *pctx, guchar *xfer_uid, guchar *xfer_desc)
967 if (pctx->xfer_uid != NULL)
968 g_free(pctx->xfer_uid); /* free prev allocated xfer */
969 if (pctx->xfer_desc != NULL)
970 g_free(pctx->xfer_desc); /* free prev allocated xfer */
973 pctx->xfer_uid = g_strdup(xfer_uid);
974 pctx->xfer_desc = g_strdup(xfer_desc);
976 if (xfer_uid == NULL) return;
977 /* this would be faster to skip the common parts, and have a FSA to
979 * Absent of coding that, this is in descending order of probability */
980 if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2"))
981 pctx->syntax = DCM_ILE; /* implicit little endian */
982 else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.1"))
983 pctx->syntax = DCM_ELE; /* explicit little endian */
984 else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.2"))
985 pctx->syntax = DCM_EBE; /* explicit big endian */
986 else if (0 == strcmp(xfer_uid, "1.2.840.113619.5.2"))
987 pctx->syntax = DCM_ILE; /* implicit little endian, big endian pixels, GE private */
988 else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.4.70"))
989 pctx->syntax = DCM_ELE; /* explicit little endian, jpeg */
990 else if (0 == strncmp(xfer_uid, "1.2.840.10008.1.2.4", 18))
991 pctx->syntax = DCM_ELE; /* explicit little endian, jpeg */
992 else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.1.99"))
993 pctx->syntax = DCM_ELE; /* explicit little endian, deflated */
997 dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, guint32 len, int vr, int tr, guchar **tag_value)
1003 guint32 tag, val32=0;
1007 static dcmTag_t utag = { 0, 0, "(unknown)" };
1009 #define MAX_BUF_LEN 1024
1011 buf=ep_alloc(MAX_BUF_LEN);
1012 *tag_value = se_alloc(MAX_BUF_LEN);
1016 if (DCM_ILE & syntax)
1017 val32 = tvb_get_letohl(tvb, offset);
1018 else val32 = tvb_get_ntohl(tvb, offset);
1019 g_snprintf(buf, MAX_BUF_LEN, "Group Length 0x%x (%d)", val32, val32);
1022 tag = (grp << 16) | elm;
1023 if (NULL == (dtag = g_hash_table_lookup(dcm_tagTable, GUINT_TO_POINTER(tag))))
1026 DISSECTOR_ASSERT(MAX_BUF_LEN > strlen(dtag->desc));
1028 p+=MIN(MAX_BUF_LEN-(p-buf),
1029 g_snprintf(p, MAX_BUF_LEN-(p-buf), "%s", dtag->desc));
1031 vval = tvb_format_text(tvb, vr, 2);
1032 p+=MIN(MAX_BUF_LEN-(p-buf),
1033 g_snprintf(p, MAX_BUF_LEN-(p-buf), " [%s]", vval));
1036 switch (tr > 0 ? tr : dtag->dtype) {
1038 default: /* try ascii */
1040 val8 = tvb_get_guint8(tvb, offset+len-1);
1042 /* Last byte of string is 0x00, i.e. padded */
1043 vval = tvb_format_text(tvb, offset, len-1);
1046 vval = tvb_format_text(tvb, offset, len);
1048 p+=MIN(MAX_BUF_LEN-(p-buf),
1049 g_snprintf(p, MAX_BUF_LEN-(p-buf), " %s", vval));
1051 g_snprintf(*tag_value, MAX_BUF_LEN, "%s", vval);
1055 if (DCM_ILE & syntax)
1056 val16 = tvb_get_letohs(tvb, offset);
1057 else val16 = tvb_get_ntohs(tvb, offset);
1058 p+=MIN(MAX_BUF_LEN-(p-buf),
1059 g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x (%d)", val16, val16));
1062 if (DCM_ILE & syntax)
1063 val32 = tvb_get_letohl(tvb, offset);
1064 else val32 = tvb_get_ntohl(tvb, offset);
1065 p+=MIN(MAX_BUF_LEN-(p-buf),
1066 g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x (%d)", val32, val32));
1070 if (DCM_ILE & syntax)
1071 valf = tvb_get_letohieee_float(tvb, offset);
1072 else valf = tvb_get_ntohieee_float(tvb, offset);
1073 p+=MIN(MAX_BUF_LEN-(p-buf),
1074 g_snprintf(p, MAX_BUF_LEN-(p-buf), " (%f)", valf));
1078 if (DCM_ILE & syntax)
1079 vald = tvb_get_letohieee_double(tvb, offset);
1080 else vald = tvb_get_ntohieee_double(tvb, offset);
1081 p+=MIN(MAX_BUF_LEN-(p-buf),
1082 g_snprintf(p, MAX_BUF_LEN-(p-buf), " (%f)", vald));
1084 case DCM_TSTAT: /* call dcm_rsp2str() on TINT2 */
1085 if (DCM_ILE & syntax)
1086 val16 = tvb_get_letohs(tvb, offset);
1087 else val16 = tvb_get_ntohs(tvb, offset);
1088 p+=MIN(MAX_BUF_LEN-(p-buf),
1089 g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x '%s'", val16, dcm_rsp2str(val16)));
1091 case DCM_TCMD: /* call dcm_cmd2str() on TINT2 */
1092 if (DCM_ILE & syntax)
1093 val16 = tvb_get_letohs(tvb, offset);
1094 else val16 = tvb_get_ntohs(tvb, offset);
1095 p+=MIN(MAX_BUF_LEN-(p-buf),
1096 g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x '%s'", val16, dcm_cmd2str(val16)));
1098 g_snprintf(*tag_value, MAX_BUF_LEN, "%s", dcm_cmd2str(val16));
1100 case DCM_SQ: /* Sequence */
1101 case DCM_OTH: /* Other BYTE, WORD, ... */
1102 case DCM_TRET: /* Retired */
1109 dcm_guin16_to_le(guint8 *buffer, guint16 value)
1112 buffer[0]=(guint8) (value & 0x00FF);
1113 buffer[1]=(guint8)((value & 0xFF00) >> 8);
1117 dcm_guin32_to_le(guint8 *buffer, guint32 value)
1120 buffer[0]=(guint8) (value & 0x000000FF);
1121 buffer[1]=(guint8)((value & 0x0000FF00) >> 8);
1122 buffer[2]=(guint8)((value & 0x00FF0000) >> 16);
1123 buffer[3]=(guint8)((value & 0xFF000000) >> 24);
1128 dcm_export_create_tag_base(guint8 *buffer, guint32 bufflen _U_, guint32 offset,
1129 guint16 grp, guint16 elm, guint16 vr,
1130 guint8 *value_buffer, guint32 value_len)
1132 /* Only Explict Littele Endian is needed to create Metafile Header
1133 Generic function to write a TAG, VR, LEN & VALUE to a combined buffer
1134 The value (buffer, len) must be preprocessed by a VR specific function
1141 dcm_guin16_to_le(pos, grp);
1143 dcm_guin16_to_le(pos, elm);
1146 memmove(pos, dcm_tag_lookup[vr], 2);
1156 /* DICOM likes it complicated. Special handling for these types */
1158 /* Add two reserved 0x00 bytes */
1159 dcm_guin16_to_le(pos, 0);
1162 /* Length is a 4 byte field */
1163 dcm_guin32_to_le(pos, (guint32)value_len);
1168 /* Length is a 2 byte field */
1169 dcm_guin16_to_le(pos, (guint16)value_len);
1173 memmove(pos, value_buffer, value_len);
1180 dcm_export_create_tag_guint16(guint8 *buffer, guint32 bufflen, guint32 offset,
1181 guint16 grp, guint16 elm, guint16 vr, guint16 value)
1184 return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr, (guint8*)&value, 2);
1188 dcm_export_create_tag_guint32(guint8 *buffer, guint32 bufflen, guint32 offset,
1189 guint16 grp, guint16 elm, guint16 vr, guint32 value)
1192 return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr, (guint8*)&value, 4);
1196 dcm_export_create_tag_str(guint8 *buffer, guint32 bufflen, guint32 offset,
1197 guint16 grp, guint16 elm, guint16 vr, guchar *value)
1202 /* NULL object. E.g. happens if UID was not found/set. Don't create element*/
1208 if ((len & 0x01) == 1) {
1209 /* Odd length: since buffer is 0 initalized, pad with a 0x00 */
1213 return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr,
1214 (guint8*)value, len);
1219 dcm_export_create_header(guint32 *dcm_header_len, gchar *sop_class_uid, gchar *sop_instance_uid, gchar *xfer_uid)
1221 guint8 *dcm_header=NULL;
1223 guint32 offset_header_len=0;
1225 #define DCM_HEADER_MAX 512
1227 dcm_header=ep_alloc(DCM_HEADER_MAX); /* Slightly longer than needed */
1229 memset(dcm_header, 0, DCM_HEADER_MAX); /* The subsequent functions rely on a 0 intitalized buffer */
1232 memmove(dcm_header+offset, "DICM", 4);
1235 offset_header_len=offset; /* remember for later */
1240 (0002,0000) File Meta Information Group Length UL
1241 (0002,0001) File Meta Information Version OB
1242 (0002,0002) Media Storage SOP Class UID UI
1243 (0002,0003) Media Storage SOP Instance UID UI
1244 (0002,0010) Transfer Syntax UID UI
1245 (0002,0012) Implementation Class UID UI
1246 (0002,0013) Implementation Version Name SH
1249 offset=dcm_export_create_tag_guint16(dcm_header, DCM_HEADER_MAX, offset,
1250 0x0002, 0x0001, DCM_VR_OB, 0x0100); /* will result on 00 01 since it is little endian */
1252 offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1253 0x0002, 0x0002, DCM_VR_UI, sop_class_uid);
1255 offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1256 0x0002, 0x0003, DCM_VR_UI, sop_instance_uid);
1258 offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1259 0x0002, 0x0010, DCM_VR_UI, xfer_uid);
1261 offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1262 0x0002, 0x0012, DCM_VR_UI, WIRESHARK_IMPLEMENTATION_UID);
1264 offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1265 0x0002, 0x0013, DCM_VR_SH, WIRESHARK_IMPLEMENTATION_VERSION);
1267 /* Finally write the meta header lenght */
1268 dcm_export_create_tag_guint32(dcm_header, DCM_HEADER_MAX, offset_header_len,
1269 0x0002, 0x0000, DCM_VR_UL, offset-offset_header_len-12);
1271 *dcm_header_len=offset;
1278 dcm_export_create_object(packet_info *pinfo, dcm_state_assoc_t *assoc, dcm_state_pdv_t *pdv)
1281 /* Concat different PDVs into one buffer and add it to export object list
1282 This function caused quite a few crashes, with all the string pointers
1285 dicom_eo_t *eo_info = NULL;
1287 dcm_state_pdv_t *pdv_curr = NULL;
1288 dcm_state_pdv_t *pdv_same_pkt = NULL;
1289 dcm_state_pctx_t *pctx = NULL;
1291 guint8 *pdv_combined = NULL;
1292 guint8 *pdv_combined_curr = NULL;
1293 guint8 *dcm_header = NULL;
1294 guint32 pdv_combined_len = 0;
1295 guint32 dcm_header_len = 0;
1296 guint16 cnt_same_pkt = 1;
1297 gchar *filename = NULL;
1298 gchar *hostname = NULL;
1300 gchar *sop_class_uid = NULL;
1301 gchar *sop_instance_uid = NULL;
1303 /* Calculate total PDV lenghth, i.e. all packets until last PDV without continuation */
1306 pdv_combined_len=pdv_curr->data_len;
1308 while (pdv_curr->prev && !pdv_curr->prev->is_last_fragment) {
1309 pdv_curr = pdv_curr->prev;
1310 pdv_combined_len += pdv_curr->data_len;
1313 /* Count number of PDVs with the same Packet Number */
1314 while (pdv_same_pkt->prev && (pdv_same_pkt->prev->packet_no == pdv_same_pkt->packet_no)) {
1315 pdv_same_pkt = pdv_same_pkt->prev;
1319 pctx=dcm_state_pctx_get(assoc, pdv_curr->pctx_id, FALSE);
1321 sop_class_uid = ep_alloc(MAX_BUF_LEN);
1322 sop_instance_uid = ep_alloc(MAX_BUF_LEN);
1324 hostname = ep_alloc(MAX_BUF_LEN);
1325 filename = ep_alloc(MAX_BUF_LEN);
1327 if (assoc->ae_calling && strlen(assoc->ae_calling)>0 &&
1328 assoc->ae_called && strlen(assoc->ae_called)>0 ) {
1329 g_snprintf(hostname, MAX_BUF_LEN, "%s <-> %s", assoc->ae_calling, assoc->ae_called);
1332 g_snprintf(hostname, MAX_BUF_LEN, "AE title(s) unknown");
1335 if (pdv->is_storage &&
1336 pdv_curr->sop_class_uid && strlen(pdv_curr->sop_class_uid)>0 &&
1337 pdv_curr->sop_instance_uid && strlen(pdv_curr->sop_instance_uid)>0) {
1339 g_snprintf(sop_class_uid, MAX_BUF_LEN, "%s", pdv_curr->sop_class_uid);
1340 g_snprintf(sop_instance_uid, MAX_BUF_LEN, "%s", pdv_curr->sop_instance_uid);
1342 /* Make sure filename does not contain invalid character. Rather conservative.
1343 Eventhough this should be a valid DICOM UID, apply the same filter rules
1344 in case of bogus data.
1346 g_snprintf(filename, MAX_BUF_LEN, "%06d-%d-%s.dcm", pinfo->fd->num, cnt_same_pkt,
1347 g_strcanon(pdv_curr->sop_instance_uid, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-.", '-'));
1350 /* No SOP Instance or SOP Class UID found in PDV. Use wireshark ones */
1352 g_snprintf(sop_class_uid, MAX_BUF_LEN, "%s", WIRESHARK_MEDIA_STORAGE_SOP_CLASS_UID);
1354 g_snprintf(sop_instance_uid, MAX_BUF_LEN, "%s.%d.%d",
1355 WIRESHARK_MEDIA_STORAGE_SOP_INSTANCE_UID_PREFIX, pinfo->fd->num, cnt_same_pkt);
1357 /* Make sure filename does not contain invalid character. Rather conservative.*/
1358 g_snprintf(filename, MAX_BUF_LEN, "%06d-%d-%s.dcm", pinfo->fd->num, cnt_same_pkt,
1359 g_strcanon(pdv->desc, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-.", '-'));
1363 if (global_dcm_header) {
1364 if (pctx && pctx->xfer_uid && strlen(pctx->xfer_uid)>0) {
1365 dcm_header=dcm_export_create_header(&dcm_header_len, sop_class_uid, sop_instance_uid, pctx->xfer_uid);
1368 /* We are running blind, i.e. no presentation context found. Don't invent one
1369 The meta header will miss this tag (even tough it is mandatory)
1371 dcm_header=dcm_export_create_header(&dcm_header_len, sop_class_uid, sop_instance_uid, NULL);
1376 /* Allocate the final size */
1377 pdv_combined = ep_alloc(dcm_header_len+pdv_combined_len);
1378 pdv_combined_curr = pdv_combined;
1380 memmove(pdv_combined, dcm_header, dcm_header_len);
1381 pdv_combined_curr += dcm_header_len;
1383 /* Copy PDV per PDV to target buffer */
1384 while (!pdv_curr->is_last_fragment) {
1385 memmove(pdv_combined_curr, pdv_curr->data, pdv_curr->data_len); /* this is a copy not move */
1386 pdv_combined_curr += pdv_curr->data_len;
1387 pdv_curr = pdv_curr->next;
1391 g_memmove(pdv_combined_curr, pdv->data, pdv->data_len); /* this is a copy not move */
1394 eo_info = ep_alloc(sizeof(dicom_eo_t));
1395 eo_info->hostname = hostname;
1396 eo_info->filename = filename;
1397 eo_info->content_type = pdv->desc;
1399 eo_info->payload_data = pdv_combined;
1400 eo_info->payload_len = dcm_header_len+pdv_combined_len;
1402 tap_queue_packet(dicom_eo_tap, pinfo, eo_info);
1407 dissect_dcm_assoc_item(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
1408 dcm_state_assoc_t *assoc _U_, int offset,
1409 guchar *pitem_prefix, int item_value_type,
1410 guchar **item_value, guchar **item_description,
1411 int *hf_type, int *hf_len, int *hf_value, int ett_subtree)
1414 * Decode one item in a association request or response. Lookup UIDs if requested
1416 * If dcm_tree is set, create a Subtree Node with summary and three elements
1421 * The Summary is also returned as
1424 proto_tree *assoc_item_ptree = NULL; /* Tree for item details */
1425 proto_item *assoc_item_pitem = NULL;
1427 guint32 item_number;
1432 guchar *buf_desc=NULL; /* Used for item text */
1434 #define MAX_BUFFER 1024
1437 *item_description=NULL;
1439 buf_desc=ep_alloc(MAX_BUFFER); /* Valid for this packet */
1442 item_type = tvb_get_guint8(tvb, offset);
1443 item_len = tvb_get_ntohs(tvb, offset+2);
1445 assoc_item_pitem = proto_tree_add_text(tree, tvb, offset, item_len+4, pitem_prefix);
1446 assoc_item_ptree = proto_item_add_subtree(assoc_item_pitem, ett_subtree);
1448 proto_tree_add_uint(assoc_item_ptree, *hf_type, tvb, offset, 1, item_type);
1449 proto_tree_add_uint(assoc_item_ptree, *hf_len, tvb, offset+2, 2, item_len);
1451 switch (item_value_type) {
1452 case DCM_ITEM_VALUE_TYPE_UID:
1453 *item_value = tvb_get_ephemeral_string(tvb, offset+4, item_len);
1454 *item_description = g_hash_table_lookup(dcm_uid_table, (gpointer) *item_value);
1456 if (NULL == *item_description) { /* Unknown UID, or no UID at all */
1457 g_snprintf(buf_desc, MAX_BUFFER, "%s", *item_value);
1460 g_snprintf(buf_desc, MAX_BUFFER, "%s (%s)", *item_description, *item_value);
1463 proto_item_append_text(assoc_item_pitem, "%s", buf_desc);
1464 proto_tree_add_string(assoc_item_ptree, *hf_value, tvb, offset+4, item_len, buf_desc);
1468 case DCM_ITEM_VALUE_TYPE_STRING:
1469 *item_value = tvb_get_ephemeral_string(tvb, offset+4, item_len);
1470 proto_item_append_text(assoc_item_pitem, "%s", *item_value);
1471 proto_tree_add_string(assoc_item_ptree, *hf_value, tvb, offset+4, item_len, *item_value);
1475 case DCM_ITEM_VALUE_TYPE_UINT32:
1476 item_number = tvb_get_ntohl(tvb, offset+4);
1477 *item_value = se_alloc(MAX_BUFFER);
1478 g_snprintf(*item_value, MAX_BUFFER, "%d", item_number);
1480 proto_item_append_text(assoc_item_pitem, "%s", *item_value);
1481 proto_tree_add_item(assoc_item_ptree, *hf_value, tvb, offset+4, 4, FALSE);
1492 dissect_dcm_pctx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1493 dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix, gboolean is_assoc_request)
1496 Decode a presentation context item in a Association Request or Response
1497 In the response, set the accepted transfer syntax, if any
1500 proto_tree *pctx_ptree = NULL; /* Tree for presentation context details */
1501 proto_item *pctx_pitem = NULL;
1503 dcm_state_pctx_t *pctx = NULL;
1508 guint8 pctx_id=0; /* Presentation Context ID */
1509 guint8 pctx_result=0;
1511 guchar *pctx_abss_uid=NULL; /* Abstract Syntax UID alias SOP Class UID */
1512 guchar *pctx_abss_desc=NULL; /* Description of UID */
1514 guchar *pctx_xfer_uid=NULL; /* Transfer Syntax UID */
1515 guchar *pctx_xfer_desc=NULL; /* Description of UID */
1517 guchar *buf_desc=NULL; /* Used in infor mode for item text */
1520 int cnt_abbs=0; /* Number of Abstract Syntax Items */
1521 int cnt_xfer=0; /* Number of Trasfer Syntax Items */
1523 #define MAX_BUFFER 1024
1525 buf_desc=ep_alloc(MAX_BUFFER); /* Valid for this packet */
1530 item_type = tvb_get_guint8(tvb, offset-4);
1531 item_len = tvb_get_ntohs(tvb, offset-2);
1533 pctx_pitem = proto_tree_add_text(tree, tvb, offset-4, item_len+4, pitem_prefix);
1534 pctx_ptree = proto_item_add_subtree(pctx_pitem, ett_assoc_pctx);
1536 pctx_id = tvb_get_guint8(tvb, offset);
1537 pctx_result = tvb_get_guint8(tvb, 2 + offset); /* only set in responses, otherwise reserved and 0x00 */
1539 /* Find or create dicom context object */
1540 pctx = dcm_state_pctx_get(assoc, pctx_id, TRUE);
1541 if (pctx == NULL) { /* Internal error. Failed to create data structre */
1545 proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_type, tvb, offset-4, 2, item_type);
1546 proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_len, tvb, offset-2, 2, item_len);
1548 proto_tree_add_uint_format(pctx_ptree, hf_dcm_pctx_id, tvb, offset, 1, pctx_id, "Context ID: 0x%02x", pctx_id);
1550 if (!is_assoc_request) {
1551 /* Accociation response. */
1552 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);
1556 while (-1 < offset && offset < endpos) {
1558 item_type = tvb_get_guint8(tvb, offset);
1559 item_len = tvb_get_ntohs(tvb, 2 + offset);
1562 switch (item_type) {
1563 case 0x30: /* Abstract syntax */
1565 /* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
1566 dissect_dcm_assoc_item(tvb, pinfo, pctx_ptree, assoc, offset-4,
1567 "Abstract Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_abss_uid, &pctx_abss_desc,
1568 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_abss_syntax, ett_assoc_pctx_abss);
1574 case 0x40: /* Transfer syntax */
1576 dissect_dcm_assoc_item(tvb, pinfo, pctx_ptree, assoc, offset-4,
1577 "Transfer Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_xfer_uid, &pctx_xfer_desc,
1578 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_xfer_syntax, ett_assoc_pctx_xfer);
1581 In a correct Association Response, only one Transfer syntax shall be present.
1582 Therefore, pctx_xfer_uid, pctx_xfer_desc are used for the accept scenario in the info mode
1585 if (!is_assoc_request && pctx_result == 0) {
1586 /* Association Response, Context Accepted */
1587 dcm_set_syntax(pctx, pctx_xfer_uid, pctx_xfer_desc);
1599 if (is_assoc_request) {
1602 expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1603 "No Abstract Syntax provided for this Presentation Context");
1606 else if (cnt_abbs>1) {
1607 expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1608 "More than one Abstract Syntax provided for this Presentation Context");
1613 expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1614 "No Transfer Syntax provided for this Presentation Context");
1618 if (pctx_abss_uid==NULL) {
1619 expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1620 "No Abstract Syntax UID found for this Presentation Context");
1628 expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1629 "Only one Transfer Syntax allowed in a Association Response");
1634 if (pctx->abss_uid==NULL) {
1635 /* Permanent copy information into structure */
1636 pctx->abss_uid =g_strdup(pctx_abss_uid);
1637 pctx->abss_desc=g_strdup(pctx_abss_desc);
1641 Copy to buffer first, because proto_item_append_text()
1642 crashed for an unknown reason using 'ID 0x%02x, %s, %s'
1643 and in my opinion correctly set parameters.
1646 if (is_assoc_request) {
1647 if (pctx_abss_desc == NULL) {
1648 g_snprintf(buf_desc, MAX_BUFFER, "%s", pctx_abss_uid);
1651 g_snprintf(buf_desc, MAX_BUFFER, "%s (%s)", pctx_abss_desc, pctx_abss_uid);
1656 /* g_snprintf() does not like NULL pointers */
1658 if (pctx_result==0) {
1660 g_snprintf(buf_desc, MAX_BUFFER, "ID 0x%02x, %s, %s, %s",
1661 pctx_id, dcm_PCresult2str(pctx_result),
1662 dcm_uid_or_desc(pctx->xfer_uid, pctx->xfer_desc),
1663 dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
1667 g_snprintf(buf_desc, MAX_BUFFER, "ID 0x%02x, %s, %s",
1668 pctx_id, dcm_PCresult2str(pctx_result),
1669 dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
1672 proto_item_append_text(pctx_pitem, "%s", buf_desc);
1677 dissect_dcm_userinfo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1678 dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix)
1681 Decode the user info item in a Association Request or Response
1684 proto_item *userinfo_pitem = NULL;
1685 proto_tree *userinfo_ptree = NULL; /* Tree for presentation context details */
1691 gboolean first_item=TRUE;
1693 /* guchar *buf_desc=NULL; Used in infor mode for item text */
1695 guchar *info_max_pdu=NULL;
1696 guchar *info_impl_uid=NULL;
1697 guchar *info_impl_version=NULL;
1704 item_type = tvb_get_guint8(tvb, offset-4);
1705 item_len = tvb_get_ntohs(tvb, offset-2);
1707 userinfo_pitem = proto_tree_add_text(tree, tvb, offset-4, item_len+4, pitem_prefix);
1708 userinfo_ptree = proto_item_add_subtree(userinfo_pitem, ett_assoc_info);
1710 proto_tree_add_uint(userinfo_ptree, hf_dcm_assoc_item_type, tvb, offset-4, 2, item_type);
1711 proto_tree_add_uint(userinfo_ptree, hf_dcm_assoc_item_len, tvb, offset-2, 2, item_len);
1713 while (-1 < offset && offset < endpos) {
1715 item_type = tvb_get_guint8(tvb, offset);
1716 item_len = tvb_get_ntohs(tvb, 2 + offset);
1719 switch (item_type) {
1720 case 0x51: /* Max length */
1722 dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, assoc, offset-4,
1723 "Max PDU Length: ", DCM_ITEM_VALUE_TYPE_UINT32, &info_max_pdu, &dummy,
1724 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pdu_maxlen, ett_assoc_info_uid);
1727 proto_item_append_text(userinfo_pitem, ", ");
1729 proto_item_append_text(userinfo_pitem, "Max PDU Length %s", info_max_pdu);
1735 case 0x52: /* UID */
1737 /* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
1738 dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, assoc, offset-4,
1739 "Implementation UID: ", DCM_ITEM_VALUE_TYPE_STRING, &info_impl_uid, &dummy,
1740 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_uid, ett_assoc_info_uid);
1743 proto_item_append_text(userinfo_pitem, ", ");
1745 proto_item_append_text(userinfo_pitem, "Implementation UID %s", info_impl_uid);
1751 case 0x55: /* version */
1753 dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, assoc, offset-4,
1754 "Implementation Version: ", DCM_ITEM_VALUE_TYPE_STRING, &info_impl_version, &dummy,
1755 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_version, ett_assoc_info_version);
1758 proto_item_append_text(userinfo_pitem, ", ");
1760 proto_item_append_text(userinfo_pitem, "Version %s", info_impl_version);
1766 case 0x53: /* async negotion */
1780 dissect_dcm_assoc(tvbuff_t *tvb, packet_info *pinfo, proto_item *ti,
1781 dcm_state_assoc_t *assoc, int offset, int len)
1783 proto_tree *assoc_tree = NULL; /* Tree for PDU details */
1790 guchar *item_value=NULL;
1791 guchar *item_description=NULL;
1793 /* guchar *info_pctx=NULL; Description of Presentation Context */
1795 endpos = offset+len;
1798 assoc_tree = proto_item_add_subtree(ti, ett_assoc);
1799 while (-1 < offset && offset < endpos) {
1801 item_type = tvb_get_guint8(tvb, offset);
1802 item_len = tvb_get_ntohs(tvb, 2 + offset);
1804 DISSECTOR_ASSERT(item_len > 0);
1808 switch (item_type) {
1809 case 0x10: /* Application context */
1810 dissect_dcm_assoc_item(tvb, pinfo, assoc_tree, assoc, offset-4,
1811 "Application Context: ", DCM_ITEM_VALUE_TYPE_UID, &item_value, &item_description,
1812 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_actx, ett_assoc_actx);
1817 case 0x20: /* Presentation context request */
1818 dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len,
1819 "Presentation Context: ", TRUE);
1823 case 0x21: /* Presentation context reply */
1824 dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len,
1825 "Presentation Context: ", FALSE);
1829 case 0x50: /* User Info */
1830 dissect_dcm_userinfo(tvb, pinfo, assoc_tree, assoc, offset, item_len, "User Info: ");
1845 dissect_dcm_pdv_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1846 dcm_state_assoc_t *assoc, int offset, guint8 *syntax,
1847 dcm_state_pdv_t **pdv)
1849 /* Dissect Context and Flags of a PDV and create new PDV stucture */
1851 proto_item *pdv_ctx_pitem = NULL;
1852 proto_item *pdv_flags_pitem = NULL;
1854 dcm_state_pctx_t *pctx = NULL;
1855 dcm_state_pdv_t *pdv_first_data = NULL;
1857 const gchar *desc_flag = NULL; /* Flag Description in tree */
1858 gchar *desc_header = NULL; /* Used for PDV description */
1865 /* 1 Byte Context */
1866 pctx_id = tvb_get_guint8(tvb, offset);
1867 pctx = dcm_state_pctx_get(assoc, pctx_id, FALSE);
1869 if (pctx && pctx->xfer_uid) {
1870 proto_tree_add_uint_format(tree, hf_dcm_pdv_ctx, tvb, offset, 1,
1871 pctx_id, "Context: 0x%02x (%s, %s)", pctx_id,
1872 dcm_uid_or_desc(pctx->xfer_uid, pctx->xfer_desc),
1873 dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
1876 pdv_ctx_pitem=proto_tree_add_uint_format(tree, hf_dcm_pdv_ctx, tvb, offset, 1,
1877 pctx_id, "Context: 0x%02x not found. A-ASSOCIATE request not found in capture.", pctx_id);
1879 expert_add_info_format(pinfo, pdv_ctx_pitem, PI_MALFORMED, PI_ERROR, "Invalid Presentation Context ID");
1881 /* Create fake PCTX and guess Syntax ILE, ELE, EBE */
1882 pctx = dcm_state_pctx_new(assoc, pctx_id);
1884 /* To be done: Guess Syntax */
1885 pctx->syntax = DCM_UNK;
1889 *syntax = pctx->syntax;
1891 /* Create PDV structure:
1893 Since we can have multiple PDV per packet (offset) and
1894 multiple merged packets per PDV (tvb->raw_offset)
1895 we need both values to uniquely identify a PDV
1898 *pdv=dcm_state_pdv_get(pctx, pinfo->fd->num, tvb->raw_offset+offset, TRUE);
1900 return 0; /* Failed to allocate memory */
1904 flags = tvb_get_guint8(tvb, offset);
1906 (*pdv)->pctx_id = pctx_id; /* TBD: Required for export */
1908 desc_header=se_alloc(MAX_BUFFER); /* Valid for this capture, since we return this buffer */
1909 memset(desc_header, 0, MAX_BUFFER);
1913 desc_flag = "Data, More Fragments";
1915 (*pdv)->is_flagvalid = TRUE;
1916 (*pdv)->is_command = FALSE;
1917 (*pdv)->is_last_fragment = FALSE;
1921 desc_flag = "Data, Last Fragment";
1923 (*pdv)->is_flagvalid = TRUE;
1924 (*pdv)->is_command = FALSE;
1925 (*pdv)->is_last_fragment = TRUE;
1929 desc_flag = "Command, More Fragments";
1930 g_snprintf(desc_header, MAX_BUFFER, "Command"); /* Will be overwritten with real command tag */
1932 *syntax = DCM_ILE; /* Command tags are always little endian*/
1934 (*pdv)->is_flagvalid = TRUE;
1935 (*pdv)->is_command = TRUE;
1936 (*pdv)->is_last_fragment = FALSE;
1940 desc_flag = "Command, Last Fragment";
1941 g_snprintf(desc_header, MAX_BUFFER, "Command");
1943 *syntax = DCM_ILE; /* Command tags are always little endian*/
1945 (*pdv)->is_flagvalid = TRUE;
1946 (*pdv)->is_command = TRUE;
1947 (*pdv)->is_last_fragment = TRUE;
1951 desc_flag = "Invalid Flags";
1952 g_snprintf(desc_header, MAX_BUFFER, "Invalid Flags");
1956 (*pdv)->is_flagvalid = FALSE;
1957 (*pdv)->is_command = FALSE;
1958 (*pdv)->is_last_fragment = FALSE;
1961 if (flags == 0 || flags == 2) {
1963 pdv_first_data = dcm_state_pdv_get_obj_start(*pdv);
1965 if (pdv_first_data->prev && pdv_first_data->prev->is_command) {
1966 /* Every Data PDV sequence should be preceeded by a Command PDV,
1967 so we should always hit this for a correct capture
1970 if (pctx && pctx->abss_desc && g_str_has_suffix(pctx->abss_desc, "Storage")) {
1971 /* Should be done far more intelligent, e.g. does not catch the (Retired) ones */
1973 g_snprintf(desc_header, MAX_BUFFER, "%s (more fragments)", pctx->abss_desc);
1976 g_snprintf(desc_header, MAX_BUFFER, "%s", pctx->abss_desc);
1978 (*pdv)->is_storage = TRUE;
1981 /* Use previous command and append DATA*/
1982 g_snprintf(desc_header, MAX_BUFFER, "%s-DATA", pdv_first_data->prev->desc);
1986 g_snprintf(desc_header, MAX_BUFFER, "DATA");
1990 (*pdv)->desc = desc_header;
1992 pdv_flags_pitem = proto_tree_add_uint_format(tree, hf_dcm_pdv_flags, tvb, offset, 1,
1993 flags, "Flags: 0x%02x (%s)", flags, desc_flag);
1996 expert_add_info_format(pinfo, pdv_flags_pitem, PI_MALFORMED, PI_ERROR, "Invalid Flags");
2005 dissect_dcm_pdv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
2006 dcm_state_assoc_t *assoc, int offset, guint32 pdv_len, guchar **pdv_description)
2008 /* Handle one PDV inside a data PDU */
2017 dcm_state_pdv_t *pdv = NULL;
2019 proto_item *pitem = NULL;
2021 int toffset, state, vr = 0, tr = 0;
2023 guint32 tag_value_fragment_len; /* used for values that span multiple PDVs */
2025 guint16 grp = 0, elm = 0;
2027 guint32 nlen = 0; /* Length of next sub item */
2029 guchar *tag_value=NULL; /* used for commands only so far */
2033 const guint8 *val=NULL;
2035 int endpos = offset + pdv_len;
2037 /* Dissect Context ID, Command/Data flag and More Fragments flag */
2038 offset = dissect_dcm_pdv_header(tvb, pinfo, tree, assoc, offset, &syntax, &pdv);
2040 if (have_tap_listener(dicom_eo_tap) && pdv->data_len==0) {
2041 /* If not yet done, copy pure dicom data to buffer when running in export object mode */
2042 pdv->data_len = endpos-offset;
2043 pdv->data = se_alloc(pdv->data_len);
2045 g_memmove(pdv->data, tvb_get_ptr(tvb, offset, pdv->data_len), pdv->data_len);
2048 pdv->data_len = 0; /* Failed to allocate memory. Don't copy anything */
2053 /* Not frist PDV in the give presentation context (Those don't have remaining data to parse :-) */
2055 if (pdv->prev->open_tag_rlen > 0) {
2056 /* previous PDV has left overs, i.e. this is a continuation PDV */
2058 if (endpos - offset >= (int)pdv->prev->open_tag_rlen) {
2060 * Remaining bytes are equal or more than we expect for the open tag
2061 * Finally reach the end of this tag
2063 tag_value_fragment_len = pdv->prev->open_tag_rlen;
2065 pdv->open_tag_len = 0;
2066 pdv->open_tag_rlen = 0;
2067 pdv->open_tag_desc = NULL;
2068 pdv->initalized = TRUE;
2070 pdv->is_corrupt = FALSE;
2072 else if (pdv->is_flagvalid && pdv->is_last_fragment) {
2074 * The tag is not yet complete, however, the flag indicates that it should be
2075 * Therefore end this tag and issue an expert_add_info
2078 tag_value_fragment_len = endpos - offset;
2080 pdv->open_tag_len = 0;
2081 pdv->open_tag_rlen = 0;
2082 pdv->open_tag_desc = NULL;
2083 pdv->initalized = TRUE;
2084 pdv->is_corrupt = TRUE;
2088 * More to do for this tag
2090 tag_value_fragment_len = endpos - offset;
2092 /* Set data in current PDV structure */
2093 pdv->open_tag_len = pdv->prev->open_tag_len;
2094 pdv->open_tag_rlen = pdv->prev->open_tag_rlen - tag_value_fragment_len;
2095 pdv->open_tag_desc = pdv->prev->open_tag_desc;
2096 pdv->initalized = TRUE;
2097 pdv->is_corrupt = FALSE;
2100 val = tvb_get_ptr(tvb, offset, tag_value_fragment_len);
2102 if (pdv->is_corrupt) {
2103 pitem = proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2104 offset, tag_value_fragment_len, val, "%s [incomplete]",
2105 pdv->prev->open_tag_desc);
2107 expert_add_info_format(pinfo, pitem, PI_MALFORMED, PI_ERROR,
2108 "Early termination of tag. Data is missing");
2112 proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2113 offset, tag_value_fragment_len, val, "%s Bytes %d - %d [%s]",
2114 pdv->prev->open_tag_desc,
2115 pdv->prev->open_tag_len - pdv->prev->open_tag_rlen + 1,
2116 pdv->prev->open_tag_len - pdv->open_tag_rlen,
2117 (pdv->open_tag_rlen > 0 ? "continuation" : "end") );
2120 offset += tag_value_fragment_len;
2124 if (syntax == DCM_UNK) {
2126 tlen = endpos - offset;
2127 val = tvb_get_ptr(tvb, offset, tlen); /* Verify */
2128 proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2129 offset, tlen, val, "(%04x,%04x) %-8x Unparsed data", 0, 0, tlen);
2137 n Value, always in Implicit Little VR
2142 while (offset + nlen <= (guint32) endpos) {
2146 if (DCM_ILE & syntax) {
2147 grp = tvb_get_letohs(tvb, offset);
2148 elm = tvb_get_letohs(tvb, offset+2);
2149 state = (DCM_EBE & syntax) ? D_VR : D_LEN4; /* is Explicit */
2150 nlen = (DCM_EBE & syntax) ? 2 : 4; /* is Explicit */
2152 grp = tvb_get_ntohs(tvb, offset);
2153 elm = tvb_get_ntohs(tvb, offset+2);
2158 if (0xfffe == grp) state = D_LEN4;
2160 } break; /* don't fall through -- check length */
2164 V = tvb_get_guint8(tvb, offset);
2165 R = tvb_get_guint8(tvb, offset+1);
2167 /* 4byte lengths OB, OW, OF, SQ, UN, UT */
2170 if ((('O' == V) && ('B' == R || 'W' == R || 'F' == R) && (tr = DCM_OTH))
2171 || (('U' == V) && ('N' == R || (('T' == R) && (tr = DCM_TSTR))))
2172 || ('S' == V && 'Q' == R && (tr = DCM_SQ))) {
2174 offset += 2; /* skip 00 (2 bytes) */
2176 } else if ('F' == V && 'L' == R) {
2178 } else if ('F' == V && 'D' == R) {
2180 } else if (('S' == V && 'L' == R) || ('U' == V && 'L' == R)) {
2182 } else if (('S' == V && 'S' == R) || ('U' == V && 'S' == R)) {
2184 } else if ('A' == V && 'T' == R) {
2189 else if (('A' == V && ('E' == R || 'S' == R))
2190 || ('C' == V && 'S' == R)
2191 || ('D' == V && ('A' == R || 'S' == R || 'T' == R))
2192 || ('I' == V && 'S' == R)
2193 || ('L' == V && ('O' == R || 'T' == R))
2194 || ('P' == V && 'N' == R)
2195 || ('S' == V && ('H' == R ||| 'T' == R))
2196 || ('T' == V && 'M' == R)
2197 || ('U' == V && ('I' == R || 'T' == R)))
2200 } break; /* don't fall through -- check length */
2202 if (DCM_ILE & syntax) /* is it LE */
2203 tlen = tvb_get_letohs(tvb, offset);
2205 tlen = tvb_get_ntohs(tvb, offset);
2210 DISSECTOR_ASSERT(tlen > 0);
2214 if (DCM_ILE & syntax) /* is it LE */
2215 tlen = tvb_get_letohl(tvb, offset);
2217 tlen = tvb_get_ntohl(tvb, offset);
2222 DISSECTOR_ASSERT(tlen > 0);
2224 } break; /* don't fall through -- check length */
2227 int totlen = (offset - toffset);
2228 if (0xffffffff == tlen || 0xfffe == grp) {
2229 val = tvb_get_ptr(tvb, toffset, totlen);
2230 proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2231 toffset, totlen, val,
2232 "(%04x,%04x) %-8x %s", grp, elm, tlen,
2233 dcm_tag2str(grp, elm, syntax, tvb, offset, 0, vr, tr, &tag_value));
2236 /* } else if (0xfffe == grp) { */ /* need to make a sub-tree here */
2239 val = tvb_get_ptr(tvb, toffset, totlen);
2240 proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2241 toffset, totlen, val,
2242 "(%04x,%04x) %-8x %s", grp, elm, tlen,
2243 dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, tr, &tag_value));
2247 /* Store SOP Class and Instance UID in first PDV of this object */
2248 if (grp == 0x0008 && elm == 0x0016) {
2249 dcm_state_pdv_get_obj_start(pdv)->sop_class_uid = g_strdup(tag_value);
2251 else if (grp == 0x0008 && elm == 0x0018) {
2252 dcm_state_pdv_get_obj_start(pdv)->sop_instance_uid = g_strdup(tag_value);
2254 else if (grp == 0x0000 && elm == 0x0100) {
2255 /* This is the command tag -> overwrite existing PDV description */
2256 pdv->desc = g_strdup(tag_value);
2265 /* After a properly formed Tag, where Tag, VR, Len and Value are present
2266 The next state should be a D_TAG
2268 If the value is too large (start of a long value) we remain in state D_VALUE
2269 But if we are in a not detected continutation we may also get 'stuck' in state D_VALUE
2272 if (D_VALUE == state) {
2277 tag_value_fragment_len = pdv_len - offset + 10; /* The 10 is a result of debugging :-((
2278 Fix once the Tag parisng has been structured
2280 val = tvb_get_ptr(tvb, offset, tag_value_fragment_len);
2282 buf=ep_alloc(2048); /* Longer than what dcm_tag2str() returns */
2285 g_snprintf(buf, 2048, "(%04x,%04x) %-8x %s",
2287 dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, DCM_OTH, &tag_value));
2289 proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2290 offset, tag_value_fragment_len, val, "%s Bytes %10d - %10d [start]", buf, 1, tag_value_fragment_len);
2292 if (!pdv->initalized) {
2293 /* First time parsing of this PDV.
2294 Save the needed data for reuse, i.e. when being called just to open a particular packet
2297 pdv->open_tag_len = tlen;
2298 pdv->open_tag_rlen = tlen-tag_value_fragment_len;
2299 pdv->open_tag_desc = g_strdup(buf); /* EP memory will be freeded. Therefore copy */
2300 pdv->initalized = TRUE;
2304 if (!pdv->initalized) {
2305 /* First time parsing of this PDV.
2306 Save the needed data for reuse, i.e. when called just to open a particular packet
2308 pdv->open_tag_len = 0;
2309 pdv->open_tag_rlen = 0;
2310 pdv->open_tag_desc = NULL;
2311 pdv->initalized = TRUE;
2315 if (have_tap_listener(dicom_eo_tap) && pdv->data_len>0) {
2316 if (pdv->is_last_fragment) {
2317 dcm_export_create_object(pinfo, assoc, pdv);
2321 *pdv_description = pdv->desc;
2327 dissect_dcm_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
2328 dcm_state_assoc_t *assoc, int offset, guint32 pdu_len, guchar **pdu_description)
2334 - (1+) presentation data value (PDV) items
2336 10 1 Presentation Context ID (odd ints 1 - 255)
2339 0x01 if set, contains Message Command info, else Message Data
2340 0x02 if set, contains last fragment
2343 proto_tree *pdv_ptree = NULL; /* Tree for item details */
2344 proto_item *pdv_pitem = NULL;
2346 guchar *buf_desc=NULL; /* PDU description */
2347 guchar *pdv_description=NULL;
2349 gboolean first_pdv=TRUE;
2351 int endpos = offset + pdu_len;
2354 buf_desc=se_alloc(MAX_BUFFER); /* Valid for this capture, since we return this buffer */
2357 /* Loop thorugh multiple PDVs */
2358 while (offset < endpos) {
2359 pdv_len = tvb_get_ntohl(tvb, offset);
2360 DISSECTOR_ASSERT(pdv_len > 0);
2362 pdv_pitem = proto_tree_add_text(tree, tvb, offset, pdv_len+4, "PDV");
2363 pdv_ptree = proto_item_add_subtree(pdv_pitem, ett_dcm_data_pdv);
2365 proto_tree_add_item(pdv_ptree, hf_dcm_pdv_len, tvb, offset, 4, FALSE);
2368 offset = dissect_dcm_pdv(tvb, pinfo, pdv_ptree, assoc, offset, pdv_len, &pdv_description);
2371 g_snprintf(buf_desc, MAX_BUFFER, "%s", pdv_description);
2374 g_snprintf(buf_desc, MAX_BUFFER, "%s, %s", buf_desc, pdv_description);
2377 proto_item_append_text(pdv_pitem, ", %s", pdv_description);
2381 /* offset should be advanced by pdv_len */
2384 *pdu_description=buf_desc;
2390 dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean require_assoc_req)
2392 /* Code to actually dissect the packets */
2395 guint32 pdu_start=0;
2403 Modified orignal code, which was optimized for a heuristic detection, and therefore
2404 caused some load and memory consumption, for every non DICOM packet not processed
2407 Since tcp packets are now assembled well by wireshark (in conjunction with the dissectors)
2408 we will only see properly alligned PDUs, at the beginnig of the buffer, else its not DICOM
2411 Therfore do the byte checking as early as possible
2412 The heurisitc hook, checks for an association request
2414 DICOM PDU are nice, but need to be managed
2416 We can have any combination:
2417 - One or more DICOM PDU per TCP packet
2418 - PDU split over different TCP packets
2419 - And both together, i.e. some complete PDUs and then a fraction of a new PDU in a TCP packet
2421 This function will handle multiple PDUs per TCP packet and will ask for more data,
2422 if the last PDU does not fit
2424 It does not reassamble fragmented PDVs by purpose, since the Tag Value parsing needs to be done
2425 per Tag, and PDU recombinaion here would
2426 a) need to eliminate PDU/PDV/Ctx header (12 bytes)
2427 b) not show the true DICOM logic in transfer
2429 The Lenght check is tricky. If not a PDV continuation, 10 Bytes are required. For PDV continuation
2430 anything seems to be possible, depending on the buffer alignment of the sending process
2432 I have seen a 4 Byte PDU 'Header' just at the end of a TCP packet, which will come in here
2433 as tlen with 4 bytes.
2436 tlen = tvb_reported_length(tvb);
2438 pdu_type = tvb_get_guint8(tvb, 0);
2439 if (pdu_type==0 || pdu_type>7) /* Wrong PDU type. 'Or' is slightly more efficient than 'and' */
2440 return 0; /* No bytes taken from the stack */
2444 /* Hopefully we don't have 1 Byte PDUs in PDV continuations, otherwise reduce to 1 */
2448 /* we need 6 bytes at least to get PDU length */
2449 pinfo->desegment_offset = offset;
2450 pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
2458 pdu_len = tvb_get_ntohl(tvb, 2);
2459 if (pdu_len<4) /* The smallest PDUs are ASSOC Rejects & Release Msgs */
2462 if (require_assoc_req) {
2464 /* find_conversation() seems to return a converstation, even if we never saw
2465 any packet yet. Not really my interpretation of this function.
2467 Therefore also check, if we already stored configuration data for converstation
2470 if (dcm_state_get(pinfo, FALSE)==NULL) {
2472 /* config data does not exist, check for association request */
2474 vers = tvb_get_ntohs(tvb, 6);
2476 if (!(pdu_type == 1 && vers == 1)) { /* Not PDU type 0x01 or not Version 1 */
2480 /* Exit if TCP payload is bigger than PDU length (plues header)
2481 ok. for PRESENTATION_DATA, questionable for ASSOCIATION requests
2483 if (pdu_len+6 < tlen)
2489 if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
2490 col_clear(pinfo->cinfo, COL_PROTOCOL);
2493 /* Process all PDUs in the buffer */
2494 while (pdu_start < tlen) {
2496 if ((pdu_len+6) > (tlen-offset)) {
2498 /* PDU is larger than the remaing packet (buffer), therefore request whole PDU
2499 The next time this function is called, tlen will be equal to pdu_len
2502 pinfo->desegment_offset = offset;
2503 pinfo->desegment_len = (pdu_len+6) - (tlen-offset);
2505 /* Why return a boolean for a deliberate int function? No clue, but
2506 no better working example found.
2511 /* Process a whole PDU */
2512 offset=dissect_dcm_pdu(tvb, pinfo, tree, pdu_start);
2515 pdu_start = pdu_start + pdu_len + 6;
2517 if (pdu_start < tlen - 6) {
2518 /* we got at least 6 bytes of the next PDU still in the buffer */
2519 pdu_len = tvb_get_ntohl(tvb, pdu_start+2);
2528 /* Call back functions used to register */
2530 dissect_dcm_static(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
2532 /* Less checking on ports that match */
2533 return dissect_dcm_main(tvb, pinfo, tree, 0);
2537 dissect_dcm_heuristic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
2539 /* Only decode conerstations, which include an Association Request */
2540 /* This will be potentially called for every packet */
2541 return dissect_dcm_main(tvb, pinfo, tree, TRUE);
2545 dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
2547 proto_item *ti=NULL;
2548 proto_item *ti_pdu_type=NULL;
2550 dcm_state_t *dcm_data=NULL;
2551 dcm_state_assoc_t *assoc=NULL;
2553 proto_tree *dcm_tree=NULL;
2558 guchar *pdu_description=NULL;
2562 gboolean valid_pdutype=TRUE;
2564 guchar *info_str = NULL;
2566 /* Get or create converstation. Used to store context IDs and xfer Syntax */
2568 dcm_data = dcm_state_get(pinfo, TRUE);
2569 if (dcm_data == NULL) { /* internal error. Failed to create main dicom data structre */
2573 if (check_col(pinfo->cinfo, COL_PROTOCOL))
2574 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DICOM");
2576 /* This field shows up as the "Info" column in the display; you should make
2577 it, if possible, summarize what's in the packet, so that a user looking
2578 at the list of packets can tell what type of packet it is. See section 1.5
2579 for more information.
2582 if (check_col(pinfo->cinfo, COL_INFO))
2583 col_clear(pinfo->cinfo, COL_INFO);
2586 /* 'Force' new association object */
2587 assoc=dcm_state_assoc_new(dcm_data, pinfo->fd->num);
2590 /* Create new association object, if needed, i.e. if we association request is not in capture */
2591 assoc=dcm_state_assoc_get(dcm_data, pinfo->fd->num, TRUE);
2594 if (assoc == NULL) { /* internal error. Failed to association structre */
2598 info_str=ep_alloc(MAX_BUFFER);
2601 pdu_type = tvb_get_guint8(tvb, offset);
2602 pdu_len = tvb_get_ntohl(tvb, offset + 2);
2605 case 1: /* ASSOC Request */
2606 tvb_memcpy(tvb, assoc->ae_called, 10, 16);
2607 tvb_memcpy(tvb, assoc->ae_calling, 26, 16);
2608 assoc->ae_called[AEEND] = 0;
2609 assoc->ae_calling[AEEND] = 0;
2610 g_snprintf(info_str, 128, "A-ASSOCIATE request %s --> %s",
2611 g_strstrip(assoc->ae_calling), g_strstrip(assoc->ae_called));
2614 case 2: /* ASSOC Accept */
2615 tvb_memcpy(tvb, assoc->ae_called_resp, 10, 16);
2616 tvb_memcpy(tvb, assoc->ae_calling_resp, 26, 16);
2617 assoc->ae_called_resp[AEEND] = 0;
2618 assoc->ae_calling_resp[AEEND] = 0;
2619 g_snprintf(info_str, MAX_BUFFER, "A-ASSOCIATE accept %s <-- %s",
2620 g_strstrip(assoc->ae_calling_resp), g_strstrip(assoc->ae_called_resp));
2623 case 3: /* ASSOC Reject */
2624 assoc->result = tvb_get_guint8(tvb, 7);
2625 assoc->source = tvb_get_guint8(tvb, 8);
2626 assoc->reason = tvb_get_guint8(tvb, 9);
2627 g_snprintf(info_str, 128, "A-ASSOCIATE reject %s <-- %s %s %s %s",
2628 g_strstrip(assoc->ae_calling), g_strstrip(assoc->ae_called),
2629 dcm_result2str(assoc->result),
2630 dcm_source2str(assoc->source),
2631 dcm_reason2str(assoc->source, assoc->reason));
2636 case 5: /* RELEASE Request */
2637 info_str="A-RELEASE request";
2639 case 6: /* RELEASE Response */
2640 info_str="A-RELEASE response";
2643 assoc->source = tvb_get_guint8(tvb, 8);
2644 assoc->reason = tvb_get_guint8(tvb, 9);
2645 g_snprintf(info_str, 128, "ABORT %s <-- %s %s %s",
2646 assoc->ae_called, assoc->ae_calling,
2647 (assoc->source == 1) ? "USER" :
2648 (assoc->source == 2) ? "PROVIDER" : "",
2649 assoc->source == 1 ? dcm_abort2str(assoc->reason) : "");
2652 info_str="Continuation or non-DICOM traffic";
2653 valid_pdutype = FALSE; /* No packets taken from stack */
2657 if (check_col(pinfo->cinfo, COL_INFO))
2658 col_add_str(pinfo->cinfo, COL_INFO, info_str);
2660 if (valid_pdutype) {
2662 if (tree || have_tap_listener(dicom_eo_tap)) {
2663 /* In the interest of speed, if "tree" is NULL, don't do any work not
2664 necessary to generate protocol tree items.
2668 ti = proto_tree_add_item(tree, proto_dcm, tvb, offset, -1, FALSE);
2669 dcm_tree = proto_item_add_subtree(ti, ett_dcm);
2670 ti_pdu_type = proto_tree_add_uint_format(dcm_tree, hf_dcm_pdu, tvb, offset, pdu_len+6,
2671 pdu_type, "PDU Type 0x%x (%s)", pdu_type, dcm_pdu2str(pdu_type));
2672 proto_tree_add_item(dcm_tree, hf_dcm_pdu_len, tvb, offset+2, 4, FALSE);
2675 expert_add_info_format(pinfo, ti_pdu_type, PI_RESPONSE_CODE, PI_WARN, "Asscociation rejected");
2677 else if (pdu_type==7) {
2678 expert_add_info_format(pinfo, ti_pdu_type, PI_RESPONSE_CODE, PI_WARN, "Asscociation aborted");
2682 case 1: /* ASSOC Request */
2683 case 2: /* ASSOC Accept */
2684 tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, offset, pdu_len+6, info_str);
2685 offset = dissect_dcm_assoc(tvb, pinfo, tf, assoc, offset+assoc_header, pdu_len+6-assoc_header);
2689 offset = dissect_dcm_data(tvb, pinfo, dcm_tree, assoc, offset+6, pdu_len, &pdu_description);
2690 proto_item_append_text(ti, ", %s", pdu_description);
2692 if (check_col(pinfo->cinfo, COL_INFO))
2693 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", pdu_description);
2697 case 3: /* ASSOC Reject */
2698 case 5: /* RELEASE Request */
2699 case 6: /* RELEASE Response */
2701 /* Info string decoding only at this point */
2702 offset += pdu_len+6;
2710 else if (pdu_type == 1 || pdu_type == 2) {
2711 /* Always dissect Association request and response in order
2712 to set the data strucures needed for the PDU Data packets
2714 offset = dissect_dcm_assoc(tvb, pinfo, NULL, assoc, offset+assoc_header, pdu_len+6-assoc_header);
2718 /* If this protocol has a sub-dissector call it here, see section 1.8 */
2720 return offset; /* return the number of processed bytes */
2725 /* Register the protocol with Wireshark */
2727 /* this format is require because a script is used to build the C function
2728 that calls all the protocol registration.
2731 static void range_delete_dcm_tcp_callback(guint32 port) {
2732 dissector_delete("tcp.port", port, dcm_handle);
2735 static void range_add_dcm_tcp_callback(guint32 port) {
2736 dissector_add("tcp.port", port, dcm_handle);
2739 static void dcm_apply_settings(void) {
2741 /* deregister first */
2742 range_foreach(global_dcm_tcp_range_backup, range_delete_dcm_tcp_callback);
2743 g_free(global_dcm_tcp_range_backup);
2745 heur_dissector_delete("tcp", dissect_dcm_heuristic, proto_dcm);
2747 /* Register 'static' tcp port range specified in properties
2748 Statically defined ports take precedence over a heuristic one,
2749 I.e., if an foreign protocol claims a port, where dicom is running on
2750 We would never be called, by just having the heuristic registration
2753 range_foreach(global_dcm_tcp_range, range_add_dcm_tcp_callback);
2755 /* remember settings for next time */
2756 global_dcm_tcp_range_backup = range_copy(global_dcm_tcp_range);
2758 /* Add heuristic search, if user selected it */
2760 if (global_dcm_heuristic)
2761 heur_dissector_add("tcp", dissect_dcm_heuristic, proto_dcm);
2765 proto_register_dcm(void)
2767 /* Setup list of header fields See Section 1.6.1 for details*/
2768 static hf_register_info hf[] = {
2769 { &hf_dcm_pdu, { "PDU Type", "dicom.pdu.type",
2770 FT_UINT8, BASE_HEX, VALS(dcm_pdu_ids), 0, "", HFILL } },
2771 { &hf_dcm_pdu_len, { "PDU Length", "dicom.pdu.len",
2772 FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
2773 { &hf_dcm_pdu_type, { "PDU Detail", "dicom.pdu.detail",
2774 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2775 { &hf_dcm_assoc_item_type, { "Item Type", "dicom.assoc.item.type",
2776 FT_UINT8, BASE_HEX, VALS(dcm_assoc_item_type), 0, "", HFILL } },
2777 { &hf_dcm_assoc_item_len, { "Item Length", "dicom.assoc.item.len",
2778 FT_UINT16, BASE_DEC, NULL, 0, "", HFILL } },
2779 { &hf_dcm_actx, { "Application Context", "dicom.actx",
2780 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2781 { &hf_dcm_pctx_id, { "Presentation Context ID", "dicom.pctx.id",
2782 FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
2783 { &hf_dcm_pctx_result, { "Presentation Context Result", "dicom.pctx.id",
2784 FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
2785 { &hf_dcm_pctx_abss_syntax, { "Abstract Syntax", "dicom.pctx.abss.syntax",
2786 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2787 { &hf_dcm_pctx_xfer_syntax, { "Transfer Syntax", "dicom.pctx.xfer.syntax",
2788 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2789 { &hf_dcm_info_uid, { "Implementation Class UID", "dicom.userinfo.uid",
2790 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2791 { &hf_dcm_info_version, { "Implementation Version", "dicom.userinfo.version",
2792 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2793 { &hf_dcm_pdu_maxlen, { "Max PDU Length", "dicom.max_pdu_len",
2794 FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
2795 { &hf_dcm_pdv_len, { "PDV Length", "dicom.pdv.len",
2796 FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
2797 { &hf_dcm_pdv_ctx, { "PDV Context", "dicom.pdv.ctx",
2798 FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
2799 { &hf_dcm_pdv_flags, { "PDV Flags", "dicom.pdv.flags",
2800 FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
2801 { &hf_dcm_data_tag, { "Tag", "dicom.data.tag",
2802 FT_BYTES, BASE_HEX, NULL, 0, "", HFILL } },
2804 { &hf_dcm_FIELDABBREV, { "FIELDNAME", "dicom.FIELDABBREV",
2805 FIELDTYPE, FIELDBASE, FIELDCONVERT, BITMASK, "FIELDDESCR", HFILL } },
2809 /* Setup protocol subtree array */
2810 static gint *ett[] = {
2815 &ett_assoc_pctx_abss,
2816 &ett_assoc_pctx_xfer,
2818 &ett_assoc_info_uid,
2819 &ett_assoc_info_version,
2825 module_t *dcm_module;
2827 /* Register the protocol name and description */
2828 proto_dcm = proto_register_protocol("DICOM", "DICOM", "dicom");
2830 /* Required function calls to register the header fields and subtrees used */
2831 proto_register_field_array(proto_dcm, hf, array_length(hf));
2832 proto_register_subtree_array(ett, array_length(ett));
2834 dcm_module = prefs_register_protocol(proto_dcm, dcm_apply_settings);
2836 range_convert_str(&global_dcm_tcp_range, DICOM_DEFAULT_RANGE, 65535);
2837 global_dcm_tcp_range_backup = range_empty();
2838 prefs_register_range_preference(dcm_module, "tcp.port",
2839 "DICOM Ports", "DICOM Ports range", &global_dcm_tcp_range, 65535);
2841 prefs_register_bool_preference(dcm_module, "heuristic",
2842 "Search on any TCP Port (heuristic mode)",
2843 "When enabled, the DICOM dissector will parse all TCP packets "
2844 "not handled by any other dissector and look for an association request. "
2845 "Disabled by default, to preserve resources for the non DICOM community.",
2846 &global_dcm_heuristic);
2848 prefs_register_bool_preference(dcm_module, "header",
2849 "Create Meta Header on Export",
2850 "Create DICOM File Meta Header according to PS 3.10 on export for PDUs. "
2851 "If the cpatured PDV does not contain a SOP Class UID and SOP Instance UID "
2852 "(e.g. for command PDVs), wireshark spefic ones will be created.",
2853 &global_dcm_header);
2855 dicom_eo_tap = register_tap("dicom_eo"); /* DICOM Export Object tap */
2857 register_init_routine(&dcm_init);
2861 /* If this dissector uses sub-dissector registration add a registration routine.
2862 This format is required because a script is used to find these routines and
2863 create the code that calls these routines.
2866 proto_reg_handoff_dcm(void)
2869 dcm_handle = new_create_dissector_handle(dissect_dcm_static, proto_dcm);
2871 dcm_apply_settings(); /* Register static and heuristic ports */
2882 6 2 protocol version (0x0 0x1)
2887 74 - presentation data value items
2892 2 protocol version (0x0 0x1)
2894 16 dest aetitle (not checked)
2895 16 src aetitle (not checked)
2897 - presentation data value items
2903 1 result (1 reject perm, 2 reject transient)
2904 1 source (1 service user, 2 service provider, 3 service profider)
2908 2 application context name not supported
2909 3 calling aetitle not recognized
2910 7 called aetitle not recognized
2913 2 protocol version not supported
2915 1 temporary congestion
2916 2 local limit exceeded
2921 - (1+) presentation data value (PDV) items
2923 10 1 Presentation Context ID (odd ints 1 - 255)
2926 0x01 if set, contains Message Command info, else Message Data
2927 0x02 if set, contains last fragment
2943 1 source (0 = user, 1 = provider)
2944 1 reason if 1 == source (0 not spec, 1 unrecognized, 2 unexpected 4 unrecognized param, 5 unexpected param, 6 invalid param)
2949 10 Application Context
2954 20 Presentation Context
2957 1 Presentation context id
2959 - (1) abstract and (1+) transfer syntax sub-items
2961 21 Presentation Context (Reply)
2964 1 ID (odd int's 1-255)
2966 1 result (0 accept, 1 user-reject, 2 no-reason, 3 abstract not supported, 4- transfer syntax not supported)
2990 From 3.7 Annex D Association Negotiation
2991 ========================================
2993 52 IMPLEMENTATION CLASS UID
2997 n Implementation-class-uid
2999 55 IMPLEMENTATION VERSION NAME
3003 n Implementation-version-name
3005 53 ASYNCHRONOUS OPERATIONS WINDOW
3009 2 Maximum-number-operations-invoked
3010 2 Maximum-number-operations-performed
3012 54 SCP/SCU ROLE SELECTION
3019 0 - non support of the SCU role
3020 1 - support of the SCU role
3022 0 - non support of the SCP role
3023 1 - support of the SCP role.
3025 56 SOP CLASS EXTENDED NEGOTIATION
3029 2 SOP-class-uid-length (m)
3031 n-m Service-class-application-information
3033 57 SOP CLASS COMMON EXTENDED NEGOTIATION
3037 2 SOP-class-uid-length (m)
3038 7-x SOP-class-uid The SOP Class identifier encoded as a UID as defined in PS 3.5.
3039 (x+1)-(x+2) Service-class-uid-length The Service-class-uid-length shall be the number of bytes in the Service-class-uid field. It shall be encoded as an unsigned binary number.
3040 (x+3)-y Service-class-uid The Service Class identifier encoded as a UID as defined in PS 3.5.
3041 (y+1)-(y+2) Related-general-sop-class-identification-length The Related-general-sop-class-identification-length shall be the number of bytes in the Related-general-sop-class-identification field. Shall be zero if no Related General SOP Classes are identified.
3042 (y+3)-z Related-general-sop-class-identification The Related-general-sop-class-identification is a sequence of pairs of length and UID sub-fields. Each pair of sub-fields shall be formatted in accordance with Table D.3-13.
3043 (z+1)-k Reserved Reserved for additional fields of the sub-item. Shall be zero-length for Version 0 of Sub-item definition.
3046 RELATED-GENERAL-SOP-CLASS-IDENTIFICATION SUB-FIELDS
3047 Bytes Sub-Field Name Description of Sub-Field
3048 1-2 Related-general-sop-class-uid-length The Related-general-sop-class-uid-length shall be the number of bytes in the Related-general-sop-class-uid sub-field. It shall be encoded as an unsigned binary number.
3049 3-n Related-general-sop-class-uid The Related General SOP Class identifier encoded as a UID as defined in PS 3.5.
3051 58 User Identity Negotiation
3055 1 User-Identity-Type Field value shall be in the range 1 to 4 with the following meanings:
3056 1
\96 Username as a string in UTF-8
3057 2
\96 Username as a string in UTF-8 and passcode
3058 3
\96 Kerberos Service ticket
3059 4
\96 SAML Assertion
3060 Other values are reserved for future standardization.
3061 1 Positive-response-requested Field value:
3062 0 - no response requested
3063 1 - positive response requested
3064 2 Primary-field-length The User-Identity-Length shall contain the length of the User-Identity value.
3065 9-n Primary-field This field shall convey the user identity, either the username as a series of characters, or the Kerberos Service ticket encoded in accordance with RFC-1510.
3066 n+1-n+2 Secondary-field-length This field shall be non-zero only if User-Identity-Type has the value 2. It shall contain the length of the secondary-field.
3067 n+3-m Secondary-field This field shall be present only if User-Identity-Type has the value 2. It shall contain the Passcode value.
3069 59 User Identity Negotiation Reply
3073 5-6 Server-response-length This field shall contain the number of bytes in the Server-response. May be zero.
3074 7-n Server-response This field shall contain the Kerberos Server ticket, encoded in accordance with RFC-1510, if the User-Identity-Type value in the A-ASSOCIATE-RQ was 3. This field shall contain the SAML response if the User-Identity-Type value in the A-ASSOCIATE-RQ was 4. This field shall be zero length if the value of the User-Identity-Type in the A-ASSOCIATE-RQ was 1 or 2.