Coverage Report

Created: 2026-08-14 06:45

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
16
#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
209
#define DCM_ILE  0x01           /* implicit, little endian */
621
187
#define DCM_EBE  0x02           /* explicit, big endian */
622
0
#define DCM_ELE  0x03           /* explicit, little endian */
623
187
#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
4
#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
16
{
756
16
    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/dissector_generators/generate-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
16
    if (dcm_uid_table == NULL) {
767
16
        dcm_uid_table = wmem_map_new(wmem_epan_scope(), wmem_str_hash, g_str_equal);
768
16
        wmem_map_reserve(dcm_uid_table, array_length(dcm_uid_data));
769
7.72k
        for (i = 0; i < array_length(dcm_uid_data); i++) {
770
7.71k
            wmem_map_insert(dcm_uid_table, (void *) dcm_uid_data[i].value,
771
7.71k
            (void *) &dcm_uid_data[i]);
772
7.71k
        }
773
16
    }
774
775
    /* Add Tag objects to hash table */
776
16
    if (dcm_tag_table == NULL) {
777
16
        dcm_tag_table = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal);
778
16
        wmem_map_reserve(dcm_tag_table, array_length(dcm_tag_data));
779
82.8k
        for (i = 0; i < array_length(dcm_tag_data); i++) {
780
82.8k
            wmem_map_insert(dcm_tag_table, GUINT_TO_POINTER(dcm_tag_data[i].tag),
781
82.8k
            (void *) &dcm_tag_data[i]);
782
82.8k
        }
783
16
    }
784
785
    /* Add Status Values to hash table */
786
16
    if (dcm_status_table == NULL) {
787
16
        dcm_status_table = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal);
788
864
        for (i = 0; i < array_length(dcm_status_data); i++) {
789
848
            wmem_map_insert(dcm_status_table, GUINT_TO_POINTER((uint32_t)dcm_status_data[i].value),
790
848
            (void *)&dcm_status_data[i]);
791
848
        }
792
16
    }
793
16
}
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
219
{
803
804
219
    conversation_t  *conv;
805
219
    dcm_state_t     *dcm_data;
806
807
219
    conv = find_or_create_conversation(pinfo);
808
219
    dcm_data = (dcm_state_t *)conversation_get_proto_data(conv, proto_dcm);
809
810
219
    if (dcm_data == NULL && create) {
811
812
26
        dcm_data =  wmem_new0(wmem_file_scope(), dcm_state_t);
813
26
        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
26
        conversation_set_dissector(conv, dcm_handle);
819
26
    }
820
821
219
    return dcm_data;
822
219
}
823
824
825
static dcm_state_assoc_t *
826
dcm_state_assoc_new(dcm_state_t *dcm_data, uint32_t packet_no)
827
26
{
828
    /* Create new association object and initialize the members */
829
830
26
    dcm_state_assoc_t *assoc;
831
832
26
    assoc = wmem_new0(wmem_file_scope(), dcm_state_assoc_t);
833
26
    assoc->packet_no = packet_no;           /* Identifier */
834
835
    /* add to the end of the list */
836
26
    if (dcm_data->last_assoc) {
837
0
        dcm_data->last_assoc->next = assoc;
838
0
        assoc->prev = dcm_data->last_assoc;
839
0
    }
840
26
    else {
841
26
        dcm_data->first_assoc = assoc;
842
26
    }
843
26
    dcm_data->last_assoc = assoc;
844
26
    return assoc;
845
26
}
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
217
{
853
854
217
    dcm_state_assoc_t *assoc = dcm_data->first_assoc;
855
856
217
    while (assoc) {
857
858
191
        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
191
        else {
864
            /* last or only associations in the same stream */
865
191
            if (assoc->packet_no <= packet_no)
866
191
                break;
867
191
        }
868
0
        assoc = assoc->next;
869
0
    }
870
871
217
    if (assoc == NULL && create) {
872
26
        assoc = dcm_state_assoc_new(dcm_data, packet_no);
873
26
    }
874
217
    return assoc;
875
217
}
876
877
static dcm_state_pctx_t *
878
dcm_state_pctx_new(dcm_state_assoc_t *assoc, uint8_t pctx_id)
879
34
{
880
    /* Create new presentation context object and initialize the members */
881
882
34
    dcm_state_pctx_t *pctx;
883
884
34
    pctx = wmem_new0(wmem_file_scope(), dcm_state_pctx_t);
885
34
    pctx->id = pctx_id;
886
34
    pctx->syntax = DCM_UNK;
887
888
    /* add to the end of the list list */
889
34
    if (assoc->last_pctx) {
890
15
        assoc->last_pctx->next = pctx;
891
15
        pctx->prev = assoc->last_pctx;
892
15
    }
893
19
    else {
894
19
        assoc->first_pctx = pctx;
895
19
    }
896
34
    assoc->last_pctx = pctx;
897
898
34
    return pctx;
899
34
}
900
901
static dcm_state_pctx_t *
902
dcm_state_pctx_get(dcm_state_assoc_t *assoc, uint8_t pctx_id, bool create)
903
77
{
904
    /*  Find or create presentation context object. Return NULL, if Context ID was not found */
905
906
77
    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
109
    while (pctx) {
912
75
        if (pctx->id == pctx_id)
913
43
            break;
914
32
        pctx = pctx->next;
915
32
    }
916
917
77
    if (pctx == NULL && create) {
918
4
        pctx = dcm_state_pctx_new(assoc, pctx_id);
919
4
    }
920
921
77
    return pctx;
922
77
}
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
69
{
931
69
    dcm_state_pdv_t *pdv;
932
933
69
    pdv = wmem_new0(wmem_file_scope(), dcm_state_pdv_t);
934
69
    pdv->syntax = DCM_UNK;
935
69
    pdv->is_last_fragment = true;       /* Continuation PDVs are more tricky */
936
69
    pdv->packet_no = packet_no;
937
69
    pdv->offset = offset;
938
939
    /* add to the end of the list */
940
69
    if (pctx->last_pdv) {
941
39
        pctx->last_pdv->next = pdv;
942
39
        pdv->prev = pctx->last_pdv;
943
39
    }
944
30
    else {
945
30
        pctx->first_pdv = pdv;
946
30
    }
947
69
    pctx->last_pdv = pdv;
948
69
    return pdv;
949
69
}
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
69
{
955
    /*  Find or create PDV object. Return NULL, if PDV was not found, based on packet number and offset */
956
957
69
    dcm_state_pdv_t *pdv = pctx->first_pdv;
958
959
305
    while (pdv) {
960
236
        if ((pdv->packet_no == packet_no) && (pdv->offset == offset))
961
0
            break;
962
236
        pdv = pdv->next;
963
236
    }
964
965
69
    if (pdv == NULL && create) {
966
69
        pdv = dcm_state_pdv_new(pctx, packet_no, offset);
967
69
    }
968
69
    return pdv;
969
69
}
970
971
static dcm_state_pdv_t*
972
dcm_state_pdv_get_obj_start(dcm_state_pdv_t *pdv_curr)
973
21
{
974
975
21
    dcm_state_pdv_t *pdv_first=pdv_curr;
976
977
    /* Get First PDV of the DICOM Object */
978
29
    while (pdv_first->prev && !pdv_first->prev->is_last_fragment) {
979
8
        pdv_first = pdv_first->prev;
980
8
    }
981
982
21
    return pdv_first;
983
21
}
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
3
{
1062
    /* Return Description, UID or error */
1063
1064
3
    return (dcm_desc == NULL ? (dcm_uid == NULL ? "Malformed Packet" : dcm_uid) : dcm_desc);
1065
3
}
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
165
{
1442
1443
165
    proto_item *assoc_header_pitem;
1444
165
    proto_tree *assoc_header_ptree;     /* Tree for item details */
1445
1446
165
    const char   *buf_desc = NULL;
1447
165
    const char   *reject_result_desc = "";
1448
165
    const char   *reject_source_desc = "";
1449
165
    const char   *reject_reason_desc = "";
1450
165
    const char   *abort_source_desc = "";
1451
165
    const char   *abort_reason_desc = "";
1452
1453
165
    char  *ae_called;
1454
165
    char  *ae_calling;
1455
165
    char  *ae_called_resp;
1456
165
    char  *ae_calling_resp;
1457
1458
165
    uint8_t reject_result;
1459
165
    uint8_t reject_source;
1460
165
    uint8_t reject_reason;
1461
165
    uint8_t abort_source;
1462
165
    uint8_t abort_reason;
1463
1464
165
    assoc_header_ptree = proto_tree_add_subtree(tree, tvb, offset, pdu_len, ett_assoc_header, &assoc_header_pitem, "Association Header");
1465
1466
165
    switch (pdu_type) {
1467
20
    case 1:                                     /* Association Request */
1468
1469
20
        proto_tree_add_item(assoc_header_ptree, hf_dcm_assoc_version, tvb, offset, 2, ENC_BIG_ENDIAN);
1470
20
        offset += 2;
1471
1472
20
        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
20
        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
20
        assoc->ae_called = wmem_strdup(wmem_file_scope(), g_strstrip(ae_called));
1491
20
        offset += 16;
1492
1493
20
        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
20
        assoc->ae_calling = wmem_strdup(wmem_file_scope(), g_strstrip(ae_calling));
1495
20
        offset += 16;
1496
1497
20
        offset += 32;                           /* 32 reserved bytes */
1498
1499
20
        buf_desc = wmem_strdup_printf(pinfo->pool, "A-ASSOCIATE request %s --> %s",
1500
20
            assoc->ae_calling, assoc->ae_called);
1501
1502
20
        offset = dissect_dcm_assoc_detail(tvb, pinfo, assoc_header_ptree, assoc, offset, pdu_len-offset);
1503
1504
20
        break;
1505
19
    case 2:                                     /* Association Accept */
1506
1507
19
        proto_tree_add_item(assoc_header_ptree, hf_dcm_assoc_version, tvb, offset, 2, ENC_BIG_ENDIAN);
1508
19
        offset += 2;
1509
1510
19
        offset += 2;                            /* Two reserved bytes*/
1511
1512
19
        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
19
        assoc->ae_called_resp = wmem_strdup(wmem_file_scope(), g_strstrip(ae_called_resp));
1514
19
        offset += 16;
1515
1516
19
        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
19
        assoc->ae_calling_resp = wmem_strdup(wmem_file_scope(), g_strstrip(ae_calling_resp));
1518
19
        offset += 16;
1519
1520
19
        offset += 32;                           /* 32 reserved bytes */
1521
1522
19
        buf_desc = wmem_strdup_printf(pinfo->pool, "A-ASSOCIATE accept  %s <-- %s",
1523
19
            assoc->ae_calling_resp, assoc->ae_called_resp);
1524
1525
19
        offset = dissect_dcm_assoc_detail(tvb, pinfo, assoc_header_ptree, assoc, offset, pdu_len-offset);
1526
1527
19
        break;
1528
1
    case 3:                                     /* Association Reject */
1529
1530
1
        offset += 1;                            /* One reserved byte */
1531
1532
1
        reject_result = tvb_get_uint8(tvb, offset);
1533
1
        reject_source = tvb_get_uint8(tvb, offset+1);
1534
1
        reject_reason = tvb_get_uint8(tvb, offset+2);
1535
1536
1
        switch (reject_result) {
1537
0
        case 1:  reject_result_desc = "Reject Permanent"; break;
1538
0
        case 2:  reject_result_desc = "Reject Transient"; break;
1539
1
        default: break;
1540
1
        }
1541
1542
1
        switch (reject_source) {
1543
0
        case 1:
1544
0
            reject_source_desc = "User";
1545
0
            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
0
            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
0
            }
1551
0
            break;
1552
0
        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
1
        }
1567
1568
1
        proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_reject_result, tvb,
1569
1
            offset  , 1, reject_result, "%s", reject_result_desc);
1570
1571
1
        proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_reject_source, tvb,
1572
1
            offset+1, 1, reject_source, "%s", reject_source_desc);
1573
1574
1
        proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_reject_reason, tvb,
1575
1
            offset+2, 1, reject_reason, "%s", reject_reason_desc);
1576
1577
1
        offset += 3;
1578
1579
        /* Provider aborted */
1580
1
        buf_desc = wmem_strdup_printf(pinfo->pool, "A-ASSOCIATE reject  %s <-- %s (%s)",
1581
1
            assoc->ae_calling, assoc->ae_called, reject_reason_desc);
1582
1583
1
        expert_add_info(pinfo, assoc_header_pitem, &ei_dcm_assoc_rejected);
1584
1585
1
        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
5
    case 6:                                     /* RELEASE Response */
1593
1594
5
        offset += 2;                            /* Two reserved bytes */
1595
5
        buf_desc="A-RELEASE response";
1596
1597
5
        break;
1598
3
    case 7:                                     /* ABORT */
1599
1600
3
        offset += 2;                            /* Two reserved bytes */
1601
1602
3
        abort_source = tvb_get_uint8(tvb, offset);
1603
3
        abort_reason = tvb_get_uint8(tvb, offset+1);
1604
1605
3
        switch (abort_source) {
1606
1
        case 0:
1607
1
            abort_source_desc = "User";
1608
1
            abort_reason_desc = "N/A";          /* No details can be provided*/
1609
1
            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
3
        }
1627
1628
2
        proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_abort_source,
1629
2
            tvb, offset  , 1, abort_source, "%s", abort_source_desc);
1630
1631
2
        proto_tree_add_uint_format_value(assoc_header_ptree, hf_dcm_assoc_abort_reason,
1632
2
            tvb, offset+1, 1, abort_reason, "%s", abort_reason_desc);
1633
2
        offset += 2;
1634
1635
2
        if (abort_source == 0) {
1636
            /* User aborted */
1637
1
            buf_desc = wmem_strdup_printf(pinfo->pool, "ABORT %s --> %s",
1638
1
                assoc->ae_calling, assoc->ae_called);
1639
1
        }
1640
1
        else {
1641
            /* Provider aborted, slightly more information */
1642
1
            buf_desc = wmem_strdup_printf(pinfo->pool, "ABORT %s <-- %s (%s)",
1643
1
                assoc->ae_calling, assoc->ae_called, abort_reason_desc);
1644
1
        }
1645
1646
2
        expert_add_info(pinfo, assoc_header_pitem, &ei_dcm_assoc_aborted);
1647
1648
2
        break;
1649
165
    }
1650
1651
146
    if (buf_desc) {
1652
31
        proto_item_set_text(assoc_header_pitem, "%s", buf_desc);
1653
31
        col_set_str(pinfo->cinfo, COL_INFO, buf_desc);
1654
1655
        /* proto_item and proto_tree are one and the same */
1656
31
        proto_item_append_text(tree, ", %s", buf_desc);
1657
31
    }
1658
146
    return offset;
1659
165
}
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
2
{
1671
1672
2
    proto_tree *assoc_item_ptree;       /* Tree for item details */
1673
2
    proto_item *assoc_item_pitem;
1674
2
    dcm_uid_t const *uid = NULL;
1675
1676
2
    uint32_t item_number = 0;
1677
1678
2
    uint8_t item_type;
1679
2
    uint16_t item_len;
1680
1681
2
    char *buf_desc;                    /* Used for item text */
1682
1683
2
    *item_value = NULL;
1684
2
    *item_description = NULL;
1685
1686
2
    item_type = tvb_get_uint8(tvb, offset);
1687
2
    item_len  = tvb_get_ntohs(tvb, offset+2);
1688
1689
2
    assoc_item_ptree = proto_tree_add_subtree(tree, tvb, offset, item_len+4, ett_subtree, &assoc_item_pitem, pitem_prefix);
1690
1691
2
    proto_tree_add_uint(assoc_item_ptree, *hf_type, tvb, offset,   1, item_type);
1692
2
    proto_tree_add_uint(assoc_item_ptree, *hf_len,  tvb, offset+2, 2, item_len);
1693
1694
2
    switch (item_value_type) {
1695
2
    case DCM_ITEM_VALUE_TYPE_UID:
1696
2
        *item_value = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset+4, item_len, ENC_ASCII);
1697
1698
2
        uid = (dcm_uid_t const *)wmem_map_lookup(dcm_uid_table, (void *) *item_value);
1699
2
        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
2
        else {
1704
            /* Unknown UID, or no UID at all */
1705
2
            buf_desc = *item_value;
1706
2
        }
1707
1708
2
        proto_item_append_text(assoc_item_pitem, "%s", buf_desc);
1709
2
        proto_tree_add_string(assoc_item_ptree, *hf_value, tvb, offset+4, item_len, buf_desc);
1710
1711
2
        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
2
    }
1732
2
}
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
8
{
2009
2010
8
    proto_tree *pctx_ptree;                 /* Tree for presentation context details */
2011
8
    proto_item *pctx_pitem;
2012
2013
8
    dcm_state_pctx_t *pctx = NULL;
2014
2015
8
    uint8_t item_type = 0;
2016
8
    uint16_t item_len = 0;
2017
2018
8
    uint8_t pctx_id = 0;                    /* Presentation Context ID */
2019
8
    uint8_t pctx_result = 0;
2020
2021
8
    const char  *pctx_result_desc = "";
2022
2023
8
    char *pctx_abss_uid  = NULL;           /* Abstract Syntax UID alias SOP Class UID */
2024
8
    const char *pctx_abss_desc = NULL;     /* Description of UID */
2025
2026
8
    char *pctx_xfer_uid = NULL;            /* Transfer Syntax UID */
2027
8
    const char *pctx_xfer_desc = NULL;     /* Description of UID */
2028
2029
8
    char *buf_desc;                        /* Used in info mode for item text */
2030
2031
8
    uint32_t endpos = 0;
2032
8
    int     cnt_abbs = 0;                   /* Number of Abstract Syntax Items */
2033
8
    int     cnt_xfer = 0;                   /* Number of Transfer Syntax Items */
2034
2035
8
    endpos = offset + len;
2036
2037
8
    item_type = tvb_get_uint8(tvb, offset-4);
2038
8
    item_len  = tvb_get_ntohs(tvb, offset-2);
2039
2040
8
    pctx_ptree = proto_tree_add_subtree(tree, tvb, offset-4, item_len+4, ett_assoc_pctx, &pctx_pitem, pitem_prefix);
2041
2042
8
    pctx_id     = tvb_get_uint8(tvb, offset);
2043
8
    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
8
    pctx = dcm_state_pctx_get(assoc, pctx_id, true);
2047
8
    if (pctx == NULL) { /* Internal error. Failed to create data structure */
2048
0
        return;
2049
0
    }
2050
2051
8
    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
8
    proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_len,  tvb, offset-2, 2, item_len);
2053
2054
8
    proto_tree_add_uint_format(pctx_ptree, hf_dcm_pctx_id, tvb, offset, 1, pctx_id, "Context ID: 0x%02x", pctx_id);
2055
2056
8
    if (!is_assoc_request) {
2057
        /* Association response. */
2058
2059
6
        switch (pctx_result) {
2060
1
        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
6
        }
2066
2067
6
        proto_tree_add_uint_format(pctx_ptree, hf_dcm_pctx_result, tvb, offset+2, 1,
2068
6
            pctx_result, "Result: %s (0x%x)", pctx_result_desc, pctx_result);
2069
6
    }
2070
2071
8
    offset += 4;
2072
18
    while (offset < endpos) {
2073
2074
13
        item_type = tvb_get_uint8(tvb, offset);
2075
13
        item_len = tvb_get_ntohs(tvb, 2 + offset);
2076
2077
13
        offset += 4;
2078
13
        switch (item_type) {
2079
1
        case 0x30:              /* Abstract syntax */
2080
2081
            /* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
2082
1
            dissect_dcm_assoc_item(tvb, pinfo, pctx_ptree, offset-4,
2083
1
                "Abstract Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_abss_uid, &pctx_abss_desc,
2084
1
                &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_abss_syntax, ett_assoc_pctx_abss);
2085
2086
1
            cnt_abbs += 1;
2087
1
            offset += item_len;
2088
1
            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
10
        default:
2110
10
            offset += item_len;
2111
10
            break;
2112
13
        }
2113
13
    }
2114
2115
5
    if (is_assoc_request) {
2116
2117
2
        if (cnt_abbs<1) {
2118
2
            expert_add_info(pinfo, pctx_pitem, &ei_dcm_no_abstract_syntax);
2119
2
            return;
2120
2
        }
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
3
    else {
2138
2139
3
        if (cnt_xfer>1) {
2140
0
            expert_add_info(pinfo, pctx_pitem, &ei_dcm_multiple_transfer_syntax);
2141
0
            return;
2142
0
        }
2143
3
    }
2144
2145
3
    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
3
    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
3
    else
2166
3
    {
2167
3
        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
3
        else {
2175
            /* Rejected */
2176
3
            buf_desc = wmem_strdup_printf(pinfo->pool, "ID 0x%02x, %s, %s",
2177
3
                pctx_id, pctx_result_desc,
2178
3
                dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
2179
3
        }
2180
3
    }
2181
3
    proto_item_append_text(pctx_pitem, "%s", buf_desc);
2182
2183
3
}
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
32
{
2318
32
    proto_tree *assoc_tree  = NULL;     /* Tree for PDU details */
2319
2320
32
    uint8_t item_type;
2321
32
    uint16_t item_len;
2322
2323
32
    uint32_t endpos;
2324
2325
32
    char *item_value = NULL;
2326
32
    const char *item_description = NULL;
2327
2328
32
    endpos = offset + len;
2329
2330
32
    assoc_tree = proto_item_add_subtree(ti, ett_assoc);
2331
61
    while (offset < endpos) {
2332
2333
39
        item_type = tvb_get_uint8(tvb, offset);
2334
39
        item_len  = tvb_get_ntohs(tvb, 2 + offset);
2335
2336
39
        if (item_len == 0) {
2337
1
            expert_add_info(pinfo, ti, &ei_dcm_assoc_item_len);
2338
1
            return endpos;
2339
1
        }
2340
2341
38
        offset += 4;
2342
2343
38
        switch (item_type) {
2344
1
        case 0x10:              /* Application context */
2345
1
            dissect_dcm_assoc_item(tvb, pinfo, assoc_tree, offset-4,
2346
1
                "Application Context: ", DCM_ITEM_VALUE_TYPE_UID, &item_value, &item_description,
2347
1
                &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_actx, ett_assoc_actx);
2348
2349
1
            offset += item_len;
2350
1
            break;
2351
2352
2
        case 0x20:              /* Presentation context request */
2353
2
            dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len, "Presentation Context: ", true);
2354
2
            offset += item_len;
2355
2
            break;
2356
2357
6
        case 0x21:              /* Presentation context reply */
2358
6
            dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len, "Presentation Context: ", false);
2359
6
            offset += item_len;
2360
6
            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
24
        default:
2368
24
            offset += item_len;
2369
24
            break;
2370
38
        }
2371
38
    }
2372
2373
22
    return offset;
2374
2375
32
}
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
69
{
2381
    /* Dissect Context and Flags of a PDV and create new PDV structure */
2382
2383
69
    proto_item *pdv_ctx_pitem = NULL;
2384
69
    proto_item *pdv_flags_pitem = NULL;
2385
2386
69
    dcm_state_pctx_t    *pctx = NULL;
2387
69
    dcm_state_pdv_t     *pdv_first_data = NULL;
2388
2389
69
    const char *desc_flag = NULL;      /* Flag Description in tree */
2390
69
    char *desc_header = NULL;          /* Used for PDV description */
2391
2392
69
    uint8_t flags = 0, o_flags = 0;
2393
69
    uint8_t pctx_id = 0;
2394
2395
    /* 1 Byte Context */
2396
69
    pctx_id = tvb_get_uint8(tvb, offset);
2397
69
    pctx = dcm_state_pctx_get(assoc, pctx_id, false);
2398
2399
69
    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
69
    else {
2406
69
        pdv_ctx_pitem=proto_tree_add_uint_format(tree, hf_dcm_pdv_ctx, tvb,  offset, 1,
2407
69
            pctx_id, "Context: 0x%02x not found. A-ASSOCIATE request not found in capture.", pctx_id);
2408
2409
69
        expert_add_info(pinfo, pdv_ctx_pitem, &ei_dcm_pdv_ctx);
2410
2411
69
        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
30
            pctx = dcm_state_pctx_new(assoc, pctx_id);
2416
2417
            /* To be done: Guess Syntax */
2418
30
            pctx->syntax = DCM_UNK;
2419
30
        }
2420
69
    }
2421
69
    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
69
    *pdv = dcm_state_pdv_get(pctx, pinfo->num, tvb_raw_offset(tvb)+offset, true);
2431
69
    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
69
    o_flags = tvb_get_uint8(tvb, offset);
2438
69
    flags = 0x3 & o_flags;
2439
2440
69
    (*pdv)->pctx_id = pctx_id;
2441
2442
69
    switch (flags) {
2443
13
    case 0:     /* 00 */
2444
13
        if (0 != (0xfc & o_flags))
2445
8
            desc_flag = "Data, More Fragments (Warning: Invalid)";
2446
5
        else
2447
5
            desc_flag = "Data, More Fragments";
2448
2449
13
        (*pdv)->is_flagvalid = true;
2450
13
        (*pdv)->is_command = false;
2451
13
        (*pdv)->is_last_fragment = false;
2452
13
        (*pdv)->syntax = pctx->syntax;      /* Inherit syntax for data PDVs*/
2453
13
        break;
2454
2455
8
    case 2:     /* 10 */
2456
8
        if (0 != (0xfc & o_flags))
2457
7
            desc_flag = "Data, Last Fragment (Warning: Invalid)";
2458
1
        else
2459
1
            desc_flag = "Data, Last Fragment";
2460
2461
8
        (*pdv)->is_flagvalid = true;
2462
8
        (*pdv)->is_command = false;
2463
8
        (*pdv)->is_last_fragment = true;
2464
8
        (*pdv)->syntax = pctx->syntax;      /* Inherit syntax for data PDVs*/
2465
8
        break;
2466
2467
2
    case 1:     /* 01 */
2468
2
        if (0 != (0xfc & o_flags))
2469
2
            desc_flag = "Command, More Fragments (Warning: Invalid)";
2470
0
        else
2471
0
            desc_flag = "Command, More Fragments";
2472
2
        desc_header = wmem_strdup(wmem_file_scope(), "Command");        /* Will be overwritten with real command tag */
2473
2474
2
        (*pdv)->is_flagvalid = true;
2475
2
        (*pdv)->is_command = true;
2476
2
        (*pdv)->is_last_fragment = false;
2477
2
        (*pdv)->syntax = DCM_ILE;           /* Command tags are always little endian*/
2478
2
        break;
2479
2480
46
    case 3:     /* 11 */
2481
46
        if (0 != (0xfc & o_flags))
2482
42
            desc_flag = "Command, Last Fragment (Warning: Invalid)";
2483
4
        else
2484
4
            desc_flag = "Command, Last Fragment";
2485
46
        desc_header = wmem_strdup(wmem_file_scope(), "Command");
2486
2487
46
        (*pdv)->is_flagvalid = true;
2488
46
        (*pdv)->is_command = true;
2489
46
        (*pdv)->is_last_fragment = true;
2490
46
        (*pdv)->syntax = DCM_ILE;           /* Command tags are always little endian*/
2491
46
        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
69
    }
2502
2503
69
    if (!PINFO_FD_VISITED(pinfo)) {
2504
69
        (*pdv)->reassembly_id = pctx->reassembly_count;
2505
69
        if ((*pdv)->is_last_fragment) {
2506
54
            pctx->reassembly_count++;
2507
54
        }
2508
69
    }
2509
2510
69
    if (flags == 0 || flags == 2) {
2511
        /* Data PDV */
2512
21
        pdv_first_data = dcm_state_pdv_get_obj_start(*pdv);
2513
2514
21
        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
4
            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
4
            else {
2530
                /* Use previous command and append DATA*/
2531
4
                desc_header = wmem_strdup_printf(wmem_file_scope(), "%s-DATA", pdv_first_data->prev->desc);
2532
4
            }
2533
4
        }
2534
17
        else {
2535
17
            desc_header = wmem_strdup(wmem_file_scope(), "DATA");
2536
17
        }
2537
21
    }
2538
2539
69
    (*pdv)->desc = desc_header;
2540
2541
69
    pdv_flags_pitem = proto_tree_add_uint_format(tree, hf_dcm_pdv_flags, tvb, offset, 1,
2542
69
        flags, "Flags: 0x%02x (%s)", o_flags, desc_flag);
2543
2544
69
    if (o_flags>3) {
2545
59
        expert_add_info(pinfo, pdv_flags_pitem, &ei_dcm_pdv_flags);
2546
59
    }
2547
69
    offset +=1;
2548
2549
69
    return offset;
2550
69
}
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
26
{
2561
2562
26
    proto_item *pitem = NULL;
2563
26
    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
26
    tvb_ensure_bytes_exist(tvb, offset, vl_max);
2570
2571
    /* ---------------------------------------------------------------------------
2572
       Potentially long types. Obey vl_max
2573
       ---------------------------------------------------------------------------
2574
    */
2575
2576
26
    if ((strncmp(vr, "AE", 2) == 0) || (strncmp(vr, "AS", 2) == 0) || (strncmp(vr, "CS", 2) == 0) ||
2577
26
        (strncmp(vr, "DA", 2) == 0) || (strncmp(vr, "DS", 2) == 0) || (strncmp(vr, "DT", 2) == 0) ||
2578
26
        (strncmp(vr, "IS", 2) == 0) || (strncmp(vr, "LO", 2) == 0) || (strncmp(vr, "LT", 2) == 0) ||
2579
26
        (strncmp(vr, "PN", 2) == 0) || (strncmp(vr, "SH", 2) == 0) || (strncmp(vr, "ST", 2) == 0) ||
2580
26
        (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
26
    else if ((strncmp(vr, "OB", 2) == 0) || (strncmp(vr, "OW", 2) == 0) ||
2629
26
             (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
26
    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
12
        uint8_t   val8;
2644
12
        char     *vals;
2645
12
        uint32_t i;
2646
2647
        /* String detector, i.e. check if we only have alpha-numeric character */
2648
12
        bool            is_string = true;
2649
12
        bool            is_padded = false;
2650
2651
209
        for (i = 0; i < vl_max ; i++) {
2652
197
            val8 = tvb_get_uint8(tvb, offset + i);
2653
2654
197
            if ((val8 == 0x09) || (val8 == 0x0A) || (val8 == 0x0D)) {
2655
                /* TAB, LF, CR */
2656
2
            }
2657
195
            else if ((val8 >= 0x20) && (val8 <= 0x7E)) {
2658
                /* No extended ASCII, 0-9, A-Z, a-z */
2659
40
            }
2660
155
            else if ((i == vl_max -1) && (val8 == 0x00)) {
2661
                /* Last Byte can be null*/
2662
5
                is_padded = true;
2663
5
            }
2664
150
            else {
2665
                /* Here's the code */
2666
150
                is_string = false;
2667
150
            }
2668
197
        }
2669
2670
12
        if (is_string) {
2671
0
            vals = tvb_format_text(pinfo->pool, tvb, offset, (is_padded ? vl_max - 1 : vl_max));
2672
0
            proto_tree_add_string(tree, hf_dcm_tag_value_str, tvb, offset, vl_max, vals);
2673
2674
0
            *tag_value = vals;
2675
0
        }
2676
12
        else {
2677
12
            proto_tree_add_bytes_format_value(tree, hf_dcm_tag_value_byte, tvb, offset, vl_max, NULL, "%s", "(binary)");
2678
2679
12
            *tag_value = wmem_strdup(pinfo->pool, "(binary)");
2680
12
        }
2681
12
    }
2682
    /* ---------------------------------------------------------------------------
2683
       Smaller types. vl/vl_max are not used. Fixed item length from 2 to 8 bytes
2684
       ---------------------------------------------------------------------------
2685
    */
2686
14
    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
        wmem_strbuf_t *at_value = wmem_strbuf_create(pinfo->pool);
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
            wmem_strbuf_append_printf(at_value, "(%04x,%04x)", at_grp, at_elm);
2710
2711
0
            i++;
2712
0
        }
2713
0
        *tag_value = wmem_strbuf_finalize(at_value);
2714
0
    }
2715
14
    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
14
    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
14
    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
14
    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
14
    else if (strncmp(vr, "UL", 2) == 0)  {          /* Unsigned Long. Can be VM > 1, but not yet supported */
2746
14
        uint32_t val32;
2747
2748
14
        proto_tree_add_item_ret_uint(tree, hf_dcm_tag_value_32u, tvb, offset, 4, encoding, &val32);
2749
2750
14
        *tag_value = wmem_strdup_printf(pinfo->pool, "%u", val32);
2751
14
    }
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
26
    offset += vl_max;
2819
2820
26
    return offset;
2821
2822
26
}
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
638
{
2830
2831
638
    if (offset + size_required > endpos) {
2832
2833
2
        pdv->open_tag.is_header_fragmented = true;
2834
2
        pdv->open_tag.len_decoded = endpos - startpos;
2835
2836
2
        return true;
2837
2
    }
2838
636
    else {
2839
636
        return false;
2840
636
    }
2841
638
}
2842
2843
static dcm_tag_t const *
2844
dcm_tag_lookup(uint16_t grp, uint16_t elm)
2845
161
{
2846
2847
161
    static dcm_tag_t const *tag_def = NULL;
2848
2849
161
    static dcm_tag_t const tag_unknown         = { 0x00000000, "(unknown)", "UN", "1", 0, 0};
2850
161
    static dcm_tag_t const tag_private         = { 0x00000000, "Private Tag", "UN", "1", 0, 0 };
2851
161
    static dcm_tag_t const tag_private_grp_len = { 0x00000000, "Private Tag Group Length", "UL", "1", 0, 0 };
2852
161
    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
161
    tag_def = (dcm_tag_t const *)wmem_map_lookup(dcm_tag_table, GUINT_TO_POINTER(((uint32_t)grp << 16) | elm));
2856
2857
161
    if (tag_def == NULL) {
2858
2859
        /* No match found */
2860
99
        if ((grp & 0x0001) && (elm == 0x0000)) {
2861
6
            tag_def = &tag_private_grp_len;
2862
6
        }
2863
93
        else if (grp & 0x0001) {
2864
50
            tag_def = &tag_private;
2865
50
        }
2866
43
        else if (elm == 0x0000) {
2867
16
            tag_def = &tag_grp_length;
2868
16
        }
2869
2870
        /* There are a few tags that require a mask to be found */
2871
27
        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
26
        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
26
        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
26
        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
26
        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
26
        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)));
2890
0
        }
2891
2892
99
        if (tag_def == NULL) {
2893
            /* Still no match found */
2894
27
            tag_def = &tag_unknown;
2895
27
        }
2896
99
    }
2897
2898
161
    return tag_def;
2899
161
}
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
189
{
2905
2906
189
    char *desc_mod;
2907
189
    char *tag_vl;
2908
189
    char *tag_sum;
2909
2910
189
    if (is_retired) {
2911
0
        desc_mod = wmem_strdup_printf(pinfo->pool, "(Retired) %-35.35s", tag_desc);
2912
0
    }
2913
189
    else {
2914
189
        desc_mod = wmem_strdup_printf(pinfo->pool, "%-45.45s", tag_desc);
2915
189
    }
2916
2917
189
    if (vl == 0xFFFFFFFF) {
2918
34
        tag_vl = wmem_strdup_printf(pinfo->pool, "%10.10s", "<udef>");
2919
34
    }
2920
155
    else {
2921
155
        tag_vl = wmem_strdup_printf(pinfo->pool, "%10u", vl);           /* Show as dec */
2922
155
    }
2923
2924
189
    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
189
    return tag_sum;
2928
189
}
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
161
{
2941
2942
2943
161
    proto_tree      *tag_ptree = NULL;      /* Tree for decoded tag details */
2944
161
    proto_tree      *seq_ptree = NULL;      /* Possible subtree for sequences and items */
2945
2946
161
    proto_item      *tag_pitem = NULL;
2947
161
    dcm_tag_t const *tag_def   = NULL;
2948
2949
161
    int ett;
2950
2951
161
    const char *vr = NULL;
2952
161
    char        *tag_value = "";      /* Tag Value converted to a string      */
2953
161
    char        *tag_summary;
2954
2955
161
    uint32_t vl = 0;
2956
161
    uint16_t vl_1 = 0;
2957
161
    uint16_t vl_2 = 0;
2958
2959
161
    uint32_t offset_tag   = 0;           /* Remember offsets for tree, since the tree    */
2960
161
    uint32_t offset_vr    = 0;           /* header is created pretty late                */
2961
161
    uint32_t offset_vl    = 0;
2962
2963
161
    uint32_t vl_max = 0;                 /* Max Value Length to Parse */
2964
2965
161
    uint16_t grp = 0;
2966
161
    uint16_t elm = 0;
2967
2968
161
    uint32_t len_decoded_remaing = 0;
2969
2970
    /* Decode the syntax a little more */
2971
161
    uint32_t encoding = (pdv->syntax == DCM_EBE) ? ENC_BIG_ENDIAN : ENC_LITTLE_ENDIAN;
2972
161
    bool is_implicit = (pdv->syntax == DCM_ILE);
2973
161
    bool is_vl_long = false;            /* True for 4 Bytes length fields */
2974
2975
161
    bool is_sequence = false;           /* True for Sequence Tags */
2976
161
    bool is_item = false;               /* True for Sequence Item Tags */
2977
2978
161
    *tag_description = NULL;                /* Reset description. It's wmem packet scope memory, so not really bad*/
2979
2980
161
    offset_tag = offset;
2981
2982
2983
161
    if (pdv->prev && is_first_tag) {
2984
16
        len_decoded_remaing = pdv->prev->open_tag.len_decoded;
2985
16
    }
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
161
    if (len_decoded_remaing >= 2) {
3009
2
        grp = pdv->prev->open_tag.grp;
3010
2
        len_decoded_remaing -= 2;
3011
2
    }
3012
159
    else {
3013
3014
159
        if (dcm_tag_is_open(pdv, offset_tag, offset, endpos, 2))
3015
0
             return endpos; /* Exit if needed */
3016
3017
159
        grp = tvb_get_uint16(tvb, offset, encoding);
3018
159
        offset += 2;
3019
159
        pdv->open_tag.grp = grp;
3020
159
    }
3021
3022
    /* Element */
3023
161
    if (len_decoded_remaing >= 2) {
3024
2
        elm = pdv->prev->open_tag.elm;
3025
2
        len_decoded_remaing -= 2;
3026
2
    }
3027
159
    else {
3028
3029
159
        if (dcm_tag_is_open(pdv, offset_tag, offset, endpos, 2))
3030
0
             return endpos;    /* Exit if needed */
3031
3032
159
        elm = tvb_get_uint16(tvb, offset, encoding);
3033
159
        offset += 2;
3034
159
        pdv->open_tag.elm = elm;
3035
159
    }
3036
3037
    /* Find the best matching tag */
3038
161
    tag_def = dcm_tag_lookup(grp, elm);
3039
3040
    /* Value Representation */
3041
161
    offset_vr = offset;
3042
161
    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
161
    else if (is_implicit) {
3048
        /* Get VR from tag definition */
3049
161
        vr = wmem_strdup(pinfo->pool, tag_def->vr);
3050
161
        is_vl_long = true;                          /* Implicit always has 4 byte length field */
3051
161
    }
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
161
    offset_vl = offset;
3099
161
    if (len_decoded_remaing >= 2) {
3100
1
        vl_1 = pdv->prev->open_tag.vl_1;
3101
1
        len_decoded_remaing -= 2;
3102
1
    }
3103
160
    else {
3104
3105
160
        if (dcm_tag_is_open(pdv, offset_tag, offset_vl, endpos, 2))
3106
1
            return endpos;
3107
159
        vl_1 = tvb_get_uint16(tvb, offset, encoding);
3108
159
        offset += 2;
3109
159
        pdv->open_tag.vl_1 = vl_1;
3110
159
    }
3111
3112
160
    if (is_vl_long) {
3113
3114
160
        if (len_decoded_remaing >= 2) {
3115
0
            vl_2 = pdv->prev->open_tag.vl_2;
3116
0
        }
3117
160
        else {
3118
3119
160
            if (dcm_tag_is_open(pdv, offset_tag, offset_vl+2, endpos, 2))
3120
1
                return endpos;
3121
159
            vl_2 = tvb_get_uint16(tvb, offset, encoding);
3122
159
            offset += 2;
3123
159
            pdv->open_tag.vl_2 = vl_2;
3124
159
        }
3125
3126
159
        if (encoding == ENC_LITTLE_ENDIAN)   vl = (vl_2 << 16) + vl_1;
3127
0
        else                    vl = (vl_1 << 16) + vl_2;
3128
159
    }
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
159
    tag_summary = dcm_tag_summary(pinfo, grp, elm, vl, tag_def->description, vr, tag_def->is_retired, is_implicit);
3145
159
    is_sequence = (strcmp(vr, "SQ") == 0) || (vl == 0xFFFFFFFF);
3146
159
    is_item = ((grp == 0xFFFE) && (elm == 0xE000));
3147
3148
159
    if ((is_sequence | is_item) &&  global_dcm_seq_subtree) {
3149
34
        ett = is_sequence ? ett_dcm_data_seq : ett_dcm_data_item;
3150
34
    }
3151
125
    else {
3152
125
        ett = ett_dcm_data_tag;
3153
125
    }
3154
3155
159
    if (vl == 0xFFFFFFFF) {
3156
        /* 'Just' mark header as the length of the item */
3157
34
        tag_ptree = proto_tree_add_subtree(tree, tvb, offset_tag, offset - offset_tag, ett, &tag_pitem, tag_summary);
3158
34
        vl_max = 0;         /* We don't know who long this sequence/item is */
3159
34
    }
3160
125
    else if (offset + vl <= endpos) {
3161
        /* Show real length of item */
3162
90
        tag_ptree = proto_tree_add_subtree(tree, tvb, offset_tag, offset + vl - offset_tag, ett, &tag_pitem, tag_summary);
3163
90
        vl_max = vl;
3164
90
    }
3165
35
    else {
3166
        /* Value is longer than what we have in the PDV, -> we do have a OPEN tag */
3167
35
        tag_ptree = proto_tree_add_subtree(tree, tvb, offset_tag, endpos - offset_tag, ett, &tag_pitem, tag_summary);
3168
35
        vl_max = endpos - offset;
3169
35
    }
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
159
    if (is_sequence | is_item) {
3175
3176
34
        if (global_dcm_seq_subtree) {
3177
            /* Use different ett_ for Sequences & Items, so that fold/unfold state makes sense */
3178
34
            seq_ptree = tag_ptree;
3179
34
            if (!global_dcm_tag_subtree) {
3180
34
                tag_ptree = NULL;
3181
34
            }
3182
34
        }
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
34
    }
3190
125
    else {
3191
        /* For tags */
3192
125
        if (!global_dcm_tag_subtree) {
3193
125
            tag_ptree = NULL;
3194
125
        }
3195
125
    }
3196
3197
    /*  ---------------------------------------------------------------
3198
        Tag details as separate items
3199
        ---------------------------------------------------------------
3200
    */
3201
3202
159
    proto_tree_add_uint_format_value(tag_ptree, hf_dcm_tag, tvb, offset_tag, 4,
3203
159
        ((unsigned)grp << 16) | elm, "%04x,%04x (%s)", grp, elm, tag_def->description);
3204
3205
    /* Add VR to tag detail, except for sequence items */
3206
159
    if (!is_item)  {
3207
159
        if (is_implicit) {
3208
            /* Select header, since no VR is present in implicit syntax */
3209
159
            proto_tree_add_string(tag_ptree, hf_dcm_tag_vr, tvb, offset_tag, 4, vr);
3210
159
        }
3211
0
        else {
3212
0
            proto_tree_add_string(tag_ptree, hf_dcm_tag_vr, tvb, offset_vr,  2, vr);
3213
0
        }
3214
159
    }
3215
3216
    /* Add length to tag detail */
3217
159
    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
159
    if ((is_sequence || is_item) && (vl > 0)) {
3225
        /* Sequence or Item Start */
3226
3227
34
        uint32_t endpos_item = 0;
3228
34
        bool local_end_of_seq_or_item = false;
3229
34
        bool is_first_desc = true;
3230
3231
34
        const char *item_description = NULL;       /* Will be allocated as wmem packet scope memory in dissect_dcm_tag() */
3232
3233
34
        if (vl == 0xFFFFFFFF) {
3234
            /* Undefined length */
3235
3236
34
            increment_dissection_depth(pinfo);
3237
91
            while ((!local_end_of_seq_or_item) && (!pdv->open_tag.is_header_fragmented) && (offset < endpos)) {
3238
3239
57
                offset = dissect_dcm_tag(tvb, pinfo, seq_ptree, pdv, offset, endpos, false, &item_description, &local_end_of_seq_or_item);
3240
3241
57
                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
57
            }
3246
34
            decrement_dissection_depth(pinfo);
3247
34
        }
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
34
    } /*  if ((is_sequence || is_item) && (vl > 0)) */
3265
125
    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
125
    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
125
    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
64
        tag_value = wmem_strdup(pinfo->pool, "<Empty>");
3281
64
    }
3282
61
    else if (vl > vl_max) {
3283
        /* Tag is longer than the PDV/PDU. Don't perform any decoding */
3284
3285
35
        char *tag_desc;
3286
3287
35
        proto_tree_add_bytes_format(tag_ptree, hf_dcm_tag_value_byte, tvb, offset, vl_max,
3288
35
            NULL, "%-8.8sBytes %d - %d [start]", "Value:", 1, vl_max);
3289
3290
35
        tag_value = wmem_strdup_printf(pinfo->pool, "<Bytes %d - %d, start>", 1, vl_max);
3291
35
        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
35
        tag_desc = dcm_tag_summary(pinfo, grp, elm, vl, tag_def->description, vr, tag_def->is_retired, is_implicit);
3301
3302
35
        if (pdv->open_tag.desc == NULL) {
3303
30
            pdv->open_tag.is_value_fragmented = true;
3304
30
            pdv->open_tag.desc = wmem_strdup(wmem_file_scope(), tag_desc);
3305
30
            pdv->open_tag.len_total = vl;
3306
30
            pdv->open_tag.len_remaining = vl - vl_max;
3307
30
        }
3308
35
    }
3309
26
    else {
3310
        /* Regular value. Identify the type, decode and display */
3311
3312
26
        increment_dissection_depth(pinfo);
3313
26
        offset = dissect_dcm_tag_value(tvb, pinfo, tag_ptree, pdv, offset, grp, elm, vl, vl_max, vr, &tag_value);
3314
26
        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
26
        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
26
        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
26
        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
26
    }
3333
3334
3335
    /* -------------------------------------------------------------------
3336
       Add the value to the already constructed item
3337
       -------------------------------------------------------------------
3338
    */
3339
3340
159
    proto_item_append_text(tag_pitem, " %s", tag_value);
3341
3342
159
    if (tag_def->add_to_summary) {
3343
0
        *tag_description = wmem_strdup(pinfo->pool, g_strstrip(tag_value));
3344
0
    }
3345
3346
159
    return offset;
3347
160
}
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
46
{
3356
3357
46
    proto_item *pitem = NULL;
3358
3359
46
    uint32_t tag_value_fragment_len = 0;
3360
3361
46
    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
10
        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
1
            tag_value_fragment_len = pdv->prev->open_tag.len_remaining;
3372
1
            pdv->is_corrupt = false;
3373
1
        }
3374
9
        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
9
            tag_value_fragment_len = endpos - offset;
3381
9
            pdv->is_corrupt = true;
3382
9
        }
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
10
        if (pdv->is_corrupt) {
3403
9
            pitem = proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
3404
9
                offset, tag_value_fragment_len, NULL,
3405
9
                "%s <incomplete>", pdv->prev->open_tag.desc);
3406
3407
9
            expert_add_info(pinfo, pitem, &ei_dcm_data_tag);
3408
3409
9
        }
3410
1
        else {
3411
1
            proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
3412
1
                offset, tag_value_fragment_len, NULL,
3413
1
                "%s <Bytes %d - %d, %s>", pdv->prev->open_tag.desc,
3414
1
                pdv->prev->open_tag.len_total - pdv->prev->open_tag.len_remaining + 1,
3415
1
                pdv->prev->open_tag.len_total - pdv->prev->open_tag.len_remaining + tag_value_fragment_len,
3416
1
                (pdv->prev->open_tag.len_remaining > tag_value_fragment_len ? "continuation" : "end") );
3417
1
        }
3418
3419
10
        offset += tag_value_fragment_len;
3420
10
        *is_first_tag = false;
3421
10
    }
3422
3423
46
    return offset;
3424
46
}
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
54
{
3444
54
    const char *tag_value = NULL;
3445
54
    bool dummy = false;
3446
54
    uint32_t startpos = offset;
3447
54
    uint32_t endpos = 0;
3448
3449
54
    endpos = offset + pdv_body_len;
3450
3451
54
    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
54
        if (pdv->syntax == DCM_UNK) {
3455
            /* Eventually, we will have a syntax detector. Until then, don't decode */
3456
3457
8
            proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
3458
8
                offset, pdv_body_len, NULL,
3459
8
                "(%04x,%04x) %-8x Unparsed data", 0, 0, pdv_body_len);
3460
8
        }
3461
46
        else {
3462
3463
46
            bool is_first_tag = true;
3464
3465
            /* Treat the left overs */
3466
46
            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
150
            while (offset < endpos) {
3470
104
                offset = dissect_dcm_tag(tvb, pinfo, tree, pdv, offset, endpos, is_first_tag, &tag_value, &dummy);
3471
104
                is_first_tag = false;
3472
104
            }
3473
46
        }
3474
54
    }
3475
3476
54
    *pdv_description = pdv->desc;
3477
3478
54
    if (pdv->is_command) {
3479
3480
39
        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
39
        else if (global_dcm_cmd_details) {
3490
            /* Show command details in header */
3491
3492
39
            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
39
            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
39
        }
3517
39
    }
3518
3519
54
    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
54
    return endpos;
3537
54
}
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
69
{
3549
3550
69
    conversation_t  *conv = NULL;
3551
3552
69
    dcm_state_pdv_t *pdv = NULL;
3553
3554
69
    tvbuff_t *combined_tvb = NULL;
3555
69
    fragment_head *head = NULL;
3556
3557
69
    uint32_t reassembly_id;
3558
69
    uint32_t pdv_body_len;
3559
3560
69
    pdv_body_len = pdv_len-2;
3561
3562
    /* Dissect Context ID, Find PDV object, Decode Command/Data flag and More Fragments flag */
3563
69
    offset = dissect_dcm_pdv_header(tvb, pinfo, tree, assoc, offset, &pdv);
3564
3565
69
    if (global_dcm_reassemble)
3566
69
    {
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
69
        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
69
        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
69
        reassembly_id = (((conv->conv_index) & 0x000FFFFF) << 12) +
3584
69
                        ((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
69
        head = fragment_add_seq_next(
3588
69
            &dcm_pdv_reassembly_table,
3589
69
            tvb,
3590
69
            offset,
3591
69
            pinfo,
3592
69
            reassembly_id,
3593
69
            NULL,
3594
69
            pdv_body_len,
3595
69
            !(pdv->is_last_fragment));
3596
3597
69
        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
51
            offset = dissect_dcm_pdv_body(tvb, pinfo, tree, assoc, pdv, offset, pdv_body_len, pdv_description);
3603
51
        }
3604
18
        else
3605
18
        {
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
18
            combined_tvb = process_reassembled_data(
3610
18
                tvb,
3611
18
                offset,
3612
18
                pinfo,
3613
18
                "Reassembled PDV",
3614
18
                head,
3615
18
                &dcm_pdv_fragment_items,
3616
18
                NULL,
3617
18
                tree);
3618
3619
18
            if (combined_tvb == NULL) {
3620
                /* Just show this as a fragment */
3621
3622
15
                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
15
                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
15
                    *pdv_description = wmem_strdup(pinfo->pool, "PDV Fragment");
3638
15
                }
3639
3640
15
                offset += pdv_body_len;
3641
15
            }
3642
3
            else {
3643
                /* Decode reassembled data. This needs to be += */
3644
3
                offset += dissect_dcm_pdv_body(combined_tvb, pinfo, tree, assoc, pdv, 0, tvb_captured_length(combined_tvb), pdv_description);
3645
3
            }
3646
18
        }
3647
69
    }
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
69
    return offset;
3659
69
}
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
52
{
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
52
    proto_tree *pdv_ptree;      /* Tree for item details */
3679
52
    proto_item *pdv_pitem, *pdvlen_item;
3680
3681
52
    wmem_strbuf_t *buf_desc = NULL;    /* PDU description */
3682
52
    char   *pdv_description = NULL;
3683
3684
52
    uint32_t endpos = 0;
3685
52
    uint32_t pdv_len = 0;
3686
3687
52
    endpos = offset + pdu_len;
3688
3689
    /* Loop through multiple PDVs */
3690
121
    while (offset < endpos) {
3691
3692
101
        pdv_len = tvb_get_ntohl(tvb, offset);
3693
3694
101
        pdv_ptree = proto_tree_add_subtree(tree, tvb, offset, pdv_len+4, ett_dcm_data_pdv, &pdv_pitem, "PDV");
3695
3696
101
        pdvlen_item = proto_tree_add_item(pdv_ptree, hf_dcm_pdv_len, tvb, offset, 4, ENC_BIG_ENDIAN);
3697
101
        offset += 4;
3698
3699
101
        if ((pdv_len + 4 > pdu_len)  || (pdv_len + 4 < pdv_len)) {
3700
24
            expert_add_info_format(pinfo, pdvlen_item, &ei_dcm_pdv_len, "Invalid PDV length (too large)");
3701
24
            return endpos;
3702
24
        }
3703
77
        else if (pdv_len <= 2) {
3704
5
            expert_add_info_format(pinfo, pdvlen_item, &ei_dcm_pdv_len, "Invalid PDV length (too small)");
3705
5
            return endpos;
3706
5
        }
3707
72
        else if (((pdv_len >> 1) << 1) != pdv_len) {
3708
3
            expert_add_info_format(pinfo, pdvlen_item, &ei_dcm_pdv_len, "Invalid PDV length (not even)");
3709
3
            return endpos;
3710
3
        }
3711
3712
69
        offset = dissect_dcm_pdv_fragmented(tvb, pinfo, pdv_ptree, assoc, offset, pdv_len, &pdv_description);
3713
3714
        /* The following doesn't seem to work anymore */
3715
69
        if (pdv_description) {
3716
62
            if (buf_desc == NULL) {
3717
46
                buf_desc = wmem_strbuf_new(pinfo->pool, pdv_description);
3718
46
            }
3719
16
            else {
3720
16
                wmem_strbuf_append_printf(buf_desc, ", %s", pdv_description);
3721
16
            }
3722
62
            proto_item_append_text(pdv_pitem, ", %s", pdv_description);
3723
62
        }
3724
3725
69
    }
3726
3727
    /* wmem_strbuf_finalize returns NULL on NULL input. */
3728
20
    *pdu_data_description = wmem_strbuf_finalize(buf_desc);
3729
3730
20
    return offset;
3731
52
}
3732
3733
3734
/*
3735
Test for DICOM traffic.
3736
3737
- Minimum 10 Bytes
3738
- Look for the association request
3739
- Check PDU size vs TCP payload size
3740
3741
Since used in heuristic mode, be picky for performance reasons.
3742
We are called in static mode, once we decoded the association request and called conversation_set_dissector()
3743
They we can be more liberal on the packet selection
3744
*/
3745
static bool
3746
test_dcm(tvbuff_t *tvb)
3747
3.08k
{
3748
3749
3.08k
    uint8_t pdu_type;
3750
3.08k
    uint32_t pdu_len;
3751
3.08k
    uint16_t vers;
3752
3753
    /*
3754
    Ensure that the tvb_captured_length is big enough before fetching the values.
3755
    Otherwise it can trigger an exception during the heuristic check,
3756
    preventing next heuristic dissectors from being called
3757
3758
    tvb_reported_length() is the real size of the packet as transmitted on the wire
3759
    tvb_captured_length() is the number of bytes captured (so you always have captured <= reported).
3760
3761
    The 10 bytes represent an association request header including the 2 reserved bytes not used below
3762
    In the captures at hand, the parsing result was equal.
3763
    */
3764
3765
3.08k
    if (tvb_captured_length(tvb) < 8) {
3766
301
        return false;
3767
301
    }
3768
2.78k
    if (tvb_reported_length(tvb) < 10) {
3769
120
        return false;
3770
120
    }
3771
3772
2.66k
    pdu_type = tvb_get_uint8(tvb, 0);
3773
2.66k
    pdu_len = tvb_get_ntohl(tvb, 2);
3774
2.66k
    vers = tvb_get_ntohs(tvb, 6);
3775
3776
    /* Exit, if not an association request at version 1 */
3777
2.66k
    if (!(pdu_type == 1 && vers == 1)) {
3778
2.65k
        return false;
3779
2.65k
    }
3780
3781
    /* Exit if TCP payload is bigger than PDU length (plus header)
3782
    OK for PRESENTATION_DATA, questionable for ASSOCIATION requests
3783
    */
3784
8
    if (tvb_reported_length(tvb) > pdu_len + 6) {
3785
2
        return false;
3786
2
    }
3787
3788
6
    return true;
3789
8
}
3790
3791
/*
3792
Main function to decode DICOM traffic. Supports reassembly of TCP packets.
3793
*/
3794
static int
3795
dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bool is_port_static)
3796
127
{
3797
3798
127
    uint8_t pdu_type = 0;
3799
127
    uint32_t pdu_start = 0;
3800
127
    uint32_t pdu_len = 0;
3801
127
    uint32_t tlen = 0;
3802
3803
127
    unsigned offset = 0;
3804
3805
    /*
3806
        TCP packets are assembled well by wireshark in conjunction with the dissectors.
3807
3808
        Therefore, we will only see properly aligned PDUs, at the beginning of the buffer.
3809
        So if the buffer does not start with the PDU header, it's not DICOM traffic.
3810
3811
        Do the byte checking as early as possible.
3812
        The heuristic hook requires an association request
3813
3814
        DICOM PDU are nice, but need to be managed
3815
3816
        We can have any combination:
3817
        - One or more DICOM PDU per TCP packet
3818
        - PDU split over different TCP packets
3819
        - And both together, i.e. some complete PDUs and then a fraction of a new PDU in a TCP packet
3820
3821
        This function will handle multiple PDUs per TCP packet and will ask for more data,
3822
        if the last PDU does not fit
3823
3824
        It does not reassemble fragmented PDVs by purpose, since the Tag Value parsing needs to be done
3825
        per Tag, and PDU recombination here would
3826
        a) need to eliminate PDU/PDV/Ctx header (12 bytes)
3827
        b) not show the true DICOM logic in transfer
3828
3829
        The length check is tricky. If not a PDV continuation, 10 Bytes are required. For PDV continuation
3830
        anything seems to be possible, depending on the buffer alignment of the sending process.
3831
3832
    */
3833
3834
127
    tlen = tvb_reported_length(tvb);
3835
3836
127
    pdu_type = tvb_get_uint8(tvb, 0);
3837
127
    if (pdu_type == 0 || pdu_type > 7)          /* Wrong PDU type. 'Or' is slightly more efficient than 'and' */
3838
30
        return 0;                               /* No bytes taken from the stack */
3839
3840
97
    if (is_port_static) {
3841
        /* Port is defined explicitly, or association request was previously found successfully.
3842
           Be more tolerant on minimum packet size. Also accept < 6
3843
        */
3844
3845
91
        if (tlen < 6) {
3846
            /* we need 6 bytes at least to get PDU length */
3847
1
            pinfo->desegment_offset = offset;
3848
1
            pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
3849
1
            return tvb_captured_length(tvb);
3850
1
        }
3851
91
    }
3852
3853
3854
    /* Passing this point, we should always have tlen >= 6 */
3855
3856
96
    pdu_len = tvb_get_ntohl(tvb, 2);
3857
96
    if (pdu_len < 4)                /* The smallest PDUs are ASSOC Rejects & Release messages */
3858
3
        return 0;
3859
3860
    /* Mark it. This is a DICOM packet */
3861
3862
93
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "DICOM");
3863
3864
     /* Process all PDUs in the buffer */
3865
311
    while (pdu_start < tlen) {
3866
272
        uint32_t old_pdu_start;
3867
3868
272
        if ((pdu_len+6) > (tlen-offset)) {
3869
3870
            /*  PDU is larger than the remaining packet (buffer), therefore request whole PDU
3871
                The next time this function is called, tlen will be equal to pdu_len
3872
            */
3873
3874
53
            pinfo->desegment_offset = offset;
3875
53
            pinfo->desegment_len = (pdu_len+6) - (tlen-offset);
3876
53
            return tvb_captured_length(tvb);
3877
53
        }
3878
3879
        /* Process a whole PDU */
3880
219
        offset=dissect_dcm_pdu(tvb, pinfo, tree, pdu_start);
3881
3882
        /* Next PDU */
3883
219
        old_pdu_start = pdu_start;
3884
219
        pdu_start =  pdu_start + pdu_len + 6;
3885
219
        if (pdu_start <= old_pdu_start) {
3886
1
            expert_add_info_format(pinfo, NULL, &ei_dcm_invalid_pdu_length, "Invalid PDU length (%u)", pdu_len);
3887
1
            break;
3888
1
        }
3889
3890
218
        if (pdu_start < tlen - 6) {
3891
            /* we got at least 6 bytes of the next PDU still in the buffer */
3892
168
             pdu_len = tvb_get_ntohl(tvb, pdu_start+2);
3893
168
        }
3894
50
        else {
3895
50
            pdu_len = 0;
3896
50
        }
3897
218
    }
3898
40
    return offset;
3899
93
}
3900
3901
/*
3902
Callback function used to register
3903
*/
3904
static int
3905
dissect_dcm_static(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
3906
121
{
3907
    /* Less checking on ports that match */
3908
121
    return dissect_dcm_main(tvb, pinfo, tree, true);
3909
121
}
3910
3911
/*
3912
Test for an Association Request. Decode, when successful.
3913
*/
3914
static bool
3915
dissect_dcm_heuristic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
3916
3.08k
{
3917
3918
    /* This will be potentially called for every packet */
3919
3920
3.08k
    if (!test_dcm(tvb))
3921
3.07k
        return false;
3922
3923
    /*
3924
    Conversation_set_dissector() is called inside dcm_state_get() once
3925
    we have enough details. From there on, we will be 'static'
3926
    */
3927
3928
6
    if (dissect_dcm_main(tvb, pinfo, tree, false) == 0) {
3929
        /* there may have been another reason why it is not DICOM */
3930
0
        return false;
3931
0
    }
3932
3933
6
    return true;
3934
3935
6
}
3936
3937
/*
3938
Only set a valued with col_set_str() if it does not yet exist.
3939
(In a multiple PDV scenario, col_set_str() actually appends for the subsequent calls)
3940
*/
3941
static void col_set_str_conditional(column_info *cinfo, const int el, const char* str)
3942
52
{
3943
52
    const char *col_string = col_get_text(cinfo, el);
3944
3945
52
    if (col_string == NULL || !g_str_has_prefix(col_string, str))
3946
52
    {
3947
52
        col_add_str(cinfo, el, str);
3948
52
    }
3949
52
}
3950
3951
/*
3952
CSV add a value to a column, if it does not exist yet
3953
*/
3954
static void col_append_str_conditional(column_info *cinfo, const int el, const char* str)
3955
11
{
3956
11
    const char *col_string = col_get_text(cinfo, el);
3957
3958
11
    if (col_string == NULL || !g_strrstr(col_string, str))
3959
11
    {
3960
11
        col_append_fstr(cinfo, el, ", %s", str);
3961
11
    }
3962
11
}
3963
3964
/*
3965
Dissect a single DICOM PDU. Can be an association or a data package. Creates a tree item.
3966
*/
3967
static uint32_t
3968
dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, uint32_t offset)
3969
219
{
3970
219
    proto_tree *dcm_ptree=NULL;     /* Root DICOM tree and its item */
3971
219
    proto_item *dcm_pitem=NULL;
3972
3973
219
    dcm_state_t *dcm_data=NULL;
3974
219
    dcm_state_assoc_t *assoc=NULL;
3975
3976
219
    uint8_t pdu_type=0;
3977
219
    uint32_t pdu_len=0;
3978
3979
219
    char *pdu_data_description=NULL;
3980
3981
    /* Get or create conversation. Used to store context IDs and xfer Syntax */
3982
3983
219
    dcm_data = dcm_state_get(pinfo, true);
3984
219
    if (dcm_data == NULL) {     /* Internal error. Failed to create main DICOM data structure */
3985
0
        return offset;
3986
0
    }
3987
3988
219
    dcm_pitem = proto_tree_add_item(tree, proto_dcm, tvb, offset, -1, ENC_NA);
3989
219
    dcm_ptree = proto_item_add_subtree(dcm_pitem, ett_dcm);
3990
3991
    /* PDU type is only one byte, then one byte reserved */
3992
219
    proto_tree_add_item_ret_uint8(dcm_ptree, hf_dcm_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN, &pdu_type);
3993
219
    offset += 2;
3994
3995
219
    proto_tree_add_item_ret_uint(dcm_ptree, hf_dcm_pdu_len, tvb, offset, 4, ENC_BIG_ENDIAN, &pdu_len);
3996
219
    offset += 4;
3997
3998
    /* Find previously detected association, else create a new one object*/
3999
219
    assoc = dcm_state_assoc_get(dcm_data, pinfo->num, true);
4000
4001
219
    if (assoc == NULL) {        /* Internal error. Failed to create association structure */
4002
0
        return offset;
4003
0
    }
4004
4005
219
    if (pdu_type == 4) {
4006
4007
52
        col_set_str_conditional(pinfo->cinfo, COL_INFO, "P-DATA");
4008
4009
        /* Everything that needs to be shown in any UI column (like COL_INFO)
4010
           needs to be calculated also with tree == null
4011
        */
4012
52
        offset = dissect_dcm_pdu_data(tvb, pinfo, dcm_ptree, assoc, offset, pdu_len, &pdu_data_description);
4013
4014
52
        if (pdu_data_description) {
4015
11
            proto_item_append_text(dcm_pitem, ", %s", pdu_data_description);
4016
11
            col_append_str_conditional(pinfo->cinfo, COL_INFO, pdu_data_description);
4017
11
        }
4018
52
    }
4019
167
    else {
4020
4021
        /* Decode Association request, response, reject, abort details */
4022
167
        offset = dissect_dcm_assoc_header(tvb, pinfo, dcm_ptree, offset, assoc, pdu_type, pdu_len);
4023
167
    }
4024
4025
219
    return offset;          /* return the number of processed bytes */
4026
219
}
4027
4028
4029
/*
4030
Register the protocol with Wireshark
4031
*/
4032
void
4033
proto_register_dcm(void)
4034
16
{
4035
16
    static hf_register_info hf[] = {
4036
16
    { &hf_dcm_pdu_type, { "PDU Type", "dicom.pdu.type",
4037
16
        FT_UINT8, BASE_HEX, VALS(dcm_pdu_ids), 0, NULL, HFILL } },
4038
16
    { &hf_dcm_pdu_len, { "PDU Length", "dicom.pdu.len",
4039
16
        FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4040
4041
16
    { &hf_dcm_assoc_version, { "Protocol Version", "dicom.assoc.version",
4042
16
        FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4043
16
    { &hf_dcm_assoc_called, { "Called  AE Title", "dicom.assoc.ae.called",
4044
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4045
16
    { &hf_dcm_assoc_calling, { "Calling AE Title", "dicom.assoc.ae.calling",
4046
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4047
16
    { &hf_dcm_assoc_reject_result, { "Result", "dicom.assoc.reject.result",
4048
16
        FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4049
16
    { &hf_dcm_assoc_reject_source, { "Source", "dicom.assoc.reject.source",
4050
16
        FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4051
16
    { &hf_dcm_assoc_reject_reason, { "Reason", "dicom.assoc.reject.reason",
4052
16
        FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4053
16
    { &hf_dcm_assoc_abort_source, { "Source", "dicom.assoc.abort.source",
4054
16
        FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4055
16
    { &hf_dcm_assoc_abort_reason, { "Reason", "dicom.assoc.abort.reason",
4056
16
        FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4057
16
    { &hf_dcm_assoc_item_type, { "Item Type", "dicom.assoc.item.type",
4058
16
        FT_UINT8, BASE_HEX, VALS(dcm_assoc_item_type), 0, NULL, HFILL } },
4059
16
    { &hf_dcm_assoc_item_len, { "Item Length", "dicom.assoc.item.len",
4060
16
        FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4061
4062
16
    { &hf_dcm_actx, { "Application Context", "dicom.actx",
4063
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4064
16
    { &hf_dcm_pctx_id, { "Presentation Context ID", "dicom.pctx.id",
4065
16
        FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
4066
16
    { &hf_dcm_pctx_result, { "Presentation Context Result", "dicom.pctx.result",
4067
16
        FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
4068
16
    { &hf_dcm_pctx_abss_syntax, { "Abstract Syntax", "dicom.pctx.abss.syntax",
4069
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4070
16
    { &hf_dcm_pctx_xfer_syntax, { "Transfer Syntax", "dicom.pctx.xfer.syntax",
4071
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4072
16
    { &hf_dcm_info, { "User Info", "dicom.userinfo",
4073
16
        FT_NONE, BASE_NONE, NULL, 0, "This field contains the ACSE User Information Item of the A-ASSOCIATErequest.", HFILL } },
4074
16
    { &hf_dcm_info_uid, { "Implementation Class UID", "dicom.userinfo.uid",
4075
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4076
16
    { &hf_dcm_info_version, { "Implementation Version", "dicom.userinfo.version",
4077
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4078
16
    { &hf_dcm_info_extneg, { "Extended Negotiation", "dicom.userinfo.extneg",
4079
16
        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 } },
4080
16
    { &hf_dcm_info_extneg_sopclassuid_len, { "SOP Class UID Length", "dicom.userinfo.extneg.sopclassuid.len",
4081
16
        FT_UINT16, BASE_DEC, NULL, 0, "This field contains the length of the SOP Class UID in the Extended Negotiation Sub-Item.", HFILL } },
4082
16
    { &hf_dcm_info_extneg_sopclassuid, { "SOP Class UID", "dicom.userinfo.extneg.sopclassuid",
4083
16
        FT_STRING, BASE_NONE, NULL, 0, "This field contains the SOP Class UID in the Extended Negotiation Sub-Item.", HFILL } },
4084
16
    { &hf_dcm_info_extneg_relational_query, { "Relational-queries", "dicom.userinfo.extneg.relational",
4085
16
        FT_UINT8, BASE_HEX, NULL, 0, "This field indicates, if relational queries are supported.", HFILL } },
4086
16
    { &hf_dcm_info_extneg_date_time_matching, { "Combined Date-Time matching", "dicom.userinfo.extneg.datetimematching",
4087
16
        FT_UINT8, BASE_HEX, NULL, 0, "This field indicates, if combined date-time matching is supported.", HFILL } },
4088
16
    { &hf_dcm_info_extneg_fuzzy_semantic_matching, { "Fuzzy semantic matching", "dicom.userinfo.extneg.fuzzymatching",
4089
16
        FT_UINT8, BASE_HEX, NULL, 0, "This field indicates, if fuzzy semantic matching of person names is supported.", HFILL } },
4090
16
    { &hf_dcm_info_extneg_timezone_query_adjustment, { "Timezone query adjustment", "dicom.userinfo.extneg.timezone",
4091
16
        FT_UINT8, BASE_HEX, NULL, 0, "This field indicates, if timezone query adjustment is supported.", HFILL } },
4092
16
    { &hf_dcm_info_rolesel, { "SCP/SCU Role Selection", "dicom.userinfo.rolesel",
4093
16
        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 } },
4094
16
    { &hf_dcm_info_rolesel_sopclassuid_len, { "SOP Class UID Length", "dicom.userinfo.rolesel.sopclassuid.len",
4095
16
        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 } },
4096
16
    { &hf_dcm_info_rolesel_sopclassuid, { "SOP Class UID", "dicom.userinfo.rolesel.sopclassuid",
4097
16
        FT_STRING, BASE_NONE, NULL, 0, "This field contains the SOP Class UID in the SCP/SCU Role Selection Sub-Item.", HFILL } },
4098
16
    { &hf_dcm_info_rolesel_scurole, { "SCU-role", "dicom.userinfo.rolesel.scurole",
4099
16
        FT_UINT8, BASE_HEX, NULL, 0, "This field contains the SCU-role as defined for the Association-requester.", HFILL } },
4100
16
    { &hf_dcm_info_rolesel_scprole, { "SCP-role", "dicom.userinfo.rolesel.scprole",
4101
16
        FT_UINT8, BASE_HEX, NULL, 0, "This field contains the SCP-role as defined for the Association-requester.", HFILL } },
4102
16
    { &hf_dcm_info_async_neg, { "Asynchronous Operations (and sub-operations) Window Negotiation", "dicom.userinfo.asyncneg",
4103
16
        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 } },
4104
16
    { &hf_dcm_info_async_neg_max_num_ops_inv, { "Maximum-number-operations-invoked", "dicom.userinfo.asyncneg.maxnumopsinv",
4105
16
        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 } },
4106
16
    { &hf_dcm_info_async_neg_max_num_ops_per, { "Maximum-number-operations-performed", "dicom.userinfo.asyncneg.maxnumopsper",
4107
16
        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 } },
4108
16
    { &hf_dcm_info_unknown, { "Unknown", "dicom.userinfo.unknown",
4109
16
        FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
4110
16
    { &hf_dcm_assoc_item_data, { "Unknown Data", "dicom.userinfo.data",
4111
16
        FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
4112
16
    { &hf_dcm_info_user_identify, { "User Identify", "dicom.userinfo.user_identify",
4113
16
        FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
4114
16
    { &hf_dcm_info_user_identify_type, { "Type", "dicom.userinfo.user_identify.type",
4115
16
        FT_UINT8, BASE_DEC, VALS(user_identify_type_vals), 0, NULL, HFILL } },
4116
16
    { &hf_dcm_info_user_identify_response_requested, { "Response Requested", "dicom.userinfo.user_identify.response_requested",
4117
16
        FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4118
16
    { &hf_dcm_info_user_identify_primary_field_length, { "Primary Field Length", "dicom.userinfo.user_identify.primary_field_length",
4119
16
        FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4120
16
    { &hf_dcm_info_user_identify_primary_field, { "Primary Field", "dicom.userinfo.user_identify.primary_field",
4121
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4122
16
    { &hf_dcm_info_user_identify_secondary_field_length, { "Secondary Field Length", "dicom.userinfo.user_identify.secondary_field_length",
4123
16
        FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4124
16
    { &hf_dcm_info_user_identify_secondary_field, { "Secondary Field", "dicom.userinfo.user_identify.secondary_field",
4125
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4126
16
    { &hf_dcm_pdu_maxlen, { "Max PDU Length", "dicom.max_pdu_len",
4127
16
        FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4128
16
    { &hf_dcm_pdv_len, { "PDV Length", "dicom.pdv.len",
4129
16
        FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4130
16
    { &hf_dcm_pdv_ctx, { "PDV Context", "dicom.pdv.ctx",
4131
16
        FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
4132
16
    { &hf_dcm_pdv_flags, { "PDV Flags", "dicom.pdv.flags",
4133
16
        FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
4134
16
    { &hf_dcm_data_tag, { "Tag", "dicom.data.tag",
4135
16
        FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
4136
4137
16
    { &hf_dcm_tag, { "Tag", "dicom.tag",
4138
16
        FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
4139
16
    { &hf_dcm_tag_vr, { "VR", "dicom.tag.vr",
4140
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4141
16
    { &hf_dcm_tag_vl, { "Length", "dicom.tag.vl",
4142
16
        FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4143
4144
16
    { &hf_dcm_tag_value_str, { "Value", "dicom.tag.value.str",
4145
16
        FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
4146
16
    { &hf_dcm_tag_value_16s, { "Value", "dicom.tag.value.16s",
4147
16
        FT_INT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4148
16
    { &hf_dcm_tag_value_16u, { "Value", "dicom.tag.value.16u",
4149
16
        FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
4150
16
    { &hf_dcm_tag_value_32s, { "Value", "dicom.tag.value.32s",
4151
16
        FT_INT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4152
16
    { &hf_dcm_tag_value_32u, { "Value", "dicom.tag.value.32u",
4153
16
        FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
4154
16
    { &hf_dcm_tag_value_byte, { "Value", "dicom.tag.value.byte",
4155
16
        FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
4156
4157
    /* Fragment entries */
4158
16
    { &hf_dcm_pdv_fragments,
4159
16
            { "Message fragments", "dicom.pdv.fragments",
4160
16
            FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4161
16
    { &hf_dcm_pdv_fragment,
4162
16
            { "Message fragment", "dicom.pdv.fragment",
4163
16
            FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4164
16
    { &hf_dcm_pdv_fragment_overlap,
4165
16
            { "Message fragment overlap", "dicom.pdv.fragment.overlap",
4166
16
            FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4167
16
    { &hf_dcm_pdv_fragment_overlap_conflicts,
4168
16
            { "Message fragment overlapping with conflicting data",
4169
16
            "dicom.pdv.fragment.overlap.conflicts",
4170
16
            FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4171
16
    { &hf_dcm_pdv_fragment_multiple_tails,
4172
16
            { "Message has multiple tail fragments",
4173
16
            "dicom.pdv.fragment.multiple_tails",
4174
16
            FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4175
16
    { &hf_dcm_pdv_fragment_too_long_fragment,
4176
16
            { "Message fragment too long", "dicom.pdv.fragment.too_long_fragment",
4177
16
            FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4178
16
    { &hf_dcm_pdv_fragment_error,
4179
16
            { "Message defragmentation error", "dicom.pdv.fragment.error",
4180
16
            FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4181
16
    { &hf_dcm_pdv_fragment_count,
4182
16
            { "Message fragment count", "dicom.pdv.fragment_count",
4183
16
            FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
4184
16
    { &hf_dcm_pdv_reassembled_in,
4185
16
            { "Reassembled in", "dicom.pdv.reassembled.in",
4186
16
            FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
4187
16
    { &hf_dcm_pdv_reassembled_length,
4188
16
            { "Reassembled PDV length", "dicom.pdv.reassembled.length",
4189
16
            FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }
4190
16
    };
4191
4192
/* Setup protocol subtree array */
4193
16
    static int *ett[] = {
4194
16
            &ett_dcm,
4195
16
            &ett_assoc,
4196
16
            &ett_assoc_header,
4197
16
            &ett_assoc_actx,
4198
16
            &ett_assoc_pctx,
4199
16
            &ett_assoc_pctx_abss,
4200
16
            &ett_assoc_pctx_xfer,
4201
16
            &ett_assoc_info,
4202
16
            &ett_assoc_info_uid,
4203
16
            &ett_assoc_info_version,
4204
16
            &ett_assoc_info_extneg,
4205
16
            &ett_assoc_info_rolesel,
4206
16
            &ett_assoc_info_async_neg,
4207
16
            &ett_assoc_info_user_identify,
4208
16
            &ett_assoc_info_unknown,
4209
16
            &ett_dcm_data,
4210
16
            &ett_dcm_data_pdv,
4211
16
            &ett_dcm_data_tag,
4212
16
            &ett_dcm_data_seq,
4213
16
            &ett_dcm_data_item,
4214
16
            &ett_dcm_pdv,               /* used for fragments */
4215
16
            &ett_dcm_pdv_fragment,
4216
16
            &ett_dcm_pdv_fragments
4217
16
    };
4218
4219
16
    static ei_register_info ei[] = {
4220
16
        { &ei_dcm_assoc_rejected, { "dicom.assoc.reject", PI_RESPONSE_CODE, PI_WARN, "Association rejected", EXPFILL }},
4221
16
        { &ei_dcm_assoc_aborted, { "dicom.assoc.abort", PI_RESPONSE_CODE, PI_WARN, "Association aborted", EXPFILL }},
4222
16
        { &ei_dcm_no_abstract_syntax, { "dicom.no_abstract_syntax", PI_MALFORMED, PI_ERROR, "No Abstract Syntax provided for this Presentation Context", EXPFILL }},
4223
16
        { &ei_dcm_multiple_abstract_syntax, { "dicom.multiple_abstract_syntax", PI_MALFORMED, PI_ERROR, "More than one Abstract Syntax provided for this Presentation Context", EXPFILL }},
4224
16
        { &ei_dcm_no_transfer_syntax, { "dicom.no_transfer_syntax", PI_MALFORMED, PI_ERROR, "No Transfer Syntax provided for this Presentation Context", EXPFILL }},
4225
16
        { &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 }},
4226
16
        { &ei_dcm_multiple_transfer_syntax, { "dicom.multiple_transfer_syntax", PI_MALFORMED, PI_ERROR, "Only one Transfer Syntax allowed in a Association Response", EXPFILL }},
4227
16
        { &ei_dcm_assoc_item_len, { "dicom.assoc.item.len.invalid", PI_MALFORMED, PI_ERROR, "Invalid Association Item Length", EXPFILL }},
4228
16
        { &ei_dcm_pdv_ctx, { "dicom.pdv.ctx.invalid", PI_MALFORMED, PI_ERROR, "Invalid Presentation Context ID", EXPFILL }},
4229
16
        { &ei_dcm_pdv_flags, { "dicom.pdv.flags.invalid", PI_MALFORMED, PI_ERROR, "Invalid Flags", EXPFILL }},
4230
16
        { &ei_dcm_status_msg, { "dicom.status_msg", PI_RESPONSE_CODE, PI_WARN, "Status Message", EXPFILL }},
4231
16
        { &ei_dcm_data_tag, { "dicom.data.tag.missing", PI_MALFORMED, PI_ERROR, "Early termination of tag. Data is missing", EXPFILL }},
4232
16
        { &ei_dcm_pdv_len, { "dicom.pdv.len.invalid", PI_MALFORMED, PI_ERROR, "Invalid PDV length", EXPFILL }},
4233
16
        { &ei_dcm_invalid_pdu_length, { "dicom.pdu_length.invalid", PI_MALFORMED, PI_ERROR, "Invalid PDU length", EXPFILL }},
4234
16
    };
4235
4236
16
    module_t *dcm_module;
4237
16
    expert_module_t* expert_dcm;
4238
4239
    /* Register the protocol name and description */
4240
16
    proto_dcm = proto_register_protocol("DICOM", "DICOM", "dicom");
4241
4242
    /* Required function calls to register the header fields and subtrees used */
4243
16
    proto_register_field_array(proto_dcm, hf, array_length(hf));
4244
16
    proto_register_subtree_array(ett, array_length(ett));
4245
16
    expert_dcm = expert_register_protocol(proto_dcm);
4246
16
    expert_register_field_array(expert_dcm, ei, array_length(ei));
4247
4248
    /* Allow other dissectors to find this one by name. */
4249
16
    dcm_handle = register_dissector("dicom", dissect_dcm_static, proto_dcm);
4250
4251
16
    dcm_module = prefs_register_protocol(proto_dcm, NULL);
4252
4253
    /*  Used to migrate an older configuration file to a newer one  */
4254
16
    prefs_register_obsolete_preference(dcm_module, "heuristic");
4255
4256
16
    prefs_register_bool_preference(dcm_module, "export_header",
4257
16
            "Create Meta Header on Export",
4258
16
            "Create DICOM File Meta Header according to PS 3.10 on export for PDUs. "
4259
16
            "If the captured PDV does not contain a SOP Class UID and SOP Instance UID "
4260
16
            "(e.g. for command PDVs), wireshark specific ones will be created.",
4261
16
            &global_dcm_export_header);
4262
4263
16
    prefs_register_uint_preference(dcm_module, "export_minsize",
4264
16
            "Min. item size in bytes to export",
4265
16
            "Do not show items below this size in the export list. "
4266
16
            "Set it to 0, to see DICOM commands and responses in the list. "
4267
16
            "Set it higher, to just export DICOM IODs (i.e. CT Images, RT Structures).", 10,
4268
16
            &global_dcm_export_minsize);
4269
4270
16
    prefs_register_bool_preference(dcm_module, "seq_tree",
4271
16
            "Create subtrees for Sequences and Items",
4272
16
            "Create a node for sequences and items, and show children in a hierarchy. "
4273
16
            "De-select this option, if you prefer a flat display or e.g. "
4274
16
            "when using TShark to create a text output.",
4275
16
            &global_dcm_seq_subtree);
4276
4277
16
    prefs_register_bool_preference(dcm_module, "tag_tree",
4278
16
            "Create subtrees for DICOM Tags",
4279
16
            "Create a node for a tag and show tag details as single elements. "
4280
16
            "This can be useful to debug a tag and to allow display filters on these attributes. "
4281
16
            "When using TShark to create a text output, it's better to have it disabled. ",
4282
16
            &global_dcm_tag_subtree);
4283
4284
16
    prefs_register_bool_preference(dcm_module, "cmd_details",
4285
16
            "Show command details in header",
4286
16
            "Show message ID and number of completed, remaining, warned or failed operations in header and info column.",
4287
16
            &global_dcm_cmd_details);
4288
4289
16
    prefs_register_bool_preference(dcm_module, "pdv_reassemble",
4290
16
            "Merge fragmented PDVs",
4291
16
            "Decode all DICOM tags in the last PDV. This will ensure the proper reassembly. "
4292
16
            "De-select, to troubleshoot PDU length issues, or to understand PDV fragmentation. "
4293
16
            "When not set, the decoding may fail and the exports may become corrupt.",
4294
16
            &global_dcm_reassemble);
4295
4296
16
    dicom_eo_tap = register_export_object(proto_dcm, dcm_eo_packet, NULL);
4297
4298
16
    register_init_routine(&dcm_init);
4299
4300
    /* Register processing of fragmented DICOM PDVs */
4301
16
    reassembly_table_register(&dcm_pdv_reassembly_table, &addresses_reassembly_table_functions);
4302
4303
16
}
4304
4305
/*
4306
Register static TCP port range specified in preferences.
4307
Register heuristic search as well.
4308
4309
Statically defined ports take precedence over a heuristic one. I.e., if a foreign protocol claims a port,
4310
where DICOM is running on, we would never be called, by just having the heuristic registration.
4311
4312
This function is also called, when preferences change.
4313
*/
4314
void
4315
proto_reg_handoff_dcm(void)
4316
16
{
4317
    /* Adds a UI element to the preferences dialog. This is the static part. */
4318
16
    dissector_add_uint_range_with_preference("tcp.port", DICOM_DEFAULT_RANGE, dcm_handle);
4319
4320
    /*
4321
    The following shows up as child protocol of 'DICOM' in 'Enable/Disable Protocols ...'
4322
4323
    The registration procedure for dissectors is a two-stage procedure.
4324
4325
    In stage 1, dissectors create tables in which other dissectors can register them. That's the stage in which proto_register_ routines are called.
4326
    In stage 2, dissectors register themselves in tables created in stage 1. That's the stage in which proto_reg_handoff_ routines are called.
4327
4328
    heur_dissector_add() needs to be called in proto_reg_handoff_dcm() function.
4329
    */
4330
4331
16
    heur_dissector_add("tcp", dissect_dcm_heuristic, "DICOM on any TCP port (heuristic)", "dicom_tcp", proto_dcm, HEURISTIC_ENABLE);
4332
16
}
4333
4334
4335
/*
4336
4337
PDU's
4338
01 ASSOC-RQ
4339
 1    1 reserved
4340
 2    4 length
4341
 6    2 protocol version (0x0 0x1)
4342
 8    2 reserved
4343
10   16 dest aetitle
4344
26   16 src  aetitle
4345
42   32 reserved
4346
74    - presentation data value items
4347
4348
02 A-ASSOC-AC
4349
    1 reserved
4350
    4 length
4351
    2 protocol version (0x0 0x1)
4352
    2 reserved
4353
   16 dest aetitle (not checked)
4354
   16 src  aetitle (not checked)
4355
   32 reserved
4356
    - presentation data value items
4357
4358
03 ASSOC-RJ
4359
    1 reserved
4360
    4 length (4)
4361
    1 reserved
4362
    1 result  (1 reject perm, 2 reject transient)
4363
    1 source  (1 service user, 2 service provider, 3 service provider)
4364
    1 reason
4365
        1 == source
4366
            1 no reason given
4367
            2 application context name not supported
4368
            3 calling aetitle not recognized
4369
            7 called aetitle not recognized
4370
        2 == source
4371
            1 no reason given
4372
            2 protocol version not supported
4373
        3 == source
4374
            1 temporary congestion
4375
            2 local limit exceeded
4376
4377
04 P-DATA
4378
 1  1 reserved
4379
 2  4 length
4380
    - (1+) presentation data value (PDV) items
4381
 6      4 length
4382
10      1 Presentation Context ID (odd ints 1 - 255)
4383
        - PDV
4384
11      1 header
4385
            0x01 if set, contains Message Command info, else Message Data
4386
            0x02 if set, contains last fragment
4387
4388
05 A-RELEASE-RQ
4389
    1 reserved
4390
    4 length (4)
4391
    4 reserved
4392
4393
06 A-RELEASE-RP
4394
    1 reserved
4395
    4 length (4)
4396
    4 reserved
4397
4398
07 A-ABORT
4399
    1  reserved
4400
    4  length (4)
4401
    2  reserved
4402
    1  source  (0 = user, 1 = provider)
4403
    1  reason  if 1 == source (0 not spec, 1 unrecognized, 2 unexpected 4 unrecognized param, 5 unexpected param, 6 invalid param)
4404
4405
4406
4407
ITEM's
4408
10 Application Context
4409
    1 reserved
4410
    2 length
4411
    - name
4412
4413
20 Presentation Context
4414
    1 reserved
4415
    2 length
4416
    1 Presentation context id
4417
    3 reserved
4418
    - (1) abstract and (1+) transfer syntax sub-items
4419
4420
21 Presentation Context (Reply)
4421
    1 reserved
4422
    2 length
4423
    1 ID (odd int's 1-255)
4424
    1 reserved
4425
    1 result (0 accept, 1 user-reject, 2 no-reason, 3 abstract not supported, 4- transfer syntax not supported)
4426
    1 reserved
4427
    - (1) type 40
4428
4429
30 Abstract syntax
4430
    1 reserved
4431
    2 length
4432
    - name (<= 64)
4433
4434
40 Transfer syntax
4435
    1 reserved
4436
    2 length
4437
    - name (<= 64)
4438
4439
50 user information
4440
    1 reserved
4441
    2 length
4442
    - user data
4443
4444
51 max length
4445
    1 reserved
4446
    2 length (4)
4447
    4 max PDU lengths
4448
4449
From 3.7 Annex D Association Negotiation
4450
========================================
4451
4452
52 IMPLEMENTATION CLASS UID
4453
    1 Item-type 52H
4454
    1 Reserved
4455
    2 Item-length
4456
    n Implementation-class-uid
4457
4458
55 IMPLEMENTATION VERSION NAME
4459
    1 Item-type 55H
4460
    1 Reserved
4461
    2 Item-length
4462
    n Implementation-version-name
4463
4464
53 ASYNCHRONOUS OPERATIONS WINDOW
4465
    1 Item-type 53H
4466
    1 Reserved
4467
    2 Item-length
4468
    2 Maximum-number-operations-invoked
4469
    2 Maximum-number-operations-performed
4470
4471
54 SCP/SCU ROLE SELECTION
4472
    1 Item-type 54H
4473
    1 Reserved
4474
    2 Item-length (n)
4475
    2 UID-length (m)
4476
    m SOP-class-uid
4477
    1 SCU-role
4478
      0 - non support of the SCU role
4479
      1 - support of the SCU role
4480
    1 SCP-role
4481
      0 - non support of the SCP role
4482
      1 - support of the SCP role.
4483
4484
56 SOP CLASS EXTENDED NEGOTIATION
4485
    1 Item-type 56H
4486
    1 Reserved
4487
    2 Item-Length (n)
4488
    2 SOP-class-uid-length (m)
4489
    m SOP-class-uid
4490
    n-m Service-class-application-information
4491
4492
57 SOP CLASS COMMON EXTENDED NEGOTIATION
4493
    1 Item-type 57H
4494
    1 Sub-item-version
4495
    2 Item-Length
4496
    2 SOP-class-uid-length (m)
4497
    7-x   SOP-class-uid The SOP Class identifier encoded as a UID as defined in PS 3.5.
4498
    (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.
4499
    (x+3)-y Service-class-uid The Service Class identifier encoded as a UID as defined in PS 3.5.
4500
    (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.
4501
    (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.
4502
    (z+1)-k Reserved  Reserved for additional fields of the sub-item. Shall be zero-length for Version 0 of Sub-item definition.
4503
4504
    Table D.3-13
4505
    RELATED-GENERAL-SOP-CLASS-IDENTIFICATION SUB-FIELDS
4506
    Bytes Sub-Field Name    Description of Sub-Field
4507
    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.
4508
    3-n Related-general-sop-class-uid The Related General SOP Class identifier encoded as a UID as defined in PS 3.5.
4509
4510
58 User Identity Negotiation
4511
    1 Item-type 58H
4512
    1 Reserved
4513
    2 Item-length
4514
    1 User-Identity-Type  Field value shall be in the range 1 to 4 with the following meanings:
4515
        1 - Username as a string in UTF-8
4516
        2 - Username as a string in UTF-8 and passcode
4517
        3 - Kerberos Service ticket
4518
        4 - SAML Assertion
4519
        Other values are reserved for future standardization.
4520
    1 Positive-response-requested Field value:
4521
        0 - no response requested
4522
        1 - positive response requested
4523
    2 Primary-field-length  The User-Identity-Length shall contain the length of the User-Identity value.
4524
    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.
4525
    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.
4526
    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.
4527
4528
59 User Identity Negotiation Reply
4529
    1 Item-type 59H
4530
    1 Reserved
4531
    2 Item-length
4532
    5-6 Server-response-length  This field shall contain the number of bytes in the Server-response.  May be zero.
4533
    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.
4534
4535
 */
4536
4537
/*
4538
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
4539
 *
4540
 * Local variables:
4541
 * c-basic-offset: 4
4542
 * tab-width: 8
4543
 * indent-tabs-mode: nil
4544
 * End:
4545
 *
4546
 * vi: set shiftwidth=4 tabstop=8 expandtab:
4547
 * :indentSize=4:tabSize=8:noTabs=true:
4548
 */