Coverage Report

Created: 2026-05-14 06:28

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