1 /* packet-dcm.c
2  * Routines for DICOM dissection
3  * Copyright 2003, Rich Coe <richcoe2@gmail.com>
4  * Copyright 2008-2019, David Aggeler <david_aggeler@hispeed.ch>
5  *
6  * DICOM communication protocol: https://www.dicomstandard.org/current/
7  *
8  * Part  5: Data Structures and Encoding
9  * Part  6: Data Dictionary
10  * Part  7: Message Exchange
11  * Part  8: Network Communication Support for Message Exchange
12  * Part 10: Media Storage and File Format
13  *
14  * Wireshark - Network traffic analyzer
15  * By Gerald Combs <gerald@wireshark.org>
16  * Copyright 1998 Gerald Combs
17  *
18  * SPDX-License-Identifier: GPL-2.0-or-later
19  */
20 
21 
22 /*
23  *
24  * ToDo
25  *
26  * - Implement value multiplicity (VM) consistently in dissect_dcm_tag_value()
27  * - Syntax detection, in case an association request is missing in capture
28  * - Read private tags from configuration and parse in capture
29  *
30  * History
31  *
32  * Feb 2019 - David Aggeler
33  *
34  * - Fixed re-assembly and export (consolidated duplicate code)
35  * - Fixed random COL_INFO issues
36  * - Improved COL_INFO for C-FIND
37  * - Improved COL_INFO for multiple PDUs in one frame
38  *
39  * Feb 2019 - Rickard Holmberg
40  *
41  * - Updated DICOM definitions to 2019a
42  *
43  * Oct 2018 - Rickard Holmberg
44  *
45  * - Moved DICOM definitions to packet-dcm.h
46  * - Generate definitions from docbook with Phyton script
47  * - Updated DICOM definitions to 2018e
48  *
49  * June 2018 - David Aggeler
50  *
51  * - Fixed initial COL_INFO for associations. It used to 'append' instead of 'set'.
52  * - Changed initial length check from tvb_reported_length() to tvb_captured_length()
53  * - Heuristic Dissection:
54  *   o Modified registration, so it can be clearly identified in the Enable/Disable Protocols dialog
55  *   o Enabled by default
56  *   o Return proper data type
57  *
58  * February 2018 - David Aggeler
59  *
60  * - Fixed Bug 14415. Some tag descriptions which are added to the parent item (32 tags).
61  *   If one of those was empty a crash occurred. Mainly the RTPlan modality was affected.
62  * - Fixed length decoding for OD, OL, UC, UR
63  * - Fixed hf_dcm_assoc_item_type to be interpreted as 1 byte
64  * - Fixed pdu_type to be interpreted as 1 byte
65  * - Fixed decoding of AT type, where value length was wrongly reported in capture as 2 (instead of n*4)
66  *
67  * Misc. authors & dates
68  *
69  * - Fixed 'AT' value representation. The 'element' was equal to the 'group'.
70  * - Changed 'FL' value representations
71  *
72  * September 2013 - Pascal Quantin
73  *
74  * - Replace all ep_ and se_ allocation with wmem_ allocations
75  *
76  * February 2013 - Stefan Allers
77  *
78  * - Support for dissection of Extended Negotiation (Query/Retrieve)
79  * - Support for dissection of SCP/SCU Role Selection
80  * - Support for dissection of Async Operations Window Negotiation
81  * - Fixed: Improper calculation of length for Association Header
82  * - Missing UIDs (Transfer Syntax, SOP Class...) added acc. PS 3.x-2011
83  *
84  * Jul 11, 2010 - David Aggeler
85  *
86  * - Finally, better reassembly using fragment_add_seq_next().
87  *   The previous mode is still supported.
88  * - Fixed sporadic decoding and export issues. Always decode
89  *   association negotiation, since performance check (tree==NULL)
90  *   is now only in dissect_dcm_pdv_fragmented().
91  * - Added one more PDV length check
92  * - Show Association Headers as individual items
93  * - Code cleanup. i.e. moved a few lookup functions to be closer to the dissection
94  *
95  * May 13, 2010 - David Aggeler (SVN 32815)
96  *
97  * - Fixed HF to separate signed & unsigned values and to have BASE_DEC all signed ones
98  * - Fixed private sequences with undefined length in ILE
99  * - Fixed some spellings in comments
100  *
101  * May 27, 2009 - David Aggeler (SVN 29060)
102  *
103  * - Fixed corrupt files on DICOM Export
104  * - Fixed memory limitation on DICOM Export
105  * - Removed minimum packet length for static port mode
106  * - Simplified checks for heuristic mode
107  * - Removed unused functions
108  *
109  * May 17, 2009 - David Aggeler (SVN 28392)
110  *
111  * - Spelling
112  * - Added expert_add_info() for status responses with warning & error level
113  * - Added command details in info column (optionally)
114  *
115  * Dec 19, 2008 to Mar 29, 2009 - Misc (SVN 27880)
116  *
117  * - Spellings, see SVN
118  *
119  * Oct 26, 2008 - David Aggeler (SVN 26662)
120  *
121  * - Support remaining DICOM/ARCNEMA tags
122  *
123  * Oct 3, 2008 - David Aggeler (SVN 26417)
124  *
125  * - DICOM Tags: Support all tags, except for group 1000, 7Fxx
126  *               and tags (0020,3100 to 0020, 31FF).
127  *               Luckily these ones are retired anyhow
128  * - DICOM Tags: Optionally show sequences, items and tags as subtree
129  * - DICOM Tags: Certain items do have a summary of a few contained tags
130  * - DICOM Tags: Support all defined VR representations
131  * - DICOM Tags: For Explicit Syntax, use VR in the capture
132  * - DICOM Tags: Lookup UIDs
133  * - DICOM Tags: Handle split at PDV start and end. RT Structures were affected by this.
134  * - DICOM Tags: Handle split in tag header
135  *
136  * - Added all status messages from PS 3.4 & PS 3.7
137  * - Fixed two more type warnings on solaris, i.e. (gchar *)tvb_get_ephemeral_string
138  * - Replaced all ep_alloc() with ep_alloc0() and se_alloc() with se_alloc0()
139  * - Replaced g_strdup with ep_strdup() or se_strdup()
140  * - Show multiple PDU description in COL_INFO, not just last one. Still not all, but more
141  *   sophisticated logic for this column is probably overkill
142  * - Since DICOM is a 32 bit protocol with all length items specified unsigned
143  *   all offset & position variables are now declared as guint32 for dissect_dcm_pdu and
144  *   its nested functions. dissect_dcm_main() remained by purpose on int,
145  *   since we request data consolidation, requiring a TRUE as return value
146  * - Decode DVTk streams when using defined ports (not in heuristic mode)
147  * - Changed to warning level 4 (for MSVC) and fixed the warnings
148  * - Code cleanup & removed last DISSECTOR_ASSERT()
149  *
150  * Jul 25, 2008 - David Aggeler (SVN 25834)
151  *
152  * - Replaced guchar with gchar, since it caused a lot of warnings on solaris.
153  * - Moved a little more form the include to this one to be consistent
154  *
155  * Jul 17, 2008 - David Aggeler
156  *
157  * - Export objects as part 10 compliant DICOM file. Finally, this major milestone has been reached.
158  * - PDVs are now a child of the PCTX rather than the ASSOC object.
159  * - Fixed PDV continuation for unknown tags (e.g. RT Structure Set)
160  * - Replaced proprietary trim() with g_strstrip()
161  * - Fixed strings that are displayed with /000 (padding of odd length)
162  * - Added expert_add_info() for invalid flags and presentation context IDs
163  *
164  * Jun 17, 2008 - David Aggeler
165  *
166  * - Support multiple PDVs per PDU
167  * - Better summary, in PDV, PDU header and in INFO Column, e.g. show commands like C-STORE
168  * - Fixed Association Reject (was working before my changes)
169  * - Fixed PDV Continuation with very small packets. Reduced minimum packet length
170  *   from 10 to 2 Bytes for PDU Type 4
171  * - Fixed PDV Continuation. Last packet was not found correctly.
172  * - Fixed compilation warning (build 56 on solaris)
173  * - Fixed tree expansion (hf_dcm_xxx)
174  * - Added expert_add_info() for Association Reject
175  * - Added expert_add_info() for Association Abort
176  * - Added expert_add_info() for short PDVs (i.e. last fragment, but PDV is not completed yet)
177  * - Clarified and grouped data structures and its related code (dcmItem, dcmState) to have
178  *   consistent _new() & _get() functions and to be according to coding conventions
179  * - Added more function declaration to be more consistent
180  * - All dissect_dcm_xx now have (almost) the same parameter order
181  * - Removed DISSECTOR_ASSERT() for packet data errors. Not designed to handle this.
182  * - Handle multiple DICOM Associations in a capture correctly, i.e. if presentation contexts are different.
183  *
184  * May 23, 2008 - David Aggeler
185  *
186  * - Added Class UID lookup, both in the association and in the transfer
187  * - Better hierarchy for items in Association request/response and therefore better overview
188  *   This was a major rework. Abstract Syntax & Transfer Syntax are now children
189  *   of a presentation context and therefore grouped. User Info is now grouped.
190  * - Re-assemble PDVs that span multiple PDUs, i.e fix continuation packets
191  *   This caused significant changes to the data structures
192  * - Added preference with DICOM TCP ports, to prevent 'stealing' the conversation
193  *   i.e. don't just rely on heuristic
194  * - Use pinfo->desegment_len instead of tcp_dissect_pdus()
195  * - Returns number of bytes parsed
196  * - For non DICOM packets, do not allocate any memory anymore,
197  * - Added one DISSECTOR_ASSERT() to prevent loop with len==0. More to come
198  * - Heuristic search is optional to save resources for non DICOM users
199  *
200  * - Output naming closer to DICOM Standard
201  * - Variable names closer to Standard
202  * - Protocol in now called DICOM not dcm anymore.
203  * - Fixed type of a few variables to guchar instead of guint8
204  * - Changed some of the length displays to decimal, because the hex value can
205  *   already be seen in the packet and decimal is easier for length calculation
206  *   in respect to TCP
207  *
208  * Apr 28, 2005 - Rich Coe
209  *
210  * - fix memory leak when Assoc packet is processed repeatedly in wireshark
211  * - removed unused partial packet flag
212  * - added better support for DICOM VR
213  *      - sequences
214  *      - report actual VR in packet display, if supplied by xfer syntax
215  *      - show that we are not displaying entire tag string with '[...]',
216  *        some tags can hold up to 2^32-1 chars
217  *
218  * - remove my goofy attempt at trying to get access to the fragmented packets
219  * - process all the data in the Assoc packet even if display is off
220  * - limit display of data in Assoc packet to defined size of the data even
221  *   if reported size is larger
222  * - show the last tag in a packet as [incomplete] if we don't have all the data
223  * - added framework for reporting DICOM async negotiation (not finished)
224  *   (I'm not aware of an implementation which currently supports this)
225  *
226  * Nov 9, 2004 - Rich Coe
227  *
228  * - Fixed the heuristic code -- sometimes a conversation already exists
229  * - Fixed the dissect code to display all the tags in the PDU
230  *
231  * Initial - Rich Coe
232  *
233  * - It currently displays most of the DICOM packets.
234  * - I've used it to debug Query/Retrieve, Storage, and Echo protocols.
235  * - Not all DICOM tags are currently displayed symbolically.
236  *   Unknown tags are displayed as '(unknown)'
237  *   More known tags might be added in the future.
238  *   If the tag data contains a string, it will be displayed.
239  *   Even if the tag contains Explicit VR, it is not currently used to
240  *   symbolically display the data.
241  *
242  */
243 
244 #include "config.h"
245 
246 #include <epan/packet.h>
247 #include <epan/exceptions.h>
248 #include <epan/prefs.h>
249 #include <epan/expert.h>
250 #include <epan/tap.h>
251 #include <epan/reassemble.h>
252 #include <epan/export_object.h>
253 
254 #include "packet-tcp.h"
255 
256 #include "packet-dcm.h"
257 
258 void proto_register_dcm(void);
259 void proto_reg_handoff_dcm(void);
260 
261 #define DICOM_DEFAULT_RANGE "104"
262 
263 /* Many thanks to http://medicalconnections.co.uk/ for the GUID */
264 #define WIRESHARK_IMPLEMENTATION_UID                    "1.2.826.0.1.3680043.8.427.10"
265 #define WIRESHARK_MEDIA_STORAGE_SOP_CLASS_UID           "1.2.826.0.1.3680043.8.427.11.1"
266 #define WIRESHARK_MEDIA_STORAGE_SOP_INSTANCE_UID_PREFIX "1.2.826.0.1.3680043.8.427.11.2"
267 #define WIRESHARK_IMPLEMENTATION_VERSION                "WIRESHARK"
268 
269 static gboolean global_dcm_export_header = TRUE;
270 static guint    global_dcm_export_minsize = 4096;           /* Filter small objects in export */
271 
272 static gboolean global_dcm_seq_subtree = TRUE;
273 static gboolean global_dcm_tag_subtree = FALSE;             /* Only useful for debugging */
274 static gboolean global_dcm_cmd_details = TRUE;              /* Show details in header and info column */
275 static gboolean global_dcm_reassemble = TRUE;               /* Merge fragmented PDVs */
276 
277 static wmem_map_t *dcm_tag_table = NULL;
278 static wmem_map_t *dcm_uid_table = NULL;
279 static wmem_map_t *dcm_status_table = NULL;
280 
281 /* Initialize the protocol and registered fields */
282 static int proto_dcm = -1;
283 
284 static int dicom_eo_tap = -1;
285 
286 static int hf_dcm_pdu_type = -1;
287 static int hf_dcm_pdu_len = -1;
288 static int hf_dcm_assoc_version = -1;
289 static int hf_dcm_assoc_called = -1;
290 static int hf_dcm_assoc_calling = -1;
291 static int hf_dcm_assoc_reject_result = -1;
292 static int hf_dcm_assoc_reject_source = -1;
293 static int hf_dcm_assoc_reject_reason = -1;
294 static int hf_dcm_assoc_abort_source = -1;
295 static int hf_dcm_assoc_abort_reason = -1;
296 static int hf_dcm_assoc_item_type = -1;
297 static int hf_dcm_assoc_item_len = -1;
298 static int hf_dcm_actx = -1;
299 static int hf_dcm_pctx_id = -1;
300 static int hf_dcm_pctx_result = -1;
301 static int hf_dcm_pctx_abss_syntax = -1;
302 static int hf_dcm_pctx_xfer_syntax = -1;
303 static int hf_dcm_info = -1;
304 static int hf_dcm_info_uid = -1;
305 static int hf_dcm_info_version = -1;
306 static int hf_dcm_info_extneg = -1;
307 static int hf_dcm_info_extneg_sopclassuid_len = -1;
308 static int hf_dcm_info_extneg_sopclassuid = -1;
309 static int hf_dcm_info_extneg_relational_query = -1;
310 static int hf_dcm_info_extneg_date_time_matching = -1;
311 static int hf_dcm_info_extneg_fuzzy_semantic_matching = -1;
312 static int hf_dcm_info_extneg_timezone_query_adjustment = -1;
313 static int hf_dcm_info_rolesel = -1;
314 static int hf_dcm_info_rolesel_sopclassuid_len = -1;
315 static int hf_dcm_info_rolesel_sopclassuid = -1;
316 static int hf_dcm_info_rolesel_scurole = -1;
317 static int hf_dcm_info_rolesel_scprole = -1;
318 static int hf_dcm_info_async_neg = -1;
319 static int hf_dcm_info_async_neg_max_num_ops_inv = -1;
320 static int hf_dcm_info_async_neg_max_num_ops_per = -1;
321 static int hf_dcm_info_user_identify = -1;
322 static int hf_dcm_info_user_identify_type = -1;
323 static int hf_dcm_info_user_identify_response_requested = -1;
324 static int hf_dcm_info_user_identify_primary_field_length = -1;
325 static int hf_dcm_info_user_identify_primary_field = -1;
326 static int hf_dcm_info_user_identify_secondary_field_length = -1;
327 static int hf_dcm_info_user_identify_secondary_field = -1;
328 static int hf_dcm_info_unknown = -1;
329 static int hf_dcm_assoc_item_data = -1;
330 static int hf_dcm_pdu_maxlen = -1;
331 static int hf_dcm_pdv_len = -1;
332 static int hf_dcm_pdv_ctx = -1;
333 static int hf_dcm_pdv_flags = -1;
334 static int hf_dcm_data_tag = -1;
335 static int hf_dcm_tag = -1;
336 static int hf_dcm_tag_vr = -1;
337 static int hf_dcm_tag_vl = -1;
338 static int hf_dcm_tag_value_str = -1;
339 static int hf_dcm_tag_value_16u = -1;
340 static int hf_dcm_tag_value_16s = -1;
341 static int hf_dcm_tag_value_32s = -1;
342 static int hf_dcm_tag_value_32u = -1;
343 static int hf_dcm_tag_value_byte = -1;
344 
345 /* Initialize the subtree pointers */
346 static gint ett_dcm = -1;
347 static gint ett_assoc = -1;
348 static gint ett_assoc_header = -1;
349 static gint ett_assoc_actx = -1;
350 static gint ett_assoc_pctx = -1;
351 static gint ett_assoc_pctx_abss = -1;
352 static gint ett_assoc_pctx_xfer = -1;
353 static gint ett_assoc_info = -1;
354 static gint ett_assoc_info_uid = -1;
355 static gint ett_assoc_info_version = -1;
356 static gint ett_assoc_info_extneg = -1;
357 static gint ett_assoc_info_rolesel = -1;
358 static gint ett_assoc_info_async_neg = -1;
359 static gint ett_assoc_info_user_identify = -1;
360 static gint ett_assoc_info_unknown = -1;
361 static gint ett_dcm_data = -1;
362 static gint ett_dcm_data_pdv = -1;
363 static gint ett_dcm_data_tag = -1;
364 static gint ett_dcm_data_seq = -1;
365 static gint ett_dcm_data_item = -1;
366 
367 static expert_field ei_dcm_data_tag = EI_INIT;
368 static expert_field ei_dcm_multiple_transfer_syntax = EI_INIT;
369 static expert_field ei_dcm_pdv_len = EI_INIT;
370 static expert_field ei_dcm_pdv_flags = EI_INIT;
371 static expert_field ei_dcm_pdv_ctx = EI_INIT;
372 static expert_field ei_dcm_no_abstract_syntax = EI_INIT;
373 static expert_field ei_dcm_no_abstract_syntax_uid = EI_INIT;
374 static expert_field ei_dcm_status_msg = EI_INIT;
375 static expert_field ei_dcm_no_transfer_syntax = EI_INIT;
376 static expert_field ei_dcm_multiple_abstract_syntax = EI_INIT;
377 static expert_field ei_dcm_invalid_pdu_length = EI_INIT;
378 static expert_field ei_dcm_assoc_item_len = EI_INIT;
379 static expert_field ei_dcm_assoc_rejected = EI_INIT;
380 static expert_field ei_dcm_assoc_aborted = EI_INIT;
381 
382 static dissector_handle_t dcm_handle;
383 
384 static const value_string dcm_pdu_ids[] = {
385     { 1, "ASSOC Request" },
386     { 2, "ASSOC Accept" },
387     { 3, "ASSOC Reject" },
388     { 4, "Data" },
389     { 5, "RELEASE Request" },
390     { 6, "RELEASE Response" },
391     { 7, "ABORT" },
392     { 0, NULL }
393 };
394 
395 static const value_string dcm_assoc_item_type[] = {
396     { 0x10, "Application Context" },
397     { 0x20, "Presentation Context" },
398     { 0x21, "Presentation Context Reply" },
399     { 0x30, "Abstract Syntax" },
400     { 0x40, "Transfer Syntax" },
401     { 0x50, "User Info" },
402     { 0x51, "Max Length" },
403     { 0x52, "Implementation Class UID" },
404     { 0x53, "Asynchronous Operations Window Negotiation" },
405     { 0x54, "SCP/SCU Role Selection" },
406     { 0x55, "Implementation Version" },
407     { 0x56, "SOP Class Extended Negotiation" },
408     { 0x58, "User Identity" },
409     { 0, NULL }
410 };
411 
412 static const value_string user_identify_type_vals[] = {
413     { 1, "Username as a string in UTF-8" },
414     { 2, "Username as a string in UTF-8 and passcode" },
415     { 3, "Kerberos Service ticket" },
416     { 4, "SAML Assertion" },
417     { 0, NULL }
418 };
419 
420 /* Used for DICOM Export Object feature */
421 typedef struct _dicom_eo_t {
422     guint32  pkt_num;
423     gchar   *hostname;
424     gchar   *filename;
425     gchar   *content_type;
426     guint32  payload_len;
427     guint8  *payload_data;
428 } dicom_eo_t;
429 
430 static tap_packet_status
dcm_eo_packet(void * tapdata,packet_info * pinfo,epan_dissect_t * edt _U_,const void * data)431 dcm_eo_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_,
432                 const void *data)
433 {
434     export_object_list_t *object_list = (export_object_list_t *)tapdata;
435     const dicom_eo_t *eo_info = (const dicom_eo_t *)data;
436     export_object_entry_t *entry;
437 
438     if (eo_info) { /* We have data waiting for us */
439         /*
440            Don't copy any data. dcm_export_create_object() is already g_malloc() the items
441            Still, the values will be freed when the export Object window is closed.
442            Therefore, strings and buffers must be copied
443         */
444         entry = g_new(export_object_entry_t, 1);
445 
446         entry->pkt_num = pinfo->num;
447         entry->hostname = eo_info->hostname;
448         entry->content_type = eo_info->content_type;
449         entry->filename = g_path_get_basename(eo_info->filename);
450         entry->payload_len  = eo_info->payload_len;
451         entry->payload_data = eo_info->payload_data;
452 
453         object_list->add_entry(object_list->gui_data, entry);
454 
455         return TAP_PACKET_REDRAW; /* State changed - window should be redrawn */
456     } else {
457         return TAP_PACKET_DONT_REDRAW; /* State unchanged - no window updates needed */
458     }
459 }
460 
461 
462 /* ************************************************************************* */
463 /*                  Fragment items                                           */
464 /* ************************************************************************* */
465 
466 /* Initialize the subtree pointers */
467 static gint ett_dcm_pdv = -1;
468 
469 static gint ett_dcm_pdv_fragment = -1;
470 static gint ett_dcm_pdv_fragments = -1;
471 
472 static int hf_dcm_pdv_fragments = -1;
473 static int hf_dcm_pdv_fragment = -1;
474 static int hf_dcm_pdv_fragment_overlap = -1;
475 static int hf_dcm_pdv_fragment_overlap_conflicts = -1;
476 static int hf_dcm_pdv_fragment_multiple_tails = -1;
477 static int hf_dcm_pdv_fragment_too_long_fragment = -1;
478 static int hf_dcm_pdv_fragment_error = -1;
479 static int hf_dcm_pdv_fragment_count = -1;
480 static int hf_dcm_pdv_reassembled_in = -1;
481 static int hf_dcm_pdv_reassembled_length = -1;
482 
483 static const fragment_items dcm_pdv_fragment_items = {
484     /* Fragment subtrees */
485     &ett_dcm_pdv_fragment,
486     &ett_dcm_pdv_fragments,
487     /* Fragment fields */
488     &hf_dcm_pdv_fragments,
489     &hf_dcm_pdv_fragment,
490     &hf_dcm_pdv_fragment_overlap,
491     &hf_dcm_pdv_fragment_overlap_conflicts,
492     &hf_dcm_pdv_fragment_multiple_tails,
493     &hf_dcm_pdv_fragment_too_long_fragment,
494     &hf_dcm_pdv_fragment_error,
495     &hf_dcm_pdv_fragment_count,
496     &hf_dcm_pdv_reassembled_in,
497     &hf_dcm_pdv_reassembled_length,
498     /* Reassembled data field */
499     NULL,
500     /* Tag */
501     "Message fragments"
502 };
503 
504 /* Structure to handle fragmented DICOM PDU packets */
505 static reassembly_table dcm_pdv_reassembly_table;
506 
507 typedef struct dcm_open_tag {
508 
509     /* Contains information about an open tag in a PDV, in case it was not complete.
510 
511        This implementation differentiates between open headers (grm, elm, vr, vl) and
512        open values. This data structure will handle both cases.
513 
514        Open headers are not shown in the packet where the tag starts, but only in the next PDV.
515        Open values are shown in the packet where the tag starts, with <Byte 1-n> as the value
516 
517        The same PDV can close an open tag from a previous PDV at the beginning
518        and at the same have time open a new tag at the end. The closing part at the beginning
519        does not have its own persistent data.
520 
521        Do not overwrite the values, once defined, to save some memory.
522 
523        Since PDVs are always n*2 bytes, store each of the 2 Bytes in a variable.
524        This way, we don't need to call tvb_get_xxx on a self created buffer
525 
526     */
527 
528     gboolean    is_header_fragmented;
529     gboolean    is_value_fragmented;
530 
531     guint32     len_decoded;    /* Should only be < 16 bytes                */
532 
533     guint16     grp;            /* Already decoded group                    */
534     guint16     elm;            /* Already decoded element                  */
535     gchar      *vr;             /* Already decoded VR                       */
536 
537     gboolean    is_vl_long;     /* If TRUE, Value Length is 4 Bytes, otherwise 2 */
538     guint16     vl_1;           /* Partially decoded 1st two bytes of length  */
539     guint16     vl_2;           /* Partially decoded 2nd two bytes of length  */
540 
541     /* These ones are, where the value was truncated */
542     guint32 len_total;          /* Tag length of 'over-sized' tags. Used for display */
543     guint32 len_remaining;      /* Remaining tag bytes to 'decoded' as binary data after this PDV */
544 
545     gchar  *desc;               /* Last decoded description */
546 
547 } dcm_open_tag_t;
548 
549 /*
550     Per Data PDV store data needed, to allow decoding of tags longer than a PDV
551 */
552 typedef struct dcm_state_pdv {
553 
554     struct dcm_state_pdv *next, *prev;
555 
556     guint32  packet_no;         /* Wireshark packet number, where pdv starts */
557     guint32  offset;            /* Offset in packet, where PDV header starts */
558 
559     gchar   *desc;              /* PDV description. wmem_file_scope() */
560 
561     guint8  pctx_id;            /* Reference to used Presentation Context */
562 
563     /* Following is derived from the transfer syntax in the parent PCTX, except for Command PDVs */
564     guint8  syntax;
565 
566     /* Used and filled for Export Object only */
567     gpointer data;              /* Copy of PDV data without any PDU/PDV header */
568     guint32  data_len;          /* Length of this PDV buffer. If >0, memory has been allocated */
569 
570     gchar   *sop_class_uid;     /* SOP Class UID.    Set in 1st PDV of a DICOM object. wmem_file_scope() */
571     gchar   *sop_instance_uid;  /* SOP Instance UID. Set in 1st PDV of a DICOM object. wmem_file_scope() */
572     /* End Export use */
573 
574     gboolean is_storage;        /* True, if the Data PDV is on the context of a storage SOP Class */
575     gboolean is_flagvalid;      /* The following two flags are initialized correctly */
576     gboolean is_command;        /* This PDV is a command rather than a data package */
577     gboolean is_last_fragment;  /* Last Fragment bit was set, i.e. termination of an object
578                                    This flag delimits different DICOM object in the same association */
579     gboolean is_corrupt;        /* Early termination of long PDVs */
580 
581                                 /* The following five attributes are only used for command PDVs */
582 
583     gchar   *command;           /* Decoded command as text */
584     gchar   *status;            /* Decoded status as text */
585     gchar   *comment;           /* Error comment, if any */
586 
587     gboolean is_warning;        /* Command response is a cancel, warning, error */
588     gboolean is_pending;        /* Command response is 'Current Match is supplied. Sub-operations are continuing' */
589 
590     guint16  message_id;        /* (0000,0110) Message ID */
591     guint16  message_id_resp;   /* (0000,0120) Message ID being responded to */
592 
593     guint16  no_remaining;      /* (0000,1020) Number of remaining sub-operations */
594     guint16  no_completed;      /* (0000,1021) Number of completed sub-operations */
595     guint16  no_failed;         /* (0000,1022) Number of failed sub-operations  */
596     guint16  no_warning;        /* (0000,1023) Number of warning sub-operations */
597 
598     dcm_open_tag_t  open_tag;   /* Container to store information about a fragmented tag */
599 
600     guint8 reassembly_id;
601 
602 } dcm_state_pdv_t;
603 
604 /*
605 Per Presentation Context in an association store data needed, for subsequent decoding
606 */
607 typedef struct dcm_state_pctx {
608 
609     struct dcm_state_pctx *next, *prev;
610 
611     guint8 id;                  /* 0x20 Presentation Context ID */
612     gchar *abss_uid;            /* 0x30 Abstract syntax */
613     gchar *abss_desc;           /* 0x30 Abstract syntax decoded*/
614     gchar *xfer_uid;            /* 0x40 Accepted Transfer syntax */
615     gchar *xfer_desc;           /* 0x40 Accepted Transfer syntax decoded*/
616     guint8 syntax;              /* Decoded transfer syntax */
617 #define DCM_ILE  0x01           /* implicit, little endian */
618 #define DCM_EBE  0x02           /* explicit, big endian */
619 #define DCM_ELE  0x03           /* explicit, little endian */
620 #define DCM_UNK  0xf0
621 
622     guint8 reassembly_count;
623     dcm_state_pdv_t     *first_pdv,  *last_pdv;         /* List of PDV objects */
624 
625 } dcm_state_pctx_t;
626 
627 
628 typedef struct dcm_state_assoc {
629 
630     struct dcm_state_assoc *next, *prev;
631 
632     dcm_state_pctx_t    *first_pctx, *last_pctx;        /* List of Presentation context objects */
633 
634     guint32 packet_no;                  /* Wireshark packet number, where association starts */
635 
636     char *ae_called;                    /* Called  AE title in A-ASSOCIATE RQ */
637     char *ae_calling;                   /* Calling AE title in A-ASSOCIATE RQ */
638     char *ae_called_resp;               /* Called  AE title in A-ASSOCIATE RP */
639     char *ae_calling_resp;              /* Calling AE title in A-ASSOCIATE RP */
640 
641 } dcm_state_assoc_t;
642 
643 typedef struct dcm_state {
644 
645     struct dcm_state_assoc *first_assoc, *last_assoc;
646 
647     gboolean valid;                     /* this conversation is a DICOM conversation */
648 
649 } dcm_state_t;
650 
651 
652 /* ---------------------------------------------------------------------
653  * DICOM Status Value Definitions
654  *
655  * Collected from PS 3.7 & 3.4
656  *
657 */
658 
659 typedef struct dcm_status {
660     const guint16 value;
661     const gchar *description;
662 } dcm_status_t;
663 
664 static dcm_status_t dcm_status_data[] = {
665 
666     /* From PS 3.7 */
667 
668     { 0x0000,   "Success"},
669     { 0x0105,   "No such attribute"},
670     { 0x0106,   "Invalid attribute value"},
671     { 0x0107,   "Attribute list error"},
672     { 0x0110,   "Processing failure"},
673     { 0x0111,   "Duplicate SOP instance"},
674     { 0x0112,   "No Such object instance"},
675     { 0x0113,   "No such event type"},
676     { 0x0114,   "No such argument"},
677     { 0x0115,   "Invalid argument value"},
678     { 0x0116,   "Attribute Value Out of Range"},
679     { 0x0117,   "Invalid object instance"},
680     { 0x0118,   "No Such SOP class"},
681     { 0x0119,   "Class-instance conflict"},
682     { 0x0120,   "Missing attribute"},
683     { 0x0121,   "Missing attribute value"},
684     { 0x0122,   "Refused: SOP class not supported"},
685     { 0x0123,   "No such action type"},
686     { 0x0210,   "Duplicate invocation"},
687     { 0x0211,   "Unrecognized operation"},
688     { 0x0212,   "Mistyped argument"},
689     { 0x0213,   "Resource limitation"},
690     { 0xFE00,   "Cancel"},
691 
692     /* from PS 3.4 */
693 
694     { 0x0001,   "Requested optional Attributes are not supported"},
695     { 0xA501,   "Refused because General Purpose Scheduled Procedure Step Object may no longer be updated"},
696     { 0xA502,   "Refused because the wrong Transaction UID is used"},
697     { 0xA503,   "Refused because the General Purpose Scheduled Procedure Step SOP Instance is already in the 'IN PROGRESS' state"},
698     { 0xA504,   "Refused because the related General Purpose Scheduled Procedure Step SOP Instance is not in the 'IN PROGRESS' state"},
699     { 0xA505,   "Refused because Referenced General Purpose Scheduled Procedure Step Transaction UID does not match the Transaction UID of the N-ACTION request"},
700     { 0xA510,   "Refused because an Initiate Media Creation action has already been received for this SOP Instance"},
701     { 0xA700,   "Refused: Out of Resources"},
702     { 0xA701,   "Refused: Out of Resources - Unable to calculate number of matches"},
703     { 0xA702,   "Refused: Out of Resources - Unable to perform sub-operations"},
704     /*
705     { 0xA7xx,   "Refused: Out of Resources"},
706     */
707     { 0xA801,   "Refused: Move Destination unknown"},
708     /*
709     { 0xA9xx,   "Error: Data Set does not match SOP Class"},
710     */
711     { 0xB000,   "Sub-operations Complete - One or more Failures"},
712     { 0xB006,   "Elements Discarded"},
713     { 0xB007,   "Data Set does not match SOP Class"},
714     { 0xB101,   "Specified Synchronization Frame of Reference UID does not match SCP Synchronization Frame of Reference"},
715     { 0xB102,   "Study Instance UID coercion; Event logged under a different Study Instance UID"},
716     { 0xB104,   "IDs inconsistent in matching a current study; Event logged"},
717     { 0xB605,   "Requested Min Density or Max Density outside of printer's operating range. The printer will use its respective minimum or maximum density value instead"},
718     { 0xC000,   "Error: Cannot understand/Unable to process"},
719     { 0xC100,   "More than one match found"},
720     { 0xC101,   "Procedural Logging not available for specified Study Instance UID"},
721     { 0xC102,   "Event Information does not match Template"},
722     { 0xC103,   "Cannot match event to a current study"},
723     { 0xC104,   "IDs inconsistent in matching a current study; Event not logged"},
724     { 0xC200,   "Unable to support requested template"},
725     { 0xC201,   "Media creation request already completed"},
726     { 0xC202,   "Media creation request already in progress and cannot be interrupted"},
727     { 0xC203,   "Cancellation denied for unspecified reason"},
728     /*
729     { 0xCxxx,   "Error: Cannot understand/Unable to Process"},
730     { 0xFE00,   "Matching/Sub-operations terminated due to Cancel request"},
731     */
732     { 0xFF00,   "Current Match is supplied. Sub-operations are continuing"},
733     { 0xFF01,   "Matches are continuing - Warning that one or more Optional Keys were not supported for existence for this Identifier"}
734 
735 };
736 
737 
738 /* following definitions are used to call dissect_dcm_assoc_item() */
739 #define DCM_ITEM_VALUE_TYPE_UID     1
740 #define DCM_ITEM_VALUE_TYPE_STRING  2
741 #define DCM_ITEM_VALUE_TYPE_UINT32  3
742 
743 /* And from here on, only use unsigned 32 bit values. Offset is always positive number in respect to the tvb buffer start */
744 static guint32  dissect_dcm_pdu     (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset);
745 
746 static guint32  dissect_dcm_assoc_detail(tvbuff_t *tvb, packet_info *pinfo, proto_item *ti,   dcm_state_assoc_t *assoc, guint32 offset, guint32 len);
747 
748 static guint32 dissect_dcm_tag_value(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, dcm_state_pdv_t * pdv, guint32 offset, guint16 grp, guint16 elm, guint32 vl, guint32 vl_max, const gchar * vr, gchar ** tag_value);
749 
750 static void
dcm_init(void)751 dcm_init(void)
752 {
753     guint   i;
754 
755     /* Create three hash tables for quick lookups */
756     /* Add UID objects to hash table */
757     dcm_uid_table = wmem_map_new(wmem_file_scope(), wmem_str_hash, g_str_equal);
758     for (i = 0; i < array_length(dcm_uid_data); i++) {
759         wmem_map_insert(dcm_uid_table, (gpointer) dcm_uid_data[i].value,
760         (gpointer) &dcm_uid_data[i]);
761     }
762 
763     /* Add Tag objects to hash table */
764     dcm_tag_table = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
765     for (i = 0; i < array_length(dcm_tag_data); i++) {
766         wmem_map_insert(dcm_tag_table, GUINT_TO_POINTER(dcm_tag_data[i].tag),
767         (gpointer) &dcm_tag_data[i]);
768     }
769 
770    /* Add Status Values to hash table */
771     dcm_status_table = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
772     for (i = 0; i < array_length(dcm_status_data); i++) {
773         wmem_map_insert(dcm_status_table, GUINT_TO_POINTER((guint32)dcm_status_data[i].value),
774         (gpointer)&dcm_status_data[i]);
775     }
776 }
777 
778 /*
779 Get or create conversation and DICOM data structure if desired.
780 Return new or existing DICOM structure, which is used to store context IDs and transfer syntax.
781 Return NULL in case of the structure couldn't be created.
782 */
783 static dcm_state_t *
dcm_state_get(packet_info * pinfo,gboolean create)784 dcm_state_get(packet_info *pinfo, gboolean create)
785 {
786 
787     conversation_t  *conv;
788     dcm_state_t     *dcm_data;
789 
790     conv = find_or_create_conversation(pinfo);
791     dcm_data = (dcm_state_t *)conversation_get_proto_data(conv, proto_dcm);
792 
793     if (dcm_data == NULL && create) {
794 
795         dcm_data =  wmem_new0(wmem_file_scope(), dcm_state_t);
796         conversation_add_proto_data(conv, proto_dcm, dcm_data);
797 
798         /*  Mark it as DICOM conversation. Needed for the heuristic mode,
799             to prevent stealing subsequent packets by other dissectors
800         */
801         conversation_set_dissector(conv, dcm_handle);
802     }
803 
804     return dcm_data;
805 }
806 
807 
808 static dcm_state_assoc_t *
dcm_state_assoc_new(dcm_state_t * dcm_data,guint32 packet_no)809 dcm_state_assoc_new(dcm_state_t *dcm_data, guint32 packet_no)
810 {
811     /* Create new association object and initialize the members */
812 
813     dcm_state_assoc_t *assoc;
814 
815     assoc = wmem_new0(wmem_file_scope(), dcm_state_assoc_t);
816     assoc->packet_no = packet_no;           /* Identifier */
817 
818     /* add to the end of the list */
819     if (dcm_data->last_assoc) {
820         dcm_data->last_assoc->next = assoc;
821         assoc->prev = dcm_data->last_assoc;
822     }
823     else {
824         dcm_data->first_assoc = assoc;
825     }
826     dcm_data->last_assoc = assoc;
827     return assoc;
828 }
829 
830 /*
831 Find or create association object based on packet number. Return NULL, if association was not found.
832 */
833 static dcm_state_assoc_t *
dcm_state_assoc_get(dcm_state_t * dcm_data,guint32 packet_no,gboolean create)834 dcm_state_assoc_get(dcm_state_t *dcm_data, guint32 packet_no, gboolean create)
835 {
836 
837     dcm_state_assoc_t *assoc = dcm_data->first_assoc;
838 
839     while (assoc) {
840 
841         if (assoc->next) {
842             /* we have more associations in the same stream */
843             if ((assoc->packet_no <= packet_no) && (packet_no < assoc->next->packet_no))
844                 break;
845         }
846         else {
847             /* last or only associations in the same stream */
848             if (assoc->packet_no <= packet_no)
849                 break;
850         }
851         assoc = assoc->next;
852     }
853 
854     if (assoc == NULL && create) {
855         assoc = dcm_state_assoc_new(dcm_data, packet_no);
856     }
857     return assoc;
858 }
859 
860 static dcm_state_pctx_t *
dcm_state_pctx_new(dcm_state_assoc_t * assoc,guint8 pctx_id)861 dcm_state_pctx_new(dcm_state_assoc_t *assoc, guint8 pctx_id)
862 {
863     /* Create new presentation context object and initialize the members */
864 
865     dcm_state_pctx_t *pctx;
866 
867     pctx = wmem_new0(wmem_file_scope(), dcm_state_pctx_t);
868     pctx->id = pctx_id;
869     pctx->syntax = DCM_UNK;
870 
871     /* add to the end of the list list */
872     if (assoc->last_pctx) {
873         assoc->last_pctx->next = pctx;
874         pctx->prev = assoc->last_pctx;
875     }
876     else {
877         assoc->first_pctx = pctx;
878     }
879     assoc->last_pctx = pctx;
880 
881     return pctx;
882 }
883 
884 static dcm_state_pctx_t *
dcm_state_pctx_get(dcm_state_assoc_t * assoc,guint8 pctx_id,gboolean create)885 dcm_state_pctx_get(dcm_state_assoc_t *assoc, guint8 pctx_id, gboolean create)
886 {
887     /*  Find or create presentation context object. Return NULL, if Context ID was not found */
888 
889     dcm_state_pctx_t *pctx = assoc->first_pctx;
890     /*
891     static char notfound[] = "not found - click on ASSOC Request";
892     static dcm_state_pctx_t dunk = { NULL, NULL, FALSE, 0, notfound, notfound, notfound, notfound, DCM_UNK };
893     */
894     while (pctx) {
895         if (pctx->id == pctx_id)
896             break;
897         pctx = pctx->next;
898     }
899 
900     if (pctx == NULL && create) {
901         pctx = dcm_state_pctx_new(assoc, pctx_id);
902     }
903 
904     return pctx;
905 }
906 
907 
908 /*
909 Create new PDV object and initialize all members
910 */
911 static dcm_state_pdv_t*
dcm_state_pdv_new(dcm_state_pctx_t * pctx,guint32 packet_no,guint32 offset)912 dcm_state_pdv_new(dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset)
913 {
914     dcm_state_pdv_t *pdv;
915 
916     pdv = wmem_new0(wmem_file_scope(), dcm_state_pdv_t);
917     pdv->syntax = DCM_UNK;
918     pdv->is_last_fragment = TRUE;       /* Continuation PDVs are more tricky */
919     pdv->packet_no = packet_no;
920     pdv->offset = offset;
921 
922     /* add to the end of the list */
923     if (pctx->last_pdv) {
924         pctx->last_pdv->next = pdv;
925         pdv->prev = pctx->last_pdv;
926     }
927     else {
928         pctx->first_pdv = pdv;
929     }
930     pctx->last_pdv = pdv;
931     return pdv;
932 }
933 
934 
935 static dcm_state_pdv_t*
dcm_state_pdv_get(dcm_state_pctx_t * pctx,guint32 packet_no,guint32 offset,gboolean create)936 dcm_state_pdv_get(dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset, gboolean create)
937 {
938     /*  Find or create PDV object. Return NULL, if PDV was not found, based on packet number and offset */
939 
940     dcm_state_pdv_t *pdv = pctx->first_pdv;
941 
942     while (pdv) {
943         if ((pdv->packet_no == packet_no) && (pdv->offset == offset))
944             break;
945         pdv = pdv->next;
946     }
947 
948     if (pdv == NULL && create) {
949         pdv = dcm_state_pdv_new(pctx, packet_no, offset);
950     }
951     return pdv;
952 }
953 
954 static dcm_state_pdv_t*
dcm_state_pdv_get_obj_start(dcm_state_pdv_t * pdv_curr)955 dcm_state_pdv_get_obj_start(dcm_state_pdv_t *pdv_curr)
956 {
957 
958     dcm_state_pdv_t *pdv_first=pdv_curr;
959 
960     /* Get First PDV of the DICOM Object */
961     while (pdv_first->prev && !pdv_first->prev->is_last_fragment) {
962         pdv_first = pdv_first->prev;
963     }
964 
965     return pdv_first;
966 }
967 
968 static const value_string dcm_cmd_vals[] = {
969     { 0x0001, "C-STORE-RQ" },
970     { 0x0010, "C-GET-RQ" },
971     { 0x0020, "C-FIND-RQ" },
972     { 0x0021, "C-MOVE-RQ" },
973     { 0x0030, "C-ECHO-RQ" },
974     { 0x0100, "N-EVENT-REPORT-RQ" },
975     { 0x0110, "N-GET-RQ" },
976     { 0x0120, "N-SET-RQ" },
977     { 0x0130, "N-ACTION-RQ" },
978     { 0x0140, "N-CREATE-RQ" },
979     { 0x0150, "N-DELETE-RQ" },
980     { 0x8001, "C-STORE-RSP" },
981     { 0x8010, "C-GET-RSP" },
982     { 0x8020, "C-FIND-RSP" },
983     { 0x8021, "C-MOVE-RSP" },
984     { 0x8030, "C-ECHO-RSP" },
985     { 0x8100, "N-EVENT-REPORT-RSP" },
986     { 0x8110, "N-GET-RSP" },
987     { 0x8120, "N-SET-RSP" },
988     { 0x8130, "N-ACTION-RSP" },
989     { 0x8140, "N-CREATE-RSP" },
990     { 0x8150, "N-DELETE-RSP" },
991     { 0x0FFF, "C-CANCEL-RQ" },
992     { 0, NULL }
993 };
994 
995 
996 /*
997 Convert the two status bytes into a text based on lookup.
998 
999 Classification
1000 0x0000          : SUCCESS
1001 0x0001 & Bxxx   : WARNING
1002 0xFE00          : CANCEL
1003 0XFFxx          : PENDING
1004 All other       : FAILURE
1005 */
1006 static const gchar *
dcm_rsp2str(guint16 status_value)1007 dcm_rsp2str(guint16 status_value)
1008 {
1009 
1010     dcm_status_t    *status = NULL;
1011     const gchar *s = "";
1012 
1013     /* Use specific text first */
1014     status = (dcm_status_t*) wmem_map_lookup(dcm_status_table, GUINT_TO_POINTER((guint32)status_value));
1015 
1016     if (status) {
1017          s = status->description;
1018     }
1019     else {
1020 
1021         if ((status_value & 0xFF00) == 0xA700) {
1022             /* 0xA7xx */
1023             s = "Refused: Out of Resources";
1024         }
1025         else if ((status_value & 0xFF00) == 0xA900) {
1026             /* 0xA9xx */
1027             s = "Error: Data Set does not match SOP Class";
1028         }
1029         else if ((status_value & 0xF000) == 0xC000) {
1030             /* 0xCxxx */
1031             s = "Error: Cannot understand/Unable to Process";
1032         }
1033         else {
1034             /* Encountered at least one case, with status_value == 0xD001 */
1035             s = "Unknown";
1036         }
1037     }
1038 
1039     return s;
1040 }
1041 
1042 static const gchar*
dcm_uid_or_desc(gchar * dcm_uid,gchar * dcm_desc)1043 dcm_uid_or_desc(gchar *dcm_uid, gchar *dcm_desc)
1044 {
1045     /* Return Description, UID or error */
1046 
1047     return (dcm_desc == NULL ? (dcm_uid == NULL ? "Malformed Packet" : dcm_uid) : dcm_desc);
1048 }
1049 
1050 static void
dcm_set_syntax(dcm_state_pctx_t * pctx,gchar * xfer_uid,const gchar * xfer_desc)1051 dcm_set_syntax(dcm_state_pctx_t *pctx, gchar *xfer_uid, const gchar *xfer_desc)
1052 {
1053     if ((pctx == NULL) || (xfer_uid == NULL) || (xfer_desc == NULL))
1054         return;
1055 
1056     g_free(pctx->xfer_uid);     /* free prev allocated xfer */
1057     g_free(pctx->xfer_desc);    /* free prev allocated xfer */
1058 
1059     pctx->syntax = 0;
1060     pctx->xfer_uid = g_strdup(xfer_uid);
1061     pctx->xfer_desc = g_strdup(xfer_desc);
1062 
1063     /* this would be faster to skip the common parts, and have a FSA to
1064      * find the syntax.
1065      * Absent of coding that, this is in descending order of probability */
1066     if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2"))
1067         pctx->syntax = DCM_ILE;  /* implicit little endian */
1068     else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.1"))
1069         pctx->syntax = DCM_ELE;  /* explicit little endian */
1070     else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.2"))
1071         pctx->syntax = DCM_EBE;  /* explicit big endian */
1072     else if (0 == strcmp(xfer_uid, "1.2.840.113619.5.2"))
1073         pctx->syntax = DCM_ILE;  /* implicit little endian, big endian pixels, GE private */
1074     else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.4.70"))
1075         pctx->syntax = DCM_ELE;  /* explicit little endian, jpeg */
1076     else if (0 == strncmp(xfer_uid, "1.2.840.10008.1.2.4", 18))
1077         pctx->syntax = DCM_ELE;  /* explicit little endian, jpeg */
1078     else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.1.99"))
1079         pctx->syntax = DCM_ELE;  /* explicit little endian, deflated */
1080 }
1081 
1082 static void
dcm_guint16_to_le(guint8 * buffer,guint16 value)1083 dcm_guint16_to_le(guint8 *buffer, guint16 value)
1084 {
1085 
1086     buffer[0]=(guint8) (value & 0x00FF);
1087     buffer[1]=(guint8)((value & 0xFF00) >> 8);
1088 }
1089 
1090 static void
dcm_guint32_to_le(guint8 * buffer,guint32 value)1091 dcm_guint32_to_le(guint8 *buffer, guint32 value)
1092 {
1093 
1094     buffer[0]=(guint8) (value & 0x000000FF);
1095     buffer[1]=(guint8)((value & 0x0000FF00) >>  8);
1096     buffer[2]=(guint8)((value & 0x00FF0000) >> 16);
1097     buffer[3]=(guint8)((value & 0xFF000000) >> 24);
1098 
1099 }
1100 
1101 static guint32
dcm_export_create_tag_base(guint8 * buffer,guint32 bufflen,guint32 offset,guint16 grp,guint16 elm,guint16 vr,const guint8 * value_buffer,guint32 value_len)1102 dcm_export_create_tag_base(guint8 *buffer, guint32 bufflen, guint32 offset,
1103                            guint16 grp, guint16 elm, guint16 vr,
1104                            const guint8 *value_buffer, guint32 value_len)
1105 {
1106     /*  Only Explicit Little Endian is needed to create Metafile Header
1107         Generic function to write a TAG, VR, LEN & VALUE to a combined buffer
1108         The value (buffer, len) must be preprocessed by a VR specific function
1109     */
1110 
1111     if (offset + 6 > bufflen) return bufflen;
1112 
1113     dcm_guint16_to_le(buffer + offset, grp);
1114     offset += 2;
1115     dcm_guint16_to_le(buffer + offset, elm);
1116     offset += 2;
1117     memmove(buffer + offset, dcm_tag_vr_lookup[vr], 2);
1118     offset += 2;
1119 
1120     switch (vr) {
1121     case DCM_VR_OB:
1122     case DCM_VR_OD:
1123     case DCM_VR_OF:
1124     case DCM_VR_OL:
1125     case DCM_VR_OW:
1126     case DCM_VR_SQ:
1127     case DCM_VR_UC:
1128     case DCM_VR_UR:
1129     case DCM_VR_UT:
1130     case DCM_VR_UN:
1131         /* DICOM likes it complicated. Special handling for these types */
1132 
1133         if (offset + 6 > bufflen) return bufflen;
1134 
1135         /* Add two reserved 0x00 bytes */
1136         dcm_guint16_to_le(buffer + offset, 0);
1137         offset += 2;
1138 
1139         /* Length is a 4 byte field */
1140         dcm_guint32_to_le(buffer + offset, value_len);
1141         offset += 4;
1142 
1143         break;
1144 
1145     default:
1146         /* Length is a 2 byte field */
1147         if (offset + 2 > bufflen) return bufflen;
1148 
1149         dcm_guint16_to_le(buffer + offset, (guint16)value_len);
1150         offset += 2;
1151     }
1152 
1153     if (offset + value_len > bufflen) return bufflen;
1154 
1155     memmove(buffer + offset, value_buffer, value_len);
1156     offset += value_len;
1157 
1158     return offset;
1159 }
1160 
1161 static guint32
dcm_export_create_tag_guint16(guint8 * buffer,guint32 bufflen,guint32 offset,guint16 grp,guint16 elm,guint16 vr,guint16 value)1162 dcm_export_create_tag_guint16(guint8 *buffer, guint32 bufflen, guint32 offset,
1163                               guint16 grp, guint16 elm, guint16 vr, guint16 value)
1164 {
1165 
1166     return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr, (guint8*)&value, 2);
1167 }
1168 
1169 static guint32
dcm_export_create_tag_guint32(guint8 * buffer,guint32 bufflen,guint32 offset,guint16 grp,guint16 elm,guint16 vr,guint32 value)1170 dcm_export_create_tag_guint32(guint8 *buffer, guint32 bufflen, guint32 offset,
1171                               guint16 grp, guint16 elm, guint16 vr, guint32 value)
1172 {
1173 
1174     return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr, (guint8*)&value, 4);
1175 }
1176 
1177 static guint32
dcm_export_create_tag_str(guint8 * buffer,guint32 bufflen,guint32 offset,guint16 grp,guint16 elm,guint16 vr,const gchar * value)1178 dcm_export_create_tag_str(guint8 *buffer, guint32 bufflen, guint32 offset,
1179                           guint16 grp, guint16 elm, guint16 vr,
1180                           const gchar *value)
1181 {
1182     guint32 len;
1183 
1184     if (!value) {
1185         /* NULL object. E.g. happens if UID was not found/set. Don't create element*/
1186         return offset;
1187     }
1188 
1189     len=(int)strlen(value);
1190 
1191     if ((len & 0x01) == 1) {
1192         /*  Odd length: since buffer is 0 initialized, pad with a 0x00 */
1193         len += 1;
1194     }
1195 
1196     return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr, (const guint8 *)value, len);
1197 }
1198 
1199 
1200 static guint8*
dcm_export_create_header(packet_info * pinfo,guint32 * dcm_header_len,const gchar * sop_class_uid,gchar * sop_instance_uid,gchar * xfer_uid)1201 dcm_export_create_header(packet_info *pinfo, guint32 *dcm_header_len, const gchar *sop_class_uid, gchar *sop_instance_uid, gchar *xfer_uid)
1202 {
1203     guint8      *dcm_header=NULL;
1204     guint32     offset=0;
1205     guint32     offset_header_len=0;
1206 
1207 #define DCM_HEADER_MAX 512
1208 
1209     dcm_header=(guint8 *)wmem_alloc0(pinfo->pool, DCM_HEADER_MAX);   /* Slightly longer than needed */
1210                                                       /* The subsequent functions rely on a 0 initialized buffer */
1211     offset=128;
1212 
1213     memmove(dcm_header+offset, "DICM", 4);
1214     offset+=4;
1215 
1216     offset_header_len=offset;   /* remember for later */
1217 
1218     offset+=12;
1219 
1220     /*
1221         (0002,0000)     File Meta Information Group Length  UL
1222         (0002,0001)     File Meta Information Version       OB
1223         (0002,0002)     Media Storage SOP Class UID         UI
1224         (0002,0003)     Media Storage SOP Instance UID      UI
1225         (0002,0010)     Transfer Syntax UID                 UI
1226         (0002,0012)     Implementation Class UID            UI
1227         (0002,0013)     Implementation Version Name         SH
1228     */
1229 
1230     offset=dcm_export_create_tag_guint16(dcm_header, DCM_HEADER_MAX, offset,
1231         0x0002, 0x0001, DCM_VR_OB, 0x0100);  /* will result on 00 01 since it is little endian */
1232 
1233     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1234         0x0002, 0x0002, DCM_VR_UI, sop_class_uid);
1235 
1236     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1237         0x0002, 0x0003, DCM_VR_UI, sop_instance_uid);
1238 
1239     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1240         0x0002, 0x0010, DCM_VR_UI, xfer_uid);
1241 
1242     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1243         0x0002, 0x0012, DCM_VR_UI, WIRESHARK_IMPLEMENTATION_UID);
1244 
1245     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1246         0x0002, 0x0013, DCM_VR_SH, WIRESHARK_IMPLEMENTATION_VERSION);
1247 
1248     /* Finally write the meta header length */
1249     dcm_export_create_tag_guint32(dcm_header, DCM_HEADER_MAX, offset_header_len,
1250         0x0002, 0x0000, DCM_VR_UL, offset-offset_header_len-12);
1251 
1252     *dcm_header_len=offset;
1253 
1254     return dcm_header;
1255 
1256 }
1257 
1258 
1259 /*
1260 Concatenate related PDVs into one buffer and add it to the export object list.
1261 
1262 Supports both modes:
1263 
1264 - Multiple DICOM PDVs are reassembled with fragment_add_seq_next()
1265   and process_reassembled_data(). In this case all data will be in the last
1266   PDV, and all its predecessors will have zero data.
1267 
1268 - DICOM PDVs are keep separate. Every PDV contains data.
1269 */
1270 static void
dcm_export_create_object(packet_info * pinfo,dcm_state_assoc_t * assoc,dcm_state_pdv_t * pdv)1271 dcm_export_create_object(packet_info *pinfo, dcm_state_assoc_t *assoc, dcm_state_pdv_t *pdv)
1272 {
1273 
1274     dicom_eo_t          *eo_info = NULL;
1275 
1276     dcm_state_pdv_t     *pdv_curr = NULL;
1277     dcm_state_pdv_t     *pdv_same_pkt = NULL;
1278     dcm_state_pctx_t    *pctx = NULL;
1279 
1280     guint8     *pdv_combined = NULL;
1281     guint8     *pdv_combined_curr = NULL;
1282     guint8     *dcm_header = NULL;
1283     guint32     pdv_combined_len = 0;
1284     guint32     dcm_header_len = 0;
1285     guint16     cnt_same_pkt = 1;
1286     gchar      *filename;
1287     const gchar *hostname;
1288 
1289     const gchar *sop_class_uid;
1290     gchar       *sop_instance_uid;
1291 
1292     /* Calculate total PDV length, i.e. all packets until last PDV without continuation  */
1293     pdv_curr = pdv;
1294     pdv_same_pkt = pdv;
1295     pdv_combined_len=pdv_curr->data_len;
1296 
1297     while (pdv_curr->prev && !pdv_curr->prev->is_last_fragment) {
1298         pdv_curr = pdv_curr->prev;
1299         pdv_combined_len += pdv_curr->data_len;
1300     }
1301 
1302     /* Count number of PDVs with the same Packet Number */
1303     while (pdv_same_pkt->prev && (pdv_same_pkt->prev->packet_no == pdv_same_pkt->packet_no)) {
1304         pdv_same_pkt = pdv_same_pkt->prev;
1305         cnt_same_pkt += 1;
1306     }
1307 
1308     pctx=dcm_state_pctx_get(assoc, pdv_curr->pctx_id, FALSE);
1309 
1310     if (assoc->ae_calling != NULL && strlen(assoc->ae_calling)>0 &&
1311         assoc->ae_called != NULL &&  strlen(assoc->ae_called)>0) {
1312         hostname = wmem_strdup_printf(pinfo->pool, "%s <-> %s", assoc->ae_calling, assoc->ae_called);
1313     }
1314     else {
1315         hostname = "AE title(s) unknown";
1316     }
1317 
1318     if (pdv->is_storage &&
1319         pdv_curr->sop_class_uid    && strlen(pdv_curr->sop_class_uid)>0 &&
1320         pdv_curr->sop_instance_uid && strlen(pdv_curr->sop_instance_uid)>0) {
1321 
1322         sop_class_uid = wmem_strdup(pinfo->pool, pdv_curr->sop_class_uid);
1323         sop_instance_uid = wmem_strdup(pinfo->pool, pdv_curr->sop_instance_uid);
1324 
1325         /* Make sure filename does not contain invalid character. Rather conservative.
1326            Even though this should be a valid DICOM UID, apply the same filter rules
1327            in case of bogus data.
1328         */
1329         filename = wmem_strdup_printf(pinfo->pool, "%06d-%d-%s.dcm", pinfo->num, cnt_same_pkt,
1330             g_strcanon(pdv_curr->sop_instance_uid, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-.", '-'));
1331     }
1332     else {
1333         /* No SOP Instance or SOP Class UID found in PDV. Use wireshark ones */
1334 
1335         sop_class_uid = wmem_strdup(pinfo->pool, WIRESHARK_MEDIA_STORAGE_SOP_CLASS_UID);
1336         sop_instance_uid = wmem_strdup_printf(pinfo->pool, "%s.%d.%d",
1337             WIRESHARK_MEDIA_STORAGE_SOP_INSTANCE_UID_PREFIX, pinfo->num, cnt_same_pkt);
1338 
1339         /* Make sure filename does not contain invalid character. Rather conservative.*/
1340         filename = wmem_strdup_printf(pinfo->pool, "%06d-%d-%s.dcm", pinfo->num, cnt_same_pkt,
1341             g_strcanon(pdv->desc, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-.", '-'));
1342 
1343     }
1344 
1345     if (global_dcm_export_header) {
1346         if (pctx && pctx->xfer_uid && strlen(pctx->xfer_uid)>0) {
1347             dcm_header=dcm_export_create_header(pinfo, &dcm_header_len, sop_class_uid, sop_instance_uid, pctx->xfer_uid);
1348         }
1349         else {
1350             /* We are running blind, i.e. no presentation context/syntax found.
1351                Don't invent one, so the meta header will miss
1352                the transfer syntax UID tag (even though it is mandatory)
1353             */
1354             dcm_header=dcm_export_create_header(pinfo, &dcm_header_len, sop_class_uid, sop_instance_uid, NULL);
1355         }
1356     }
1357 
1358 
1359     if (dcm_header_len + pdv_combined_len >= global_dcm_export_minsize) {
1360         /* Allocate the final size */
1361 
1362         pdv_combined = (guint8 *)wmem_alloc0(wmem_file_scope(), dcm_header_len + pdv_combined_len);
1363 
1364         pdv_combined_curr = pdv_combined;
1365 
1366         if (dcm_header_len != 0) {  /* Will be 0 when global_dcm_export_header is FALSE */
1367             memmove(pdv_combined, dcm_header, dcm_header_len);
1368             pdv_combined_curr += dcm_header_len;
1369         }
1370 
1371         /* Copy PDV per PDV to target buffer */
1372         while (!pdv_curr->is_last_fragment) {
1373             memmove(pdv_combined_curr, pdv_curr->data, pdv_curr->data_len);         /* this is a copy not move */
1374             pdv_combined_curr += pdv_curr->data_len;
1375             pdv_curr = pdv_curr->next;
1376         }
1377 
1378         /* Last packet */
1379         memmove(pdv_combined_curr, pdv->data, pdv->data_len);       /* this is a copy not a move */
1380 
1381         /* Add to list */
1382         eo_info = wmem_new0(wmem_file_scope(), dicom_eo_t);
1383         eo_info->hostname = g_strdup(hostname);
1384         eo_info->filename = g_strdup(filename);
1385         eo_info->content_type = g_strdup(pdv->desc);
1386 
1387         eo_info->payload_data = pdv_combined;
1388         eo_info->payload_len  = dcm_header_len + pdv_combined_len;
1389 
1390         tap_queue_packet(dicom_eo_tap, pinfo, eo_info);
1391     }
1392 }
1393 
1394 /*
1395 For tags with fixed length items, calculate the value multiplicity (VM). String tags use a separator, which is not supported by this function.
1396 Support item count from 0 to n. and handles bad encoding (e.g. an 'AT' tag was reported to be 2 bytes instead of 4 bytes)
1397 */
1398 static guint32
dcm_vm_item_count(guint32 value_length,guint32 item_length)1399 dcm_vm_item_count(guint32 value_length, guint32 item_length)
1400 {
1401 
1402     /* This could all be formulated in a single line but it does not make it easier to read */
1403 
1404     if (value_length == 0) {
1405         return 0;
1406     }
1407     else if (value_length <= item_length) {
1408         return 1;                           /* This is the special case of bad encoding */
1409     }
1410     else {
1411         return (value_length / item_length);
1412     }
1413 
1414 }
1415 
1416 /*
1417 Decode the association header
1418  */
1419 static guint32
dissect_dcm_assoc_header(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset,dcm_state_assoc_t * assoc,guint8 pdu_type,guint32 pdu_len)1420 dissect_dcm_assoc_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, dcm_state_assoc_t *assoc,
1421                          guint8 pdu_type, guint32 pdu_len)
1422 {
1423 
1424     proto_item *assoc_header_pitem;
1425     proto_tree *assoc_header_ptree;     /* Tree for item details */
1426 
1427     const gchar  *buf_desc = NULL;
1428     const char   *reject_result_desc = "";
1429     const char   *reject_source_desc = "";
1430     const char   *reject_reason_desc = "";
1431     const char   *abort_source_desc = "";
1432     const char   *abort_reason_desc = "";
1433 
1434     char  *ae_called;
1435     char  *ae_calling;
1436     char  *ae_called_resp;
1437     char  *ae_calling_resp;
1438 
1439     guint8  reject_result;
1440     guint8  reject_source;
1441     guint8  reject_reason;
1442     guint8  abort_source;
1443     guint8  abort_reason;
1444 
1445     assoc_header_ptree = proto_tree_add_subtree(tree, tvb, offset, pdu_len, ett_assoc_header, &assoc_header_pitem, "Association Header");
1446 
1447     switch (pdu_type) {
1448     case 1:                                     /* Association Request */
1449 
1450         proto_tree_add_item(assoc_header_ptree, hf_dcm_assoc_version, tvb, offset, 2, ENC_BIG_ENDIAN);
1451         offset += 2;
1452 
1453         offset += 2;                            /* Two reserved bytes*/
1454 
1455         /*
1456          * XXX - this is in "the ISO 646:1990-Basic G0 Set"; ISO/IEC 646:1991
1457          * claims to be the third edition of the standard, with the second
1458          * version being ISO 646:1983, so I'm not sure what happened to
1459          * ISO 646:1990.  ISO/IEC 646:1991 speaks of "the basic 7-bit code
1460          * table", which leaves positions 2/3 (0x23) and 2/4 (0x24) as
1461          * being either NUMBER SIGN or POUND SIGN and either DOLLAR SIGN or
1462          * CURRENCY SIGN, respectively, and positions 4/0 (0x40), 5/11 (0x5b),
1463          * 5/12 (0x5c), 5/13 (0x5d), 5/14 (0x5e), 6/0 (0x60), 7/11 (0x7b),
1464          * 7/12 (0x7c), 7/13 (0x7d), and 7/14 (0x7e) as being "available for
1465          * national or application-oriented use", so I'm *guessing* that
1466          * "the ISO 646:1990-Basic G0 Set" means "those positions aren't
1467          * specified" and thus should probably be treated as not valid
1468          * in that "Basic" set.
1469          */
1470         proto_tree_add_item_ret_display_string(assoc_header_ptree, hf_dcm_assoc_called, tvb, offset, 16, ENC_ISO_646_BASIC|ENC_NA, pinfo->pool, &ae_called);
1471         assoc->ae_called = wmem_strdup(wmem_file_scope(), g_strstrip(ae_called));
1472         offset += 16;
1473 
1474         proto_tree_add_item_ret_display_string(assoc_header_ptree, hf_dcm_assoc_calling, tvb, offset, 16, ENC_ISO_646_BASIC|ENC_NA, pinfo->pool, &ae_calling);
1475         assoc->ae_calling = wmem_strdup(wmem_file_scope(), g_strstrip(ae_calling));
1476         offset += 16;
1477 
1478         offset += 32;                           /* 32 reserved bytes */
1479 
1480         buf_desc = wmem_strdup_printf(pinfo->pool, "A-ASSOCIATE request %s --> %s",
1481             assoc->ae_calling, assoc->ae_called);
1482 
1483         offset = dissect_dcm_assoc_detail(tvb, pinfo, assoc_header_ptree, assoc, offset, pdu_len-offset);
1484 
1485         break;
1486     case 2:                                     /* Association Accept */
1487 
1488         proto_tree_add_item(assoc_header_ptree, hf_dcm_assoc_version, tvb, offset, 2, ENC_BIG_ENDIAN);
1489         offset += 2;
1490 
1491         offset += 2;                            /* Two reserved bytes*/
1492 
1493         proto_tree_add_item_ret_display_string(assoc_header_ptree, hf_dcm_assoc_called, tvb, offset, 16, ENC_ISO_646_BASIC|ENC_NA, pinfo->pool, &ae_called_resp);
1494         assoc->ae_called_resp = wmem_strdup(wmem_file_scope(), g_strstrip(ae_called_resp));
1495         offset += 16;
1496 
1497         proto_tree_add_item_ret_display_string(assoc_header_ptree, hf_dcm_assoc_calling, tvb, offset, 16, ENC_ISO_646_BASIC|ENC_NA, pinfo->pool, &ae_calling_resp);
1498         assoc->ae_calling_resp = wmem_strdup(wmem_file_scope(), g_strstrip(ae_calling_resp));
1499         offset += 16;
1500 
1501         offset += 32;                           /* 32 reserved bytes */
1502 
1503         buf_desc = wmem_strdup_printf(pinfo->pool, "A-ASSOCIATE accept  %s <-- %s",
1504             assoc->ae_calling_resp, assoc->ae_called_resp);
1505 
1506         offset = dissect_dcm_assoc_detail(tvb, pinfo, assoc_header_ptree, assoc, offset, pdu_len-offset);
1507 
1508         break;
1509     case 3:                                     /* Association Reject */
1510 
1511         offset += 1;                            /* One reserved byte */
1512 
1513         reject_result = tvb_get_guint8(tvb, offset);
1514         reject_source = tvb_get_guint8(tvb, offset+1);
1515         reject_reason = tvb_get_guint8(tvb, offset+2);
1516 
1517         switch (reject_result) {
1518         case 1:  reject_result_desc = "Reject Permanent"; break;
1519         case 2:  reject_result_desc = "Reject Transient"; break;
1520         default: break;
1521         }
1522 
1523         switch (reject_source) {
1524         case 1:
1525             reject_source_desc = "User";
1526             switch (reject_reason) {
1527             case 1:  reject_reason_desc = "No reason given"; break;
1528             case 2:  reject_reason_desc = "Application context name not supported"; break;
1529             case 3:  reject_reason_desc = "Calling AE title not recognized"; break;
1530             case 7:  reject_reason_desc = "Called AE title not recognized"; break;
1531             }
1532             break;
1533         case 2:
1534             reject_source_desc = "Provider (ACSE)";
1535             switch (reject_reason) {
1536             case 1:  reject_reason_desc = "No reason given"; break;
1537             case 2:  reject_reason_desc = "Protocol version not supported"; break;
1538             }
1539             break;
1540         case 3:
1541             reject_source_desc = "Provider (Presentation)";
1542             switch (reject_reason) {
1543             case 1:  reject_reason_desc = "Temporary congestion"; break;
1544             case 2:  reject_reason_desc = "Local limit exceeded"; break;
1545             }
1546             break;
1547         }
1548 
1549         proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_reject_result, tvb,
1550             offset  , 1, reject_result, "%s", reject_result_desc);
1551 
1552         proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_reject_source, tvb,
1553             offset+1, 1, reject_source, "%s", reject_source_desc);
1554 
1555         proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_reject_reason, tvb,
1556             offset+2, 1, reject_reason, "%s", reject_reason_desc);
1557 
1558         offset += 3;
1559 
1560         /* Provider aborted */
1561         buf_desc = wmem_strdup_printf(pinfo->pool, "A-ASSOCIATE reject  %s <-- %s (%s)",
1562             assoc->ae_calling, assoc->ae_called, reject_reason_desc);
1563 
1564         expert_add_info(pinfo, assoc_header_pitem, &ei_dcm_assoc_rejected);
1565 
1566         break;
1567     case 5:                                     /* RELEASE Request */
1568 
1569         offset += 2;                            /* Two reserved bytes */
1570         buf_desc="A-RELEASE request";
1571 
1572         break;
1573     case 6:                                     /* RELEASE Response */
1574 
1575         offset += 2;                            /* Two reserved bytes */
1576         buf_desc="A-RELEASE response";
1577 
1578         break;
1579     case 7:                                     /* ABORT */
1580 
1581         offset += 2;                            /* Two reserved bytes */
1582 
1583         abort_source = tvb_get_guint8(tvb, offset);
1584         abort_reason = tvb_get_guint8(tvb, offset+1);
1585 
1586         switch (abort_source) {
1587         case 0:
1588             abort_source_desc = "User";
1589             abort_reason_desc = "N/A";          /* No details can be provided*/
1590             break;
1591         case 1:
1592             /* reserved */
1593             break;
1594         case 2:
1595             abort_source_desc = "Provider";
1596 
1597             switch (abort_reason) {
1598             case 0:  abort_reason_desc = "Not specified"; break;
1599             case 1:  abort_reason_desc = "Unrecognized PDU"; break;
1600             case 2:  abort_reason_desc = "Unexpected PDU"; break;
1601             case 4:  abort_reason_desc = "Unrecognized PDU parameter"; break;
1602             case 5:  abort_reason_desc = "Unexpected PDU parameter"; break;
1603             case 6:  abort_reason_desc = "Invalid PDU parameter value"; break;
1604             }
1605 
1606             break;
1607         }
1608 
1609         proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_abort_source,
1610             tvb, offset  , 1, abort_source, "%s", abort_source_desc);
1611 
1612         proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_abort_reason,
1613             tvb, offset+1, 1, abort_reason, "%s", abort_reason_desc);
1614         offset += 2;
1615 
1616         if (abort_source == 0) {
1617             /* User aborted */
1618             buf_desc = wmem_strdup_printf(pinfo->pool, "ABORT %s --> %s",
1619                 assoc->ae_calling, assoc->ae_called);
1620         }
1621         else {
1622             /* Provider aborted, slightly more information */
1623             buf_desc = wmem_strdup_printf(pinfo->pool, "ABORT %s <-- %s (%s)",
1624                 assoc->ae_calling, assoc->ae_called, abort_reason_desc);
1625         }
1626 
1627         expert_add_info(pinfo, assoc_header_pitem, &ei_dcm_assoc_aborted);
1628 
1629         break;
1630     }
1631 
1632     proto_item_set_text(assoc_header_pitem, "%s", buf_desc);
1633     col_set_str(pinfo->cinfo, COL_INFO, buf_desc);
1634 
1635     /* proto_item and proto_tree are one and the same */
1636     proto_item_append_text(tree, ", %s", buf_desc);
1637 
1638     return offset;
1639 }
1640 
1641 /*
1642 Decode one item in a association request or response. Lookup UIDs if requested.
1643 Create a subtree node with summary and three elements (item_type, item_len, value)
1644 */
1645 static void
dissect_dcm_assoc_item(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset,const gchar * pitem_prefix,int item_value_type,gchar ** item_value,const gchar ** item_description,int * hf_type,int * hf_len,int * hf_value,int ett_subtree)1646 dissect_dcm_assoc_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset,
1647                        const gchar *pitem_prefix, int item_value_type,
1648                        gchar **item_value, const gchar **item_description,
1649                        int *hf_type, int *hf_len, int *hf_value, int ett_subtree)
1650 {
1651 
1652     proto_tree *assoc_item_ptree;       /* Tree for item details */
1653     proto_item *assoc_item_pitem;
1654     dcm_uid_t  *uid = NULL;
1655 
1656     guint32 item_number = 0;
1657 
1658     guint8  item_type;
1659     guint16 item_len;
1660 
1661     gchar *buf_desc = "";             /* Used for item text */
1662 
1663     *item_value = NULL;
1664     *item_description = NULL;
1665 
1666     item_type = tvb_get_guint8(tvb, offset);
1667     item_len  = tvb_get_ntohs(tvb, offset+2);
1668 
1669     assoc_item_ptree = proto_tree_add_subtree(tree, tvb, offset, item_len+4, ett_subtree, &assoc_item_pitem, pitem_prefix);
1670 
1671     proto_tree_add_uint(assoc_item_ptree, *hf_type, tvb, offset,   1, item_type);
1672     proto_tree_add_uint(assoc_item_ptree, *hf_len,  tvb, offset+2, 2, item_len);
1673 
1674     switch (item_value_type) {
1675     case DCM_ITEM_VALUE_TYPE_UID:
1676         *item_value = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset+4, item_len, ENC_ASCII);
1677 
1678         uid = (dcm_uid_t *)wmem_map_lookup(dcm_uid_table, (gpointer) *item_value);
1679         if (uid) {
1680             *item_description = uid->name;
1681             buf_desc = wmem_strdup_printf(pinfo->pool, "%s (%s)", *item_description, *item_value);
1682         }
1683         else {
1684             /* Unknown UID, or no UID at all */
1685             buf_desc = *item_value;
1686         }
1687 
1688         proto_item_append_text(assoc_item_pitem, "%s", buf_desc);
1689         proto_tree_add_string(assoc_item_ptree, *hf_value, tvb, offset+4, item_len, buf_desc);
1690 
1691         break;
1692 
1693     case DCM_ITEM_VALUE_TYPE_STRING:
1694         *item_value = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset+4, item_len, ENC_ASCII);
1695         proto_item_append_text(assoc_item_pitem, "%s", *item_value);
1696         proto_tree_add_string(assoc_item_ptree, *hf_value, tvb, offset+4, item_len, *item_value);
1697 
1698         break;
1699 
1700     case DCM_ITEM_VALUE_TYPE_UINT32:
1701         item_number = tvb_get_ntohl(tvb, offset+4);
1702         *item_value = (gchar *)wmem_strdup_printf(wmem_file_scope(), "%d", item_number);
1703 
1704         proto_item_append_text(assoc_item_pitem, "%s", *item_value);
1705         proto_tree_add_item(assoc_item_ptree, *hf_value, tvb, offset+4, 4, ENC_BIG_ENDIAN);
1706 
1707         break;
1708 
1709     default:
1710         break;
1711     }
1712 }
1713 
1714 /*
1715 Decode the SOP Class Extended Negotiation Sub-Item Fields in a association request or response.
1716 Lookup UIDs if requested
1717 */
1718 static void
dissect_dcm_assoc_sopclass_extneg(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset)1719 dissect_dcm_assoc_sopclass_extneg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
1720 {
1721 
1722     proto_tree *assoc_item_extneg_tree = NULL;  /* Tree for item details */
1723     proto_item *assoc_item_extneg_item = NULL;
1724 
1725     guint16 item_len  = 0;
1726     guint16 sop_class_uid_len  = 0;
1727     gint32 cnt = 0;
1728 
1729     gchar *buf_desc = NULL;             /* Used for item text */
1730     dcm_uid_t *sopclassuid=NULL;
1731     gchar *sopclassuid_str = NULL;
1732 
1733     item_len  = tvb_get_ntohs(tvb, offset+2);
1734     sop_class_uid_len  = tvb_get_ntohs(tvb, offset+4);
1735 
1736     assoc_item_extneg_item = proto_tree_add_item(tree, hf_dcm_info_extneg, tvb, offset, item_len+4, ENC_NA);
1737     proto_item_set_text(assoc_item_extneg_item, "Ext. Neg.: ");
1738     assoc_item_extneg_tree = proto_item_add_subtree(assoc_item_extneg_item, ett_assoc_info_extneg);
1739 
1740     proto_tree_add_item(assoc_item_extneg_tree, hf_dcm_assoc_item_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1741     proto_tree_add_item(assoc_item_extneg_tree, hf_dcm_assoc_item_len, tvb, offset+2, 2, ENC_BIG_ENDIAN);
1742     proto_tree_add_item(assoc_item_extneg_tree, hf_dcm_info_extneg_sopclassuid_len, tvb, offset+4, 2, ENC_BIG_ENDIAN);
1743 
1744     sopclassuid_str = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset+6, sop_class_uid_len, ENC_ASCII);
1745     sopclassuid = (dcm_uid_t *)wmem_map_lookup(dcm_uid_table, (gpointer) sopclassuid_str);
1746 
1747     if (sopclassuid) {
1748         buf_desc = wmem_strdup_printf(pinfo->pool, "%s (%s)", sopclassuid->name, sopclassuid->value);
1749     }
1750     else {
1751         buf_desc = sopclassuid_str;
1752     }
1753 
1754     proto_item_append_text(assoc_item_extneg_item, "%s", buf_desc);
1755     proto_tree_add_string(assoc_item_extneg_tree, hf_dcm_info_extneg_sopclassuid, tvb, offset+6, sop_class_uid_len, buf_desc);
1756 
1757     /* Count how many fields are following. */
1758     cnt = item_len - 2 - sop_class_uid_len;
1759 
1760     /*
1761      * The next field contains Service Class specific information identified by the SOP Class UID.
1762      */
1763     if (0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_PATIENT_ROOT_QUERYRETRIEVE_INFORMATION_MODEL_FIND) ||
1764         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_STUDY_ROOT_QUERYRETRIEVE_INFORMATION_MODEL_FIND) ||
1765         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_PATIENTSTUDY_ONLY_QUERYRETRIEVE_INFORMATION_MODEL_FIND_RETIRED) ||
1766         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_PATIENT_ROOT_QUERYRETRIEVE_INFORMATION_MODEL_MOVE) ||
1767         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_STUDY_ROOT_QUERYRETRIEVE_INFORMATION_MODEL_MOVE) ||
1768         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_PATIENTSTUDY_ONLY_QUERYRETRIEVE_INFORMATION_MODEL_MOVE_RETIRED) ||
1769         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_PATIENT_ROOT_QUERYRETRIEVE_INFORMATION_MODEL_GET) ||
1770         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_STUDY_ROOT_QUERYRETRIEVE_INFORMATION_MODEL_GET) ||
1771         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_PATIENTSTUDY_ONLY_QUERYRETRIEVE_INFORMATION_MODEL_GET_RETIRED))
1772     {
1773         if (cnt<=0)
1774         {
1775             return;
1776         }
1777 
1778         /* Support for Relational queries. */
1779         proto_tree_add_item(assoc_item_extneg_tree, hf_dcm_info_extneg_relational_query, tvb, offset+6+sop_class_uid_len, 1, ENC_BIG_ENDIAN);
1780         --cnt;
1781     }
1782 
1783     /* More sub-items are only allowed for the C-FIND SOP Classes. */
1784     if (0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_PATIENT_ROOT_QUERYRETRIEVE_INFORMATION_MODEL_FIND) ||
1785         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_STUDY_ROOT_QUERYRETRIEVE_INFORMATION_MODEL_FIND) ||
1786         0 == strcmp(sopclassuid_str, DCM_UID_SOP_CLASS_PATIENTSTUDY_ONLY_QUERYRETRIEVE_INFORMATION_MODEL_FIND_RETIRED))
1787     {
1788         if (cnt<=0)
1789         {
1790             return;
1791         }
1792 
1793         /* Combined Date-Time matching. */
1794         proto_tree_add_item(assoc_item_extneg_tree, hf_dcm_info_extneg_date_time_matching, tvb, offset+7+sop_class_uid_len, 1, ENC_BIG_ENDIAN);
1795         --cnt;
1796 
1797         if (cnt<=0)
1798         {
1799             return;
1800         }
1801 
1802         /* Fuzzy semantic matching of person names. */
1803         proto_tree_add_item(assoc_item_extneg_tree, hf_dcm_info_extneg_fuzzy_semantic_matching, tvb, offset+8+sop_class_uid_len, 1, ENC_BIG_ENDIAN);
1804         --cnt;
1805 
1806         if (cnt<=0)
1807         {
1808             return;
1809         }
1810 
1811         /* Timezone query adjustment. */
1812         proto_tree_add_item(assoc_item_extneg_tree, hf_dcm_info_extneg_timezone_query_adjustment, tvb, offset+9+sop_class_uid_len, 1, ENC_BIG_ENDIAN);
1813         --cnt;
1814     }
1815 }
1816 
1817 /*
1818 Decode user identities in the association
1819 */
1820 static void
dissect_dcm_assoc_user_identify(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset)1821 dissect_dcm_assoc_user_identify(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
1822 {
1823 
1824     proto_tree *assoc_item_user_identify_tree = NULL;  /* Tree for item details */
1825     proto_item *assoc_item_user_identify_item = NULL;
1826 
1827     guint16 primary_field_length, secondary_field_length, item_len  = 0;
1828     guint8 type;
1829 
1830     item_len  = tvb_get_ntohs(tvb, offset+2);
1831 
1832     assoc_item_user_identify_item = proto_tree_add_item(tree, hf_dcm_info_user_identify, tvb, offset, item_len+4, ENC_NA);
1833     assoc_item_user_identify_tree = proto_item_add_subtree(assoc_item_user_identify_item, ett_assoc_info_user_identify);
1834 
1835     proto_tree_add_item(assoc_item_user_identify_tree, hf_dcm_assoc_item_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1836     offset += 2;
1837     proto_tree_add_item(assoc_item_user_identify_tree, hf_dcm_assoc_item_len, tvb, offset, 2, ENC_BIG_ENDIAN);
1838     offset += 2;
1839 
1840     type = tvb_get_guint8(tvb, offset);
1841     proto_tree_add_item(assoc_item_user_identify_tree, hf_dcm_info_user_identify_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1842     offset += 1;
1843 
1844     proto_tree_add_item(assoc_item_user_identify_tree, hf_dcm_info_user_identify_response_requested, tvb, offset, 1, ENC_BIG_ENDIAN);
1845     offset += 1;
1846 
1847     primary_field_length = tvb_get_ntohs(tvb, offset);
1848     proto_tree_add_item(assoc_item_user_identify_tree, hf_dcm_info_user_identify_primary_field_length, tvb, offset, 2, ENC_BIG_ENDIAN);
1849     offset += 2;
1850 
1851     proto_tree_add_item(assoc_item_user_identify_tree, hf_dcm_info_user_identify_primary_field, tvb, offset, primary_field_length, ENC_UTF_8|ENC_NA);
1852     proto_item_append_text(assoc_item_user_identify_item, ": %s", tvb_get_string_enc(pinfo->pool, tvb, offset, primary_field_length, ENC_UTF_8|ENC_NA));
1853     offset += primary_field_length;
1854 
1855     if (type == 2) {
1856         secondary_field_length = tvb_get_ntohs(tvb, offset);
1857         proto_tree_add_item(assoc_item_user_identify_tree, hf_dcm_info_user_identify_secondary_field_length, tvb, offset, 2, ENC_BIG_ENDIAN);
1858         offset += 2;
1859 
1860         proto_tree_add_item(assoc_item_user_identify_tree, hf_dcm_info_user_identify_secondary_field, tvb, offset, secondary_field_length, ENC_UTF_8|ENC_NA);
1861         proto_item_append_text(assoc_item_user_identify_item, ", %s", tvb_get_string_enc(pinfo->pool, tvb, offset, secondary_field_length, ENC_UTF_8|ENC_NA));
1862     }
1863 }
1864 
1865 /*
1866 Decode unknown item types in the association
1867 */
1868 static void
dissect_dcm_assoc_unknown(tvbuff_t * tvb,proto_tree * tree,guint32 offset)1869 dissect_dcm_assoc_unknown(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
1870 {
1871 
1872     proto_tree *assoc_item_unknown_tree = NULL;  /* Tree for item details */
1873     proto_item *assoc_item_unknown_item = NULL;
1874 
1875     guint16 item_len  = 0;
1876 
1877     item_len  = tvb_get_ntohs(tvb, offset+2);
1878 
1879     assoc_item_unknown_item = proto_tree_add_item(tree, hf_dcm_info_unknown, tvb, offset, item_len+4, ENC_NA);
1880     assoc_item_unknown_tree = proto_item_add_subtree(assoc_item_unknown_item, ett_assoc_info_unknown);
1881 
1882     proto_tree_add_item(assoc_item_unknown_tree, hf_dcm_assoc_item_type, tvb, offset,   1, ENC_BIG_ENDIAN);
1883     proto_tree_add_item(assoc_item_unknown_tree, hf_dcm_assoc_item_len,  tvb, offset+2, 2, ENC_BIG_ENDIAN);
1884     offset += 4;
1885 
1886     proto_tree_add_item(assoc_item_unknown_tree, hf_dcm_assoc_item_data, tvb, offset, item_len, ENC_NA);
1887 }
1888 
1889 /*
1890 Decode the SCP/SCU Role Selection Sub-Item Fields in a association request or response.
1891 Lookup UIDs if requested
1892 */
1893 static void
dissect_dcm_assoc_role_selection(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset)1894 dissect_dcm_assoc_role_selection(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
1895 {
1896 
1897     proto_tree *assoc_item_rolesel_tree; /* Tree for item details */
1898     proto_item *assoc_item_rolesel_item;
1899 
1900     guint16 item_len, sop_class_uid_len;
1901     guint8 scp_role, scu_role;
1902 
1903     gchar *buf_desc;     /* Used for item text */
1904     dcm_uid_t *sopclassuid;
1905     gchar *sopclassuid_str;
1906 
1907     item_len  = tvb_get_ntohs(tvb, offset+2);
1908     sop_class_uid_len  = tvb_get_ntohs(tvb, offset+4);
1909 
1910     assoc_item_rolesel_item = proto_tree_add_item(tree, hf_dcm_info_rolesel, tvb, offset, item_len+4, ENC_NA);
1911     proto_item_set_text(assoc_item_rolesel_item, "Role Selection: ");
1912     assoc_item_rolesel_tree = proto_item_add_subtree(assoc_item_rolesel_item, ett_assoc_info_rolesel);
1913 
1914     proto_tree_add_item(assoc_item_rolesel_tree, hf_dcm_assoc_item_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1915     proto_tree_add_item(assoc_item_rolesel_tree, hf_dcm_assoc_item_len, tvb, offset+2, 2, ENC_BIG_ENDIAN);
1916     proto_tree_add_item(assoc_item_rolesel_tree, hf_dcm_info_rolesel_sopclassuid_len, tvb, offset+4, 2, ENC_BIG_ENDIAN);
1917 
1918     sopclassuid_str = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset+6, sop_class_uid_len, ENC_ASCII);
1919     sopclassuid = (dcm_uid_t *)wmem_map_lookup(dcm_uid_table, (gpointer) sopclassuid_str);
1920 
1921     scu_role = tvb_get_guint8(tvb, offset+6+sop_class_uid_len);
1922     scp_role = tvb_get_guint8(tvb, offset+7+sop_class_uid_len);
1923 
1924     if (scu_role) {
1925         proto_item_append_text(assoc_item_rolesel_item, "%s", "SCU-role: yes");
1926     }
1927     else {
1928         proto_item_append_text(assoc_item_rolesel_item, "%s", "SCU-role: no");
1929     }
1930 
1931     if (scp_role) {
1932         proto_item_append_text(assoc_item_rolesel_item, ", %s", "SCP-role: yes");
1933     }
1934     else {
1935         proto_item_append_text(assoc_item_rolesel_item, ", %s", "SCP-role: no");
1936     }
1937 
1938     if (sopclassuid) {
1939         buf_desc = wmem_strdup_printf(pinfo->pool, "%s (%s)", sopclassuid->name, sopclassuid->value);
1940     }
1941     else {
1942         buf_desc = sopclassuid_str;
1943     }
1944 
1945     proto_tree_add_string(assoc_item_rolesel_tree, hf_dcm_info_rolesel_sopclassuid, tvb, offset+6, sop_class_uid_len, buf_desc);
1946 
1947     proto_tree_add_item(assoc_item_rolesel_tree, hf_dcm_info_rolesel_scurole, tvb, offset+6+sop_class_uid_len, 1, ENC_BIG_ENDIAN);
1948     proto_tree_add_item(assoc_item_rolesel_tree, hf_dcm_info_rolesel_scprole, tvb, offset+7+sop_class_uid_len, 1, ENC_BIG_ENDIAN);
1949 }
1950 
1951 /*
1952 Decode the Asynchronous operations (and sub-operations) Window Negotiation Sub-Item Fields in a association request or response.
1953 */
1954 static void
dissect_dcm_assoc_async_negotiation(tvbuff_t * tvb,proto_tree * tree,guint32 offset)1955 dissect_dcm_assoc_async_negotiation(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
1956 {
1957 
1958     proto_tree *assoc_item_asyncneg_tree; /* Tree for item details */
1959     proto_item *assoc_item_asyncneg_item;
1960 
1961     guint16 item_len, max_num_ops_inv, max_num_ops_per = 0;
1962 
1963     item_len  = tvb_get_ntohs(tvb, offset+2);
1964 
1965     assoc_item_asyncneg_item = proto_tree_add_item(tree, hf_dcm_info_async_neg, tvb, offset, item_len+4, ENC_NA);
1966     proto_item_set_text(assoc_item_asyncneg_item, "Async Negotiation: ");
1967     assoc_item_asyncneg_tree = proto_item_add_subtree(assoc_item_asyncneg_item, ett_assoc_info_async_neg);
1968 
1969     proto_tree_add_item(assoc_item_asyncneg_tree, hf_dcm_assoc_item_type, tvb, offset, 1, ENC_BIG_ENDIAN);
1970     proto_tree_add_item(assoc_item_asyncneg_tree, hf_dcm_assoc_item_len, tvb, offset+2, 2, ENC_BIG_ENDIAN);
1971     proto_tree_add_item(assoc_item_asyncneg_tree, hf_dcm_info_async_neg_max_num_ops_inv, tvb, offset+4, 2, ENC_BIG_ENDIAN);
1972     proto_tree_add_item(assoc_item_asyncneg_tree, hf_dcm_info_async_neg_max_num_ops_per, tvb, offset+6, 2, ENC_BIG_ENDIAN);
1973 
1974     max_num_ops_inv = tvb_get_ntohs(tvb, offset+4);
1975     max_num_ops_per = tvb_get_ntohs(tvb, offset+6);
1976 
1977     proto_item_append_text(assoc_item_asyncneg_item, "%s%d", "Maximum Number Operations Invoked: ", max_num_ops_inv);
1978     if (max_num_ops_inv==0) proto_item_append_text(assoc_item_asyncneg_item, "%s", " (unlimited)");
1979     proto_item_append_text(assoc_item_asyncneg_item, ", %s%d", "Maximum Number Operations Performed: ", max_num_ops_per);
1980     if (max_num_ops_per==0) proto_item_append_text(assoc_item_asyncneg_item, "%s", " (unlimited)");
1981 }
1982 
1983 /*
1984 Decode a presentation context item in a Association Request or Response. In the response, set the accepted transfer syntax, if any.
1985 */
1986 static void
dissect_dcm_pctx(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,dcm_state_assoc_t * assoc,guint32 offset,guint32 len,const gchar * pitem_prefix,gboolean is_assoc_request)1987 dissect_dcm_pctx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1988                  dcm_state_assoc_t *assoc, guint32 offset, guint32 len,
1989                  const gchar *pitem_prefix, gboolean is_assoc_request)
1990 {
1991 
1992     proto_tree *pctx_ptree;                 /* Tree for presentation context details */
1993     proto_item *pctx_pitem;
1994 
1995     dcm_state_pctx_t *pctx = NULL;
1996 
1997     guint8  item_type = 0;
1998     guint16 item_len = 0;
1999 
2000     guint8  pctx_id = 0;                    /* Presentation Context ID */
2001     guint8  pctx_result = 0;
2002 
2003     const char  *pctx_result_desc = "";
2004 
2005     gchar *pctx_abss_uid  = NULL;           /* Abstract Syntax UID alias SOP Class UID */
2006     const gchar *pctx_abss_desc = NULL;     /* Description of UID */
2007 
2008     gchar *pctx_xfer_uid = NULL;            /* Transfer Syntax UID */
2009     const gchar *pctx_xfer_desc = NULL;     /* Description of UID */
2010 
2011     gchar *buf_desc = "";                   /* Used in info mode for item text */
2012 
2013     guint32 endpos = 0;
2014     int     cnt_abbs = 0;                   /* Number of Abstract Syntax Items */
2015     int     cnt_xfer = 0;                   /* Number of Transfer Syntax Items */
2016 
2017     endpos = offset + len;
2018 
2019     item_type = tvb_get_guint8(tvb, offset-4);
2020     item_len  = tvb_get_ntohs(tvb, offset-2);
2021 
2022     pctx_ptree = proto_tree_add_subtree(tree, tvb, offset-4, item_len+4, ett_assoc_pctx, &pctx_pitem, pitem_prefix);
2023 
2024     pctx_id     = tvb_get_guint8(tvb, offset);
2025     pctx_result = tvb_get_guint8(tvb, 2 + offset);      /* only set in responses, otherwise reserved and 0x00 */
2026 
2027     /* Find or create DICOM context object */
2028     pctx = dcm_state_pctx_get(assoc, pctx_id, TRUE);
2029     if (pctx == NULL) { /* Internal error. Failed to create data structure */
2030         return;
2031     }
2032 
2033     proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_type, tvb, offset-4, 1, item_type);           /* The type is only one byte long */
2034     proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_len,  tvb, offset-2, 2, item_len);
2035 
2036     proto_tree_add_uint_format(pctx_ptree, hf_dcm_pctx_id, tvb, offset, 1, pctx_id, "Context ID: 0x%02x", pctx_id);
2037 
2038     if (!is_assoc_request) {
2039         /* Association response. */
2040 
2041         switch (pctx_result) {
2042         case 0:  pctx_result_desc = "Accept"; break;
2043         case 1:  pctx_result_desc = "User Reject"; break;
2044         case 2:  pctx_result_desc = "No Reason"; break;
2045         case 3:  pctx_result_desc = "Abstract Syntax Unsupported"; break;
2046         case 4:  pctx_result_desc = "Transfer Syntax Unsupported"; break;
2047         }
2048 
2049         proto_tree_add_uint_format(pctx_ptree, hf_dcm_pctx_result, tvb, offset+2, 1,
2050             pctx_result, "Result: %s (0x%x)", pctx_result_desc, pctx_result);
2051     }
2052 
2053     offset += 4;
2054     while (offset < endpos) {
2055 
2056         item_type = tvb_get_guint8(tvb, offset);
2057         item_len = tvb_get_ntohs(tvb, 2 + offset);
2058 
2059         offset += 4;
2060         switch (item_type) {
2061         case 0x30:              /* Abstract syntax */
2062 
2063             /* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
2064             dissect_dcm_assoc_item(tvb, pinfo, pctx_ptree, offset-4,
2065                 "Abstract Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_abss_uid, &pctx_abss_desc,
2066                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_abss_syntax, ett_assoc_pctx_abss);
2067 
2068             cnt_abbs += 1;
2069             offset += item_len;
2070             break;
2071 
2072         case 0x40:              /* Transfer syntax */
2073 
2074             dissect_dcm_assoc_item(tvb, pinfo, pctx_ptree, offset-4,
2075                 "Transfer Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_xfer_uid, &pctx_xfer_desc,
2076                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_xfer_syntax, ett_assoc_pctx_xfer);
2077 
2078             /*
2079                In a correct Association Response, only one Transfer syntax shall be present.
2080                Therefore, pctx_xfer_uid, pctx_xfer_desc are used for the accept scenario in the info mode
2081             */
2082 
2083             if (!is_assoc_request && pctx_result == 0) {
2084                 /* Association Response, Context Accepted */
2085                 dcm_set_syntax(pctx, pctx_xfer_uid, pctx_xfer_desc);
2086             }
2087             cnt_xfer += 1;
2088             offset += item_len;
2089             break;
2090 
2091         default:
2092             offset += item_len;
2093             break;
2094         }
2095     }
2096 
2097     if (is_assoc_request) {
2098 
2099         if (cnt_abbs<1) {
2100             expert_add_info(pinfo, pctx_pitem, &ei_dcm_no_abstract_syntax);
2101             return;
2102         }
2103         else if (cnt_abbs>1) {
2104             expert_add_info(pinfo, pctx_pitem, &ei_dcm_multiple_abstract_syntax);
2105             return;
2106         }
2107 
2108         if (cnt_xfer==0) {
2109             expert_add_info(pinfo, pctx_pitem, &ei_dcm_no_transfer_syntax);
2110             return;
2111         }
2112 
2113         if (pctx_abss_uid==NULL) {
2114             expert_add_info(pinfo, pctx_pitem, &ei_dcm_no_abstract_syntax_uid);
2115             return;
2116         }
2117 
2118     }
2119     else {
2120 
2121         if (cnt_xfer>1) {
2122             expert_add_info(pinfo, pctx_pitem, &ei_dcm_multiple_transfer_syntax);
2123             return;
2124         }
2125     }
2126 
2127     if (pctx->abss_uid==NULL) {
2128         /* Permanent copy information into structure */
2129         pctx->abss_uid  = wmem_strdup(wmem_file_scope(), pctx_abss_uid);
2130         pctx->abss_desc = wmem_strdup(wmem_file_scope(), pctx_abss_desc);
2131     }
2132 
2133     /*
2134       Copy to buffer first, because proto_item_append_text()
2135       crashed for an unknown reason using 'ID 0x%02x, %s, %s'
2136       and in my opinion correctly set parameters.
2137     */
2138 
2139     if (is_assoc_request) {
2140         if (pctx_abss_desc == NULL) {
2141             buf_desc = pctx_abss_uid;
2142         }
2143         else {
2144             buf_desc = wmem_strdup_printf(pinfo->pool, "%s (%s)", pctx_abss_desc, pctx_abss_uid);
2145         }
2146     }
2147     else
2148     {
2149         if (pctx_result==0) {
2150             /* Accepted */
2151             buf_desc = wmem_strdup_printf(pinfo->pool, "ID 0x%02x, %s, %s, %s",
2152                 pctx_id, pctx_result_desc,
2153                 dcm_uid_or_desc(pctx->xfer_uid, pctx->xfer_desc),
2154                 dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
2155         }
2156         else {
2157             /* Rejected */
2158             buf_desc = wmem_strdup_printf(pinfo->pool, "ID 0x%02x, %s, %s",
2159                 pctx_id, pctx_result_desc,
2160                 dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
2161         }
2162     }
2163     proto_item_append_text(pctx_pitem, "%s", buf_desc);
2164 
2165 }
2166 
2167 /*
2168 Decode the user info item in a Association Request or Response
2169 */
2170 static void
dissect_dcm_userinfo(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset,guint32 len,const gchar * pitem_prefix)2171 dissect_dcm_userinfo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint32 len, const gchar *pitem_prefix)
2172 {
2173 
2174     proto_item *userinfo_pitem = NULL;
2175     proto_tree *userinfo_ptree = NULL;  /* Tree for presentation context details */
2176 
2177     guint8  item_type;
2178     guint16 item_len;
2179 
2180     gboolean first_item=TRUE;
2181 
2182     gchar *info_max_pdu=NULL;
2183     gchar *info_impl_uid=NULL;
2184     gchar *info_impl_version=NULL;
2185     const gchar *dummy=NULL;
2186 
2187     guint32 endpos;
2188 
2189     endpos = offset + len;
2190 
2191     item_type = tvb_get_guint8(tvb, offset-4);
2192     item_len  = tvb_get_ntohs(tvb, offset-2);
2193 
2194     userinfo_pitem = proto_tree_add_item(tree, hf_dcm_info, tvb, offset-4, item_len+4, ENC_NA);
2195     proto_item_set_text(userinfo_pitem, "%s", pitem_prefix);
2196     userinfo_ptree = proto_item_add_subtree(userinfo_pitem, ett_assoc_info);
2197 
2198     proto_tree_add_uint(userinfo_ptree, hf_dcm_assoc_item_type, tvb, offset-4, 1, item_type);       /* The type is only one byte long */
2199     proto_tree_add_uint(userinfo_ptree, hf_dcm_assoc_item_len,  tvb, offset-2, 2, item_len);
2200 
2201     while (offset < endpos) {
2202 
2203         item_type = tvb_get_guint8(tvb, offset);
2204         item_len = tvb_get_ntohs(tvb, 2 + offset);
2205 
2206         offset += 4;
2207         switch (item_type) {
2208         case 0x51:              /* Max length */
2209 
2210             dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, offset-4,
2211                 "Max PDU Length: ", DCM_ITEM_VALUE_TYPE_UINT32, &info_max_pdu, &dummy,
2212                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pdu_maxlen, ett_assoc_info_uid);
2213 
2214             if (!first_item) {
2215                 proto_item_append_text(userinfo_pitem, ", ");
2216             }
2217             proto_item_append_text(userinfo_pitem, "Max PDU Length %s", info_max_pdu);
2218             first_item=FALSE;
2219 
2220             offset += item_len;
2221             break;
2222 
2223         case 0x52:              /* UID */
2224 
2225             /* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
2226             dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, offset-4,
2227                 "Implementation UID: ", DCM_ITEM_VALUE_TYPE_STRING, &info_impl_uid, &dummy,
2228                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_uid, ett_assoc_info_uid);
2229 
2230             if (!first_item) {
2231                 proto_item_append_text(userinfo_pitem, ", ");
2232             }
2233             proto_item_append_text(userinfo_pitem, "Implementation UID %s", info_impl_uid);
2234             first_item=FALSE;
2235 
2236             offset += item_len;
2237             break;
2238 
2239         case 0x55:              /* version */
2240 
2241             dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, offset-4,
2242                 "Implementation Version: ", DCM_ITEM_VALUE_TYPE_STRING, &info_impl_version, &dummy,
2243                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_version, ett_assoc_info_version);
2244 
2245             if (!first_item) {
2246                 proto_item_append_text(userinfo_pitem, ", ");
2247             }
2248             proto_item_append_text(userinfo_pitem, "Version %s", info_impl_version);
2249             first_item=FALSE;
2250 
2251             offset += item_len;
2252             break;
2253 
2254         case 0x53:              /* async negotiation */
2255 
2256             dissect_dcm_assoc_async_negotiation(tvb, userinfo_ptree, offset-4);
2257 
2258             offset += item_len;
2259             break;
2260 
2261         case 0x54:              /* scp/scu role selection */
2262 
2263            dissect_dcm_assoc_role_selection(tvb, pinfo, userinfo_ptree, offset-4);
2264 
2265            offset += item_len;
2266            break;
2267 
2268         case 0x56:              /* extended negotiation */
2269 
2270             dissect_dcm_assoc_sopclass_extneg(tvb, pinfo, userinfo_ptree, offset-4);
2271 
2272             offset += item_len;
2273             break;
2274 
2275         case 0x58:              /* User Identify */
2276 
2277             dissect_dcm_assoc_user_identify(tvb, pinfo, userinfo_ptree, offset-4);
2278 
2279             offset += item_len;
2280             break;
2281 
2282         default:
2283 
2284             dissect_dcm_assoc_unknown(tvb, userinfo_ptree, offset-4);
2285 
2286             offset += item_len;
2287             break;
2288         }
2289     }
2290 }
2291 
2292 
2293 /*
2294 Create a subtree for association requests or responses
2295 */
2296 static guint32
dissect_dcm_assoc_detail(tvbuff_t * tvb,packet_info * pinfo,proto_item * ti,dcm_state_assoc_t * assoc,guint32 offset,guint32 len)2297 dissect_dcm_assoc_detail(tvbuff_t *tvb, packet_info *pinfo, proto_item *ti,
2298                          dcm_state_assoc_t *assoc, guint32 offset, guint32 len)
2299 {
2300     proto_tree *assoc_tree  = NULL;     /* Tree for PDU details */
2301 
2302     guint8  item_type;
2303     guint16 item_len;
2304 
2305     guint32 endpos;
2306 
2307     gchar *item_value = NULL;
2308     const gchar *item_description = NULL;
2309 
2310     endpos = offset + len;
2311 
2312     assoc_tree = proto_item_add_subtree(ti, ett_assoc);
2313     while (offset < endpos) {
2314 
2315         item_type = tvb_get_guint8(tvb, offset);
2316         item_len  = tvb_get_ntohs(tvb, 2 + offset);
2317 
2318         if (item_len == 0) {
2319             expert_add_info(pinfo, ti, &ei_dcm_assoc_item_len);
2320             return endpos;
2321         }
2322 
2323         offset += 4;
2324 
2325         switch (item_type) {
2326         case 0x10:              /* Application context */
2327             dissect_dcm_assoc_item(tvb, pinfo, assoc_tree, offset-4,
2328                 "Application Context: ", DCM_ITEM_VALUE_TYPE_UID, &item_value, &item_description,
2329                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_actx, ett_assoc_actx);
2330 
2331             offset += item_len;
2332             break;
2333 
2334         case 0x20:              /* Presentation context request */
2335             dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len, "Presentation Context: ", TRUE);
2336             offset += item_len;
2337             break;
2338 
2339         case 0x21:              /* Presentation context reply */
2340             dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len, "Presentation Context: ", FALSE);
2341             offset += item_len;
2342             break;
2343 
2344         case 0x50:              /* User Info */
2345             dissect_dcm_userinfo(tvb, pinfo, assoc_tree, offset, item_len, "User Info: ");
2346             offset += item_len;
2347             break;
2348 
2349         default:
2350             offset += item_len;
2351             break;
2352         }
2353     }
2354 
2355     return offset;
2356 
2357 }
2358 
2359 static guint32
dissect_dcm_pdv_header(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,dcm_state_assoc_t * assoc,guint32 offset,dcm_state_pdv_t ** pdv)2360 dissect_dcm_pdv_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
2361                        dcm_state_assoc_t *assoc, guint32 offset, dcm_state_pdv_t **pdv)
2362 {
2363     /* Dissect Context and Flags of a PDV and create new PDV structure */
2364 
2365     proto_item *pdv_ctx_pitem = NULL;
2366     proto_item *pdv_flags_pitem = NULL;
2367 
2368     dcm_state_pctx_t    *pctx = NULL;
2369     dcm_state_pdv_t     *pdv_first_data = NULL;
2370 
2371     const gchar *desc_flag = NULL;      /* Flag Description in tree */
2372     gchar *desc_header = NULL;          /* Used for PDV description */
2373 
2374     guint8  flags = 0, o_flags = 0;
2375     guint8  pctx_id = 0;
2376 
2377     /* 1 Byte Context */
2378     pctx_id = tvb_get_guint8(tvb, offset);
2379     pctx = dcm_state_pctx_get(assoc, pctx_id, FALSE);
2380 
2381     if (pctx && pctx->xfer_uid) {
2382         proto_tree_add_uint_format(tree, hf_dcm_pdv_ctx, tvb, offset, 1,
2383             pctx_id, "Context: 0x%02x (%s, %s)", pctx_id,
2384         dcm_uid_or_desc(pctx->xfer_uid, pctx->xfer_desc),
2385         dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
2386     }
2387     else {
2388         pdv_ctx_pitem=proto_tree_add_uint_format(tree, hf_dcm_pdv_ctx, tvb,  offset, 1,
2389             pctx_id, "Context: 0x%02x not found. A-ASSOCIATE request not found in capture.", pctx_id);
2390 
2391         expert_add_info(pinfo, pdv_ctx_pitem, &ei_dcm_pdv_ctx);
2392 
2393         if (pctx == NULL) {
2394             /* only create presentation context, if it does not yet exist */
2395 
2396             /* Create fake PCTX and guess Syntax ILE, ELE, EBE */
2397             pctx = dcm_state_pctx_new(assoc, pctx_id);
2398 
2399             /* To be done: Guess Syntax */
2400             pctx->syntax = DCM_UNK;
2401         }
2402     }
2403     offset +=1;
2404 
2405     /* Create PDV structure:
2406 
2407        Since we can have multiple PDV per packet (offset) and
2408        multiple merged packets per PDV (the tvb raw_offset)
2409        we need both values to uniquely identify a PDV
2410     */
2411 
2412     *pdv = dcm_state_pdv_get(pctx, pinfo->num, tvb_raw_offset(tvb)+offset, TRUE);
2413     if (*pdv == NULL) {
2414         return 0;                   /* Failed to allocate memory */
2415     }
2416 
2417     /* 1 Byte Flag */
2418     /* PS3.8 E.2  Bits 2 through 7 are always set to 0 by the sender and never checked by the receiver. */
2419     o_flags = tvb_get_guint8(tvb, offset);
2420     flags = 0x3 & o_flags;
2421 
2422     (*pdv)->pctx_id = pctx_id;
2423 
2424     switch (flags) {
2425     case 0:     /* 00 */
2426         if (0 != (0xfc & o_flags))
2427             desc_flag = "Data, More Fragments (Warning: Invalid)";
2428         else
2429             desc_flag = "Data, More Fragments";
2430 
2431         (*pdv)->is_flagvalid = TRUE;
2432         (*pdv)->is_command = FALSE;
2433         (*pdv)->is_last_fragment = FALSE;
2434         (*pdv)->syntax = pctx->syntax;      /* Inherit syntax for data PDVs*/
2435         break;
2436 
2437     case 2:     /* 10 */
2438         if (0 != (0xfc & o_flags))
2439             desc_flag = "Data, Last Fragment (Warning: Invalid)";
2440         else
2441             desc_flag = "Data, Last Fragment";
2442 
2443         (*pdv)->is_flagvalid = TRUE;
2444         (*pdv)->is_command = FALSE;
2445         (*pdv)->is_last_fragment = TRUE;
2446         (*pdv)->syntax = pctx->syntax;      /* Inherit syntax for data PDVs*/
2447         break;
2448 
2449     case 1:     /* 01 */
2450         if (0 != (0xfc & o_flags))
2451             desc_flag = "Command, More Fragments (Warning: Invalid)";
2452         else
2453             desc_flag = "Command, More Fragments";
2454         desc_header = wmem_strdup(wmem_file_scope(), "Command");        /* Will be overwritten with real command tag */
2455 
2456         (*pdv)->is_flagvalid = TRUE;
2457         (*pdv)->is_command = TRUE;
2458         (*pdv)->is_last_fragment = FALSE;
2459         (*pdv)->syntax = DCM_ILE;           /* Command tags are always little endian*/
2460         break;
2461 
2462     case 3:     /* 11 */
2463         if (0 != (0xfc & o_flags))
2464             desc_flag = "Command, Last Fragment (Warning: Invalid)";
2465         else
2466             desc_flag = "Command, Last Fragment";
2467         desc_header = wmem_strdup(wmem_file_scope(), "Command");
2468 
2469         (*pdv)->is_flagvalid = TRUE;
2470         (*pdv)->is_command = TRUE;
2471         (*pdv)->is_last_fragment = TRUE;
2472         (*pdv)->syntax = DCM_ILE;           /* Command tags are always little endian*/
2473         break;
2474 
2475     default:
2476         desc_flag = "Invalid Flags";
2477         desc_header = wmem_strdup(wmem_file_scope(), desc_flag);
2478 
2479         (*pdv)->is_flagvalid = FALSE;
2480         (*pdv)->is_command = FALSE;
2481         (*pdv)->is_last_fragment = FALSE;
2482         (*pdv)->syntax = DCM_UNK;
2483     }
2484 
2485     if (!PINFO_FD_VISITED(pinfo)) {
2486         (*pdv)->reassembly_id = pctx->reassembly_count;
2487         if ((*pdv)->is_last_fragment) {
2488             pctx->reassembly_count++;
2489         }
2490     }
2491 
2492     if (flags == 0 || flags == 2) {
2493         /* Data PDV */
2494         pdv_first_data = dcm_state_pdv_get_obj_start(*pdv);
2495 
2496         if (pdv_first_data->prev && pdv_first_data->prev->is_command) {
2497             /* Every Data PDV sequence should be preceded by a Command PDV,
2498                so we should always hit this for a correct capture
2499             */
2500 
2501             if (pctx->abss_desc && g_str_has_suffix(pctx->abss_desc, "Storage")) {
2502                 /* Should be done far more intelligent, e.g. does not catch the (Retired) ones */
2503                 if (flags == 0) {
2504                     desc_header = wmem_strdup_printf(wmem_file_scope(), "%s Fragment", pctx->abss_desc);
2505                 }
2506                 else {
2507                     desc_header = wmem_strdup(wmem_file_scope(), pctx->abss_desc);
2508                 }
2509                 (*pdv)->is_storage = TRUE;
2510             }
2511             else {
2512                 /* Use previous command and append DATA*/
2513                 desc_header = wmem_strdup_printf(wmem_file_scope(), "%s-DATA", pdv_first_data->prev->desc);
2514             }
2515         }
2516         else {
2517             desc_header = wmem_strdup(wmem_file_scope(), "DATA");
2518         }
2519     }
2520 
2521     (*pdv)->desc = desc_header;
2522 
2523     pdv_flags_pitem = proto_tree_add_uint_format(tree, hf_dcm_pdv_flags, tvb, offset, 1,
2524         flags, "Flags: 0x%02x (%s)", o_flags, desc_flag);
2525 
2526     if (o_flags>3) {
2527         expert_add_info(pinfo, pdv_flags_pitem, &ei_dcm_pdv_flags);
2528     }
2529     offset +=1;
2530 
2531     return offset;
2532 }
2533 
2534 /*
2535 Based on the value representation, decode the value of one tag.
2536 Support VM>1 for most types, but not all. Returns new offset
2537 */
2538 static guint32
dissect_dcm_tag_value(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,dcm_state_pdv_t * pdv,guint32 offset,guint16 grp,guint16 elm,guint32 vl,guint32 vl_max,const gchar * vr,gchar ** tag_value)2539 dissect_dcm_tag_value(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_pdv_t *pdv,
2540                       guint32 offset, guint16 grp, guint16 elm,
2541                       guint32 vl, guint32 vl_max, const gchar* vr, gchar **tag_value)
2542 {
2543 
2544     proto_item *pitem = NULL;
2545     guint encoding = (pdv->syntax == DCM_EBE) ? ENC_BIG_ENDIAN : ENC_LITTLE_ENDIAN;
2546 
2547 
2548     /* Make sure we have all the bytes of the item; this should throw
2549        and exception if vl_max is so large that it causes the offset
2550        to overflow. */
2551     tvb_ensure_bytes_exist(tvb, offset, vl_max);
2552 
2553     /* ---------------------------------------------------------------------------
2554        Potentially long types. Obey vl_max
2555        ---------------------------------------------------------------------------
2556     */
2557 
2558     if ((strncmp(vr, "AE", 2) == 0) || (strncmp(vr, "AS", 2) == 0) || (strncmp(vr, "CS", 2) == 0) ||
2559         (strncmp(vr, "DA", 2) == 0) || (strncmp(vr, "DS", 2) == 0) || (strncmp(vr, "DT", 2) == 0) ||
2560         (strncmp(vr, "IS", 2) == 0) || (strncmp(vr, "LO", 2) == 0) || (strncmp(vr, "LT", 2) == 0) ||
2561         (strncmp(vr, "PN", 2) == 0) || (strncmp(vr, "SH", 2) == 0) || (strncmp(vr, "ST", 2) == 0) ||
2562         (strncmp(vr, "TM", 2) == 0) || (strncmp(vr, "UI", 2) == 0) || (strncmp(vr, "UT", 2) == 0) ) {
2563         /*
2564             15 ways to represent a string.
2565 
2566             For LT, ST, UT the DICOM standard does not allow multi-value
2567             For the others, VM is built into 'automatically, because it uses '\' as separator
2568         */
2569 
2570         gchar   *vals;
2571         dcm_uid_t *uid = NULL;
2572         guint8 val8;
2573 
2574         val8 = tvb_get_guint8(tvb, offset + vl_max - 1);
2575         if (val8 == 0x00) {
2576             /* Last byte of string is 0x00, i.e. padded */
2577             vals = tvb_format_text(pinfo->pool, tvb, offset, vl_max - 1);
2578         }
2579         else {
2580             vals = tvb_format_text(pinfo->pool, tvb, offset, vl_max);
2581         }
2582 
2583         if ((strncmp(vr, "UI", 2) == 0)) {
2584             /* This is a UID. Attempt a lookup. Will only return something for classes of course */
2585 
2586             uid = (dcm_uid_t *)wmem_map_lookup(dcm_uid_table, (gpointer) vals);
2587             if (uid) {
2588                 *tag_value = wmem_strdup_printf(pinfo->pool, "%s (%s)", vals, uid->name);
2589             }
2590             else {
2591                 *tag_value = vals;
2592             }
2593         }
2594         else {
2595             if (strlen(vals) > 50) {
2596                 *tag_value = wmem_strdup_printf(pinfo->pool, "%-50.50s...", vals);
2597             }
2598             else {
2599                 *tag_value = vals;
2600             }
2601         }
2602         proto_tree_add_string(tree, hf_dcm_tag_value_str, tvb, offset, vl_max, *tag_value);
2603 
2604         if (grp == 0x0000 && elm == 0x0902) {
2605             /* The error comment */
2606             pdv->comment = wmem_strdup(wmem_file_scope(), g_strstrip(vals));
2607         }
2608     }
2609     else if ((strncmp(vr, "OB", 2) == 0) || (strncmp(vr, "OW", 2) == 0) ||
2610              (strncmp(vr, "OF", 2) == 0) || (strncmp(vr, "OD", 2) == 0)) {
2611 
2612         /* Array of Bytes, Words, Float, or Doubles. Don't perform any decoding. VM=1. Multiple arrays are not possible */
2613 
2614         proto_tree_add_bytes_format_value(tree, hf_dcm_tag_value_byte, tvb, offset, vl_max, NULL, "%s", "(binary)");
2615 
2616         *tag_value = wmem_strdup(pinfo->pool, "(binary)");
2617     }
2618     else if (strncmp(vr, "UN", 2) == 0) {
2619 
2620         /*  Usually the case for private tags in implicit syntax, since tag was not found and VR not specified.
2621             Not been able to create UN yet. No need to support VM > 1.
2622         */
2623 
2624         guint8    val8;
2625         gchar    *vals;
2626         guint32  i;
2627 
2628         /* String detector, i.e. check if we only have alpha-numeric character */
2629         gboolean        is_string = TRUE;
2630         gboolean        is_padded = FALSE;
2631 
2632         for (i = 0; i < vl_max ; i++) {
2633             val8 = tvb_get_guint8(tvb, offset + i);
2634 
2635             if ((val8 == 0x09) || (val8 == 0x0A) || (val8 == 0x0D)) {
2636                 /* TAB, LF, CR */
2637             }
2638             else if ((val8 >= 0x20) && (val8 <= 0x7E)) {
2639                 /* No extended ASCII, 0-9, A-Z, a-z */
2640             }
2641             else if ((i == vl_max -1) && (val8 == 0x00)) {
2642                 /* Last Byte can be null*/
2643                 is_padded = TRUE;
2644             }
2645             else {
2646                 /* Here's the code */
2647                 is_string = FALSE;
2648             }
2649         }
2650 
2651         if (is_string) {
2652             vals = tvb_format_text(pinfo->pool, tvb, offset, (is_padded ? vl_max - 1 : vl_max));
2653             proto_tree_add_string(tree, hf_dcm_tag_value_str, tvb, offset, vl_max, vals);
2654 
2655             *tag_value = vals;
2656         }
2657         else {
2658             proto_tree_add_bytes_format_value(tree, hf_dcm_tag_value_byte, tvb, offset, vl_max, NULL, "%s", "(binary)");
2659 
2660             *tag_value = wmem_strdup(pinfo->pool, "(binary)");
2661         }
2662     }
2663     /* ---------------------------------------------------------------------------
2664        Smaller types. vl/vl_max are not used. Fixed item length from 2 to 8 bytes
2665        ---------------------------------------------------------------------------
2666     */
2667     else if (strncmp(vr, "AT", 2) == 0)  {
2668 
2669         /* Attribute Tag e.g. (0022,8866). 2*2 Bytes, Can have VM > 1 */
2670 
2671         guint16 at_grp;
2672         guint16 at_elm;
2673         gchar *at_value = "";
2674 
2675         /* In on capture the reported length for this tag was 2 bytes. And since vl_max is unsigned long, -3 caused it to be 2^32-1
2676            So make it at least one loop so set it to at least 4.
2677         */
2678 
2679         guint32 vm_item_len = 4;
2680         guint32 vm_item_count = dcm_vm_item_count(vl_max, vm_item_len);
2681 
2682         guint32 i = 0;
2683         while (i < vm_item_count) {
2684             at_grp = tvb_get_guint16(tvb, offset+ i*vm_item_len,   encoding);
2685             at_elm = tvb_get_guint16(tvb, offset+ i*vm_item_len+2, encoding);
2686 
2687             proto_tree_add_uint_format_value(tree, hf_dcm_tag_value_32u, tvb, offset + i*vm_item_len, vm_item_len,
2688                 (at_grp << 16) | at_elm, "%04x,%04x", at_grp, at_elm);
2689 
2690             at_value = wmem_strdup_printf(pinfo->pool,"%s(%04x,%04x)", at_value, at_grp, at_elm);
2691 
2692             i++;
2693         }
2694         *tag_value = at_value;
2695     }
2696     else if (strncmp(vr, "FL", 2) == 0)  {      /* Single Float. Can be VM > 1, but not yet supported */
2697 
2698         gfloat valf = tvb_get_ieee_float(tvb, offset, encoding);
2699 
2700         proto_tree_add_bytes_format_value(tree, hf_dcm_tag_value_byte, tvb, offset, 4, NULL, "%f", valf);
2701 
2702         *tag_value = wmem_strdup_printf(pinfo->pool, "%f", valf);
2703     }
2704     else if (strncmp(vr, "FD", 2) == 0)  {      /* Double Float. Can be VM > 1, but not yet supported */
2705 
2706         gdouble vald = tvb_get_ieee_double(tvb, offset, encoding);
2707 
2708         proto_tree_add_bytes_format_value(tree, hf_dcm_tag_value_byte, tvb, offset, 8, NULL, "%f", vald);
2709 
2710         *tag_value = wmem_strdup_printf(pinfo->pool, "%f", vald);
2711     }
2712     else if (strncmp(vr, "SL", 2) == 0)  {      /* Signed Long. Can be VM > 1, but not yet supported */
2713         gint32  val32;
2714 
2715         proto_tree_add_item_ret_int(tree, hf_dcm_tag_value_32s, tvb, offset, 4, encoding, &val32);
2716 
2717         *tag_value = wmem_strdup_printf(pinfo->pool, "%d", val32);
2718     }
2719     else if (strncmp(vr, "SS", 2) == 0)  {          /* Signed Short. Can be VM > 1, but not yet supported */
2720         gint32  val32;
2721 
2722         proto_tree_add_item_ret_int(tree, hf_dcm_tag_value_16s, tvb, offset, 2, encoding, &val32);
2723 
2724         *tag_value = wmem_strdup_printf(pinfo->pool, "%d", val32);
2725     }
2726     else if (strncmp(vr, "UL", 2) == 0)  {          /* Unsigned Long. Can be VM > 1, but not yet supported */
2727         guint32  val32;
2728 
2729         proto_tree_add_item_ret_uint(tree, hf_dcm_tag_value_32u, tvb, offset, 4, encoding, &val32);
2730 
2731         *tag_value = wmem_strdup_printf(pinfo->pool, "%u", val32);
2732     }
2733     else if (strncmp(vr, "US", 2) == 0)  {          /* Unsigned Short. Can be VM > 1, but not yet supported */
2734         const gchar *status_message = NULL;
2735         guint16     val16 = tvb_get_guint16(tvb, offset, encoding);
2736 
2737         if (grp == 0x0000 && elm == 0x0100) {
2738             /* This is a command */
2739             pdv->command = wmem_strdup(wmem_file_scope(), val_to_str(val16, dcm_cmd_vals, " "));
2740             *tag_value = pdv->command;
2741         }
2742         else if (grp == 0x0000 && elm == 0x0900) {
2743             /* This is a status message. If value is not 0x0000, add an expert info */
2744 
2745             status_message = dcm_rsp2str(val16);
2746             *tag_value = wmem_strdup_printf(pinfo->pool, "%s (0x%02x)", status_message, val16);
2747 
2748             if ((val16 & 0xFF00) == 0xFF00) {
2749                 /* C-FIND also has a 0xFF01 as a valid response */
2750                 pdv->is_pending = TRUE;
2751             }
2752             else if (val16 != 0x0000) {
2753                 /* Neither success nor pending */
2754                 pdv->is_warning = TRUE;
2755             }
2756 
2757             pdv->status = wmem_strdup(wmem_file_scope(), status_message);
2758 
2759         }
2760         else {
2761             *tag_value = wmem_strdup_printf(pinfo->pool, "%u", val16);
2762         }
2763 
2764         if (grp == 0x0000) {
2765             if (elm == 0x0110) {                /* (0000,0110) Message ID */
2766                 pdv->message_id = val16;
2767             }
2768             else if (elm == 0x0120) {           /* (0000,0120) Message ID Being Responded To */
2769                 pdv->message_id_resp = val16;
2770             }
2771             else if (elm == 0x1020) {           /* (0000,1020) Number of Remaining Sub-operations */
2772                 pdv->no_remaining = val16;
2773             }
2774             else if (elm == 0x1021) {           /* (0000,1021) Number of Completed Sub-operations */
2775                 pdv->no_completed = val16;
2776             }
2777             else if (elm == 0x1022) {           /* (0000,1022) Number of Failed Sub-operations  */
2778                 pdv->no_failed = val16;
2779             }
2780             else if (elm == 0x1023) {           /* (0000,1023) Number of Warning Sub-operations */
2781                 pdv->no_warning = val16;
2782             }
2783         }
2784 
2785         pitem = proto_tree_add_uint_format_value(tree, hf_dcm_tag_value_16u, tvb, offset, 2,
2786                     val16, "%s", *tag_value);
2787 
2788         if (pdv->is_warning && status_message) {
2789             expert_add_info(pinfo, pitem, &ei_dcm_status_msg);
2790         }
2791     }
2792     /* Invalid VR, can only occur with Explicit syntax */
2793     else {
2794         proto_tree_add_bytes_format_value(tree, hf_dcm_tag_value_byte, tvb, offset, vl_max,
2795             NULL, "%s", (vl > vl_max ? "" : "(unknown VR)"));
2796 
2797         *tag_value = wmem_strdup(pinfo->pool, "(unknown VR)");
2798     }
2799     offset += vl_max;
2800 
2801     return offset;
2802 
2803 }
2804 
2805 /*
2806 Return true, if the required size does not fit at position 'offset'.
2807 */
2808 static gboolean
dcm_tag_is_open(dcm_state_pdv_t * pdv,guint32 startpos,guint32 offset,guint32 endpos,guint32 size_required)2809 dcm_tag_is_open(dcm_state_pdv_t *pdv, guint32 startpos, guint32 offset, guint32 endpos, guint32 size_required)
2810 {
2811 
2812     if (offset + size_required > endpos) {
2813 
2814         pdv->open_tag.is_header_fragmented = TRUE;
2815         pdv->open_tag.len_decoded = endpos - startpos;
2816 
2817         return TRUE;
2818     }
2819     else {
2820         return FALSE;
2821     }
2822 }
2823 
2824 static dcm_tag_t*
dcm_tag_lookup(guint16 grp,guint16 elm)2825 dcm_tag_lookup(guint16 grp, guint16 elm)
2826 {
2827 
2828     static dcm_tag_t *tag_def = NULL;
2829 
2830     static dcm_tag_t tag_unknown         = { 0x00000000, "(unknown)", "UN", "1", 0, 0};
2831     static dcm_tag_t tag_private         = { 0x00000000, "Private Tag", "UN", "1", 0, 0 };
2832     static dcm_tag_t tag_private_grp_len = { 0x00000000, "Private Tag Group Length", "UL", "1", 0, 0 };
2833     static dcm_tag_t tag_grp_length      = { 0x00000000, "Group Length", "UL", "1", 0, 0 };
2834 
2835     /* Try a direct hit first before doing a masked search */
2836     tag_def = (dcm_tag_t *)wmem_map_lookup(dcm_tag_table, GUINT_TO_POINTER(((guint32)grp << 16) | elm));
2837 
2838     if (tag_def == NULL) {
2839 
2840         /* No match found */
2841         if ((grp & 0x0001) && (elm == 0x0000)) {
2842             tag_def = &tag_private_grp_len;
2843         }
2844         else if (grp & 0x0001) {
2845             tag_def = &tag_private;
2846         }
2847         else if (elm == 0x0000) {
2848             tag_def = &tag_grp_length;
2849         }
2850 
2851         /* There are a few tags that require a mask to be found */
2852         else if (((grp & 0xFF00) == 0x5000) || ((grp & 0xFF00) == 0x6000) || ((grp & 0xFF00) == 0x7F00)) {
2853             /* Do a special for groups 0x50xx, 0x60xx and 0x7Fxx */
2854             tag_def = (dcm_tag_t *)wmem_map_lookup(dcm_tag_table, GUINT_TO_POINTER((((guint32)grp & 0xFF00) << 16) | elm));
2855         }
2856         else if ((grp == 0x0020) && ((elm & 0xFF00) == 0x3100)) {
2857             tag_def = (dcm_tag_t *)wmem_map_lookup(dcm_tag_table, GUINT_TO_POINTER(((guint32)grp << 16) | (elm & 0xFF00)));
2858         }
2859         else if ((grp == 0x0028) && ((elm & 0xFF00) == 0x0400)) {
2860             /* This map was done to 0x041x */
2861             tag_def = (dcm_tag_t *)wmem_map_lookup(dcm_tag_table, GUINT_TO_POINTER(((guint32)grp << 16) | (elm & 0xFF0F) | 0x0010));
2862         }
2863         else if ((grp == 0x0028) && ((elm & 0xFF00) == 0x0800)) {
2864             tag_def = (dcm_tag_t *)wmem_map_lookup(dcm_tag_table, GUINT_TO_POINTER(((guint32)grp << 16) | (elm & 0xFF0F)));
2865         }
2866         else if (grp == 0x1000) {
2867             tag_def = (dcm_tag_t *)wmem_map_lookup(dcm_tag_table, GUINT_TO_POINTER(((guint32)grp << 16) | (elm & 0x000F)));
2868         }
2869         else if (grp == 0x1010) {
2870             tag_def = (dcm_tag_t *)wmem_map_lookup(dcm_tag_table, GUINT_TO_POINTER(((guint32)grp << 16) | (elm & 0x0000)));
2871         }
2872 
2873         if (tag_def == NULL) {
2874             /* Still no match found */
2875             tag_def = &tag_unknown;
2876         }
2877     }
2878 
2879     return tag_def;
2880 }
2881 
2882 static gchar*
dcm_tag_summary(packet_info * pinfo,guint16 grp,guint16 elm,guint32 vl,const gchar * tag_desc,const gchar * vr,gboolean is_retired,gboolean is_implicit)2883 dcm_tag_summary(packet_info *pinfo, guint16 grp, guint16 elm, guint32 vl, const gchar *tag_desc, const gchar *vr,
2884                 gboolean is_retired, gboolean is_implicit)
2885 {
2886 
2887     gchar *desc_mod;
2888     gchar *tag_vl;
2889     gchar *tag_sum;
2890 
2891     if (is_retired) {
2892         desc_mod = wmem_strdup_printf(pinfo->pool, "(Retired) %-35.35s", tag_desc);
2893     }
2894     else {
2895         desc_mod = wmem_strdup_printf(pinfo->pool, "%-45.45s", tag_desc);
2896     }
2897 
2898     if (vl == 0xFFFFFFFF) {
2899         tag_vl = wmem_strdup_printf(pinfo->pool, "%10.10s", "<udef>");
2900     }
2901     else {
2902         tag_vl = wmem_strdup_printf(pinfo->pool, "%10u", vl);           /* Show as dec */
2903     }
2904 
2905     if (is_implicit)    tag_sum = wmem_strdup_printf(pinfo->pool, "(%04x,%04x) %s %s", grp, elm, tag_vl, desc_mod);
2906     else                tag_sum = wmem_strdup_printf(pinfo->pool, "(%04x,%04x) %s %s [%s]", grp, elm, tag_vl, desc_mod, vr);
2907 
2908     return tag_sum;
2909 }
2910 
2911 /*
2912 Decode one tag. If it is a sequence or item start create a subtree. Returns new offset.
2913 http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html
2914 */
2915 static guint32
dissect_dcm_tag(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,dcm_state_pdv_t * pdv,guint32 offset,guint32 endpos,gboolean is_first_tag,const gchar ** tag_description,gboolean * end_of_seq_or_item)2916 dissect_dcm_tag(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
2917                 dcm_state_pdv_t *pdv, guint32 offset, guint32 endpos,
2918                 gboolean is_first_tag, const gchar **tag_description,
2919                 gboolean *end_of_seq_or_item)
2920 {
2921 
2922 
2923     proto_tree  *tag_ptree = NULL;      /* Tree for decoded tag details */
2924     proto_tree  *seq_ptree = NULL;      /* Possible subtree for sequences and items */
2925 
2926     proto_item  *tag_pitem = NULL;
2927     dcm_tag_t   *tag_def   = NULL;
2928 
2929     gint ett;
2930 
2931     const gchar *vr = NULL;
2932     gchar       *tag_value = "";      /* Tag Value converted to a string      */
2933     gchar       *tag_summary;
2934 
2935     guint32 vl = 0;
2936     guint16 vl_1 = 0;
2937     guint16 vl_2 = 0;
2938 
2939     guint32 offset_tag   = 0;           /* Remember offsets for tree, since the tree    */
2940     guint32 offset_vr    = 0;           /* header is created pretty late                */
2941     guint32 offset_vl    = 0;
2942 
2943     guint32 vl_max = 0;                 /* Max Value Length to Parse */
2944 
2945     guint16 grp = 0;
2946     guint16 elm = 0;
2947 
2948     guint32 len_decoded_remaing = 0;
2949 
2950     /* Decode the syntax a little more */
2951     guint32 encoding = (pdv->syntax == DCM_EBE) ? ENC_BIG_ENDIAN : ENC_LITTLE_ENDIAN;
2952     gboolean is_implicit = (pdv->syntax == DCM_ILE);
2953     gboolean is_vl_long = FALSE;            /* True for 4 Bytes length fields */
2954 
2955     gboolean is_sequence = FALSE;           /* True for Sequence Tags */
2956     gboolean is_item = FALSE;               /* True for Sequence Item Tags */
2957 
2958     *tag_description = NULL;                /* Reset description. It's wmem packet scope memory, so not really bad*/
2959 
2960     offset_tag = offset;
2961 
2962 
2963     if (pdv->prev && is_first_tag) {
2964         len_decoded_remaing = pdv->prev->open_tag.len_decoded;
2965     }
2966 
2967 
2968     /* Since we may have a fragmented header, check for every attribute,
2969        whether we have already decoded left-overs from the previous PDV.
2970        Since we have implicit & explicit syntax, copying the open tag to
2971        a buffer without decoding, would have caused tvb_get_xxtohs()
2972        implementations on the copy.
2973 
2974        An alternative approach would have been to resemble the PDVs first.
2975 
2976        The attempts to reassemble without named sources (to be implemented)
2977        were very sensitive to missing packets. In such a case, no packet
2978        of a PDV chain was decoded, not even the start.
2979 
2980        So for the time being, use this rather cumbersome approach.
2981 
2982        For every two bytes (PDV length are always a factor of 2)
2983        check whether we have enough data in the buffer and store the value
2984        accordingly. In the next frame check, whether we have decoded this yet.
2985     */
2986 
2987     /* Group */
2988     if (len_decoded_remaing >= 2) {
2989         grp = pdv->prev->open_tag.grp;
2990         len_decoded_remaing -= 2;
2991     }
2992     else {
2993 
2994         if (dcm_tag_is_open(pdv, offset_tag, offset, endpos, 2))
2995              return endpos; /* Exit if needed */
2996 
2997         grp = tvb_get_guint16(tvb, offset, encoding);
2998         offset += 2;
2999         pdv->open_tag.grp = grp;
3000     }
3001 
3002     /* Element */
3003     if (len_decoded_remaing >= 2) {
3004         elm = pdv->prev->open_tag.elm;
3005         len_decoded_remaing -= 2;
3006     }
3007     else {
3008 
3009         if (dcm_tag_is_open(pdv, offset_tag, offset, endpos, 2))
3010              return endpos;    /* Exit if needed */
3011 
3012         elm = tvb_get_guint16(tvb, offset, encoding);
3013         offset += 2;
3014         pdv->open_tag.elm = elm;
3015     }
3016 
3017     /* Find the best matching tag */
3018     tag_def = dcm_tag_lookup(grp, elm);
3019 
3020     /* Value Representation */
3021     offset_vr = offset;
3022     if ((grp == 0xFFFE) && (elm == 0xE000 || elm == 0xE00D || elm == 0xE0DD))  {
3023         /* Item start, Item Delimitation or Sequence Delimitation */
3024         vr = "UL";
3025         is_vl_long = TRUE;                          /* These tags always have a 4 byte length field */
3026     }
3027     else if (is_implicit) {
3028         /* Get VR from tag definition */
3029         vr = wmem_strdup(pinfo->pool, tag_def->vr);
3030         is_vl_long = TRUE;                          /* Implicit always has 4 byte length field */
3031     }
3032     else {
3033 
3034         if (len_decoded_remaing >= 2) {
3035             vr = wmem_strdup(pinfo->pool, pdv->prev->open_tag.vr);
3036             len_decoded_remaing -= 2;
3037         }
3038         else {
3039 
3040             /* Controlled exit, if VR does not fit. */
3041             if (dcm_tag_is_open(pdv, offset_tag, offset_vr, endpos, 2))
3042                 return endpos;
3043 
3044             vr = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset, 2, ENC_ASCII);
3045             offset += 2;
3046 
3047             g_free(pdv->open_tag.vr);
3048             pdv->open_tag.vr = g_strdup(vr);        /* needs to survive within a session */
3049         }
3050 
3051         if ((strcmp(vr, "OB") == 0) || (strcmp(vr, "OW") == 0) || (strcmp(vr, "OF") == 0) || (strcmp(vr, "OD") == 0) || (strcmp(vr, "OL") == 0) ||
3052             (strcmp(vr, "SQ") == 0) || (strcmp(vr, "UC") == 0) || (strcmp(vr, "UR") == 0) || (strcmp(vr, "UT") == 0) || (strcmp(vr, "UN") == 0)) {
3053             /* Part 5, Table 7.1-1 in the standard */
3054             /* Length is always 4 bytes: OB, OD, OF, OL, OW, SQ, UC, UR, UT or UN */
3055 
3056             is_vl_long = TRUE;
3057 
3058             /* Skip 2 Bytes */
3059             if (len_decoded_remaing >= 2) {
3060                 len_decoded_remaing -= 2;
3061             }
3062             else {
3063                 if (dcm_tag_is_open(pdv, offset_tag, offset_vr, endpos, 2))
3064                     return endpos;
3065                 offset += 2;
3066             }
3067         }
3068         else {
3069             is_vl_long = FALSE;
3070         }
3071     }
3072 
3073 
3074     /* Value Length. This is rather cumbersome code to get a 4 byte length, but in the
3075        fragmented case, we have 2*2 bytes. So always use that pattern
3076     */
3077 
3078     offset_vl = offset;
3079     if (len_decoded_remaing >= 2) {
3080         vl_1 = pdv->prev->open_tag.vl_1;
3081         len_decoded_remaing -= 2;
3082     }
3083     else {
3084 
3085         if (dcm_tag_is_open(pdv, offset_tag, offset_vl, endpos, 2))
3086             return endpos;
3087         vl_1 = tvb_get_guint16(tvb, offset, encoding);
3088         offset += 2;
3089         pdv->open_tag.vl_1 = vl_1;
3090     }
3091 
3092     if (is_vl_long) {
3093 
3094         if (len_decoded_remaing >= 2) {
3095             vl_2 = pdv->prev->open_tag.vl_2;
3096         }
3097         else {
3098 
3099             if (dcm_tag_is_open(pdv, offset_tag, offset_vl+2, endpos, 2))
3100                 return endpos;
3101             vl_2 = tvb_get_guint16(tvb, offset, encoding);
3102             offset += 2;
3103             pdv->open_tag.vl_2 = vl_2;
3104         }
3105 
3106         if (encoding == ENC_LITTLE_ENDIAN)   vl = (vl_2 << 16) + vl_1;
3107         else                    vl = (vl_1 << 16) + vl_2;
3108     }
3109     else {
3110         vl = vl_1;
3111     }
3112 
3113     /* Now we have most of the information, except for sequences and items with undefined
3114        length :-/. But, whether we know the length or not, we now need to create the tree
3115        item and subtree, before we can loop into sequences and items
3116 
3117        Display the information we collected so far. Don't wait until the value is parsed,
3118        because that parsing might cause an exception. If that happens within a sequence,
3119        the sequence tag would not show up with the value
3120 
3121        Use different ett_ for Sequences & Items, so that fold/unfold state makes sense
3122     */
3123 
3124     tag_summary = dcm_tag_summary(pinfo, grp, elm, vl, tag_def->description, vr, tag_def->is_retired, is_implicit);
3125     is_sequence = (strcmp(vr, "SQ") == 0) || (vl == 0xFFFFFFFF);
3126     is_item = ((grp == 0xFFFE) && (elm == 0xE000));
3127 
3128     if ((is_sequence | is_item) &&  global_dcm_seq_subtree) {
3129         ett = is_sequence ? ett_dcm_data_seq : ett_dcm_data_item;
3130     }
3131     else {
3132         ett = ett_dcm_data_tag;
3133     }
3134 
3135     if (vl == 0xFFFFFFFF) {
3136         /* 'Just' mark header as the length of the item */
3137         tag_ptree = proto_tree_add_subtree(tree, tvb, offset_tag, offset - offset_tag, ett, &tag_pitem, tag_summary);
3138         vl_max = 0;         /* We don't know who long this sequence/item is */
3139     }
3140     else if (offset + vl <= endpos) {
3141         /* Show real length of item */
3142         tag_ptree = proto_tree_add_subtree(tree, tvb, offset_tag, offset + vl - offset_tag, ett, &tag_pitem, tag_summary);
3143         vl_max = vl;
3144     }
3145     else {
3146         /* Value is longer than what we have in the PDV, -> we do have a OPEN tag */
3147         tag_ptree = proto_tree_add_subtree(tree, tvb, offset_tag, endpos - offset_tag, ett, &tag_pitem, tag_summary);
3148         vl_max = endpos - offset;
3149     }
3150 
3151     /* If you are going to touch the following 25 lines, make sure you reserve a few hours to go
3152         through both display options and check for proper tree display :-)
3153     */
3154     if (is_sequence | is_item) {
3155 
3156         if (global_dcm_seq_subtree) {
3157             /* Use different ett_ for Sequences & Items, so that fold/unfold state makes sense */
3158             seq_ptree = tag_ptree;
3159             if (!global_dcm_tag_subtree) {
3160                 tag_ptree = NULL;
3161             }
3162         }
3163         else {
3164             seq_ptree = tree;
3165             if (!global_dcm_tag_subtree) {
3166                 tag_ptree = NULL;
3167             }
3168         }
3169     }
3170     else {
3171         /* For tags */
3172         if (!global_dcm_tag_subtree) {
3173             tag_ptree = NULL;
3174         }
3175     }
3176 
3177     /*  ---------------------------------------------------------------
3178         Tag details as separate items
3179         ---------------------------------------------------------------
3180     */
3181 
3182     proto_tree_add_uint_format_value(tag_ptree, hf_dcm_tag, tvb, offset_tag, 4,
3183         (grp << 16) | elm, "%04x,%04x (%s)", grp, elm, tag_def->description);
3184 
3185     /* Add VR to tag detail, except for sequence items */
3186     if (!is_item)  {
3187         if (is_implicit) {
3188             /* Select header, since no VR is present in implicit syntax */
3189             proto_tree_add_string(tag_ptree, hf_dcm_tag_vr, tvb, offset_tag, 4, vr);
3190         }
3191         else {
3192             proto_tree_add_string(tag_ptree, hf_dcm_tag_vr, tvb, offset_vr,  2, vr);
3193         }
3194     }
3195 
3196     /* Add length to tag detail */
3197     proto_tree_add_uint(tag_ptree, hf_dcm_tag_vl, tvb, offset_vl, (is_vl_long ? 4 : 2), vl);
3198 
3199 
3200     /*  ---------------------------------------------------------------
3201         Finally the Tag Value
3202         ---------------------------------------------------------------
3203     */
3204     if ((is_sequence || is_item) && (vl > 0)) {
3205         /* Sequence or Item Start */
3206 
3207         guint32 endpos_item = 0;
3208         gboolean local_end_of_seq_or_item = FALSE;
3209         gboolean is_first_desc = TRUE;
3210 
3211         const gchar *item_description = NULL;       /* Will be allocated as wmem packet scope memory in dissect_dcm_tag() */
3212 
3213         if (vl == 0xFFFFFFFF) {
3214             /* Undefined length */
3215 
3216             while ((!local_end_of_seq_or_item) && (!pdv->open_tag.is_header_fragmented) && (offset < endpos)) {
3217 
3218                 offset = dissect_dcm_tag(tvb, pinfo, seq_ptree, pdv, offset, endpos, FALSE, &item_description, &local_end_of_seq_or_item);
3219 
3220                 if (item_description && global_dcm_seq_subtree) {
3221                     proto_item_append_text(tag_pitem, (is_first_desc ? " %s" : ", %s"), item_description);
3222                     is_first_desc = FALSE;
3223                 }
3224             }
3225         }
3226         else {
3227             /* Defined length */
3228             endpos_item = offset + vl_max;
3229 
3230             while (offset < endpos_item) {
3231 
3232                 offset = dissect_dcm_tag(tvb, pinfo, seq_ptree, pdv, offset, endpos_item, FALSE, &item_description, &local_end_of_seq_or_item);
3233 
3234                 if (item_description && global_dcm_seq_subtree) {
3235                     proto_item_append_text(tag_pitem, (is_first_desc ? " %s" : ", %s"), item_description);
3236                     is_first_desc = FALSE;
3237                 }
3238             }
3239         }
3240     } /*  if ((is_sequence || is_item) && (vl > 0)) */
3241     else if ((grp == 0xFFFE) && (elm == 0xE00D)) {
3242         /* Item delimitation for items with undefined length */
3243         *end_of_seq_or_item = TRUE;
3244     }
3245     else if ((grp == 0xFFFE) && (elm == 0xE0DD)) {
3246         /* Sequence delimitation for sequences with undefined length */
3247         *end_of_seq_or_item = TRUE;
3248     }
3249     else if (vl == 0) {
3250         /* No value for this tag */
3251 
3252         /*  The following copy is needed. tag_value is post processed with g_strstrip()
3253             and that one will crash the whole application, when a constant is used.
3254         */
3255 
3256         tag_value = wmem_strdup(pinfo->pool, "<Empty>");
3257     }
3258     else if (vl > vl_max) {
3259         /* Tag is longer than the PDV/PDU. Don't perform any decoding */
3260 
3261         gchar *tag_desc;
3262 
3263         proto_tree_add_bytes_format(tag_ptree, hf_dcm_tag_value_byte, tvb, offset, vl_max,
3264             NULL, "%-8.8sBytes %d - %d [start]", "Value:", 1, vl_max);
3265 
3266         tag_value = wmem_strdup_printf(pinfo->pool, "<Bytes %d - %d, start>", 1, vl_max);
3267         offset += vl_max;
3268 
3269         /*  Save the needed data for reuse, and subsequent packets
3270             This will leak a little within the session.
3271 
3272             But since we may have tags being closed and reopen in the same PDV
3273             we will always need to store this
3274         */
3275 
3276         tag_desc = dcm_tag_summary(pinfo, grp, elm, vl, tag_def->description, vr, tag_def->is_retired, is_implicit);
3277 
3278         if (pdv->open_tag.desc == NULL) {
3279             pdv->open_tag.is_value_fragmented = TRUE;
3280             pdv->open_tag.desc = wmem_strdup(wmem_file_scope(), tag_desc);
3281             pdv->open_tag.len_total = vl;
3282             pdv->open_tag.len_remaining = vl - vl_max;
3283         }
3284     }
3285     else {
3286         /* Regular value. Identify the type, decode and display */
3287 
3288         offset = dissect_dcm_tag_value(tvb, pinfo, tag_ptree, pdv, offset, grp, elm, vl, vl_max, vr, &tag_value);
3289 
3290         /* -------------------------------------------------------------
3291            We have decoded the value. Now store those tags of interest
3292            -------------------------------------------------------------
3293         */
3294 
3295         /* Store SOP Class and Instance UID in first PDV of this object */
3296         if (grp == 0x0008 && elm == 0x0016) {
3297             dcm_state_pdv_get_obj_start(pdv)->sop_class_uid = wmem_strdup(wmem_file_scope(), tag_value);
3298         }
3299         else if (grp == 0x0008 && elm == 0x0018) {
3300             dcm_state_pdv_get_obj_start(pdv)->sop_instance_uid = wmem_strdup(wmem_file_scope(), tag_value);
3301         }
3302         else if (grp == 0x0000 && elm == 0x0100) {
3303             /* This is the command tag -> overwrite existing PDV description */
3304             pdv->desc = wmem_strdup(wmem_file_scope(), tag_value);
3305         }
3306     }
3307 
3308 
3309     /* -------------------------------------------------------------------
3310        Add the value to the already constructed item
3311        -------------------------------------------------------------------
3312     */
3313 
3314     proto_item_append_text(tag_pitem, " %s", tag_value);
3315 
3316     if (tag_def->add_to_summary) {
3317         *tag_description = wmem_strdup(pinfo->pool, g_strstrip(tag_value));
3318     }
3319 
3320     return offset;
3321 }
3322 
3323 /*
3324 'Decode' open tags from previous PDV. It mostly ends in 'continuation' or 'end' in the description.
3325 */
3326 static guint32
dissect_dcm_tag_open(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,dcm_state_pdv_t * pdv,guint32 offset,guint32 endpos,gboolean * is_first_tag)3327 dissect_dcm_tag_open(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
3328                 dcm_state_pdv_t *pdv, guint32 offset, guint32 endpos, gboolean *is_first_tag)
3329 {
3330 
3331     proto_item *pitem = NULL;
3332 
3333     guint32 tag_value_fragment_len = 0;
3334 
3335     if ((pdv->prev) && (pdv->prev->open_tag.len_remaining > 0))  {
3336         /* Not first PDV in the given presentation context (Those don't have remaining data to parse :-) */
3337         /* And previous PDV has left overs, i.e. this is a continuation PDV */
3338 
3339         if (endpos - offset >= pdv->prev->open_tag.len_remaining) {
3340             /*
3341                Remaining bytes are equal or more than we expect for the open tag
3342                Finally reach the end of this tag. Don't touch the open_tag structure
3343                of this PDV, as we may see a new open tag at the end
3344             */
3345             tag_value_fragment_len = pdv->prev->open_tag.len_remaining;
3346             pdv->is_corrupt = FALSE;
3347         }
3348         else if (pdv->is_flagvalid && pdv->is_last_fragment) {
3349             /*
3350               The tag is not yet complete, however, the flag indicates that it should be
3351               Therefore end this tag and issue an expert_add_info. Don't touch the
3352               open_tag structure of this PDV, as we may see a new open tag at the end
3353             */
3354             tag_value_fragment_len = endpos - offset;
3355             pdv->is_corrupt = TRUE;
3356         }
3357         else {
3358             /*
3359              * More to do for this tag
3360              */
3361             tag_value_fragment_len = endpos - offset;
3362 
3363             /* Set data in current PDV structure */
3364             if (!pdv->open_tag.is_value_fragmented)  {
3365                 /* No need to do it twice or more */
3366 
3367                 pdv->open_tag.is_value_fragmented = TRUE;
3368                 pdv->open_tag.len_total = pdv->prev->open_tag.len_total;
3369                 pdv->open_tag.len_remaining = pdv->prev->open_tag.len_remaining - tag_value_fragment_len;
3370                 pdv->open_tag.desc = wmem_strdup(wmem_file_scope(), pdv->prev->open_tag.desc);
3371 
3372             }
3373             pdv->is_corrupt = FALSE;
3374         }
3375 
3376         if (pdv->is_corrupt) {
3377             pitem = proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
3378                 offset, tag_value_fragment_len, NULL,
3379                 "%s <incomplete>", pdv->prev->open_tag.desc);
3380 
3381             expert_add_info(pinfo, pitem, &ei_dcm_data_tag);
3382 
3383         }
3384         else {
3385             proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
3386                 offset, tag_value_fragment_len, NULL,
3387                 "%s <Bytes %d - %d, %s>", pdv->prev->open_tag.desc,
3388                 pdv->prev->open_tag.len_total - pdv->prev->open_tag.len_remaining + 1,
3389                 pdv->prev->open_tag.len_total - pdv->prev->open_tag.len_remaining + tag_value_fragment_len,
3390                 (pdv->prev->open_tag.len_remaining > tag_value_fragment_len ? "continuation" : "end") );
3391         }
3392 
3393         offset += tag_value_fragment_len;
3394         *is_first_tag = FALSE;
3395     }
3396 
3397     return offset;
3398 }
3399 
3400 /*
3401 Decode the tag section inside a PDV. This can be a single combined dataset
3402 or DICOM natively split PDVs. Therefore it needs to resume previously opened tags.
3403 For data PDVs, only process tags when tree is set or listening to export objects tap.
3404 For command PDVs, process all tags.
3405 On export copy the content to the export buffer.
3406 */
3407 static guint32
dissect_dcm_pdv_body(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,dcm_state_assoc_t * assoc,dcm_state_pdv_t * pdv,guint32 offset,guint32 pdv_body_len,gchar ** pdv_description)3408 dissect_dcm_pdv_body(
3409         tvbuff_t *tvb,
3410         packet_info *pinfo,
3411         proto_tree *tree,
3412         dcm_state_assoc_t *assoc,
3413         dcm_state_pdv_t *pdv,
3414         guint32 offset,
3415         guint32 pdv_body_len,
3416         gchar **pdv_description)
3417 {
3418     const gchar *tag_value = NULL;
3419     gboolean dummy = FALSE;
3420     guint32 startpos = offset;
3421     guint32 endpos = 0;
3422 
3423     endpos = offset + pdv_body_len;
3424 
3425     if (pdv->is_command || tree || have_tap_listener(dicom_eo_tap)) {
3426         /* Performance optimization starts here. Don't put any COL_INFO related stuff in here */
3427 
3428         if (pdv->syntax == DCM_UNK) {
3429             /* Eventually, we will have a syntax detector. Until then, don't decode */
3430 
3431             proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
3432                 offset, pdv_body_len, NULL,
3433                 "(%04x,%04x) %-8x Unparsed data", 0, 0, pdv_body_len);
3434         }
3435         else {
3436 
3437             gboolean is_first_tag = TRUE;
3438 
3439             /* Treat the left overs */
3440             offset = dissect_dcm_tag_open(tvb, pinfo, tree, pdv, offset, endpos, &is_first_tag);
3441 
3442             /* Decode all tags, sequences and items in this PDV recursively */
3443             while (offset < endpos) {
3444                 offset = dissect_dcm_tag(tvb, pinfo, tree, pdv, offset, endpos, is_first_tag, &tag_value, &dummy);
3445                 is_first_tag = FALSE;
3446             }
3447         }
3448     }
3449 
3450     *pdv_description = pdv->desc;
3451 
3452     if (pdv->is_command) {
3453 
3454         if (pdv->is_warning) {
3455             if (pdv->comment) {
3456                 *pdv_description = wmem_strdup_printf(pinfo->pool, "%s (%s, %s)", pdv->desc, pdv->status, pdv->comment);
3457             }
3458             else {
3459                 *pdv_description = wmem_strdup_printf(pinfo->pool, "%s (%s)", pdv->desc, pdv->status);
3460             }
3461 
3462         }
3463         else if (global_dcm_cmd_details) {
3464             /* Show command details in header */
3465 
3466             if (pdv->message_id > 0) {
3467                 *pdv_description = wmem_strdup_printf(pinfo->pool, "%s ID=%d", pdv->desc, pdv->message_id);
3468             }
3469             else if (pdv->message_id_resp > 0) {
3470 
3471                 *pdv_description = wmem_strdup_printf(pinfo->pool, "%s ID=%d", pdv->desc, pdv->message_id_resp);
3472 
3473                 if (pdv->no_completed > 0) {
3474                     *pdv_description = wmem_strdup_printf(pinfo->pool, "%s C=%d", *pdv_description, pdv->no_completed);
3475                 }
3476                 if (pdv->no_remaining > 0) {
3477                     *pdv_description = wmem_strdup_printf(pinfo->pool, "%s R=%d", *pdv_description, pdv->no_remaining);
3478                 }
3479                 if (pdv->no_warning > 0) {
3480                     *pdv_description = wmem_strdup_printf(pinfo->pool, "%s W=%d", *pdv_description, pdv->no_warning);
3481                 }
3482                 if (pdv->no_failed > 0) {
3483                     *pdv_description = wmem_strdup_printf(pinfo->pool, "%s F=%d", *pdv_description, pdv->no_failed);
3484                 }
3485                 if (!pdv->is_pending && pdv->status)
3486                 {
3487                     *pdv_description = wmem_strdup_printf(pinfo->pool, "%s (%s)", *pdv_description, pdv->status);
3488                 }
3489             }
3490         }
3491     }
3492 
3493     if (have_tap_listener(dicom_eo_tap)) {
3494 
3495         if (pdv->data_len == 0) {
3496             /* Copy pure DICOM data to buffer, without PDV flags
3497                Packet scope for the memory allocation is too small, since we may have PDV in different tvb.
3498                Therefore check if this was already done.
3499             */
3500             pdv->data = wmem_alloc0(wmem_file_scope(), pdv_body_len);
3501             pdv->data_len = pdv_body_len;
3502             tvb_memcpy(tvb, pdv->data, startpos, pdv_body_len);
3503         }
3504         if ((pdv_body_len > 0) && (pdv->is_last_fragment)) {
3505             /* At the last segment, merge all related previous PDVs and copy to export buffer */
3506             dcm_export_create_object(pinfo, assoc, pdv);
3507         }
3508     }
3509 
3510     return endpos;
3511 }
3512 
3513 /*
3514 Handle one PDV inside a data PDU. When needed, perform the reassembly of PDV fragments.
3515 PDV fragments are different from TCP fragmentation.
3516 Create PDV object when needed.
3517 Return pdv_description to be used e.g. in COL_INFO.
3518 */
3519 static guint32
dissect_dcm_pdv_fragmented(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,dcm_state_assoc_t * assoc,guint32 offset,guint32 pdv_len,gchar ** pdv_description)3520 dissect_dcm_pdv_fragmented(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
3521                 dcm_state_assoc_t *assoc, guint32 offset, guint32 pdv_len, gchar **pdv_description)
3522 {
3523 
3524     conversation_t  *conv = NULL;
3525 
3526     dcm_state_pdv_t *pdv = NULL;
3527 
3528     tvbuff_t *combined_tvb = NULL;
3529     fragment_head *head = NULL;
3530 
3531     guint32 reassembly_id;
3532     guint32 pdv_body_len;
3533 
3534     pdv_body_len = pdv_len-2;
3535 
3536     /* Dissect Context ID, Find PDV object, Decode Command/Data flag and More Fragments flag */
3537     offset = dissect_dcm_pdv_header(tvb, pinfo, tree, assoc, offset, &pdv);
3538 
3539     if (global_dcm_reassemble)
3540     {
3541         /* Combine the different PDVs. This is the default preference and useful in most scenarios.
3542            This will create one 'huge' PDV. E.g. a CT image will fits in one buffer.
3543         */
3544         conv = find_conversation_pinfo(pinfo, 0);
3545 
3546         /* Try to create somewhat unique ID.
3547            Include the conversation index, to separate TCP session
3548            Include bits from the reassembly number in the current Presentation
3549            Context (that we track ourselves) in order to distinguish between
3550            PDV fragments from the same frame but different reassemblies.
3551         */
3552         DISSECTOR_ASSERT(conv);
3553 
3554         /* The following expression seems to executed late in VS2017 in 'RelWithDebInf'.
3555            Therefore it may appear as 0 at first
3556         */
3557         reassembly_id = (((conv->conv_index) & 0x000FFFFF) << 12) +
3558                         ((guint32)(pdv->pctx_id) << 4) + ((guint32)(pdv->reassembly_id & 0xF));
3559 
3560         /* This one will chain the packets until 'is_last_fragment' */
3561         head = fragment_add_seq_next(
3562             &dcm_pdv_reassembly_table,
3563             tvb,
3564             offset,
3565             pinfo,
3566             reassembly_id,
3567             NULL,
3568             pdv_body_len,
3569             !(pdv->is_last_fragment));
3570 
3571         if (head && (head->next == NULL)) {
3572             /* Was not really fragmented, therefore use 'conventional' decoding.
3573                process_reassembled_data() does not cope with two PDVs in the same frame, therefore catch it here
3574             */
3575 
3576             offset = dissect_dcm_pdv_body(tvb, pinfo, tree, assoc, pdv, offset, pdv_body_len, pdv_description);
3577         }
3578         else
3579         {
3580             /* Will return a complete buffer, once last fragment is hit.
3581                The description is not used in packet-dcm. COL_INFO is set specifically in dissect_dcm_pdu()
3582             */
3583             combined_tvb = process_reassembled_data(
3584                 tvb,
3585                 offset,
3586                 pinfo,
3587                 "Reassembled PDV",
3588                 head,
3589                 &dcm_pdv_fragment_items,
3590                 NULL,
3591                 tree);
3592 
3593             if (combined_tvb == NULL) {
3594                 /* Just show this as a fragment */
3595 
3596                 if (head && head->reassembled_in != pinfo->num) {
3597 
3598                     if (pdv->desc) {
3599                         /* We know the presentation context already */
3600                         *pdv_description = wmem_strdup_printf(pinfo->pool, "%s (reassembled in #%u)", pdv->desc, head->reassembled_in);
3601                     }
3602                     else {
3603                         /* Decoding of the presentation context did not occur yet or did not succeed */
3604                         *pdv_description = wmem_strdup_printf(pinfo->pool, "PDV Fragment (reassembled in #%u)", head->reassembled_in);
3605                     }
3606                 }
3607                 else {
3608                     /* We don't know the last fragment yet (and/or we'll never see it).
3609                        This can happen, e.g. when TCP packet arrive our of order.
3610                     */
3611                     *pdv_description = wmem_strdup(pinfo->pool, "PDV Fragment");
3612                 }
3613 
3614                 offset += pdv_body_len;
3615             }
3616             else {
3617                 /* Decode reassembled data. This needs to be += */
3618                 offset += dissect_dcm_pdv_body(combined_tvb, pinfo, tree, assoc, pdv, 0, tvb_captured_length(combined_tvb), pdv_description);
3619             }
3620         }
3621     }
3622     else {
3623         /* Do not reassemble DICOM PDVs, i.e. decode PDVs one by one.
3624            This may be useful when troubleshooting PDU length issues,
3625            or to better understand the PDV split.
3626            The tag level decoding is more challenging, as leftovers need
3627            to be displayed adequately. Not a big deal for binary values.
3628         */
3629         offset = dissect_dcm_pdv_body(tvb, pinfo, tree, assoc, pdv, offset, pdv_body_len, pdv_description);
3630     }
3631 
3632     return offset;
3633 }
3634 
3635 static guint32
dissect_dcm_pdu_data(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,dcm_state_assoc_t * assoc,guint32 offset,guint32 pdu_len,gchar ** pdu_data_description)3636 dissect_dcm_pdu_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
3637                      dcm_state_assoc_t *assoc, guint32 offset, guint32 pdu_len, gchar **pdu_data_description)
3638 {
3639 
3640     /*  04 P-DATA-TF
3641          1  1 reserved
3642          2  4 length
3643               - (1+) presentation data value (PDV) items
3644          6  4 length
3645         10  1 Presentation Context ID (odd ints 1 - 255)
3646               - PDV
3647         11  1 header
3648               0x01 if set, contains Message Command info, else Message Data
3649               0x02 if set, contains last fragment
3650     */
3651 
3652     proto_tree *pdv_ptree;      /* Tree for item details */
3653     proto_item *pdv_pitem, *pdvlen_item;
3654 
3655     gchar  *buf_desc = NULL;            /* PDU description */
3656     gchar  *pdv_description = NULL;
3657 
3658     gboolean first_pdv = TRUE;
3659 
3660     guint32 endpos = 0;
3661     guint32 pdv_len = 0;
3662 
3663     endpos = offset + pdu_len;
3664 
3665     /* Loop through multiple PDVs */
3666     while (offset < endpos) {
3667 
3668         pdv_len = tvb_get_ntohl(tvb, offset);
3669 
3670         pdv_ptree = proto_tree_add_subtree(tree, tvb, offset, pdv_len+4, ett_dcm_data_pdv, &pdv_pitem, "PDV");
3671 
3672         pdvlen_item = proto_tree_add_item(pdv_ptree, hf_dcm_pdv_len, tvb, offset, 4, ENC_BIG_ENDIAN);
3673         offset += 4;
3674 
3675         if ((pdv_len + 4 > pdu_len)  || (pdv_len + 4 < pdv_len)) {
3676             expert_add_info_format(pinfo, pdvlen_item, &ei_dcm_pdv_len, "Invalid PDV length (too large)");
3677             return endpos;
3678         }
3679         else if (pdv_len <= 2) {
3680             expert_add_info_format(pinfo, pdvlen_item, &ei_dcm_pdv_len, "Invalid PDV length (too small)");
3681             return endpos;
3682         }
3683         else if (((pdv_len >> 1) << 1) != pdv_len) {
3684             expert_add_info_format(pinfo, pdvlen_item, &ei_dcm_pdv_len, "Invalid PDV length (not even)");
3685             return endpos;
3686         }
3687 
3688         offset = dissect_dcm_pdv_fragmented(tvb, pinfo, pdv_ptree, assoc, offset, pdv_len, &pdv_description);
3689 
3690         /* The following doesn't seem to work anymore */
3691         if (pdv_description) {
3692             if (first_pdv) {
3693                 buf_desc = wmem_strdup(pinfo->pool, pdv_description);
3694             }
3695             else {
3696                 buf_desc = wmem_strdup_printf(pinfo->pool, "%s, %s", buf_desc, pdv_description);
3697             }
3698         }
3699 
3700         proto_item_append_text(pdv_pitem, ", %s", pdv_description);
3701         first_pdv=FALSE;
3702 
3703     }
3704 
3705     *pdu_data_description = buf_desc;
3706 
3707     return offset;
3708 }
3709 
3710 
3711 /*
3712 Test for DICOM traffic.
3713 
3714 - Minimum 10 Bytes
3715 - Look for the association request
3716 - Check PDU size vs TCP payload size
3717 
3718 Since used in heuristic mode, be picky for performance reasons.
3719 We are called in static mode, once we decoded the association request and called conversation_set_dissector()
3720 They we can be more liberal on the packet selection
3721 */
3722 static gboolean
test_dcm(tvbuff_t * tvb)3723 test_dcm(tvbuff_t *tvb)
3724 {
3725 
3726     guint8  pdu_type;
3727     guint32 pdu_len;
3728     guint16 vers;
3729 
3730     /*
3731     Ensure that the tvb_captured_length is big enough before fetching the values.
3732     Otherwise it can trigger an exception during the heuristic check,
3733     preventing next heuristic dissectors from being called
3734 
3735     tvb_reported_length() is the real size of the packet as transmitted on the wire
3736     tvb_captured_length() is the number of bytes captured (so you always have captured <= reported).
3737 
3738     The 10 bytes represent an association request header including the 2 reserved bytes not used below
3739     In the captures at hand, the parsing result was equal.
3740     */
3741 
3742     if (tvb_captured_length(tvb) < 8) {
3743         return FALSE;
3744     }
3745     if (tvb_reported_length(tvb) < 10) {
3746         return FALSE;
3747     }
3748 
3749     pdu_type = tvb_get_guint8(tvb, 0);
3750     pdu_len = tvb_get_ntohl(tvb, 2);
3751     vers = tvb_get_ntohs(tvb, 6);
3752 
3753     /* Exit, if not an association request at version 1 */
3754     if (!(pdu_type == 1 && vers == 1)) {
3755         return FALSE;
3756     }
3757 
3758     /* Exit if TCP payload is bigger than PDU length (plus header)
3759     OK for PRESENTATION_DATA, questionable for ASSOCIATION requests
3760     */
3761     if (tvb_reported_length(tvb) > pdu_len + 6) {
3762         return FALSE;
3763     }
3764 
3765     return TRUE;
3766 }
3767 
3768 /*
3769 Main function to decode DICOM traffic. Supports reassembly of TCP packets.
3770 */
3771 static int
dissect_dcm_main(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,gboolean is_port_static)3772 dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean is_port_static)
3773 {
3774 
3775     guint8  pdu_type = 0;
3776     guint32 pdu_start = 0;
3777     guint32 pdu_len = 0;
3778     guint32 tlen = 0;
3779 
3780     int offset = 0;
3781 
3782     /*
3783         TCP packets are assembled well by wireshark in conjunction with the dissectors.
3784 
3785         Therefore, we will only see properly aligned PDUs, at the beginning of the buffer.
3786         So if the buffer does not start with the PDU header, it's not DICOM traffic.
3787 
3788         Do the byte checking as early as possible.
3789         The heuristic hook requires an association request
3790 
3791         DICOM PDU are nice, but need to be managed
3792 
3793         We can have any combination:
3794         - One or more DICOM PDU per TCP packet
3795         - PDU split over different TCP packets
3796         - And both together, i.e. some complete PDUs and then a fraction of a new PDU in a TCP packet
3797 
3798         This function will handle multiple PDUs per TCP packet and will ask for more data,
3799         if the last PDU does not fit
3800 
3801         It does not reassemble fragmented PDVs by purpose, since the Tag Value parsing needs to be done
3802         per Tag, and PDU recombination here would
3803         a) need to eliminate PDU/PDV/Ctx header (12 bytes)
3804         b) not show the true DICOM logic in transfer
3805 
3806         The length check is tricky. If not a PDV continuation, 10 Bytes are required. For PDV continuation
3807         anything seems to be possible, depending on the buffer alignment of the sending process.
3808 
3809     */
3810 
3811     tlen = tvb_reported_length(tvb);
3812 
3813     pdu_type = tvb_get_guint8(tvb, 0);
3814     if (pdu_type == 0 || pdu_type > 7)          /* Wrong PDU type. 'Or' is slightly more efficient than 'and' */
3815         return 0;                               /* No bytes taken from the stack */
3816 
3817     if (is_port_static) {
3818         /* Port is defined explicitly, or association request was previously found successfully.
3819            Be more tolerant on minimum packet size. Also accept < 6
3820         */
3821 
3822         if (tlen < 6) {
3823             /* we need 6 bytes at least to get PDU length */
3824             pinfo->desegment_offset = offset;
3825             pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
3826             return tvb_captured_length(tvb);
3827         }
3828     }
3829 
3830 
3831     /* Passing this point, we should always have tlen >= 6 */
3832 
3833     pdu_len = tvb_get_ntohl(tvb, 2);
3834     if (pdu_len < 4)                /* The smallest PDUs are ASSOC Rejects & Release messages */
3835         return 0;
3836 
3837     /* Mark it. This is a DICOM packet */
3838 
3839     col_set_str(pinfo->cinfo, COL_PROTOCOL, "DICOM");
3840 
3841      /* Process all PDUs in the buffer */
3842     while (pdu_start < tlen) {
3843         guint32 old_pdu_start;
3844 
3845         if ((pdu_len+6) > (tlen-offset)) {
3846 
3847             /*  PDU is larger than the remaining packet (buffer), therefore request whole PDU
3848                 The next time this function is called, tlen will be equal to pdu_len
3849             */
3850 
3851             pinfo->desegment_offset = offset;
3852             pinfo->desegment_len = (pdu_len+6) - (tlen-offset);
3853             return tvb_captured_length(tvb);
3854         }
3855 
3856         /* Process a whole PDU */
3857         offset=dissect_dcm_pdu(tvb, pinfo, tree, pdu_start);
3858 
3859         /* Next PDU */
3860         old_pdu_start = pdu_start;
3861         pdu_start =  pdu_start + pdu_len + 6;
3862         if (pdu_start <= old_pdu_start) {
3863             expert_add_info_format(pinfo, NULL, &ei_dcm_invalid_pdu_length, "Invalid PDU length (%u)", pdu_len);
3864             break;
3865         }
3866 
3867         if (pdu_start < tlen - 6) {
3868             /* we got at least 6 bytes of the next PDU still in the buffer */
3869              pdu_len = tvb_get_ntohl(tvb, pdu_start+2);
3870         }
3871         else {
3872             pdu_len = 0;
3873         }
3874     }
3875     return offset;
3876 }
3877 
3878 /*
3879 Callback function used to register
3880 */
3881 static int
dissect_dcm_static(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)3882 dissect_dcm_static(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
3883 {
3884     /* Less checking on ports that match */
3885     return dissect_dcm_main(tvb, pinfo, tree, TRUE);
3886 }
3887 
3888 /*
3889 Test for an Association Request. Decode, when successful.
3890 */
3891 static gboolean
dissect_dcm_heuristic(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)3892 dissect_dcm_heuristic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
3893 {
3894 
3895     /* This will be potentially called for every packet */
3896 
3897     if (!test_dcm(tvb))
3898         return FALSE;
3899 
3900     /*
3901     Conversation_set_dissector() is called inside dcm_state_get() once
3902     we have enough details. From there on, we will be 'static'
3903     */
3904 
3905     if (dissect_dcm_main(tvb, pinfo, tree, FALSE) == 0) {
3906         /* there may have been another reason why it is not DICOM */
3907         return FALSE;
3908     }
3909 
3910     return TRUE;
3911 
3912 }
3913 
3914 /*
3915 Only set a valued with col_set_str() if it does not yet exist.
3916 (In a multiple PDV scenario, col_set_str() actually appends for the subsequent calls)
3917 */
col_set_str_conditional(column_info * cinfo,const gint el,const gchar * str)3918 static void col_set_str_conditional(column_info *cinfo, const gint el, const gchar* str)
3919 {
3920     const char *col_string = col_get_text(cinfo, el);
3921 
3922     if (col_string == NULL || !g_str_has_prefix(col_string, str))
3923     {
3924         col_add_str(cinfo, el, str);
3925     }
3926 }
3927 
3928 /*
3929 CSV add a value to a column, if it does not exist yet
3930 */
col_append_str_conditional(column_info * cinfo,const gint el,const gchar * str)3931 static void col_append_str_conditional(column_info *cinfo, const gint el, const gchar* str)
3932 {
3933     const char *col_string = col_get_text(cinfo, el);
3934 
3935     if (col_string == NULL || !g_strrstr(col_string, str))
3936     {
3937         col_append_fstr(cinfo, el, ", %s", str);
3938     }
3939 }
3940 
3941 /*
3942 Dissect a single DICOM PDU. Can be an association or a data package. Creates a tree item.
3943 */
3944 static guint32
dissect_dcm_pdu(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset)3945 dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset)
3946 {
3947     proto_tree *dcm_ptree=NULL;     /* Root DICOM tree and its item */
3948     proto_item *dcm_pitem=NULL;
3949 
3950     dcm_state_t *dcm_data=NULL;
3951     dcm_state_assoc_t *assoc=NULL;
3952 
3953     guint8  pdu_type=0;
3954     guint32 pdu_len=0;
3955 
3956     gchar *pdu_data_description=NULL;
3957 
3958     /* Get or create conversation. Used to store context IDs and xfer Syntax */
3959 
3960     dcm_data = dcm_state_get(pinfo, TRUE);
3961     if (dcm_data == NULL) {     /* Internal error. Failed to create main DICOM data structure */
3962         return offset;
3963     }
3964 
3965     dcm_pitem = proto_tree_add_item(tree, proto_dcm, tvb, offset, -1, ENC_NA);
3966     dcm_ptree = proto_item_add_subtree(dcm_pitem, ett_dcm);
3967 
3968     /* PDU type is only one byte, then one byte reserved */
3969     pdu_type = tvb_get_guint8(tvb, offset);
3970     proto_tree_add_item(dcm_ptree, hf_dcm_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN);
3971     offset += 2;
3972 
3973     pdu_len = tvb_get_ntohl(tvb, offset);
3974     proto_tree_add_item(dcm_ptree, hf_dcm_pdu_len, tvb, offset, 4, ENC_BIG_ENDIAN);
3975     offset += 4;
3976 
3977     /* Find previously detected association, else create a new one object*/
3978     assoc = dcm_state_assoc_get(dcm_data, pinfo->num, TRUE);
3979 
3980     if (assoc == NULL) {        /* Internal error. Failed to create association structure */
3981         return offset;
3982     }
3983 
3984     if (pdu_type == 4) {
3985 
3986         col_set_str_conditional(pinfo->cinfo, COL_INFO, "P-DATA");
3987 
3988         /* Everything that needs to be shown in any UI column (like COL_INFO)
3989            needs to be calculated also with tree == null
3990         */
3991         offset = dissect_dcm_pdu_data(tvb, pinfo, dcm_ptree, assoc, offset, pdu_len, &pdu_data_description);
3992 
3993         if (pdu_data_description) {
3994             proto_item_append_text(dcm_pitem, ", %s", pdu_data_description);
3995             col_append_str_conditional(pinfo->cinfo, COL_INFO, pdu_data_description);
3996         }
3997     }
3998     else {
3999 
4000         /* Decode Association request, response, reject, abort details */
4001         offset = dissect_dcm_assoc_header(tvb, pinfo, dcm_ptree, offset, assoc, pdu_type, pdu_len);
4002     }
4003 
4004     return offset;          /* return the number of processed bytes */
4005 }
4006 
4007 
4008 /*
4009 Register the protocol with Wireshark
4010 */
4011 void
proto_register_dcm(void)4012 proto_register_dcm(void)
4013 {
4014     static hf_register_info hf[] = {
4015     { &hf_dcm_pdu_type, { "PDU Type", "dicom.pdu.type",
4016         FT_UINT8, BASE_HEX, VALS(dcm_pdu_ids), 0, NULL, HFILL } },
4017     { &hf_dcm_pdu_len, { "PDU Length", "dicom.pdu.len",
4018         FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4019 
4020     { &hf_dcm_assoc_version, { "Protocol Version", "dicom.assoc.version",
4021         FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4022     { &hf_dcm_assoc_called, { "Called  AE Title", "dicom.assoc.ae.called",
4023         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4024     { &hf_dcm_assoc_calling, { "Calling AE Title", "dicom.assoc.ae.calling",
4025         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4026     { &hf_dcm_assoc_reject_result, { "Result", "dicom.assoc.reject.result",
4027         FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4028     { &hf_dcm_assoc_reject_source, { "Source", "dicom.assoc.reject.source",
4029         FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4030     { &hf_dcm_assoc_reject_reason, { "Reason", "dicom.assoc.reject.reason",
4031         FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4032     { &hf_dcm_assoc_abort_source, { "Source", "dicom.assoc.abort.source",
4033         FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4034     { &hf_dcm_assoc_abort_reason, { "Reason", "dicom.assoc.abort.reason",
4035         FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4036     { &hf_dcm_assoc_item_type, { "Item Type", "dicom.assoc.item.type",
4037         FT_UINT8, BASE_HEX, VALS(dcm_assoc_item_type), 0, NULL, HFILL } },
4038     { &hf_dcm_assoc_item_len, { "Item Length", "dicom.assoc.item.len",
4039         FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4040 
4041     { &hf_dcm_actx, { "Application Context", "dicom.actx",
4042         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4043     { &hf_dcm_pctx_id, { "Presentation Context ID", "dicom.pctx.id",
4044         FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
4045     { &hf_dcm_pctx_result, { "Presentation Context Result", "dicom.pctx.result",
4046         FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
4047     { &hf_dcm_pctx_abss_syntax, { "Abstract Syntax", "dicom.pctx.abss.syntax",
4048         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4049     { &hf_dcm_pctx_xfer_syntax, { "Transfer Syntax", "dicom.pctx.xfer.syntax",
4050         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4051     { &hf_dcm_info, { "User Info", "dicom.userinfo",
4052         FT_NONE, BASE_NONE, NULL, 0, "This field contains the ACSE User Information Item of the A-ASSOCIATErequest.", HFILL } },
4053     { &hf_dcm_info_uid, { "Implementation Class UID", "dicom.userinfo.uid",
4054         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4055     { &hf_dcm_info_version, { "Implementation Version", "dicom.userinfo.version",
4056         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4057     { &hf_dcm_info_extneg, { "Extended Negotiation", "dicom.userinfo.extneg",
4058         FT_NONE, BASE_NONE, NULL, 0, "This field contains the optional SOP Class Extended Negotiation Sub-Item of the ACSE User Information Item of the A-ASSOCIATE-RQ/RSP.", HFILL } },
4059     { &hf_dcm_info_extneg_sopclassuid_len, { "SOP Class UID Length", "dicom.userinfo.extneg.sopclassuid.len",
4060         FT_UINT16, BASE_DEC, NULL, 0, "This field contains the length of the SOP Class UID in the Extended Negotiation Sub-Item.", HFILL } },
4061     { &hf_dcm_info_extneg_sopclassuid, { "SOP Class UID", "dicom.userinfo.extneg.sopclassuid",
4062         FT_STRING, BASE_NONE, NULL, 0, "This field contains the SOP Class UID in the Extended Negotiation Sub-Item.", HFILL } },
4063     { &hf_dcm_info_extneg_relational_query, { "Relational-queries", "dicom.userinfo.extneg.relational",
4064         FT_UINT8, BASE_HEX, NULL, 0, "This field indicates, if relational queries are supported.", HFILL } },
4065     { &hf_dcm_info_extneg_date_time_matching, { "Combined Date-Time matching", "dicom.userinfo.extneg.datetimematching",
4066         FT_UINT8, BASE_HEX, NULL, 0, "This field indicates, if combined date-time matching is supported.", HFILL } },
4067     { &hf_dcm_info_extneg_fuzzy_semantic_matching, { "Fuzzy semantic matching", "dicom.userinfo.extneg.fuzzymatching",
4068         FT_UINT8, BASE_HEX, NULL, 0, "This field indicates, if fuzzy semantic matching of person names is supported.", HFILL } },
4069     { &hf_dcm_info_extneg_timezone_query_adjustment, { "Timezone query adjustment", "dicom.userinfo.extneg.timezone",
4070         FT_UINT8, BASE_HEX, NULL, 0, "This field indicates, if timezone query adjustment is supported.", HFILL } },
4071     { &hf_dcm_info_rolesel, { "SCP/SCU Role Selection", "dicom.userinfo.rolesel",
4072         FT_NONE, BASE_NONE, NULL, 0, "This field contains the optional SCP/SCU Role Selection Sub-Item of the ACSE User Information Item of the A-ASSOCIATE-RQ/RSP.", HFILL } },
4073     { &hf_dcm_info_rolesel_sopclassuid_len, { "SOP Class UID Length", "dicom.userinfo.rolesel.sopclassuid.len",
4074         FT_UINT16, BASE_DEC, NULL, 0, "This field contains the length of the SOP Class UID in the SCP/SCU Role Selection Sub-Item.", HFILL } },
4075     { &hf_dcm_info_rolesel_sopclassuid, { "SOP Class UID", "dicom.userinfo.rolesel.sopclassuid",
4076         FT_STRING, BASE_NONE, NULL, 0, "This field contains the SOP Class UID in the SCP/SCU Role Selection Sub-Item.", HFILL } },
4077     { &hf_dcm_info_rolesel_scurole, { "SCU-role", "dicom.userinfo.rolesel.scurole",
4078         FT_UINT8, BASE_HEX, NULL, 0, "This field contains the SCU-role as defined for the Association-requester.", HFILL } },
4079     { &hf_dcm_info_rolesel_scprole, { "SCP-role", "dicom.userinfo.rolesel.scprole",
4080         FT_UINT8, BASE_HEX, NULL, 0, "This field contains the SCP-role as defined for the Association-requester.", HFILL } },
4081     { &hf_dcm_info_async_neg, { "Asynchronous Operations (and sub-operations) Window Negotiation", "dicom.userinfo.asyncneg",
4082         FT_NONE, BASE_NONE, NULL, 0, "This field contains the optional Asynchronous Operations (and sub-operations) Window Negotiation Sub-Item of the ACSE User Information Item of the A-ASSOCIATE-RQ/RSP.", HFILL } },
4083     { &hf_dcm_info_async_neg_max_num_ops_inv, { "Maximum-number-operations-invoked", "dicom.userinfo.asyncneg.maxnumopsinv",
4084         FT_UINT16, BASE_DEC, NULL, 0, "This field contains the maximum-number-operations-invoked in the Asynchronous Operations (and sub-operations) Window Negotiation Sub-Item.", HFILL } },
4085     { &hf_dcm_info_async_neg_max_num_ops_per, { "Maximum-number-operations-performed", "dicom.userinfo.asyncneg.maxnumopsper",
4086         FT_UINT16, BASE_DEC, NULL, 0, "This field contains the maximum-number-operations-performed in the Asynchronous Operations (and sub-operations) Window Negotiation Sub-Item.", HFILL } },
4087     { &hf_dcm_info_unknown, { "Unknown", "dicom.userinfo.unknown",
4088         FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
4089     { &hf_dcm_assoc_item_data, { "Unknown Data", "dicom.userinfo.data",
4090         FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
4091     { &hf_dcm_info_user_identify, { "User Identify", "dicom.userinfo.user_identify",
4092         FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
4093     { &hf_dcm_info_user_identify_type, { "Type", "dicom.userinfo.user_identify.type",
4094         FT_UINT8, BASE_DEC, VALS(user_identify_type_vals), 0, NULL, HFILL } },
4095     { &hf_dcm_info_user_identify_response_requested, { "Response Requested", "dicom.userinfo.user_identify.response_requested",
4096         FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4097     { &hf_dcm_info_user_identify_primary_field_length, { "Primary Field Length", "dicom.userinfo.user_identify.primary_field_length",
4098         FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4099     { &hf_dcm_info_user_identify_primary_field, { "Primary Field", "dicom.userinfo.user_identify.primary_field",
4100         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4101     { &hf_dcm_info_user_identify_secondary_field_length, { "Secondary Field Length", "dicom.userinfo.user_identify.secondary_field_length",
4102         FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4103     { &hf_dcm_info_user_identify_secondary_field, { "Secondary Field", "dicom.userinfo.user_identify.secondary_field",
4104         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4105     { &hf_dcm_pdu_maxlen, { "Max PDU Length", "dicom.max_pdu_len",
4106         FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4107     { &hf_dcm_pdv_len, { "PDV Length", "dicom.pdv.len",
4108         FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4109     { &hf_dcm_pdv_ctx, { "PDV Context", "dicom.pdv.ctx",
4110         FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4111     { &hf_dcm_pdv_flags, { "PDV Flags", "dicom.pdv.flags",
4112         FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
4113     { &hf_dcm_data_tag, { "Tag", "dicom.data.tag",
4114         FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
4115 
4116     { &hf_dcm_tag, { "Tag", "dicom.tag",
4117         FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
4118     { &hf_dcm_tag_vr, { "VR", "dicom.tag.vr",
4119         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4120     { &hf_dcm_tag_vl, { "Length", "dicom.tag.vl",
4121         FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4122 
4123     { &hf_dcm_tag_value_str, { "Value", "dicom.tag.value.str",
4124         FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4125     { &hf_dcm_tag_value_16s, { "Value", "dicom.tag.value.16s",
4126         FT_INT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4127     { &hf_dcm_tag_value_16u, { "Value", "dicom.tag.value.16u",
4128         FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4129     { &hf_dcm_tag_value_32s, { "Value", "dicom.tag.value.32s",
4130         FT_INT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4131     { &hf_dcm_tag_value_32u, { "Value", "dicom.tag.value.32u",
4132         FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4133     { &hf_dcm_tag_value_byte, { "Value", "dicom.tag.value.byte",
4134         FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
4135 
4136     /* Fragment entries */
4137     { &hf_dcm_pdv_fragments,
4138             { "Message fragments", "dicom.pdv.fragments",
4139             FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4140     { &hf_dcm_pdv_fragment,
4141             { "Message fragment", "dicom.pdv.fragment",
4142             FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4143     { &hf_dcm_pdv_fragment_overlap,
4144             { "Message fragment overlap", "dicom.pdv.fragment.overlap",
4145             FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4146     { &hf_dcm_pdv_fragment_overlap_conflicts,
4147             { "Message fragment overlapping with conflicting data",
4148             "dicom.pdv.fragment.overlap.conflicts",
4149             FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4150     { &hf_dcm_pdv_fragment_multiple_tails,
4151             { "Message has multiple tail fragments",
4152             "dicom.pdv.fragment.multiple_tails",
4153             FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4154     { &hf_dcm_pdv_fragment_too_long_fragment,
4155             { "Message fragment too long", "dicom.pdv.fragment.too_long_fragment",
4156             FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4157     { &hf_dcm_pdv_fragment_error,
4158             { "Message defragmentation error", "dicom.pdv.fragment.error",
4159             FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4160     { &hf_dcm_pdv_fragment_count,
4161             { "Message fragment count", "dicom.pdv.fragment_count",
4162             FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
4163     { &hf_dcm_pdv_reassembled_in,
4164             { "Reassembled in", "dicom.pdv.reassembled.in",
4165             FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4166     { &hf_dcm_pdv_reassembled_length,
4167             { "Reassembled PDV length", "dicom.pdv.reassembled.length",
4168             FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }
4169     };
4170 
4171 /* Setup protocol subtree array */
4172     static gint *ett[] = {
4173             &ett_dcm,
4174             &ett_assoc,
4175             &ett_assoc_header,
4176             &ett_assoc_actx,
4177             &ett_assoc_pctx,
4178             &ett_assoc_pctx_abss,
4179             &ett_assoc_pctx_xfer,
4180             &ett_assoc_info,
4181             &ett_assoc_info_uid,
4182             &ett_assoc_info_version,
4183             &ett_assoc_info_extneg,
4184             &ett_assoc_info_rolesel,
4185             &ett_assoc_info_async_neg,
4186             &ett_assoc_info_user_identify,
4187             &ett_assoc_info_unknown,
4188             &ett_dcm_data,
4189             &ett_dcm_data_pdv,
4190             &ett_dcm_data_tag,
4191             &ett_dcm_data_seq,
4192             &ett_dcm_data_item,
4193             &ett_dcm_pdv,               /* used for fragments */
4194             &ett_dcm_pdv_fragment,
4195             &ett_dcm_pdv_fragments
4196     };
4197 
4198     static ei_register_info ei[] = {
4199         { &ei_dcm_assoc_rejected, { "dicom.assoc.reject", PI_RESPONSE_CODE, PI_WARN, "Association rejected", EXPFILL }},
4200         { &ei_dcm_assoc_aborted, { "dicom.assoc.abort", PI_RESPONSE_CODE, PI_WARN, "Association aborted", EXPFILL }},
4201         { &ei_dcm_no_abstract_syntax, { "dicom.no_abstract_syntax", PI_MALFORMED, PI_ERROR, "No Abstract Syntax provided for this Presentation Context", EXPFILL }},
4202         { &ei_dcm_multiple_abstract_syntax, { "dicom.multiple_abstract_syntax", PI_MALFORMED, PI_ERROR, "More than one Abstract Syntax provided for this Presentation Context", EXPFILL }},
4203         { &ei_dcm_no_transfer_syntax, { "dicom.no_transfer_syntax", PI_MALFORMED, PI_ERROR, "No Transfer Syntax provided for this Presentation Context", EXPFILL }},
4204         { &ei_dcm_no_abstract_syntax_uid, { "dicom.no_abstract_syntax_uid", PI_MALFORMED, PI_ERROR, "No Abstract Syntax UID found for this Presentation Context", EXPFILL }},
4205         { &ei_dcm_multiple_transfer_syntax, { "dicom.multiple_transfer_syntax", PI_MALFORMED, PI_ERROR, "Only one Transfer Syntax allowed in a Association Response", EXPFILL }},
4206         { &ei_dcm_assoc_item_len, { "dicom.assoc.item.len.invalid", PI_MALFORMED, PI_ERROR, "Invalid Association Item Length", EXPFILL }},
4207         { &ei_dcm_pdv_ctx, { "dicom.pdv.ctx.invalid", PI_MALFORMED, PI_ERROR, "Invalid Presentation Context ID", EXPFILL }},
4208         { &ei_dcm_pdv_flags, { "dicom.pdv.flags.invalid", PI_MALFORMED, PI_ERROR, "Invalid Flags", EXPFILL }},
4209         { &ei_dcm_status_msg, { "dicom.status_msg", PI_RESPONSE_CODE, PI_WARN, "%s", EXPFILL }},
4210         { &ei_dcm_data_tag, { "dicom.data.tag.missing", PI_MALFORMED, PI_ERROR, "Early termination of tag. Data is missing", EXPFILL }},
4211         { &ei_dcm_pdv_len, { "dicom.pdv.len.invalid", PI_MALFORMED, PI_ERROR, "Invalid PDV length", EXPFILL }},
4212         { &ei_dcm_invalid_pdu_length, { "dicom.pdu_length.invalid", PI_MALFORMED, PI_ERROR, "Invalid PDU length", EXPFILL }},
4213     };
4214 
4215     module_t *dcm_module;
4216     expert_module_t* expert_dcm;
4217 
4218     /* Register the protocol name and description */
4219     proto_dcm = proto_register_protocol("DICOM", "DICOM", "dicom");
4220 
4221     /* Required function calls to register the header fields and subtrees used */
4222     proto_register_field_array(proto_dcm, hf, array_length(hf));
4223     proto_register_subtree_array(ett, array_length(ett));
4224     expert_dcm = expert_register_protocol(proto_dcm);
4225     expert_register_field_array(expert_dcm, ei, array_length(ei));
4226 
4227     /* Allow other dissectors to find this one by name. */
4228     dcm_handle = register_dissector("dicom", dissect_dcm_static, proto_dcm);
4229 
4230     dcm_module = prefs_register_protocol(proto_dcm, NULL);
4231 
4232     /*  Used to migrate an older configuration file to a newer one  */
4233     prefs_register_obsolete_preference(dcm_module, "heuristic");
4234 
4235     prefs_register_bool_preference(dcm_module, "export_header",
4236             "Create Meta Header on Export",
4237             "Create DICOM File Meta Header according to PS 3.10 on export for PDUs. "
4238             "If the captured PDV does not contain a SOP Class UID and SOP Instance UID "
4239             "(e.g. for command PDVs), wireshark specific ones will be created.",
4240             &global_dcm_export_header);
4241 
4242     prefs_register_uint_preference(dcm_module, "export_minsize",
4243             "Min. item size in bytes to export",
4244             "Do not show items below this size in the export list. "
4245             "Set it to 0, to see DICOM commands and responses in the list. "
4246             "Set it higher, to just export DICOM IODs (i.e. CT Images, RT Structures).", 10,
4247             &global_dcm_export_minsize);
4248 
4249     prefs_register_bool_preference(dcm_module, "seq_tree",
4250             "Create subtrees for Sequences and Items",
4251             "Create a node for sequences and items, and show children in a hierarchy. "
4252             "De-select this option, if you prefer a flat display or e.g. "
4253             "when using TShark to create a text output.",
4254             &global_dcm_seq_subtree);
4255 
4256     prefs_register_bool_preference(dcm_module, "tag_tree",
4257             "Create subtrees for DICOM Tags",
4258             "Create a node for a tag and show tag details as single elements. "
4259             "This can be useful to debug a tag and to allow display filters on these attributes. "
4260             "When using TShark to create a text output, it's better to have it disabled. ",
4261             &global_dcm_tag_subtree);
4262 
4263     prefs_register_bool_preference(dcm_module, "cmd_details",
4264             "Show command details in header",
4265             "Show message ID and number of completed, remaining, warned or failed operations in header and info column.",
4266             &global_dcm_cmd_details);
4267 
4268     prefs_register_bool_preference(dcm_module, "pdv_reassemble",
4269             "Merge fragmented PDVs",
4270             "Decode all DICOM tags in the last PDV. This will ensure the proper reassembly. "
4271             "De-select, to troubleshoot PDU length issues, or to understand PDV fragmentation. "
4272             "When not set, the decoding may fail and the exports may become corrupt.",
4273             &global_dcm_reassemble);
4274 
4275     dicom_eo_tap = register_export_object(proto_dcm, dcm_eo_packet, NULL);
4276 
4277     register_init_routine(&dcm_init);
4278 
4279     /* Register processing of fragmented DICOM PDVs */
4280     reassembly_table_register(&dcm_pdv_reassembly_table, &addresses_reassembly_table_functions);
4281 
4282 }
4283 
4284 /*
4285 Register static TCP port range specified in preferences.
4286 Register heuristic search as well.
4287 
4288 Statically defined ports take precedence over a heuristic one. I.e., if a foreign protocol claims a port,
4289 where DICOM is running on, we would never be called, by just having the heuristic registration.
4290 
4291 This function is also called, when preferences change.
4292 */
4293 void
proto_reg_handoff_dcm(void)4294 proto_reg_handoff_dcm(void)
4295 {
4296     /* Adds a UI element to the preferences dialog. This is the static part. */
4297     dissector_add_uint_range_with_preference("tcp.port", DICOM_DEFAULT_RANGE, dcm_handle);
4298 
4299     /*
4300     The following shows up as child protocol of 'DICOM' in 'Enable/Disable Protocols ...'
4301 
4302     The registration procedure for dissectors is a two-stage procedure.
4303 
4304     In stage 1, dissectors create tables in which other dissectors can register them. That's the stage in which proto_register_ routines are called.
4305     In stage 2, dissectors register themselves in tables created in stage 1. That's the stage in which proto_reg_handoff_ routines are called.
4306 
4307     heur_dissector_add() needs to be called in proto_reg_handoff_dcm() function.
4308     */
4309 
4310     heur_dissector_add("tcp", dissect_dcm_heuristic, "DICOM on any TCP port (heuristic)", "dicom_tcp", proto_dcm, HEURISTIC_ENABLE);
4311 }
4312 
4313 
4314 /*
4315 
4316 PDU's
4317 01 ASSOC-RQ
4318  1    1 reserved
4319  2    4 length
4320  6    2 protocol version (0x0 0x1)
4321  8    2 reserved
4322 10   16 dest aetitle
4323 26   16 src  aetitle
4324 42   32 reserved
4325 74    - presentation data value items
4326 
4327 02 A-ASSOC-AC
4328     1 reserved
4329     4 length
4330     2 protocol version (0x0 0x1)
4331     2 reserved
4332    16 dest aetitle (not checked)
4333    16 src  aetitle (not checked)
4334    32 reserved
4335     - presentation data value items
4336 
4337 03 ASSOC-RJ
4338     1 reserved
4339     4 length (4)
4340     1 reserved
4341     1 result  (1 reject perm, 2 reject transient)
4342     1 source  (1 service user, 2 service provider, 3 service provider)
4343     1 reason
4344         1 == source
4345             1 no reason given
4346             2 application context name not supported
4347             3 calling aetitle not recognized
4348             7 called aetitle not recognized
4349         2 == source
4350             1 no reason given
4351             2 protocol version not supported
4352         3 == source
4353             1 temporary congestion
4354             2 local limit exceeded
4355 
4356 04 P-DATA
4357  1  1 reserved
4358  2  4 length
4359     - (1+) presentation data value (PDV) items
4360  6      4 length
4361 10      1 Presentation Context ID (odd ints 1 - 255)
4362         - PDV
4363 11      1 header
4364             0x01 if set, contains Message Command info, else Message Data
4365             0x02 if set, contains last fragment
4366 
4367 05 A-RELEASE-RQ
4368     1 reserved
4369     4 length (4)
4370     4 reserved
4371 
4372 06 A-RELEASE-RP
4373     1 reserved
4374     4 length (4)
4375     4 reserved
4376 
4377 07 A-ABORT
4378     1  reserved
4379     4  length (4)
4380     2  reserved
4381     1  source  (0 = user, 1 = provider)
4382     1  reason  if 1 == source (0 not spec, 1 unrecognized, 2 unexpected 4 unrecognized param, 5 unexpected param, 6 invalid param)
4383 
4384 
4385 
4386 ITEM's
4387 10 Application Context
4388     1 reserved
4389     2 length
4390     - name
4391 
4392 20 Presentation Context
4393     1 reserved
4394     2 length
4395     1 Presentation context id
4396     3 reserved
4397     - (1) abstract and (1+) transfer syntax sub-items
4398 
4399 21 Presentation Context (Reply)
4400     1 reserved
4401     2 length
4402     1 ID (odd int's 1-255)
4403     1 reserved
4404     1 result (0 accept, 1 user-reject, 2 no-reason, 3 abstract not supported, 4- transfer syntax not supported)
4405     1 reserved
4406     - (1) type 40
4407 
4408 30 Abstract syntax
4409     1 reserved
4410     2 length
4411     - name (<= 64)
4412 
4413 40 Transfer syntax
4414     1 reserved
4415     2 length
4416     - name (<= 64)
4417 
4418 50 user information
4419     1 reserved
4420     2 length
4421     - user data
4422 
4423 51 max length
4424     1 reserved
4425     2 length (4)
4426     4 max PDU lengths
4427 
4428 From 3.7 Annex D Association Negotiation
4429 ========================================
4430 
4431 52 IMPLEMENTATION CLASS UID
4432     1 Item-type 52H
4433     1 Reserved
4434     2 Item-length
4435     n Implementation-class-uid
4436 
4437 55 IMPLEMENTATION VERSION NAME
4438     1 Item-type 55H
4439     1 Reserved
4440     2 Item-length
4441     n Implementation-version-name
4442 
4443 53 ASYNCHRONOUS OPERATIONS WINDOW
4444     1 Item-type 53H
4445     1 Reserved
4446     2 Item-length
4447     2 Maximum-number-operations-invoked
4448     2 Maximum-number-operations-performed
4449 
4450 54 SCP/SCU ROLE SELECTION
4451     1 Item-type 54H
4452     1 Reserved
4453     2 Item-length (n)
4454     2 UID-length (m)
4455     m SOP-class-uid
4456     1 SCU-role
4457       0 - non support of the SCU role
4458       1 - support of the SCU role
4459     1 SCP-role
4460       0 - non support of the SCP role
4461       1 - support of the SCP role.
4462 
4463 56 SOP CLASS EXTENDED NEGOTIATION
4464     1 Item-type 56H
4465     1 Reserved
4466     2 Item-Length (n)
4467     2 SOP-class-uid-length (m)
4468     m SOP-class-uid
4469     n-m Service-class-application-information
4470 
4471 57 SOP CLASS COMMON EXTENDED NEGOTIATION
4472     1 Item-type 57H
4473     1 Sub-item-version
4474     2 Item-Length
4475     2 SOP-class-uid-length (m)
4476     7-x   SOP-class-uid The SOP Class identifier encoded as a UID as defined in PS 3.5.
4477     (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.
4478     (x+3)-y Service-class-uid The Service Class identifier encoded as a UID as defined in PS 3.5.
4479     (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.
4480     (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.
4481     (z+1)-k Reserved  Reserved for additional fields of the sub-item. Shall be zero-length for Version 0 of Sub-item definition.
4482 
4483     Table D.3-13
4484     RELATED-GENERAL-SOP-CLASS-IDENTIFICATION SUB-FIELDS
4485     Bytes Sub-Field Name    Description of Sub-Field
4486     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.
4487     3-n Related-general-sop-class-uid The Related General SOP Class identifier encoded as a UID as defined in PS 3.5.
4488 
4489 58 User Identity Negotiation
4490     1 Item-type 58H
4491     1 Reserved
4492     2 Item-length
4493     1 User-Identity-Type  Field value shall be in the range 1 to 4 with the following meanings:
4494         1 - Username as a string in UTF-8
4495         2 - Username as a string in UTF-8 and passcode
4496         3 - Kerberos Service ticket
4497         4 - SAML Assertion
4498         Other values are reserved for future standardization.
4499     1 Positive-response-requested Field value:
4500         0 - no response requested
4501         1 - positive response requested
4502     2 Primary-field-length  The User-Identity-Length shall contain the length of the User-Identity value.
4503     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.
4504     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.
4505     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.
4506 
4507 59 User Identity Negotiation Reply
4508     1 Item-type 59H
4509     1 Reserved
4510     2 Item-length
4511     5-6 Server-response-length  This field shall contain the number of bytes in the Server-response.  May be zero.
4512     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.
4513 
4514  */
4515 
4516 /*
4517  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
4518  *
4519  * Local variables:
4520  * c-basic-offset: 4
4521  * tab-width: 8
4522  * indent-tabs-mode: nil
4523  * End:
4524  *
4525  * vi: set shiftwidth=4 tabstop=8 expandtab:
4526  * :indentSize=4:tabSize=8:noTabs=true:
4527  */
4528