Remove an unused variable and cast a string constant into a guchar. Also add newline...
[obnox/wireshark/wip.git] / epan / dissectors / packet-dcm.c
1 /* packet-dcm.c
2  * Routines for DICOM dissection
3  * Copyright 2003, Rich Coe <Richard.Coe@med.ge.com>
4  * Copyright 2008, David Aggeler <david_aggeler@hispeed.ch>
5  *
6  * DICOM communication protocol
7  * http://medical.nema.org/dicom/2008
8  *   DICOM Part 8: Network Communication Support for Message Exchange
9  *
10  * (NOTE: you need to turn on 'Allow subdissector to desegment TCP streams'
11  *        in Preferences/Protocols/TCP Option menu, in order to view
12  *        DICOM packets correctly.
13  *        Also, you might have to turn off tcp.check_checksum if tcp
14  *        detects that the checksum is bad - for example, if you're
15  *        capturing on a network interface that does TCP checksum
16  *        offloading and you're capturing outgoing packets.
17  *        This should probably be documented somewhere besides here.)
18  *
19  * $Id$
20  *
21  * Wireshark - Network traffic analyzer
22  * By Gerald Combs <gerald@wireshark.org>
23  * Copyright 1998 Gerald Combs
24  *
25  * This program is free software; you can redistribute it and/or
26  * modify it under the terms of the GNU General Public License
27  * as published by the Free Software Foundation; either version 2
28  * of the License, or (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program; if not, write to the Free Software
37  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
38  */
39
40 #ifdef HAVE_CONFIG_H
41 # include "config.h"
42 #endif
43
44 /* Notes:
45  * This is my first pass at a Wireshark dissector to display
46  * DICOM (Digital Imaging and Communications in Medicine) packets.
47  *
48  * - It currently displays most of the DICOM packets.
49  *
50  * - I've used it to debug Query/Retrieve, Storage, and Echo protocols.
51  *
52  * - Not all DICOM tags are currently displayed symbolically.
53  *   Unknown tags are displayed as '(unknown)'
54  *   More known tags might be added in the future.
55  *   If the tag data contains a string, it will be displayed.
56  *   Even if the tag contains Explicit VR, it is not currently used to
57  *   symbolically display the data.  Consider this a future enhancement.
58  *
59  * - The 'value to string' routines should probably be hash lookups.
60  *
61  * 9 Nov 2004, Rich Coe
62  * - Fixed the heuristic code -- sometimes a conversation already exists
63  * - Fixed the dissect code to display all the tags in the pdu
64  *
65  * 28 Apr 2005, Rich Coe
66  * - fix memory leak when Assoc packet is processed repeatedly in wireshark
67  *
68  * - removed unused partial packet flag
69  *
70  * - added better support for DICOM VR
71  *      - sequences
72  *      - report actual VR in packet display, if supplied by xfer syntax
73  *      - show that we are not displaying entire tag string with '[...]',
74  *        some tags can hold up to 2^32-1 chars
75  *
76  * - remove my goofy attempt at trying to get access to the fragmented packets
77  *   (anyone have an idea on how to fix this ???)
78  *
79  * - process all the data in the Assoc packet even if display is off
80  *
81  * - limit display of data in Assoc packet to defined size of the data even
82  *   if reported size is larger
83  *
84  * - show the last tag in a packet as [incomplete] if we don't have all the data
85  *
86  * - added framework for reporting DICOM async negotiation (not finished)
87  *   (I'm not aware of an implementation which currently supports this)
88  *
89  *
90  * May 23 2008, David Aggeler
91  *
92  * - Added Class UID lookup, both in the association and in the transfer
93  * - Better hierarchy for items in Association request/response and therefore better overview
94  *   This was a major rework. Abstract Syntax & Transfer Syntax are now children
95  *   of a presentation context and therefore grouped. User Info is now grouped.
96  * - Re-assemble PDVs that span multiple PDUs, i.e fix continuation packets
97  *   This caused significant changes to the data structures
98  * - Added preference with dicom tcp ports, to prevent 'stealing' the converstation
99  *   i.e. don't just rely on heuristic
100  * - Use pinfo->desegment_len instead of tcp_dissect_pdus()
101  * - Returns number of bytes parsed
102  * - For non DICOM packets, do not allocate any memory anymore,
103  * - Added one DISSECTOR_ASSERT() to prevent loop with len==0. More to come
104  * - Heuristic search is optional to save resources for non DICOM users
105  *
106  * - Output naming closer to DICOM Standard
107  * - Variable names closer to Standard
108  * - Protocol in now called DICOM not dcm anymore.
109  * - Fixed type of a few variables to guchar instead of guint8
110  * - Changed some of the length displays to decimal, because the hex value can
111  *   already be seen in the packet and decimal is easier for length calculation
112  *   in respect to TCP
113  *
114  * Jun 17 2008, David Aggeler
115  *
116  * - Support multiple PDVs per PDU
117  * - Better summary, in PDV, PDU header and in INFO Column, e.g. show commands like C-STORE
118  * - Fixed Association Reject (was working before my changes)
119  * - Fixed PDV Continuation with very small packets. Reduced minimum packet length
120  *   from 10 to 2 Bytes for PDU Type 4
121  * - Fixed PDV Continuation. Last packet was not found correctly.
122  * - Fixed complilation warning (build 56 on solaris)
123  * - Fixed tree expansion (hf_dcm_xxx)
124  * - Added expert_add_info() for Assoctiation Reject
125  * - Added expert_add_info() for Assoctiation Abort
126  * - Added expert_add_info() for short PDVs (i.e. last fragment, but PDV is not completed yet)
127  * - Clarified and grouped data structures and its related code (dcmItem, dcmState) to have
128  *   consistent _new() & _get() functions and to be be according to coding conventions
129  * - Added more function declaration to be more consistent
130  * - All dissect_dcm_xx now have (almost) the same parameter order
131  * - Removed DISSECTOR_ASSERT() for packet data errors. Not designed to handle this.
132  * - Handle multiple DICOM Associations in a capture correctly, i.e. if presentation contexts are different.
133  *
134  * Jul 17 2008, David Aggeler
135  *
136  * - Export objects as part 10 compliant DICOM file. Finally, this major milestone has beed reached.
137  * - PDVs are now a child of the PCTX rather than the ASSOC object.
138  * - Fixed PDV continuation for unknown tags (e.g. RT Structure Set)
139  * - Replaced proprietary trim() with g_strstrip()
140  * - Fixed strings that are displayed with /000 (padding of odd length)
141  * - Added expert_add_info() for invalid flags and presentation context IDs
142  *
143  * ****************************************************************************************
144  * - Still ToDo
145  *   Decent error handlung for expert_add_info(), i.e. return value handling and info column text
146  *   Support almost all tags
147  *   Show tags as subtree
148  *   Show Association Headers as individual items
149  *   Cleanup types of offset & position
150  *   Create subtrees for sequences
151  *   Support item 56-59 in Accociation Request
152  */
153
154 #include <stdio.h>
155 #include <stdlib.h>
156 #include <string.h>
157 #include <ctype.h>
158
159 #include <glib.h>
160
161 #include "isprint.h"
162
163 #include <epan/prefs.h>
164 #include <epan/packet.h>
165 #include <epan/emem.h>
166 #include <epan/strutil.h>
167 #include <epan/conversation.h>
168 #include <epan/expert.h>
169 #include <epan/tap.h>
170
171 #include "packet-tcp.h"
172
173 #include "packet-dcm.h"
174
175 #define DICOM_DEFAULT_RANGE "104"
176
177 /* Many thanks to http://medicalconnections.co.uk/ for the GUID */
178 #define WIRESHARK_IMPLEMENTATION_UID                    "1.2.826.0.1.3680043.8.427.10"
179 #define WIRESHARK_MEDIA_STORAGE_SOP_CLASS_UID           "1.2.826.0.1.3680043.8.427.11.1"
180 #define WIRESHARK_MEDIA_STORAGE_SOP_INSTANCE_UID_PREFIX "1.2.826.0.1.3680043.8.427.11.2"
181 #define WIRESHARK_IMPLEMENTATION_VERSION                "WIRESHARK"
182
183 static range_t *global_dcm_tcp_range = NULL;
184 static range_t *global_dcm_tcp_range_backup = NULL;         /* needed to deregister */
185
186 static gboolean global_dcm_heuristic = FALSE;
187 static gboolean global_dcm_header = TRUE;
188
189 /* Initialize the protocol and registered fields */
190 static int proto_dcm = -1;
191
192 static int dicom_eo_tap = -1;
193
194 static int hf_dcm_pdu = -1,
195     hf_dcm_pdu_len = -1,
196     hf_dcm_pdu_type = -1,
197     hf_dcm_assoc_item_type = -1,
198     hf_dcm_assoc_item_len = -1,
199     hf_dcm_actx = -1,
200     hf_dcm_pctx_id = -1,
201     hf_dcm_pctx_result = -1,
202     hf_dcm_pctx_abss_syntax = -1,
203     hf_dcm_pctx_xfer_syntax = -1,
204     hf_dcm_info_uid = -1,
205     hf_dcm_info_version = -1,
206     hf_dcm_pdu_maxlen = -1,
207     hf_dcm_pdv_len = -1,
208     hf_dcm_pdv_ctx = -1,
209     hf_dcm_pdv_flags = -1,
210     hf_dcm_data_tag = -1;
211
212 /* Initialize the subtree pointers */
213 static gint
214     ett_dcm = -1,
215     ett_assoc = -1,
216     ett_assoc_actx = -1,
217     ett_assoc_pctx = -1,
218     ett_assoc_pctx_abss = -1,
219     ett_assoc_pctx_xfer = -1,
220     ett_assoc_info = -1,
221     ett_assoc_info_uid = -1,
222     ett_assoc_info_version = -1,
223     ett_dcm_data = -1,
224     ett_dcm_data_pdv = -1,
225     ett_dcm_data_tag = -1;
226
227 static dissector_handle_t dcm_handle;
228
229 static const value_string dcm_pdu_ids[] = {
230     { 1, "ASSOC Request" },
231     { 2, "ASSOC Accept" },
232     { 3, "ASSOC Reject" },
233     { 4, "Data" },
234     { 5, "RELEASE Request" },
235     { 6, "RELEASE Response" },
236     { 7, "ABORT" },
237     { 0, NULL }
238 };
239
240 static const value_string dcm_assoc_item_type[] = {
241     { 0x10, "Application Context" },
242     { 0x20, "Presentation Context" },
243     { 0x21, "Presentation Context Reply" },
244     { 0x30, "Abstract Syntax" },
245     { 0x40, "Transfer Syntax" },
246     { 0x50, "User Info" },
247     { 0x51, "Max Length" },
248     { 0x52, "Implementation Class UID" },
249     { 0x55, "Implementation Version" },
250     { 0, NULL }
251 };
252
253 /*
254     Per Data PDV store data needed, to allow decoding of tags longer than a PDV
255 */
256 typedef struct dcm_state_pdv {
257
258     struct dcm_state_pdv *next, *prev;
259
260     gboolean initalized;        /* define, wheter open_tag_len, open_tag_rlen and open_tag_desc have been set */
261     guint32  packet_no;         /* Wireshark packet number, where pdv starts */
262     guint32  offset;            /* Offset in packet, where PDV header starts */
263
264     gchar   *desc;              /* PDV description.         se_alloc()  */
265
266     guint8  pctx_id;            /* Reference to used Presentation Context */
267
268     /* Used and filled for Export Object only */
269     gpointer data;              /* Copy of PDV data without any PDU/PDV header */
270     guint32  data_len;          /* Length of this PDV buffer. If >0, memory has been alocated */
271
272     gchar   *sop_class_uid;     /* SOP Class UID.    Set in 1st PDV of a DICOM object. se_alloc() */
273     gchar   *sop_instance_uid;  /* SOP Instance UID. Set in 1st PDV of a DICOM object. se_alloc() */
274     /* End Export use */
275
276     gboolean is_storage;        /* Ture, if the Data PDV is on the context of a storage SOP Class */
277     gboolean is_flagvalid;      /* The following two flags are initalized correctly (TBD if needed) */
278     gboolean is_command;        /* This PDV is a command rather than a data package */
279     gboolean is_last_fragment;  /* Last Fragment bit was set, i.e. termination of an object
280                                    This flag delimits different dicom object in the same
281                                    association */
282     gboolean is_corrupt;        /* Early termination of long PDVs */
283
284     /* Used to reassemble PDVs */
285     guint32 open_tag_len;       /* Tag lenght of 'oversized' tags. Used for display */
286     guint32 open_tag_rlen;      /* Remining tag bytes to 'decoded' as binary data after this PDV */
287     gchar  *open_tag_desc;      /* last decoded description */
288
289 } dcm_state_pdv_t;
290
291 /*
292     Per Presentation Context in an association store data needed, for subsequent decoding
293 */
294 typedef struct dcm_state_pctx {
295
296     struct dcm_state_pctx *next, *prev;
297
298     guint8 id;                  /* 0x20 Presentation Context ID */
299     guchar *abss_uid;           /* 0x30 Abstract syntax */
300     guchar *abss_desc;          /* 0x30 Abstract syntax decoded*/
301     guchar *xfer_uid;           /* 0x40 Acepted Transfer syntax */
302     guchar *xfer_desc;          /* 0x40 Acepted Transfer syntax decoded*/
303     guint8 syntax;              /* Decoded transfer syntax */
304 #define DCM_ILE  0x01           /* implicit, little endian */
305 #define DCM_EBE  0x02           /* explicit, big endian */
306 #define DCM_ELE  0x03           /* explicit, little endian */
307 #define DCM_UNK  0xf0
308
309     dcm_state_pdv_t     *first_pdv,  *last_pdv;         /* List of PDV objects */
310
311 } dcm_state_pctx_t;
312
313
314 typedef struct dcm_state_assoc {
315
316     struct dcm_state_assoc *next, *prev;
317
318     dcm_state_pctx_t    *first_pctx, *last_pctx;        /* List of Presentation context objects */
319
320     guint32 packet_no;                  /* Wireshark packet number, where association starts */
321
322 #define AEEND 16
323     guchar ae_called[1+AEEND];          /* Called  AE tilte in A-ASSOCIATE RQ */
324     guchar ae_calling[1+AEEND];         /* Calling AE tilte in A-ASSOCIATE RQ */
325     guchar ae_called_resp[1+AEEND];     /* Called  AE tilte in A-ASSOCIATE RP */
326     guchar ae_calling_resp[1+AEEND];    /* Calling AE tilte in A-ASSOCIATE RP */
327     guint8 source, result, reason;
328 } dcm_state_assoc_t;
329
330 typedef struct dcm_state {
331
332     struct dcm_state_assoc *first_assoc, *last_assoc;
333
334     gboolean valid;                     /* this conversation is a DICOM conversation */
335
336 } dcm_state_t;
337
338
339 /* Following defines around tags have a potential to be merged */
340 typedef struct dcmTag {
341     guint32 tag;
342     int dtype;
343     const char *desc;
344 #define DCM_TSTR  1
345 #define DCM_TINT2 2
346 #define DCM_TINT4 3
347 #define DCM_TFLT  4
348 #define DCM_TDBL  5
349 #define DCM_TSTAT 6     /* call dcm_rsp2str() on TINT2 */
350 #define DCM_TRET  7
351 #define DCM_TCMD  8
352 #define DCM_SQ    9     /* sequence */
353 #define DCM_OTH   10    /* other */
354 } dcmTag_t;
355
356
357 #define DCM_VR_AE  1  /* Application Entity        */
358 #define DCM_VR_AS  2  /* Age String                */
359 #define DCM_VR_AT  3  /* Attribute Tag             */
360 #define DCM_VR_CS  4  /* Code String               */
361 #define DCM_VR_DA  5  /* Date                      */
362 #define DCM_VR_DS  6  /* Decimal String            */
363 #define DCM_VR_DT  7  /* Date Time                 */
364 #define DCM_VR_FL  8  /* Floating Point Single     */
365 #define DCM_VR_FD  9  /* Floating Point Double     */
366 #define DCM_VR_IS 10  /* Integer String            */
367 #define DCM_VR_LO 11  /* Long String               */
368 #define DCM_VR_LT 12  /* Long Text                 */
369 #define DCM_VR_OB 13  /* Other Byte String         */
370 #define DCM_VR_OF 14  /* Other Float String        */
371 #define DCM_VR_OW 15  /* Other Word String         */
372 #define DCM_VR_PN 16  /* Person Name               */
373 #define DCM_VR_SH 17  /* Short String              */
374 #define DCM_VR_SL 18  /* Signed Long               */
375 #define DCM_VR_SQ 19  /* Sequence of Items         */
376 #define DCM_VR_SS 20  /* Signed Short              */
377 #define DCM_VR_ST 21  /* Short Text                */
378 #define DCM_VR_TM 22  /* Time                      */
379 #define DCM_VR_UI 23  /* Unique Identifier (UID)   */
380 #define DCM_VR_UL 24  /* Unsigned Long             */
381 #define DCM_VR_UN 25  /* Unknown                   */
382 #define DCM_VR_US 26  /* Unsigned Short            */
383 #define DCM_VR_UT 27  /* Unlimited Text            */
384
385 /* Following must be in the same order as the defintions above */
386 static const guchar* dcm_tag_lookup[] = {
387     "  ",
388     "AE","AS","AT","CS","DA","DS","DT","FL",
389     "FD","IS","LO","LT","OB","OF","OW","PN",
390     "SH","SL","SQ","SS","ST","TM","UI","UL",
391     "UN","US","UT"
392 };
393
394
395 static GHashTable *dcm_tagTable = NULL;
396
397 static dcmTag_t tagData[] = {
398     {  0x1,    DCM_TRET,  "(Ret) Length to End" },
399     {  0x2,    DCM_TSTR,  "Affected Class" },
400     {  0x3,    DCM_TSTR,  "Requested Class" },
401     {  0x0010, DCM_TRET,  "(Ret) Recognition Code" },
402     {  0x0100, DCM_TCMD,  "Command Field" },
403     {  0x0110, DCM_TINT2, "Message ID" },
404     {  0x0120, DCM_TINT2, "Resp Message ID" },
405     {  0x0200, DCM_TRET,  "(Ret) Initiator" },
406     {  0x0300, DCM_TRET,  "(Ret) Reciever" },
407     {  0x0400, DCM_TRET,  "(Ret) Find Location" },
408     {  0x0600, DCM_TSTR,  "Dest AE" },
409     {  0x0700, DCM_TINT2, "Priority" },
410     {  0x0800, DCM_TINT2, "Data Set (0x0101 means no data set present)" },
411     {  0x0850, DCM_TRET,  "(Ret) Num Matches" },
412     {  0x0860, DCM_TRET,  "(Ret) Resp Seq Num" },
413     {  0x0900, DCM_TSTAT, "Status" },
414     {  0x0901, DCM_TSTR,  "Offending elm(s)" },
415     {  0x0902, DCM_TSTR,  "Error Comment" },
416     {  0x0903, DCM_TINT2, "Error Id" },
417     {  0x1000, DCM_TSTR,  "Affected Instance UID" },
418     {  0x1001, DCM_TSTR,  "Requested Instance UID" },
419     {  0x1002, DCM_TINT2, "Event Type Id" },
420     {  0x1005, DCM_TSTR,  "Attr Id List" },
421     {  0x1008, DCM_TINT2, "Action Type Id" },
422     {  0x1020, DCM_TINT2, "Num Remaining Ops" },
423     {  0x1021, DCM_TINT2, "Num Completed Ops" },
424     {  0x1022, DCM_TINT2, "Num Failed Ops" },
425     {  0x1023, DCM_TINT2, "Num Warning Ops" },
426     {  0x1030, DCM_TSTR,  "Move ae_called AE" },
427     {  0x1031, DCM_TINT2, "Move ae_called Id" },
428     {  0x4000, DCM_TRET,  "(Ret) DIALOG Recv'r" },
429     {  0x4010, DCM_TRET,  "(Ret) Terminal Type" },
430     {  0x5010, DCM_TRET,  "(Ret) Msg Set ID" },
431     {  0x5020, DCM_TRET,  "(Ret) End Msg ID" },
432     {  0x5110, DCM_TRET,  "(Ret) Display Fmt" },
433     {  0x5120, DCM_TRET,  "(Ret) Page Position ID" },
434     {  0x5130, DCM_TRET,  "(Ret) Text Fmt ID" },
435     {  0x5140, DCM_TRET,  "(Ret) Nor/Rev" },
436     {  0x5150, DCM_TRET,  "(Ret) Add Gray Scale" },
437     {  0x5160, DCM_TRET,  "(Ret) Borders" },
438     {  0x5170, DCM_TRET,  "(Ret) Copies" },
439     {  0x5180, DCM_TRET,  "(Ret) Mag Type" },
440     {  0x5190, DCM_TRET,  "(Ret) Erase" },
441     {  0x51a0, DCM_TRET,  "(Ret) Print" },
442     {  0x080018, DCM_TSTR, "Image UID" },
443     {  0x080020, DCM_TSTR, "Study Date" },
444     {  0x080030, DCM_TSTR, "Study Time" },
445     {  0x080050, DCM_TSTR, "Acc Num" },
446     {  0x080052, DCM_TSTR, "Q/R Level" },
447     {  0x080054, DCM_TSTR, "Retrieve AE" },
448     {  0x080060, DCM_TSTR, "Modality" },
449     {  0x080070, DCM_TSTR, "Manuf" },
450     {  0x081030, DCM_TSTR, "Study Desc" },
451     {  0x08103e, DCM_TSTR, "Series Desc" },
452     {  0x100010, DCM_TSTR, "Patient Name" },
453     {  0x100020, DCM_TSTR, "Patient Id" },
454     {  0x20000d, DCM_TSTR, "Study UID" },
455     {  0x20000e, DCM_TSTR, "Series UID" },
456     {  0x200010, DCM_TSTR, "Study Num" },
457     {  0x200011, DCM_TSTR, "Series Num" },
458     {  0x200012, DCM_TSTR, "Acq Num" },
459     {  0x200013, DCM_TSTR, "Image Num" },
460     {  0x7fe00010, DCM_OTH, "Pixels" },
461     {  0xfffee000, DCM_TRET, "Item Begin" },
462     {  0xfffee00d, DCM_TRET, "Item End" },
463     {  0xfffee0dd, DCM_TRET, "Sequence End" },
464 };
465
466 /* following definitions are used to call dissect_dcm_assoc_item() */
467 #define DCM_ITEM_VALUE_TYPE_UID     1
468 #define DCM_ITEM_VALUE_TYPE_STRING  2
469 #define DCM_ITEM_VALUE_TYPE_UINT32  3
470
471 /* A few function declataions */
472
473 /* Per object, a xxx_new() and a xxx_get() function. The _get() will create one if specified. */
474
475 static dcm_state_t*      dcm_state_new(void);
476 static dcm_state_t*      dcm_state_get(packet_info *pinfo, gboolean create);
477
478 static dcm_state_assoc_t*   dcm_state_assoc_new (dcm_state_t *dcm_data, guint32 packet_no);
479 static dcm_state_assoc_t*   dcm_state_assoc_get (dcm_state_t *dcm_data, guint32 packet_no, gboolean create);
480 static dcm_state_pctx_t*    dcm_state_pctx_new  (dcm_state_assoc_t *assoc, guint8 pctx_id);
481 static dcm_state_pctx_t*    dcm_state_pctx_get  (dcm_state_assoc_t *assoc, guint8 pctx_id, gboolean create);
482 static dcm_state_pdv_t*     dcm_state_pdv_new   (dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset);
483 static dcm_state_pdv_t*     dcm_state_pdv_get   (dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset, gboolean create);
484
485 static int  dissect_dcm_static      (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
486 static int  dissect_dcm_heuristic   (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
487 static int  dissect_dcm_main        (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean require_assoc_req);
488 static int  dissect_dcm_pdu         (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset);
489
490 static int  dissect_dcm_assoc       (tvbuff_t *tvb, packet_info *pinfo, proto_item *ti,   dcm_state_assoc_t *assoc, int offset, int len);
491 static void dissect_dcm_pctx        (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix, gboolean request);
492 static void dissect_dcm_assoc_item  (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, guchar *pitem_prefix, int item_value_type, guchar **item_value, guchar **item_description, int *hf_type, int *hf_len, int *hf_value, int ett_subtree);
493 static void dissect_dcm_userinfo    (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix);
494
495 static int  dissect_dcm_data        (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, guint32 pdu_len, guchar **pdu_description);
496 static int  dissect_dcm_pdv         (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, guint32 pdv_len, guchar **pdv_description);
497 static int  dissect_dcm_pdv_header  (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dcm_state_assoc_t *assoc, int offset, guint8 *syntax, dcm_state_pdv_t **pdv);
498
499 static void dcm_set_syntax              (dcm_state_pctx_t *pctx, guchar *xfer_uid, guchar *xfer_desc);
500 static void dcm_export_create_object    (packet_info *pinfo, dcm_state_assoc_t *assoc, dcm_state_pdv_t *pdv); 
501
502 static void
503 dcm_init(void)
504 {
505
506     if (NULL == dcm_tagTable) {
507         unsigned int i;
508         dcm_tagTable = g_hash_table_new(NULL, NULL);
509         for (i = 0; i < sizeof(tagData) / sizeof(dcmTag_t); i++)
510             g_hash_table_insert(dcm_tagTable, GINT_TO_POINTER(tagData[i].tag),
511                 (gpointer) (tagData+i));
512     }
513
514     if (NULL == dcm_uid_table) {
515         unsigned int i;
516         dcm_uid_table = g_hash_table_new(g_str_hash, g_str_equal);
517         for (i = 0; i < sizeof(dcm_uid_data) / sizeof(dcm_uid_t); i++)
518             g_hash_table_insert(dcm_uid_table, (gpointer) dcm_uid_data[i].value, (gpointer) dcm_uid_data[i].name);
519
520     }
521
522 }
523
524 static dcm_state_t *
525 dcm_state_new(void)
526 {
527     /* Not much fun. Just create very simple root structure */
528
529     dcm_state_t *ds=NULL;
530
531     ds = (dcm_state_t *) se_alloc(sizeof(dcm_state_t));
532     if (ds) {
533         ds->first_assoc=NULL;
534         ds->last_assoc=NULL;
535     }
536     return ds;
537 }
538
539 static dcm_state_t *
540 dcm_state_get(packet_info *pinfo, gboolean create)
541 {
542
543     /*  Get or create converstation and DICOM data structure if desired
544         Return new or existing dicom struture, which is used to store context IDs and xfer Syntax
545         Return NULL in case of the structure couldn't be created
546     */
547
548     conversation_t  *conv=NULL;
549     dcm_state_t     *dcm_data=NULL;
550
551     conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
552         pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
553
554     if (conv == NULL) {
555         /* Conversation does not exist, create one.
556            Usually set for the first packet already. Probably by dissect-tcp
557         */
558         conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
559             pinfo->srcport, pinfo->destport, 0);
560     }
561     else {                      /* conversation exists, try to get data already filled */
562         dcm_data = conversation_get_proto_data(conv, proto_dcm);
563     }
564
565     if (dcm_data == NULL && create) {
566
567         dcm_data = dcm_state_new();
568         if (dcm_data != NULL) {
569             conversation_add_proto_data(conv, proto_dcm, dcm_data);
570         }
571
572         /*  Mark it as DICOM conversation. Needed for the heuristic mode,
573             to prevent stealing subsequent packets by other dissectors
574         */
575         conversation_set_dissector(conv, dcm_handle);
576     }
577
578     return dcm_data;
579 }
580
581
582 static dcm_state_assoc_t *
583 dcm_state_assoc_new(dcm_state_t *dcm_data, guint32 packet_no)
584 {
585     /* Create new accociation object and initalize the members */
586
587     dcm_state_assoc_t *assoc;
588
589     assoc = (dcm_state_assoc_t *) g_malloc(sizeof(dcm_state_assoc_t));
590     if (assoc) {
591
592         assoc->next = NULL;
593         assoc->prev = NULL;
594         assoc->packet_no = packet_no;       /* Identifier */
595
596         assoc->first_pctx = NULL;           /* List of Presentation context objects */
597         assoc->last_pctx  = NULL;
598
599         memset(assoc->ae_called, 0, sizeof(assoc->ae_called));
600         memset(assoc->ae_calling, 0, sizeof(assoc->ae_calling));
601         memset(assoc->ae_called_resp, 0, sizeof(assoc->ae_called_resp));
602         memset(assoc->ae_calling_resp, 0, sizeof(assoc->ae_calling_resp));
603
604         /* add to the end of the list */
605         if (dcm_data->last_assoc) {
606             dcm_data->last_assoc->next = assoc;
607             assoc->prev = dcm_data->last_assoc;
608         }
609         else {
610             dcm_data->first_assoc = assoc;
611         }
612         dcm_data->last_assoc = assoc;
613     }
614     return assoc;
615 }
616
617 static dcm_state_assoc_t *
618 dcm_state_assoc_get(dcm_state_t *dcm_data, guint32 packet_no, gboolean create)
619 {
620   /*  Find or create Association object.
621       Return NULL, if Association was not found, based on packet number
622   */
623
624     dcm_state_assoc_t *assoc = NULL;
625
626     assoc=dcm_data->first_assoc;
627
628     while (assoc) {
629
630         if (assoc->next) {
631             /* we have more associations in the same stream */
632             if ((assoc->packet_no <= packet_no) && (packet_no < assoc->next->packet_no))
633                 break;
634         }
635         else {
636             /* last or only associations in the same stream */
637             if (assoc->packet_no <= packet_no)
638                 break;
639         }
640         assoc = assoc->next;
641     }
642
643     if (assoc == NULL && create) {
644         assoc = dcm_state_assoc_new(dcm_data, packet_no);
645     }
646     return assoc;
647 }
648
649 static dcm_state_pctx_t *
650 dcm_state_pctx_new(dcm_state_assoc_t *assoc, guint8 pctx_id)
651 {
652     /* Create new presentation context object and initalize the members */
653
654     dcm_state_pctx_t *pctx=NULL;
655
656     pctx = se_alloc(sizeof(dcm_state_pctx_t));
657     if (pctx) {
658
659         pctx->next = NULL;
660         pctx->prev = NULL;
661
662         pctx->id = pctx_id;
663
664         pctx->abss_uid = NULL;
665         pctx->abss_desc = NULL;
666         pctx->xfer_uid = NULL;
667         pctx->xfer_desc = NULL;
668         pctx->syntax = DCM_UNK;
669
670         pctx->first_pdv = NULL;     /* List of PDV objects */
671         pctx->last_pdv  = NULL;
672
673         /* add to the end of the list list */
674         if (assoc->last_pctx) {
675             assoc->last_pctx->next = pctx;
676             pctx->prev = assoc->last_pctx;
677         }
678         else {
679             assoc->first_pctx = pctx;
680         }
681         assoc->last_pctx = pctx;
682     }
683
684     return pctx;
685 }
686
687 static dcm_state_pctx_t *
688 dcm_state_pctx_get(dcm_state_assoc_t *assoc, guint8 pctx_id, gboolean create)
689 {
690     /*  Find or create presentation context object. Return NULL, if Context ID was not found */
691
692     dcm_state_pctx_t *pctx =NULL;
693
694     pctx = assoc->first_pctx;
695     /*
696     static char notfound[] = "not found - click on ASSOC Request";
697     static dcm_state_pctx_t dunk = { NULL, NULL, FALSE, 0, notfound, notfound, notfound, notfound, DCM_UNK };
698     */
699     while (pctx) {
700         if (pctx->id == pctx_id)
701             break;
702         pctx = pctx->next;
703     }
704
705     if (pctx == NULL && create) {
706         pctx = dcm_state_pctx_new(assoc, pctx_id);
707     }
708
709     return pctx;
710 }
711
712
713 static dcm_state_pdv_t*
714 dcm_state_pdv_new(dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset)
715 {
716     /* Create new PDV object and initalize the members */
717
718     dcm_state_pdv_t *pdv=NULL;
719
720     pdv = (dcm_state_pdv_t *) se_alloc(sizeof(dcm_state_pdv_t));
721     if (pdv != NULL) {
722
723         pdv->prev = NULL;
724         pdv->next = NULL;
725
726         pdv->data = NULL;
727         pdv->data_len = 0;
728         pdv->pctx_id = 0;
729
730         pdv->desc = NULL;
731
732         pdv->sop_class_uid = NULL;
733         pdv->sop_instance_uid = NULL;
734
735         pdv->is_storage = FALSE;
736         pdv->is_flagvalid = FALSE;
737         pdv->is_command = FALSE;
738         pdv->is_last_fragment = TRUE;   /* Continuation PDVs are more tricky */
739         pdv->is_corrupt = FALSE;
740
741         pdv->packet_no = packet_no;
742         pdv->offset = offset;
743         pdv->initalized = FALSE;
744         pdv->open_tag_desc = NULL;
745         pdv->open_tag_len = 0;
746         pdv->open_tag_rlen = 0;
747
748         /* add to the end of the list list */
749         if (pctx->last_pdv) {
750             pctx->last_pdv->next = pdv;
751             pdv->prev = pctx->last_pdv;
752         }
753         else {
754             pctx->first_pdv = pdv;
755         }
756         pctx->last_pdv = pdv;
757     }
758     return pdv;
759 }
760
761
762 static dcm_state_pdv_t*
763 dcm_state_pdv_get(dcm_state_pctx_t *pctx, guint32 packet_no, guint32 offset, gboolean create)
764 {
765     /*  Find or create PDV object. Return NULL, if PDV was not found, based on packet number and offset */
766
767     dcm_state_pdv_t *pdv = NULL;
768
769     pdv=pctx->first_pdv;
770
771     while (pdv) {
772         if ((pdv->packet_no == packet_no) && (pdv->offset == offset))
773             break;
774         pdv = pdv->next;
775     }
776
777     if (pdv == NULL && create) {
778         pdv = dcm_state_pdv_new(pctx, packet_no, offset);
779     }
780     return pdv;
781 }
782
783 static dcm_state_pdv_t*
784 dcm_state_pdv_get_obj_start(dcm_state_pdv_t *pdv_curr)
785 {
786
787     dcm_state_pdv_t *pdv_first=pdv_curr;
788
789     /* Get First PDV of the DICOM Object */
790     while (pdv_first->prev && !pdv_first->prev->is_last_fragment) {
791         pdv_first = pdv_first->prev;
792     }
793
794     return pdv_first;
795 }
796
797 static const char *
798 dcm_pdu2str(guint8 item)
799 {
800     const char *s = "";
801     switch (item) {
802     case 1: s = "ASSOC Request"; break;
803     case 2: s = "ASSOC Accept"; break;
804     case 3: s = "ASSOC Reject"; break;
805     case 4: s = "Data"; break;
806     case 5: s = "RELEASE Request"; break;
807     case 6: s = "RELEASE Response"; break;
808     case 7: s = "ABORT"; break;
809     case 0x10: s = "Application Context"; break;
810     case 0x20: s = "Presentation Context"; break;
811     case 0x21: s = "Presentation Context Reply"; break;
812     case 0x30: s = "Abstract syntax"; break;
813     case 0x40: s = "Transfer syntax"; break;
814     case 0x50: s = "User Info"; break;
815     case 0x51: s = "Max Length"; break;
816     default: break;
817     }
818     return s;
819 }
820
821 static const char *
822 dcm_result2str(guint8 result)
823 {
824     const char *s = "";
825     switch (result) {
826     case 1:  s = "Reject Permanent"; break;
827     case 2:  s = "Reject Transient"; break;
828     default: break;
829     }
830     return s;
831 }
832
833 static const char *
834 dcm_source2str(guint8 source)
835 {
836     const char *s = "";
837     switch (source) {
838     case 1:  s = "User"; break;
839     case 2:  s = "Provider (ACSE)"; break;
840     case 3:  s = "Provider (Presentation)"; break;
841     default: break;
842     }
843     return s;
844 }
845
846 static const char *
847 dcm_reason2str(guint8 source, guint8 reason)
848 {
849     const char *s = "";
850     if (1 == source) switch (reason) {
851         case 1:  s = "No reason"; break;
852         case 2:  s = "App Name not supported"; break;
853         case 3:  s = "calling AET not recognized"; break;
854         case 7:  s = "called AET not recognized"; break;
855         default: break;
856     } else if (2 == source) switch (reason) {
857         case 1:  s = "No reason"; break;
858         case 2:  s = "protocol unsupported"; break;
859         default: break;
860     } else if (3 == source) switch (reason) {
861         case 1:  s = "temporary congestion"; break;
862         case 2:  s = "local limit exceeded"; break;
863         default: break;
864     }
865     return s;
866 }
867
868 static const char *
869 dcm_abort2str(guint8 reason)
870 {
871     const char *s = "";
872     switch (reason) {
873     case 0:  s = "not specified"; break;
874     case 1:  s = "unrecognized"; break;
875     case 2:  s = "unexpected"; break;
876     case 4:  s = "unrecognized parameter"; break;
877     case 5:  s = "unexpected parameter"; break;
878     case 6:  s = "invalid parameter"; break;
879     default: break;
880     }
881     return s;
882 }
883
884 static const char *
885 dcm_PCresult2str(guint8 result)
886 {
887     const char *s = "";
888     switch (result) {
889     case 0:  s = "Accept"; break;
890     case 1:  s = "User Reject"; break;
891     case 2:  s = "No Reason"; break;
892     case 3:  s = "Abstract Syntax Unsupported"; break;
893     case 4:  s = "Transfer Syntax Unsupported"; break;
894     default: break;
895     }
896     return s;
897 }
898
899
900 static const char *
901 dcm_cmd2str(guint16 us)
902 {
903     const char *s = "";
904     /* there should be a better way to do this */
905     switch (us) {
906     case 0x0001:  s = "C-STORE-RQ"; break;
907     case 0x8001:  s = "C-STORE-RSP"; break;
908     case 0x0010:  s = "C-GET-RQ"; break;
909     case 0x8010:  s = "C-GET-RSP"; break;
910     case 0x0020:  s = "C-FIND-RQ"; break;
911     case 0x8020:  s = "C-FIND-RSP"; break;
912     case 0x0021:  s = "C-MOVE-RQ"; break;
913     case 0x8021:  s = "C-MOVE-RSP"; break;
914     case 0x0030:  s = "C-ECHO-RQ"; break;
915     case 0x8030:  s = "C-ECHO-RSP"; break;
916     case 0x0100:  s = "N-EVENT-REPORT-RQ"; break;
917     case 0x8100:  s = "N-EVENT-REPORT-RSP"; break;
918     case 0x0110:  s = "N-GET-RQ"; break;
919     case 0x8110:  s = "N-GET-RSP"; break;
920     case 0x0120:  s = "N-SET-RQ"; break;
921     case 0x8120:  s = "N-SET-RSP"; break;
922     case 0x0130:  s = "N-ACTION-RQ"; break;
923     case 0x8130:  s = "N-ACTION-RSP"; break;
924     case 0x0140:  s = "N-CREATE-RQ"; break;
925     case 0x8140:  s = "N-CREATE-RSP"; break;
926     case 0x0150:  s = "N-DELETE-RQ"; break;
927     case 0x8150:  s = "N-DELETE-RSP"; break;
928     case 0x0fff:  s = "C-CANCEL-RQ"; break;
929     default: break;
930     }
931     return s;
932 }
933
934 static const char *
935 dcm_rsp2str(guint16 us)
936 {
937     const char *s = "";
938     switch (us) {
939     case 0x0000:  s = "Success"; break;
940     case 0xa701:
941     case 0xa702:  s = "Refused: Out of Resources"; break;
942     case 0xa801:  s = "Refused: Move Destination unknown"; break;
943     case 0xa900:  s = "Failed:  Id does not match Class"; break;
944     case 0xb000:  s = "Warning: operations complete -- One or more Failures"; break;
945     case 0xfe00:  s = "Cancel:  operations terminated by Cancel"; break;
946     case 0xff00:  s = "Pending: operations are continuing"; break;
947     default: break;
948     }
949     if (0xC000 == (0xF000 & us))  s = "Failed:  Unable to Process";
950     return s;
951 }
952
953 static guchar*
954 dcm_uid_or_desc(guchar *dcm_uid, guchar *dcm_desc)
955 {
956     /* Return Description, UID or error */
957
958     return (dcm_desc == NULL ? (dcm_uid == NULL ? (guchar *)"Malformed Packet" : dcm_uid) : dcm_desc);
959 }
960
961 static void
962 dcm_set_syntax(dcm_state_pctx_t *pctx, guchar *xfer_uid, guchar *xfer_desc)
963 {
964     if (pctx == NULL)
965         return;
966
967     if (pctx->xfer_uid != NULL)
968         g_free(pctx->xfer_uid); /* free prev allocated xfer */
969     if (pctx->xfer_desc != NULL)
970         g_free(pctx->xfer_desc);        /* free prev allocated xfer */
971
972     pctx->syntax = 0;
973     pctx->xfer_uid = g_strdup(xfer_uid);
974     pctx->xfer_desc = g_strdup(xfer_desc);
975
976     if (xfer_uid == NULL) return;
977     /* this would be faster to skip the common parts, and have a FSA to
978      * find the syntax.
979      * Absent of coding that, this is in descending order of probability */
980     if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2"))
981         pctx->syntax = DCM_ILE;  /* implicit little endian */
982     else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.1"))
983         pctx->syntax = DCM_ELE;  /* explicit little endian */
984     else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.2"))
985         pctx->syntax = DCM_EBE;  /* explicit big endian */
986     else if (0 == strcmp(xfer_uid, "1.2.840.113619.5.2"))
987         pctx->syntax = DCM_ILE;  /* implicit little endian, big endian pixels, GE private */
988     else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.4.70"))
989         pctx->syntax = DCM_ELE;  /* explicit little endian, jpeg */
990     else if (0 == strncmp(xfer_uid, "1.2.840.10008.1.2.4", 18))
991         pctx->syntax = DCM_ELE;  /* explicit little endian, jpeg */
992     else if (0 == strcmp(xfer_uid, "1.2.840.10008.1.2.1.99"))
993         pctx->syntax = DCM_ELE;  /* explicit little endian, deflated */
994 }
995
996 static char *
997 dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, guint32 len, int vr, int tr, guchar **tag_value)
998 {
999     guchar *buf;
1000     const guchar *vval;
1001     guchar *p;
1002
1003     guint32 tag, val32=0;
1004     guint16 val16=0;
1005     guint8  val8=0;
1006     dcmTag_t *dtag;
1007     static dcmTag_t utag = { 0, 0, "(unknown)" };
1008
1009 #define MAX_BUF_LEN 1024
1010
1011     buf=ep_alloc(MAX_BUF_LEN);
1012     *tag_value = se_alloc(MAX_BUF_LEN);
1013
1014     *buf = 0;
1015     if (0 == elm) {
1016         if (DCM_ILE & syntax)
1017              val32 = tvb_get_letohl(tvb, offset);
1018         else val32 = tvb_get_ntohl(tvb, offset);
1019         g_snprintf(buf, MAX_BUF_LEN, "Group Length 0x%x (%d)", val32, val32);
1020         return buf;
1021     }
1022     tag = (grp << 16) | elm;
1023     if (NULL == (dtag = g_hash_table_lookup(dcm_tagTable, GUINT_TO_POINTER(tag))))
1024         dtag = &utag;
1025
1026     DISSECTOR_ASSERT(MAX_BUF_LEN > strlen(dtag->desc));
1027     p=buf;
1028     p+=MIN(MAX_BUF_LEN-(p-buf),
1029            g_snprintf(p, MAX_BUF_LEN-(p-buf), "%s", dtag->desc));
1030     if (vr > 0) {
1031         vval = tvb_format_text(tvb, vr, 2);
1032         p+=MIN(MAX_BUF_LEN-(p-buf),
1033                g_snprintf(p, MAX_BUF_LEN-(p-buf), " [%s]", vval));
1034     }
1035
1036     switch (tr > 0 ? tr : dtag->dtype) {
1037     case DCM_TSTR:
1038     default:            /* try ascii */
1039
1040         val8 = tvb_get_guint8(tvb, offset+len-1);
1041         if (val8 == 0x00) {
1042             /* Last byte of string is 0x00, i.e. padded */
1043             vval = tvb_format_text(tvb, offset, len-1);
1044         }
1045         else {
1046             vval = tvb_format_text(tvb, offset, len);
1047         }
1048         p+=MIN(MAX_BUF_LEN-(p-buf),
1049                g_snprintf(p, MAX_BUF_LEN-(p-buf), " %s", vval));
1050
1051         g_snprintf(*tag_value, MAX_BUF_LEN, "%s", vval);
1052
1053         break;
1054     case DCM_TINT2:
1055         if (DCM_ILE & syntax)
1056              val16 = tvb_get_letohs(tvb, offset);
1057         else val16 = tvb_get_ntohs(tvb, offset);
1058         p+=MIN(MAX_BUF_LEN-(p-buf),
1059                g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x (%d)", val16, val16));
1060         break;
1061     case DCM_TINT4:
1062         if (DCM_ILE & syntax)
1063              val32 = tvb_get_letohl(tvb, offset);
1064         else val32 = tvb_get_ntohl(tvb, offset);
1065         p+=MIN(MAX_BUF_LEN-(p-buf),
1066                g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x (%d)", val32, val32));
1067         break;
1068     case DCM_TFLT: {
1069         gfloat valf;
1070         if (DCM_ILE & syntax)
1071              valf = tvb_get_letohieee_float(tvb, offset);
1072         else valf = tvb_get_ntohieee_float(tvb, offset);
1073         p+=MIN(MAX_BUF_LEN-(p-buf),
1074                g_snprintf(p, MAX_BUF_LEN-(p-buf), " (%f)", valf));
1075         } break;
1076     case DCM_TDBL: {
1077         gdouble vald;
1078         if (DCM_ILE & syntax)
1079              vald = tvb_get_letohieee_double(tvb, offset);
1080         else vald = tvb_get_ntohieee_double(tvb, offset);
1081         p+=MIN(MAX_BUF_LEN-(p-buf),
1082                g_snprintf(p, MAX_BUF_LEN-(p-buf), " (%f)", vald));
1083         } break;
1084     case DCM_TSTAT: /* call dcm_rsp2str() on TINT2 */
1085         if (DCM_ILE & syntax)
1086              val16 = tvb_get_letohs(tvb, offset);
1087         else val16 = tvb_get_ntohs(tvb, offset);
1088         p+=MIN(MAX_BUF_LEN-(p-buf),
1089                g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x '%s'", val16, dcm_rsp2str(val16)));
1090         break;
1091     case DCM_TCMD:   /* call dcm_cmd2str() on TINT2 */
1092         if (DCM_ILE & syntax)
1093              val16 = tvb_get_letohs(tvb, offset);
1094         else val16 = tvb_get_ntohs(tvb, offset);
1095         p+=MIN(MAX_BUF_LEN-(p-buf),
1096                g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x '%s'", val16, dcm_cmd2str(val16)));
1097
1098         g_snprintf(*tag_value, MAX_BUF_LEN, "%s", dcm_cmd2str(val16));
1099         break;
1100     case DCM_SQ:        /* Sequence */
1101     case DCM_OTH:       /* Other BYTE, WORD, ... */
1102     case DCM_TRET:      /* Retired */
1103         break;
1104     }
1105     return buf;
1106 }
1107
1108 static void
1109 dcm_guin16_to_le(guint8 *buffer, guint16 value)
1110 {
1111
1112     buffer[0]=(guint8) (value & 0x00FF);
1113     buffer[1]=(guint8)((value & 0xFF00) >> 8);
1114 }
1115
1116 static void
1117 dcm_guin32_to_le(guint8 *buffer, guint32 value)
1118 {
1119
1120     buffer[0]=(guint8) (value & 0x000000FF);
1121     buffer[1]=(guint8)((value & 0x0000FF00) >>  8);
1122     buffer[2]=(guint8)((value & 0x00FF0000) >> 16);
1123     buffer[3]=(guint8)((value & 0xFF000000) >> 24);
1124
1125 }
1126
1127 static guint32
1128 dcm_export_create_tag_base(guint8 *buffer, guint32 bufflen _U_, guint32 offset,
1129                            guint16 grp, guint16 elm, guint16 vr,
1130                            guint8 *value_buffer, guint32 value_len)
1131
1132     /*  Only Explict Littele Endian is needed to create Metafile Header
1133         Generic function to write a TAG, VR, LEN & VALUE to a combined buffer
1134         The value (buffer, len) must be preprocessed by a VR specific function
1135     */
1136 {
1137     guint8 *pos=NULL;
1138
1139     pos=buffer+offset;
1140
1141     dcm_guin16_to_le(pos, grp);
1142     pos+=2;
1143     dcm_guin16_to_le(pos, elm);
1144     pos+=2;
1145
1146     memmove(pos, dcm_tag_lookup[vr], 2);
1147     pos+=2;
1148
1149     switch (vr) {
1150     case DCM_VR_OB:
1151     case DCM_VR_OW:
1152     case DCM_VR_OF:
1153     case DCM_VR_SQ:
1154     case DCM_VR_UT:
1155     case DCM_VR_UN:
1156         /* DICOM likes it complicated. Special handling for these types */
1157
1158         /* Add two reserved 0x00 bytes */
1159         dcm_guin16_to_le(pos, 0);
1160         pos+=2;
1161
1162         /* Length is a 4 byte field */
1163         dcm_guin32_to_le(pos, (guint32)value_len);
1164         pos+=4;
1165         break;
1166
1167     default:
1168         /* Length is a 2 byte field */
1169         dcm_guin16_to_le(pos, (guint16)value_len);
1170         pos+=2;
1171     }
1172
1173     memmove(pos, value_buffer, value_len);
1174     pos+=value_len;
1175
1176     return pos-buffer;
1177 }
1178
1179 static guint32
1180 dcm_export_create_tag_guint16(guint8 *buffer, guint32 bufflen, guint32 offset,
1181                               guint16 grp, guint16 elm, guint16 vr, guint16 value)
1182 {
1183
1184     return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr, (guint8*)&value, 2);
1185 }
1186
1187 static guint32
1188 dcm_export_create_tag_guint32(guint8 *buffer, guint32 bufflen, guint32 offset,
1189                               guint16 grp, guint16 elm, guint16 vr, guint32 value)
1190 {
1191
1192     return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr, (guint8*)&value, 4);
1193 }
1194
1195 static guint32
1196 dcm_export_create_tag_str(guint8 *buffer, guint32 bufflen, guint32 offset,
1197                           guint16 grp, guint16 elm, guint16 vr, guchar *value)
1198 {
1199     guint16 len;
1200
1201     if (!value) {
1202         /* NULL object. E.g. happens if UID was not found/set. Don't create element*/
1203         return offset;
1204     }
1205
1206     len=strlen(value);
1207
1208     if ((len & 0x01) == 1) {
1209         /*  Odd length: since buffer is 0 initalized, pad with a 0x00 */
1210         len += 1;
1211     }
1212
1213     return dcm_export_create_tag_base(buffer, bufflen, offset, grp, elm, vr,
1214         (guint8*)value, len);
1215 }
1216
1217
1218 static guint8*
1219 dcm_export_create_header(guint32 *dcm_header_len, gchar *sop_class_uid, gchar *sop_instance_uid, gchar *xfer_uid)
1220 {
1221     guint8      *dcm_header=NULL;
1222     guint32     offset=0;
1223     guint32     offset_header_len=0;
1224
1225 #define DCM_HEADER_MAX 512
1226
1227     dcm_header=ep_alloc(DCM_HEADER_MAX);        /* Slightly longer than needed */
1228
1229     memset(dcm_header, 0, DCM_HEADER_MAX);      /* The subsequent functions rely on a 0 intitalized buffer */
1230     offset=128;
1231
1232     memmove(dcm_header+offset, "DICM", 4);
1233     offset+=4;
1234
1235     offset_header_len=offset;   /* remember for later */
1236
1237     offset+=12;
1238
1239     /*
1240         (0002,0000)     File Meta Information Group Length  UL
1241         (0002,0001)     File Meta Information Version       OB
1242         (0002,0002)     Media Storage SOP Class UID         UI
1243         (0002,0003)     Media Storage SOP Instance UID      UI
1244         (0002,0010)     Transfer Syntax UID                 UI
1245         (0002,0012)     Implementation Class UID            UI
1246         (0002,0013)     Implementation Version Name         SH
1247     */
1248
1249     offset=dcm_export_create_tag_guint16(dcm_header, DCM_HEADER_MAX, offset,
1250         0x0002, 0x0001, DCM_VR_OB, 0x0100);  /* will result on 00 01 since it is little endian */
1251
1252     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1253         0x0002, 0x0002, DCM_VR_UI, sop_class_uid);
1254
1255     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1256         0x0002, 0x0003, DCM_VR_UI, sop_instance_uid);
1257
1258     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1259         0x0002, 0x0010, DCM_VR_UI, xfer_uid);
1260
1261     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1262         0x0002, 0x0012, DCM_VR_UI, WIRESHARK_IMPLEMENTATION_UID);
1263
1264     offset=dcm_export_create_tag_str(dcm_header, DCM_HEADER_MAX, offset,
1265         0x0002, 0x0013, DCM_VR_SH, WIRESHARK_IMPLEMENTATION_VERSION);
1266
1267     /* Finally write the meta header lenght */
1268     dcm_export_create_tag_guint32(dcm_header, DCM_HEADER_MAX, offset_header_len,
1269         0x0002, 0x0000, DCM_VR_UL, offset-offset_header_len-12);
1270
1271     *dcm_header_len=offset;
1272
1273     return dcm_header;
1274
1275 }
1276
1277 static void
1278 dcm_export_create_object(packet_info *pinfo, dcm_state_assoc_t *assoc, dcm_state_pdv_t *pdv)
1279 {
1280
1281     /* Concat different PDVs into one buffer and add it to export object list
1282        This function caused quite a few crashes, with all the string pointers
1283     */
1284
1285     dicom_eo_t          *eo_info = NULL;
1286
1287     dcm_state_pdv_t     *pdv_curr = NULL;
1288     dcm_state_pdv_t     *pdv_same_pkt = NULL;
1289     dcm_state_pctx_t    *pctx = NULL;
1290
1291     guint8     *pdv_combined = NULL;
1292     guint8     *pdv_combined_curr = NULL;
1293     guint8     *dcm_header = NULL;
1294     guint32     pdv_combined_len = 0;
1295     guint32     dcm_header_len = 0;
1296     guint16     cnt_same_pkt = 1;
1297     gchar      *filename = NULL;
1298     gchar      *hostname = NULL;
1299
1300     gchar       *sop_class_uid = NULL;
1301     gchar       *sop_instance_uid = NULL;
1302
1303     /* Calculate total PDV lenghth, i.e. all packets until last PDV without continuation  */
1304     pdv_curr = pdv;
1305     pdv_same_pkt = pdv;
1306     pdv_combined_len=pdv_curr->data_len;
1307
1308     while (pdv_curr->prev && !pdv_curr->prev->is_last_fragment) {
1309         pdv_curr = pdv_curr->prev;
1310         pdv_combined_len += pdv_curr->data_len;
1311     }
1312
1313     /* Count number of PDVs with the same Packet Number */
1314     while (pdv_same_pkt->prev && (pdv_same_pkt->prev->packet_no == pdv_same_pkt->packet_no)) {
1315         pdv_same_pkt = pdv_same_pkt->prev;
1316         cnt_same_pkt += 1;
1317     }
1318
1319     pctx=dcm_state_pctx_get(assoc, pdv_curr->pctx_id, FALSE);
1320
1321     sop_class_uid = ep_alloc(MAX_BUF_LEN);
1322     sop_instance_uid = ep_alloc(MAX_BUF_LEN);
1323
1324     hostname = ep_alloc(MAX_BUF_LEN);
1325     filename = ep_alloc(MAX_BUF_LEN);
1326
1327     if (assoc->ae_calling && strlen(assoc->ae_calling)>0 &&
1328         assoc->ae_called  && strlen(assoc->ae_called)>0 ) {
1329         g_snprintf(hostname, MAX_BUF_LEN, "%s <-> %s", assoc->ae_calling, assoc->ae_called);
1330     }
1331     else {
1332         g_snprintf(hostname, MAX_BUF_LEN, "AE title(s) unknown");
1333     }
1334
1335     if (pdv->is_storage &&
1336         pdv_curr->sop_class_uid    && strlen(pdv_curr->sop_class_uid)>0 &&
1337         pdv_curr->sop_instance_uid && strlen(pdv_curr->sop_instance_uid)>0) {
1338
1339         g_snprintf(sop_class_uid, MAX_BUF_LEN, "%s", pdv_curr->sop_class_uid);
1340         g_snprintf(sop_instance_uid, MAX_BUF_LEN, "%s", pdv_curr->sop_instance_uid);
1341
1342         /* Make sure filename does not contain invalid character. Rather conservative.
1343            Eventhough this should be a valid DICOM UID, apply the same filter rules
1344            in case of bogus data.
1345         */
1346         g_snprintf(filename, MAX_BUF_LEN, "%06d-%d-%s.dcm", pinfo->fd->num, cnt_same_pkt,
1347             g_strcanon(pdv_curr->sop_instance_uid, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-.", '-'));
1348     }
1349     else {
1350         /* No SOP Instance or SOP Class UID found in PDV. Use wireshark ones */
1351
1352         g_snprintf(sop_class_uid, MAX_BUF_LEN, "%s", WIRESHARK_MEDIA_STORAGE_SOP_CLASS_UID);
1353
1354         g_snprintf(sop_instance_uid, MAX_BUF_LEN, "%s.%d.%d",
1355             WIRESHARK_MEDIA_STORAGE_SOP_INSTANCE_UID_PREFIX, pinfo->fd->num, cnt_same_pkt);
1356
1357         /* Make sure filename does not contain invalid character. Rather conservative.*/
1358         g_snprintf(filename, MAX_BUF_LEN, "%06d-%d-%s.dcm", pinfo->fd->num, cnt_same_pkt,
1359             g_strcanon(pdv->desc, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-.", '-'));
1360
1361     }
1362
1363     if (global_dcm_header) {
1364         if (pctx && pctx->xfer_uid && strlen(pctx->xfer_uid)>0) {
1365             dcm_header=dcm_export_create_header(&dcm_header_len, sop_class_uid, sop_instance_uid, pctx->xfer_uid);
1366         }
1367         else {
1368             /* We are running blind, i.e. no presentation context found. Don't invent one
1369                The meta header will miss this tag (even tough it is mandatory)
1370             */
1371             dcm_header=dcm_export_create_header(&dcm_header_len, sop_class_uid, sop_instance_uid, NULL);
1372         }
1373     }
1374
1375
1376     /* Allocate the final size */
1377     pdv_combined = ep_alloc(dcm_header_len+pdv_combined_len);
1378     pdv_combined_curr = pdv_combined;
1379
1380     memmove(pdv_combined, dcm_header, dcm_header_len);
1381     pdv_combined_curr += dcm_header_len;
1382
1383     /* Copy PDV per PDV to target buffer */
1384     while (!pdv_curr->is_last_fragment) {
1385         memmove(pdv_combined_curr, pdv_curr->data, pdv_curr->data_len);     /* this is a copy not move */
1386         pdv_combined_curr += pdv_curr->data_len;
1387         pdv_curr = pdv_curr->next;
1388     }
1389
1390     /* Last packet */
1391     g_memmove(pdv_combined_curr, pdv->data, pdv->data_len);         /* this is a copy not move */
1392
1393     /* Add to list */
1394     eo_info = ep_alloc(sizeof(dicom_eo_t));
1395     eo_info->hostname = hostname;
1396     eo_info->filename = filename;
1397     eo_info->content_type = pdv->desc;
1398
1399     eo_info->payload_data = pdv_combined;
1400     eo_info->payload_len  = dcm_header_len+pdv_combined_len;
1401
1402     tap_queue_packet(dicom_eo_tap, pinfo, eo_info);
1403 }
1404
1405
1406 static void
1407 dissect_dcm_assoc_item(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
1408                        dcm_state_assoc_t *assoc _U_, int offset,
1409                        guchar *pitem_prefix, int item_value_type,
1410                        guchar **item_value, guchar **item_description,
1411                        int *hf_type, int *hf_len, int *hf_value, int ett_subtree)
1412 {
1413     /*
1414      *  Decode one item in a association request or response. Lookup UIDs if requested
1415      *
1416      *  If dcm_tree is set, create a Subtree Node with summary and three elements
1417      *  - item_type
1418      *  - item_len
1419      *  - value
1420      *
1421      *  The Summary is also returned as
1422      */
1423
1424     proto_tree *assoc_item_ptree = NULL;        /* Tree for item details */
1425     proto_item *assoc_item_pitem = NULL;
1426
1427     guint32 item_number;
1428
1429     guint8  item_type;
1430     guint16 item_len;
1431
1432     guchar *buf_desc=NULL;              /* Used for item text */
1433
1434     #define MAX_BUFFER 1024
1435
1436     *item_value=NULL;
1437     *item_description=NULL;
1438
1439     buf_desc=ep_alloc(MAX_BUFFER);      /* Valid for this packet */
1440     buf_desc[0]=0;
1441
1442     item_type = tvb_get_guint8(tvb, offset);
1443     item_len  = tvb_get_ntohs(tvb, offset+2);
1444
1445     assoc_item_pitem = proto_tree_add_text(tree, tvb, offset, item_len+4, pitem_prefix);
1446     assoc_item_ptree = proto_item_add_subtree(assoc_item_pitem, ett_subtree);
1447
1448     proto_tree_add_uint(assoc_item_ptree, *hf_type, tvb, offset, 1, item_type);
1449     proto_tree_add_uint(assoc_item_ptree, *hf_len, tvb, offset+2, 2, item_len);
1450
1451     switch (item_value_type) {
1452     case DCM_ITEM_VALUE_TYPE_UID:
1453         *item_value = tvb_get_ephemeral_string(tvb, offset+4, item_len);
1454         *item_description = g_hash_table_lookup(dcm_uid_table, (gpointer) *item_value);
1455
1456         if (NULL == *item_description) {            /* Unknown UID, or no UID at all */
1457             g_snprintf(buf_desc, MAX_BUFFER, "%s", *item_value);
1458         }
1459         else {
1460             g_snprintf(buf_desc, MAX_BUFFER, "%s (%s)", *item_description, *item_value);
1461         }
1462
1463         proto_item_append_text(assoc_item_pitem, "%s", buf_desc);
1464         proto_tree_add_string(assoc_item_ptree, *hf_value, tvb, offset+4, item_len, buf_desc);
1465
1466         break;
1467
1468     case DCM_ITEM_VALUE_TYPE_STRING:
1469         *item_value = tvb_get_ephemeral_string(tvb, offset+4, item_len);
1470         proto_item_append_text(assoc_item_pitem, "%s", *item_value);
1471         proto_tree_add_string(assoc_item_ptree, *hf_value, tvb, offset+4, item_len, *item_value);
1472
1473         break;
1474
1475     case DCM_ITEM_VALUE_TYPE_UINT32:
1476         item_number = tvb_get_ntohl(tvb, offset+4);
1477         *item_value = se_alloc(MAX_BUFFER);
1478         g_snprintf(*item_value, MAX_BUFFER, "%d", item_number);
1479
1480         proto_item_append_text(assoc_item_pitem, "%s", *item_value);
1481         proto_tree_add_item(assoc_item_ptree, *hf_value, tvb, offset+4, 4, FALSE);
1482
1483         break;
1484
1485     default:
1486         break;
1487     }
1488 }
1489
1490
1491 static void
1492 dissect_dcm_pctx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1493                  dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix, gboolean is_assoc_request)
1494 {
1495     /*
1496         Decode a presentation context item in a Association Request or Response
1497         In the response, set the accepted transfer syntax, if any
1498     */
1499
1500     proto_tree *pctx_ptree = NULL;      /* Tree for presentation context details */
1501     proto_item *pctx_pitem = NULL;
1502
1503     dcm_state_pctx_t *pctx = NULL;
1504
1505     guint8  item_type=0;
1506     guint16 item_len=0;
1507
1508     guint8  pctx_id=0;              /* Presentation Context ID */
1509     guint8  pctx_result=0;
1510
1511     guchar *pctx_abss_uid=NULL;     /* Abstract Syntax UID alias SOP Class UID */
1512     guchar *pctx_abss_desc=NULL;    /* Description of UID */
1513
1514     guchar *pctx_xfer_uid=NULL;     /* Transfer Syntax UID */
1515     guchar *pctx_xfer_desc=NULL;    /* Description of UID */
1516
1517     guchar *buf_desc=NULL;          /* Used in infor mode for item text */
1518     int     endpos=0;
1519
1520     int     cnt_abbs=0;             /* Number of Abstract Syntax Items */
1521     int     cnt_xfer=0;             /* Number of Trasfer Syntax Items */
1522
1523     #define MAX_BUFFER 1024
1524
1525     buf_desc=ep_alloc(MAX_BUFFER);      /* Valid for this packet */
1526     buf_desc[0]=0;
1527
1528     endpos=offset+len;
1529
1530     item_type = tvb_get_guint8(tvb, offset-4);
1531     item_len  = tvb_get_ntohs(tvb, offset-2);
1532
1533     pctx_pitem = proto_tree_add_text(tree, tvb, offset-4, item_len+4, pitem_prefix);
1534     pctx_ptree = proto_item_add_subtree(pctx_pitem, ett_assoc_pctx);
1535
1536     pctx_id     = tvb_get_guint8(tvb, offset);
1537     pctx_result = tvb_get_guint8(tvb, 2 + offset);      /* only set in responses, otherwise reserved and 0x00 */
1538
1539     /* Find or create dicom context object */
1540     pctx = dcm_state_pctx_get(assoc, pctx_id, TRUE);
1541     if (pctx == NULL) { /* Internal error. Failed to create data structre */
1542         return;
1543     }
1544
1545     proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_type, tvb, offset-4, 2, item_type);
1546     proto_tree_add_uint(pctx_ptree, hf_dcm_assoc_item_len,  tvb, offset-2, 2, item_len);
1547
1548     proto_tree_add_uint_format(pctx_ptree, hf_dcm_pctx_id, tvb, offset, 1, pctx_id, "Context ID: 0x%02x", pctx_id);
1549
1550     if (!is_assoc_request) {
1551         /* Accociation response. */
1552         proto_tree_add_uint_format(pctx_ptree, hf_dcm_pctx_result, tvb, offset+2, 1, pctx_result, "Result: %s (0x%x)", dcm_PCresult2str(pctx_result), pctx_result);
1553     }
1554
1555     offset += 4;
1556     while (-1 < offset && offset < endpos) {
1557
1558         item_type = tvb_get_guint8(tvb, offset);
1559         item_len = tvb_get_ntohs(tvb, 2 + offset);
1560
1561         offset += 4;
1562         switch (item_type) {
1563         case 0x30:              /* Abstract syntax */
1564
1565             /* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
1566             dissect_dcm_assoc_item(tvb, pinfo, pctx_ptree, assoc, offset-4,
1567                 "Abstract Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_abss_uid, &pctx_abss_desc,
1568                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_abss_syntax, ett_assoc_pctx_abss);
1569
1570             cnt_abbs += 1;
1571             offset += item_len;
1572             break;
1573
1574         case 0x40:              /* Transfer syntax */
1575
1576             dissect_dcm_assoc_item(tvb, pinfo, pctx_ptree, assoc, offset-4,
1577                 "Transfer Syntax: ", DCM_ITEM_VALUE_TYPE_UID, &pctx_xfer_uid, &pctx_xfer_desc,
1578                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pctx_xfer_syntax, ett_assoc_pctx_xfer);
1579
1580             /*
1581                In a correct Association Response, only one Transfer syntax shall be present.
1582                Therefore, pctx_xfer_uid, pctx_xfer_desc are used for the accept scenario in the info mode
1583             */
1584
1585             if (!is_assoc_request && pctx_result == 0) {
1586                 /* Association Response, Context Accepted */
1587                 dcm_set_syntax(pctx, pctx_xfer_uid, pctx_xfer_desc);
1588             }
1589             cnt_xfer += 1;
1590             offset += item_len;
1591             break;
1592
1593         default:
1594             offset += item_len;
1595             break;
1596         }
1597     }
1598
1599     if (is_assoc_request) {
1600
1601         if (cnt_abbs<1) {
1602             expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1603                 "No Abstract Syntax provided for this Presentation Context");
1604             return;
1605         }
1606         else if (cnt_abbs>1) {
1607             expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1608                 "More than one Abstract Syntax provided for this Presentation Context");
1609             return;
1610         }
1611
1612         if (cnt_xfer==0) {
1613             expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1614                 "No Transfer Syntax provided for this Presentation Context");
1615             return;
1616         }
1617
1618         if (pctx_abss_uid==NULL) {
1619             expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1620                 "No Abstract Syntax UID found for this Presentation Context");
1621             return;
1622         }
1623
1624     }
1625     else {
1626
1627         if (cnt_xfer>1) {
1628             expert_add_info_format(pinfo, pctx_pitem, PI_MALFORMED, PI_ERROR,
1629                 "Only one Transfer Syntax allowed in a Association Response");
1630             return;
1631         }
1632     }
1633
1634     if (pctx->abss_uid==NULL) {
1635         /* Permanent copy information into structure */
1636         pctx->abss_uid =g_strdup(pctx_abss_uid);
1637         pctx->abss_desc=g_strdup(pctx_abss_desc);
1638     }
1639
1640     /*
1641       Copy to buffer first, because proto_item_append_text()
1642       crashed for an unknown reason using 'ID 0x%02x, %s, %s'
1643       and in my opinion correctly set parameters.
1644     */
1645
1646     if (is_assoc_request) {
1647         if (pctx_abss_desc == NULL) {
1648             g_snprintf(buf_desc, MAX_BUFFER, "%s", pctx_abss_uid);
1649         }
1650         else {
1651             g_snprintf(buf_desc, MAX_BUFFER, "%s (%s)", pctx_abss_desc, pctx_abss_uid);
1652         }
1653     }
1654     else
1655     {
1656         /* g_snprintf() does not like NULL pointers */
1657
1658         if (pctx_result==0) {
1659             /* Accepted */
1660             g_snprintf(buf_desc, MAX_BUFFER, "ID 0x%02x, %s, %s, %s",
1661                 pctx_id, dcm_PCresult2str(pctx_result),
1662                 dcm_uid_or_desc(pctx->xfer_uid, pctx->xfer_desc),
1663                 dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
1664         }
1665         else {
1666             /* Rejected */
1667             g_snprintf(buf_desc, MAX_BUFFER, "ID 0x%02x, %s, %s",
1668                 pctx_id, dcm_PCresult2str(pctx_result),
1669                 dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
1670         }
1671     }
1672     proto_item_append_text(pctx_pitem, "%s", buf_desc);
1673
1674 }
1675
1676 static void
1677 dissect_dcm_userinfo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1678                      dcm_state_assoc_t *assoc, int offset, int len, guchar *pitem_prefix)
1679 {
1680     /*
1681         Decode the user info item in a Association Request or Response
1682     */
1683
1684     proto_item *userinfo_pitem = NULL;
1685     proto_tree *userinfo_ptree = NULL;  /* Tree for presentation context details */
1686
1687     guint8  item_type;
1688     guint16 item_len;
1689
1690
1691     gboolean first_item=TRUE;
1692
1693 /*     guchar *buf_desc=NULL;               Used in infor mode for item text */
1694
1695     guchar *info_max_pdu=NULL;
1696     guchar *info_impl_uid=NULL;
1697     guchar *info_impl_version=NULL;
1698     guchar *dummy=NULL;
1699
1700     int     endpos;
1701
1702     endpos=offset+len;
1703
1704     item_type = tvb_get_guint8(tvb, offset-4);
1705     item_len  = tvb_get_ntohs(tvb, offset-2);
1706
1707     userinfo_pitem = proto_tree_add_text(tree, tvb, offset-4, item_len+4, pitem_prefix);
1708     userinfo_ptree = proto_item_add_subtree(userinfo_pitem, ett_assoc_info);
1709
1710     proto_tree_add_uint(userinfo_ptree, hf_dcm_assoc_item_type, tvb, offset-4, 2, item_type);
1711     proto_tree_add_uint(userinfo_ptree, hf_dcm_assoc_item_len, tvb, offset-2, 2, item_len);
1712
1713     while (-1 < offset && offset < endpos) {
1714
1715         item_type = tvb_get_guint8(tvb, offset);
1716         item_len = tvb_get_ntohs(tvb, 2 + offset);
1717
1718         offset += 4;
1719         switch (item_type) {
1720         case 0x51:              /* Max length */
1721
1722             dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, assoc, offset-4,
1723                 "Max PDU Length: ", DCM_ITEM_VALUE_TYPE_UINT32, &info_max_pdu, &dummy,
1724                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_pdu_maxlen, ett_assoc_info_uid);
1725
1726             if (!first_item) {
1727                 proto_item_append_text(userinfo_pitem, ", ");
1728             }
1729             proto_item_append_text(userinfo_pitem, "Max PDU Length %s", info_max_pdu);
1730             first_item=FALSE;
1731
1732             offset += item_len;
1733             break;
1734
1735         case 0x52:              /* UID */
1736
1737             /* Parse Item. Works also in info mode where dcm_pctx_tree is NULL */
1738             dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, assoc, offset-4,
1739                 "Implementation UID: ", DCM_ITEM_VALUE_TYPE_STRING, &info_impl_uid, &dummy,
1740                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_uid, ett_assoc_info_uid);
1741
1742             if (!first_item) {
1743                 proto_item_append_text(userinfo_pitem, ", ");
1744             }
1745             proto_item_append_text(userinfo_pitem, "Implementation UID %s", info_impl_uid);
1746             first_item=FALSE;
1747
1748             offset += item_len;
1749             break;
1750
1751         case 0x55:              /* version */
1752
1753             dissect_dcm_assoc_item(tvb, pinfo, userinfo_ptree, assoc, offset-4,
1754                 "Implementation Version: ", DCM_ITEM_VALUE_TYPE_STRING, &info_impl_version, &dummy,
1755                 &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_info_version, ett_assoc_info_version);
1756
1757             if (!first_item) {
1758                 proto_item_append_text(userinfo_pitem, ", ");
1759             }
1760             proto_item_append_text(userinfo_pitem, "Version %s", info_impl_version);
1761             first_item=FALSE;
1762
1763             offset += item_len;
1764             break;
1765
1766         case 0x53:              /* async negotion */
1767             /* hf_dcm_async */
1768             offset += item_len;
1769             break;
1770
1771         default:
1772             offset += item_len;
1773             break;
1774         }
1775     }
1776 }
1777
1778
1779 static int
1780 dissect_dcm_assoc(tvbuff_t *tvb, packet_info *pinfo, proto_item *ti,
1781                   dcm_state_assoc_t *assoc, int offset, int len)
1782 {
1783     proto_tree *assoc_tree  = NULL;     /* Tree for PDU details */
1784
1785     guint8  item_type;
1786     guint16 item_len;
1787
1788     int     endpos;
1789
1790     guchar *item_value=NULL;
1791     guchar *item_description=NULL;
1792
1793 /*    guchar *info_pctx=NULL;            Description of Presentation Context */
1794
1795     endpos = offset+len;
1796
1797     if (ti) {
1798         assoc_tree = proto_item_add_subtree(ti, ett_assoc);
1799         while (-1 < offset && offset < endpos) {
1800
1801             item_type = tvb_get_guint8(tvb, offset);
1802             item_len  = tvb_get_ntohs(tvb, 2 + offset);
1803
1804             DISSECTOR_ASSERT(item_len > 0);
1805
1806             offset += 4;
1807
1808             switch (item_type) {
1809             case 0x10:          /* Application context */
1810                 dissect_dcm_assoc_item(tvb, pinfo, assoc_tree, assoc, offset-4,
1811                     "Application Context: ", DCM_ITEM_VALUE_TYPE_UID, &item_value, &item_description,
1812                     &hf_dcm_assoc_item_type, &hf_dcm_assoc_item_len, &hf_dcm_actx, ett_assoc_actx);
1813
1814                 offset += item_len;
1815                 break;
1816
1817             case 0x20:          /* Presentation context request */
1818                 dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len,
1819                     "Presentation Context: ", TRUE);
1820                 offset += item_len;
1821                 break;
1822
1823             case 0x21:          /* Presentation context reply */
1824                 dissect_dcm_pctx(tvb, pinfo, assoc_tree, assoc, offset, item_len,
1825                     "Presentation Context: ", FALSE);
1826                 offset += item_len;
1827                 break;
1828
1829             case 0x50:          /* User Info */
1830                 dissect_dcm_userinfo(tvb, pinfo, assoc_tree, assoc, offset, item_len, "User Info: ");
1831                 offset += item_len;
1832                 break;
1833
1834             default:
1835                 offset += item_len;
1836                 break;
1837             }
1838         }
1839     }
1840     return offset;
1841
1842 }
1843
1844 static int
1845 dissect_dcm_pdv_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1846                        dcm_state_assoc_t *assoc, int offset, guint8 *syntax,
1847                        dcm_state_pdv_t **pdv)
1848 {
1849     /* Dissect Context and Flags of a PDV and create new PDV stucture */
1850
1851     proto_item *pdv_ctx_pitem = NULL;
1852     proto_item *pdv_flags_pitem = NULL;
1853
1854     dcm_state_pctx_t    *pctx = NULL;
1855     dcm_state_pdv_t     *pdv_first_data = NULL;
1856
1857     const gchar *desc_flag = NULL;      /* Flag Description in tree */
1858     gchar *desc_header = NULL;          /* Used for PDV description */
1859
1860     guint8  flags = 0;
1861     guint8  pctx_id = 0;
1862
1863     *syntax = DCM_UNK;
1864
1865     /* 1 Byte Context */
1866     pctx_id = tvb_get_guint8(tvb, offset);
1867     pctx = dcm_state_pctx_get(assoc, pctx_id, FALSE);
1868
1869     if (pctx && pctx->xfer_uid) {
1870         proto_tree_add_uint_format(tree, hf_dcm_pdv_ctx, tvb, offset, 1,
1871             pctx_id, "Context: 0x%02x (%s, %s)", pctx_id,
1872         dcm_uid_or_desc(pctx->xfer_uid, pctx->xfer_desc),
1873         dcm_uid_or_desc(pctx->abss_uid, pctx->abss_desc));
1874     }
1875     else {
1876         pdv_ctx_pitem=proto_tree_add_uint_format(tree, hf_dcm_pdv_ctx, tvb,  offset, 1,
1877             pctx_id, "Context: 0x%02x not found. A-ASSOCIATE request not found in capture.", pctx_id);
1878         
1879         expert_add_info_format(pinfo, pdv_ctx_pitem, PI_MALFORMED, PI_ERROR, "Invalid Presentation Context ID");
1880
1881         /* Create fake PCTX and guess Syntax ILE, ELE, EBE */
1882         pctx = dcm_state_pctx_new(assoc, pctx_id);
1883
1884         /* To be done: Guess Syntax */
1885         pctx->syntax = DCM_UNK;
1886     }
1887     offset +=1;
1888
1889     *syntax = pctx->syntax;
1890
1891     /* Create PDV structure:
1892
1893        Since we can have multiple PDV per packet (offset) and
1894        multiple merged packets per PDV (tvb->raw_offset)
1895        we need both values to uniquely identify a PDV
1896     */
1897
1898     *pdv=dcm_state_pdv_get(pctx, pinfo->fd->num, tvb->raw_offset+offset, TRUE);
1899     if (*pdv==NULL) {
1900         return 0;                   /* Failed to allocate memory */
1901     }
1902
1903     /* 1 Byte Flag */
1904     flags = tvb_get_guint8(tvb, offset);
1905
1906     (*pdv)->pctx_id = pctx_id;          /* TBD: Required for export */
1907   
1908     desc_header=se_alloc(MAX_BUFFER);   /* Valid for this capture, since we return this buffer */
1909     memset(desc_header, 0, MAX_BUFFER);
1910
1911     switch (flags) {
1912     case 0:     /* 00 */
1913         desc_flag = "Data, More Fragments";
1914
1915         (*pdv)->is_flagvalid = TRUE;
1916         (*pdv)->is_command = FALSE;
1917         (*pdv)->is_last_fragment = FALSE;
1918         break;
1919
1920     case 2:     /* 10 */
1921         desc_flag = "Data, Last Fragment";
1922
1923         (*pdv)->is_flagvalid = TRUE;
1924         (*pdv)->is_command = FALSE;
1925         (*pdv)->is_last_fragment = TRUE;
1926         break;
1927
1928     case 1:     /* 01 */
1929         desc_flag = "Command, More Fragments";
1930         g_snprintf(desc_header, MAX_BUFFER, "Command");         /* Will be overwritten with real command tag */
1931
1932         *syntax = DCM_ILE;              /* Command tags are always little endian*/
1933
1934         (*pdv)->is_flagvalid = TRUE;
1935         (*pdv)->is_command = TRUE;
1936         (*pdv)->is_last_fragment = FALSE;
1937         break;
1938
1939     case 3:     /* 11 */
1940         desc_flag = "Command, Last Fragment";
1941         g_snprintf(desc_header, MAX_BUFFER, "Command");
1942
1943         *syntax = DCM_ILE;              /* Command tags are always little endian*/
1944
1945         (*pdv)->is_flagvalid = TRUE;
1946         (*pdv)->is_command = TRUE;
1947         (*pdv)->is_last_fragment = TRUE;
1948         break;
1949
1950     default:
1951         desc_flag = "Invalid Flags";
1952         g_snprintf(desc_header, MAX_BUFFER, "Invalid Flags");
1953
1954         *syntax = DCM_UNK;
1955
1956         (*pdv)->is_flagvalid = FALSE;
1957         (*pdv)->is_command = FALSE;
1958         (*pdv)->is_last_fragment = FALSE;
1959     }
1960
1961     if (flags == 0 || flags == 2) {
1962         /* Data PDV */
1963         pdv_first_data = dcm_state_pdv_get_obj_start(*pdv);
1964
1965         if (pdv_first_data->prev && pdv_first_data->prev->is_command) {
1966             /* Every Data PDV sequence should be preceeded by a Command PDV, 
1967                so we should always hit this for a correct capture
1968             */
1969
1970             if (pctx && pctx->abss_desc && g_str_has_suffix(pctx->abss_desc, "Storage")) {
1971                 /* Should be done far more intelligent, e.g. does not catch the (Retired) ones */
1972                 if (flags == 0) {
1973                     g_snprintf(desc_header, MAX_BUFFER, "%s (more fragments)", pctx->abss_desc);                
1974                 }
1975                 else {
1976                     g_snprintf(desc_header, MAX_BUFFER, "%s", pctx->abss_desc);         
1977                 }
1978                 (*pdv)->is_storage = TRUE;
1979             }
1980             else {
1981                 /* Use previous command and append DATA*/
1982                 g_snprintf(desc_header, MAX_BUFFER, "%s-DATA", pdv_first_data->prev->desc);
1983             }
1984         }
1985         else {
1986             g_snprintf(desc_header, MAX_BUFFER, "DATA");
1987         }
1988     }
1989
1990     (*pdv)->desc = desc_header;
1991
1992     pdv_flags_pitem = proto_tree_add_uint_format(tree, hf_dcm_pdv_flags, tvb, offset, 1,
1993         flags, "Flags: 0x%02x (%s)", flags, desc_flag);
1994
1995     if (flags>3) {
1996         expert_add_info_format(pinfo, pdv_flags_pitem, PI_MALFORMED, PI_ERROR, "Invalid Flags");
1997     }
1998     offset +=1;
1999
2000     return offset;
2001 }
2002
2003
2004 static int
2005 dissect_dcm_pdv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
2006                 dcm_state_assoc_t *assoc, int offset, guint32 pdv_len, guchar **pdv_description)
2007 {
2008     /* Handle one PDV inside a data PDU */
2009
2010 #define D_HEADER 1
2011 #define D_TAG    2
2012 #define D_VR     3
2013 #define D_LEN2   4
2014 #define D_LEN4   5
2015 #define D_VALUE  6
2016
2017     dcm_state_pdv_t *pdv = NULL;
2018
2019     proto_item *pitem = NULL;
2020
2021     int toffset, state, vr = 0, tr = 0;
2022
2023     guint32 tag_value_fragment_len;     /* used for values that span multiple PDVs */
2024
2025     guint16 grp = 0, elm = 0;
2026     guint32 tlen = 0;
2027     guint32 nlen = 0;                   /* Length of next sub item */
2028
2029     guchar *tag_value=NULL;             /* used for commands only so far */
2030
2031     guint8  syntax;
2032
2033     const guint8 *val=NULL;
2034
2035     int endpos = offset + pdv_len;
2036
2037     /* Dissect Context ID, Command/Data flag and More Fragments flag */
2038     offset = dissect_dcm_pdv_header(tvb, pinfo, tree, assoc, offset, &syntax, &pdv);
2039
2040     if (have_tap_listener(dicom_eo_tap) && pdv->data_len==0) {
2041         /* If not yet done, copy pure dicom data to buffer when running in export object mode */
2042         pdv->data_len = endpos-offset;
2043         pdv->data = se_alloc(pdv->data_len);
2044         if (pdv->data) {
2045             g_memmove(pdv->data, tvb_get_ptr(tvb, offset, pdv->data_len), pdv->data_len);
2046         }
2047         else {
2048             pdv->data_len = 0;          /* Failed to allocate memory. Don't copy anything */
2049         }
2050     }
2051
2052     if (pdv->prev) {
2053         /* Not frist PDV in the give presentation context (Those don't have remaining data to parse :-) */
2054
2055         if (pdv->prev->open_tag_rlen > 0) {
2056             /* previous PDV has left overs, i.e. this is a continuation PDV */
2057
2058             if (endpos - offset >= (int)pdv->prev->open_tag_rlen) {
2059                 /*
2060                  * Remaining bytes are equal or more than we expect for the open tag
2061                  * Finally reach the end of this tag
2062                  */
2063                 tag_value_fragment_len = pdv->prev->open_tag_rlen;
2064
2065                 pdv->open_tag_len  = 0;
2066                 pdv->open_tag_rlen = 0;
2067                 pdv->open_tag_desc = NULL;
2068                 pdv->initalized = TRUE;
2069
2070                 pdv->is_corrupt = FALSE;
2071             }
2072             else if (pdv->is_flagvalid && pdv->is_last_fragment) {
2073                 /*
2074                  * The tag is not yet complete, however, the flag indicates that it should be
2075                  * Therefore end this tag and issue an expert_add_info
2076                  */
2077
2078                 tag_value_fragment_len = endpos - offset;
2079
2080                 pdv->open_tag_len  = 0;
2081                 pdv->open_tag_rlen = 0;
2082                 pdv->open_tag_desc = NULL;
2083                 pdv->initalized = TRUE;
2084                 pdv->is_corrupt = TRUE;
2085             }
2086             else {
2087                 /*
2088                  * More to do for this tag
2089                  */
2090                 tag_value_fragment_len = endpos - offset;
2091
2092                 /* Set data in current PDV structure */
2093                 pdv->open_tag_len  = pdv->prev->open_tag_len;
2094                 pdv->open_tag_rlen = pdv->prev->open_tag_rlen - tag_value_fragment_len;
2095                 pdv->open_tag_desc = pdv->prev->open_tag_desc;
2096                 pdv->initalized = TRUE;
2097                 pdv->is_corrupt = FALSE;
2098             }
2099
2100             val = tvb_get_ptr(tvb, offset, tag_value_fragment_len);
2101
2102             if (pdv->is_corrupt) {
2103                 pitem = proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2104                     offset, tag_value_fragment_len, val, "%s [incomplete]",
2105                     pdv->prev->open_tag_desc);
2106
2107                 expert_add_info_format(pinfo, pitem, PI_MALFORMED, PI_ERROR,
2108                     "Early termination of tag. Data is missing");
2109
2110             }
2111             else {
2112                 proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2113                     offset, tag_value_fragment_len, val, "%s Bytes %d - %d [%s]",
2114                     pdv->prev->open_tag_desc,
2115                     pdv->prev->open_tag_len - pdv->prev->open_tag_rlen + 1,
2116                     pdv->prev->open_tag_len - pdv->open_tag_rlen,
2117                     (pdv->open_tag_rlen > 0 ? "continuation" : "end") );
2118             }
2119
2120             offset += tag_value_fragment_len;
2121         }
2122     }
2123
2124     if (syntax == DCM_UNK) {
2125         const guint8 *val;
2126         tlen = endpos - offset;
2127         val = tvb_get_ptr(tvb, offset, tlen);       /* Verify */
2128         proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2129             offset, tlen, val, "(%04x,%04x) %-8x Unparsed data", 0, 0, tlen);
2130         offset = pdv_len;
2131     }
2132
2133     /*  Command Tags
2134         2   Group
2135         2   Element
2136         4   unsigned Length n
2137         n   Value, always in Implicit Little VR
2138     */
2139     toffset = offset;
2140     state = D_TAG;
2141     nlen = 4;
2142     while (offset + nlen <= (guint32) endpos) {
2143         switch (state) {
2144         case D_TAG: {
2145             vr = tr = 0;
2146             if (DCM_ILE & syntax) {
2147                 grp = tvb_get_letohs(tvb, offset);
2148                 elm = tvb_get_letohs(tvb, offset+2);
2149                 state = (DCM_EBE & syntax) ? D_VR : D_LEN4;  /* is Explicit */
2150                 nlen  = (DCM_EBE & syntax) ? 2 : 4;         /* is Explicit */
2151             } else {
2152                 grp = tvb_get_ntohs(tvb, offset);
2153                 elm = tvb_get_ntohs(tvb, offset+2);
2154                 state = D_VR;
2155                 nlen = 2;
2156             }
2157             toffset = offset;
2158             if (0xfffe == grp) state = D_LEN4;
2159             offset += 4;
2160             } break;            /* don't fall through -- check length */
2161         case D_VR:  {
2162             guint8 V, R;
2163             vr = offset;
2164             V = tvb_get_guint8(tvb, offset);
2165             R = tvb_get_guint8(tvb, offset+1);
2166             offset +=2;
2167             /* 4byte lengths OB, OW, OF, SQ, UN, UT */
2168             state = D_LEN2;
2169             nlen = 2;
2170             if ((('O' == V) && ('B' == R || 'W' == R || 'F' == R) && (tr = DCM_OTH))
2171                 || (('U' == V) && ('N' == R || (('T' == R) && (tr = DCM_TSTR))))
2172                 || ('S' == V && 'Q' == R && (tr = DCM_SQ))) {
2173                 state = D_LEN4;
2174                 offset += 2;    /* skip 00 (2 bytes) */
2175                 nlen = 4;
2176             } else if ('F' == V && 'L' == R) {
2177                 tr = DCM_TFLT;
2178             } else if ('F' == V && 'D' == R) {
2179                 tr = DCM_TDBL;
2180             } else if (('S' == V && 'L' == R) || ('U' == V && 'L' == R)) {
2181                 tr = DCM_TINT4;
2182             } else if (('S' == V && 'S' == R) || ('U' == V && 'S' == R)) {
2183                 tr = DCM_TINT2;
2184             } else if ('A' == V && 'T' == R) {
2185                 tr = DCM_OTH;
2186             } else
2187                 tr = DCM_TSTR;
2188     /*
2189             else if (('A' == V && ('E' == R || 'S' == R))
2190                 || ('C' == V && 'S' == R)
2191                 || ('D' == V && ('A' == R || 'S' == R || 'T' == R))
2192                 || ('I' == V && 'S' == R)
2193                 || ('L' == V && ('O' == R || 'T' == R))
2194                 || ('P' == V && 'N' == R)
2195                 || ('S' == V && ('H' == R ||| 'T' == R))
2196                 || ('T' == V && 'M' == R)
2197                 || ('U' == V && ('I' == R || 'T' == R)))
2198                 tr = DCM_TSTR;
2199      */
2200             } break;            /* don't fall through -- check length */
2201         case D_LEN2: {
2202             if (DCM_ILE & syntax)       /* is it LE */
2203                 tlen = tvb_get_letohs(tvb, offset);
2204             else
2205                 tlen = tvb_get_ntohs(tvb, offset);
2206             offset += 2;
2207             state = D_VALUE;
2208             nlen = tlen;
2209             /*
2210             DISSECTOR_ASSERT(tlen > 0);
2211             */
2212             } break;
2213         case D_LEN4: {
2214             if (DCM_ILE & syntax)       /* is it LE */
2215                 tlen = tvb_get_letohl(tvb, offset);
2216             else
2217                 tlen = tvb_get_ntohl(tvb, offset);
2218             offset += 4;
2219             state = D_VALUE;
2220             nlen = tlen;
2221             /*
2222             DISSECTOR_ASSERT(tlen > 0);
2223             */
2224             } break;            /* don't fall through -- check length */
2225         case D_VALUE: {
2226             const guint8 *val;
2227             int totlen = (offset - toffset);
2228             if (0xffffffff == tlen || 0xfffe == grp) {
2229                 val = tvb_get_ptr(tvb, toffset, totlen);
2230                 proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2231                     toffset, totlen, val,
2232                     "(%04x,%04x) %-8x %s", grp, elm, tlen,
2233                         dcm_tag2str(grp, elm, syntax, tvb, offset, 0, vr, tr, &tag_value));
2234
2235                 tlen = 0;
2236             /* } else if (0xfffe == grp) { */ /* need to make a sub-tree here */
2237             } else {
2238                 totlen += tlen;
2239                 val = tvb_get_ptr(tvb, toffset, totlen);
2240                 proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2241                     toffset, totlen, val,
2242                     "(%04x,%04x) %-8x %s", grp, elm, tlen,
2243                         dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, tr, &tag_value));
2244             }
2245
2246
2247             /* Store SOP Class and Instance UID in first PDV of this object */
2248             if (grp == 0x0008 && elm == 0x0016) {
2249                 dcm_state_pdv_get_obj_start(pdv)->sop_class_uid = g_strdup(tag_value);
2250             }
2251             else if (grp == 0x0008 && elm == 0x0018) {
2252                 dcm_state_pdv_get_obj_start(pdv)->sop_instance_uid = g_strdup(tag_value);
2253             }
2254             else if (grp == 0x0000 && elm == 0x0100) {
2255                 /* This is the command tag -> overwrite existing PDV description */
2256                 pdv->desc = g_strdup(tag_value);
2257             }
2258
2259             offset += tlen;
2260             state = D_TAG;
2261             nlen = 4;
2262             } break;
2263         }
2264     }
2265     /*  After a properly formed Tag, where Tag, VR, Len and Value are present
2266         The next state should be a D_TAG
2267
2268         If the value is too large (start of a long value) we remain in state D_VALUE
2269         But if we are in a not detected continutation we may also get 'stuck' in state D_VALUE
2270     */
2271
2272     if (D_VALUE == state) {
2273
2274         const guint8 *val;
2275         guchar  *buf;
2276
2277         tag_value_fragment_len = pdv_len - offset + 10;     /*  The 10 is a result of debugging :-((
2278                                                                 Fix once the Tag parisng has been structured
2279                                                             */
2280         val = tvb_get_ptr(tvb, offset, tag_value_fragment_len);
2281
2282         buf=ep_alloc(2048);         /* Longer than what dcm_tag2str() returns */
2283         *buf = 0;
2284
2285         g_snprintf(buf, 2048, "(%04x,%04x) %-8x %s",
2286             grp, elm, tlen,
2287             dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, DCM_OTH, &tag_value));
2288
2289         proto_tree_add_bytes_format(tree, hf_dcm_data_tag, tvb,
2290             offset, tag_value_fragment_len, val, "%s Bytes %10d - %10d [start]", buf, 1, tag_value_fragment_len);
2291
2292         if (!pdv->initalized) {
2293             /*  First time parsing of this PDV.
2294                 Save the needed data for reuse, i.e. when being called just to open a particular packet
2295             */
2296
2297             pdv->open_tag_len  = tlen;
2298             pdv->open_tag_rlen = tlen-tag_value_fragment_len;
2299             pdv->open_tag_desc = g_strdup(buf);             /* EP memory will be freeded. Therefore copy */
2300             pdv->initalized = TRUE;
2301         }
2302     }
2303     else {
2304         if (!pdv->initalized) {
2305             /*  First time parsing of this PDV.
2306                 Save the needed data for reuse, i.e. when called just to open a particular packet
2307             */
2308             pdv->open_tag_len = 0;
2309             pdv->open_tag_rlen = 0;
2310             pdv->open_tag_desc = NULL;
2311             pdv->initalized = TRUE;
2312         }
2313     }
2314
2315     if (have_tap_listener(dicom_eo_tap) && pdv->data_len>0) {
2316         if (pdv->is_last_fragment) {
2317             dcm_export_create_object(pinfo, assoc, pdv); 
2318         }
2319     }
2320
2321     *pdv_description = pdv->desc; 
2322
2323     return endpos;
2324 }
2325
2326 static int
2327 dissect_dcm_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
2328                  dcm_state_assoc_t *assoc, int offset, guint32 pdu_len, guchar **pdu_description)
2329 {
2330
2331     /*  04 P-DATA-TF
2332          1  1 reserved
2333          2  4 length
2334               - (1+) presentation data value (PDV) items
2335          6  4 length
2336         10  1 Presentation Context ID (odd ints 1 - 255)
2337               - PDV
2338         11  1 header
2339               0x01 if set, contains Message Command info, else Message Data
2340               0x02 if set, contains last fragment
2341     */
2342
2343     proto_tree *pdv_ptree = NULL;       /* Tree for item details */
2344     proto_item *pdv_pitem = NULL;
2345
2346     guchar  *buf_desc=NULL;             /* PDU description */
2347     guchar  *pdv_description=NULL;
2348
2349     gboolean first_pdv=TRUE;
2350
2351     int endpos = offset + pdu_len;
2352     int pdv_len=0;
2353
2354     buf_desc=se_alloc(MAX_BUFFER);      /* Valid for this capture, since we return this buffer */
2355     buf_desc[0]=0;
2356
2357     /* Loop thorugh multiple PDVs */
2358     while (offset < endpos) {
2359         pdv_len = tvb_get_ntohl(tvb, offset);
2360         DISSECTOR_ASSERT(pdv_len > 0);
2361
2362         pdv_pitem = proto_tree_add_text(tree, tvb, offset, pdv_len+4, "PDV");
2363         pdv_ptree = proto_item_add_subtree(pdv_pitem, ett_dcm_data_pdv);
2364
2365         proto_tree_add_item(pdv_ptree, hf_dcm_pdv_len, tvb, offset, 4, FALSE);
2366         offset +=4;
2367
2368         offset = dissect_dcm_pdv(tvb, pinfo, pdv_ptree, assoc, offset, pdv_len, &pdv_description);
2369
2370         if (first_pdv) {
2371             g_snprintf(buf_desc, MAX_BUFFER, "%s", pdv_description);
2372         }
2373         else {
2374             g_snprintf(buf_desc, MAX_BUFFER, "%s, %s", buf_desc, pdv_description);
2375         }
2376
2377         proto_item_append_text(pdv_pitem, ", %s", pdv_description);
2378         first_pdv=FALSE;
2379
2380
2381         /* offset should be advanced by pdv_len */
2382     }
2383
2384     *pdu_description=buf_desc;
2385     return offset;
2386 }
2387
2388
2389 static int
2390 dissect_dcm_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean require_assoc_req)
2391 {
2392     /* Code to actually dissect the packets */
2393
2394     guint8  pdu_type=0;
2395     guint32 pdu_start=0;
2396     guint32 pdu_len=0;
2397     guint16 vers=0;
2398     guint32 tlen=0;
2399
2400     int offset=0;
2401
2402     /*
2403         Modified orignal code, which was optimized for a heuristic detection, and therefore
2404         caused some load and memory consumption, for every non DICOM packet not processed
2405         by someone else.
2406
2407         Since tcp packets are now assembled well by wireshark (in conjunction with the dissectors)
2408         we will only see properly alligned PDUs, at the beginnig of the buffer, else its not DICOM
2409         traffic.
2410
2411         Therfore do the byte checking as early as possible
2412         The heurisitc hook, checks for an association request
2413
2414         DICOM PDU are nice, but need to be managed
2415
2416         We can have any combination:
2417         - One or more DICOM PDU per TCP packet
2418         - PDU split over different TCP packets
2419         - And both together, i.e. some complete PDUs and then a fraction of a new PDU in a TCP packet
2420
2421         This function will handle multiple PDUs per TCP packet and will ask for more data,
2422         if the last PDU does not fit
2423
2424         It does not reassamble fragmented PDVs by purpose, since the Tag Value parsing needs to be done
2425         per Tag, and PDU recombinaion here would
2426         a) need to eliminate PDU/PDV/Ctx header (12 bytes)
2427         b) not show the true DICOM logic in transfer
2428
2429         The Lenght check is tricky. If not a PDV continuation, 10 Bytes are required. For PDV continuation
2430         anything seems to be possible, depending on the buffer alignment of the sending process
2431
2432         I have seen a 4 Byte PDU 'Header' just at the end of a TCP packet, which will come in here
2433         as tlen with 4 bytes.
2434     */
2435
2436     tlen = tvb_reported_length(tvb);
2437
2438     pdu_type = tvb_get_guint8(tvb, 0);
2439     if (pdu_type==0 || pdu_type>7)              /* Wrong PDU type. 'Or' is slightly more efficient than 'and' */
2440         return 0;                               /* No bytes taken from the stack */
2441
2442     if (pdu_type==4) {
2443         if (tlen<2) {
2444             /* Hopefully we don't have 1 Byte PDUs in PDV continuations, otherwise reduce to 1 */
2445             return 0;
2446         }
2447         else if (tlen<6) {
2448             /* we need 6 bytes at least to get PDU length */
2449             pinfo->desegment_offset = offset;
2450             pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
2451             return TRUE;
2452         }
2453     }
2454     else if (tlen<10) {
2455         return 0;
2456     }
2457
2458     pdu_len = tvb_get_ntohl(tvb, 2);
2459     if (pdu_len<4)                              /* The smallest PDUs are ASSOC Rejects & Release Msgs */
2460         return 0;
2461
2462     if (require_assoc_req) {
2463
2464         /* find_conversation() seems to return a converstation, even if we never saw
2465            any packet yet. Not really my interpretation of this function.
2466
2467            Therefore also check, if we already stored configuration data for converstation
2468         */
2469
2470         if (dcm_state_get(pinfo, FALSE)==NULL) {
2471
2472             /* config data does not exist, check for association request */
2473
2474             vers = tvb_get_ntohs(tvb, 6);
2475
2476             if (!(pdu_type == 1 && vers == 1)) {    /* Not PDU type 0x01 or not Version 1 */
2477                 return 0;
2478             }
2479
2480             /* Exit if TCP payload is bigger than PDU length (plues header)
2481                ok. for PRESENTATION_DATA, questionable for ASSOCIATION requests
2482             */
2483             if (pdu_len+6 < tlen)
2484                 return 0;
2485
2486         }
2487     }
2488
2489     if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
2490         col_clear(pinfo->cinfo, COL_PROTOCOL);
2491     }
2492
2493      /* Process all PDUs in the buffer */
2494     while (pdu_start < tlen) {
2495
2496         if ((pdu_len+6) > (tlen-offset)) {
2497
2498             /*  PDU is larger than the remaing packet (buffer), therefore request whole PDU
2499                 The next time this function is called, tlen will be equal to pdu_len
2500             */
2501
2502             pinfo->desegment_offset = offset;
2503             pinfo->desegment_len = (pdu_len+6) - (tlen-offset);
2504
2505             /*  Why return a boolean for a deliberate int function? No clue, but
2506                 no better working example found.
2507             */
2508             return TRUE;
2509         }
2510
2511         /* Process a whole PDU */
2512         offset=dissect_dcm_pdu(tvb, pinfo, tree, pdu_start);
2513
2514         /* Next PDU */
2515         pdu_start =  pdu_start + pdu_len + 6;
2516
2517         if (pdu_start < tlen - 6) {
2518             /* we got at least 6 bytes of the next PDU still in the buffer */
2519              pdu_len = tvb_get_ntohl(tvb, pdu_start+2);
2520         }
2521         else {
2522             pdu_len = 0;
2523         }
2524     }
2525     return offset;
2526 }
2527
2528 /* Call back functions used to register */
2529 static int
2530 dissect_dcm_static(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
2531 {
2532     /* Less checking on ports that match */
2533     return dissect_dcm_main(tvb, pinfo, tree, 0);
2534 }
2535
2536 static int
2537 dissect_dcm_heuristic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
2538 {
2539     /* Only decode conerstations, which include an Association Request */
2540     /* This will be potentially called for every packet */
2541     return dissect_dcm_main(tvb, pinfo, tree, TRUE);
2542 }
2543
2544 static int
2545 dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
2546 {
2547     proto_item *ti=NULL;
2548     proto_item *ti_pdu_type=NULL;
2549
2550     dcm_state_t *dcm_data=NULL;
2551     dcm_state_assoc_t *assoc=NULL;
2552
2553     proto_tree *dcm_tree=NULL;
2554
2555     guint8  pdu_type=0;
2556     guint32 pdu_len=0;
2557
2558     guchar *pdu_description=NULL;
2559
2560     int assoc_header=0;
2561
2562     gboolean    valid_pdutype=TRUE;
2563
2564     guchar *info_str = NULL;
2565
2566     /* Get or create converstation. Used to store context IDs and xfer Syntax */
2567
2568     dcm_data = dcm_state_get(pinfo, TRUE);
2569     if (dcm_data == NULL) {     /* internal error. Failed to create main dicom data structre */
2570         return  0;
2571     }
2572
2573     if (check_col(pinfo->cinfo, COL_PROTOCOL))
2574         col_set_str(pinfo->cinfo, COL_PROTOCOL, "DICOM");
2575
2576     /* This field shows up as the "Info" column in the display; you should make
2577        it, if possible, summarize what's in the packet, so that a user looking
2578        at the list of packets can tell what type of packet it is. See section 1.5
2579        for more information.
2580     */
2581
2582     if (check_col(pinfo->cinfo, COL_INFO))
2583         col_clear(pinfo->cinfo, COL_INFO);
2584
2585     if (pdu_type==1) {
2586         /* 'Force' new association object */
2587         assoc=dcm_state_assoc_new(dcm_data, pinfo->fd->num);
2588     }
2589     else {
2590         /* Create new association object, if needed, i.e. if we association request is not in capture */
2591         assoc=dcm_state_assoc_get(dcm_data, pinfo->fd->num, TRUE);
2592     }
2593
2594     if (assoc == NULL) {        /* internal error. Failed to association structre */
2595         return  0;
2596     }
2597
2598     info_str=ep_alloc(MAX_BUFFER);
2599     info_str[0]=0;
2600
2601     pdu_type = tvb_get_guint8(tvb, offset);
2602     pdu_len = tvb_get_ntohl(tvb, offset + 2);
2603
2604     switch (pdu_type) {
2605     case 1:                                     /* ASSOC Request */
2606         tvb_memcpy(tvb, assoc->ae_called, 10, 16);
2607         tvb_memcpy(tvb, assoc->ae_calling, 26, 16);
2608         assoc->ae_called[AEEND] = 0;
2609         assoc->ae_calling[AEEND] = 0;
2610         g_snprintf(info_str, 128, "A-ASSOCIATE request %s --> %s",
2611             g_strstrip(assoc->ae_calling), g_strstrip(assoc->ae_called));
2612         assoc_header = 74;
2613         break;
2614     case 2:                                     /* ASSOC Accept */
2615         tvb_memcpy(tvb, assoc->ae_called_resp, 10, 16);
2616         tvb_memcpy(tvb, assoc->ae_calling_resp, 26, 16);
2617         assoc->ae_called_resp[AEEND] = 0;
2618         assoc->ae_calling_resp[AEEND] = 0;
2619         g_snprintf(info_str, MAX_BUFFER, "A-ASSOCIATE accept  %s <-- %s",
2620             g_strstrip(assoc->ae_calling_resp), g_strstrip(assoc->ae_called_resp));
2621         assoc_header = 74;
2622         break;
2623     case 3:                                     /* ASSOC Reject */
2624         assoc->result = tvb_get_guint8(tvb, 7);
2625         assoc->source = tvb_get_guint8(tvb, 8);
2626         assoc->reason = tvb_get_guint8(tvb, 9);
2627         g_snprintf(info_str, 128, "A-ASSOCIATE reject  %s <-- %s %s %s %s",
2628             g_strstrip(assoc->ae_calling), g_strstrip(assoc->ae_called),
2629             dcm_result2str(assoc->result),
2630             dcm_source2str(assoc->source),
2631             dcm_reason2str(assoc->source, assoc->reason));
2632         break;
2633     case 4:                                     /* DATA */
2634         info_str="P-DATA";
2635         break;
2636     case 5:                                     /* RELEASE Request */
2637         info_str="A-RELEASE request";
2638         break;
2639     case 6:                                     /* RELEASE Response */
2640         info_str="A-RELEASE response";
2641         break;
2642     case 7:                                     /* ABORT */
2643         assoc->source = tvb_get_guint8(tvb, 8);
2644         assoc->reason = tvb_get_guint8(tvb, 9);
2645         g_snprintf(info_str, 128, "ABORT %s <-- %s %s %s",
2646             assoc->ae_called, assoc->ae_calling,
2647             (assoc->source == 1) ? "USER" :
2648                 (assoc->source == 2) ? "PROVIDER" : "",
2649             assoc->source == 1 ? dcm_abort2str(assoc->reason) : "");
2650         break;
2651     default:
2652         info_str="Continuation or non-DICOM traffic";
2653         valid_pdutype = FALSE;                          /* No packets taken from stack */
2654         break;
2655     }
2656
2657     if (check_col(pinfo->cinfo, COL_INFO))
2658         col_add_str(pinfo->cinfo, COL_INFO, info_str);
2659
2660     if (valid_pdutype) {
2661
2662         if (tree || have_tap_listener(dicom_eo_tap)) {
2663             /*  In the interest of speed, if "tree" is NULL, don't do any work not
2664                 necessary to generate protocol tree items.
2665             */
2666
2667             proto_item *tf;
2668             ti = proto_tree_add_item(tree, proto_dcm, tvb, offset, -1, FALSE);
2669             dcm_tree = proto_item_add_subtree(ti, ett_dcm);
2670             ti_pdu_type = proto_tree_add_uint_format(dcm_tree, hf_dcm_pdu, tvb, offset, pdu_len+6,
2671                 pdu_type, "PDU Type 0x%x (%s)", pdu_type, dcm_pdu2str(pdu_type));
2672             proto_tree_add_item(dcm_tree, hf_dcm_pdu_len, tvb, offset+2, 4, FALSE);
2673
2674             if (pdu_type==3) {
2675                 expert_add_info_format(pinfo, ti_pdu_type, PI_RESPONSE_CODE, PI_WARN, "Asscociation rejected");
2676             }
2677             else if (pdu_type==7) {
2678                 expert_add_info_format(pinfo, ti_pdu_type, PI_RESPONSE_CODE, PI_WARN, "Asscociation aborted");
2679             }
2680
2681             switch (pdu_type) {
2682             case 1:                                     /* ASSOC Request */
2683             case 2:                                     /* ASSOC Accept */
2684                 tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, offset, pdu_len+6, info_str);
2685                 offset = dissect_dcm_assoc(tvb, pinfo, tf, assoc, offset+assoc_header, pdu_len+6-assoc_header);
2686                 break;
2687
2688             case 4:                                     /* DATA */
2689                 offset = dissect_dcm_data(tvb, pinfo, dcm_tree, assoc, offset+6, pdu_len, &pdu_description);
2690                 proto_item_append_text(ti, ", %s", pdu_description);
2691
2692                 if (check_col(pinfo->cinfo, COL_INFO))
2693                     col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", pdu_description);
2694
2695                 break;
2696
2697             case 3:                                     /* ASSOC Reject */
2698             case 5:                                     /* RELEASE Request */
2699             case 6:                                     /* RELEASE Response */
2700             case 7:                                     /* ABORT */
2701                 /* Info string decoding only at this point */
2702                 offset += pdu_len+6;
2703                 break;
2704
2705             default:
2706                 offset=0;
2707                 break;
2708             }
2709         }
2710         else if (pdu_type == 1 || pdu_type == 2) {
2711             /*  Always dissect Association request and response in order
2712                 to set the data strucures needed for the PDU Data packets
2713             */
2714             offset = dissect_dcm_assoc(tvb, pinfo, NULL, assoc, offset+assoc_header, pdu_len+6-assoc_header);
2715         }
2716     }
2717
2718     /* If this protocol has a sub-dissector call it here, see section 1.8 */
2719
2720     return offset;          /* return the number of processed bytes */
2721
2722 }
2723
2724
2725 /* Register the protocol with Wireshark */
2726
2727 /* this format is require because a script is used to build the C function
2728    that calls all the protocol registration.
2729 */
2730
2731 static void range_delete_dcm_tcp_callback(guint32 port) {
2732     dissector_delete("tcp.port", port, dcm_handle);
2733 }
2734
2735 static void range_add_dcm_tcp_callback(guint32 port) {
2736     dissector_add("tcp.port", port, dcm_handle);
2737 }
2738
2739 static void dcm_apply_settings(void) {
2740
2741     /* deregister first */
2742     range_foreach(global_dcm_tcp_range_backup, range_delete_dcm_tcp_callback);
2743     g_free(global_dcm_tcp_range_backup);
2744
2745     heur_dissector_delete("tcp", dissect_dcm_heuristic, proto_dcm);
2746
2747     /*  Register 'static' tcp port range specified in properties
2748         Statically defined ports take precedence over a heuristic one,
2749         I.e., if an foreign protocol claims a port, where dicom is running on
2750         We would never be called, by just having the heuristic registration
2751     */
2752
2753     range_foreach(global_dcm_tcp_range, range_add_dcm_tcp_callback);
2754
2755     /* remember settings for next time */
2756     global_dcm_tcp_range_backup = range_copy(global_dcm_tcp_range);
2757
2758     /*  Add heuristic search, if user selected it */
2759
2760     if (global_dcm_heuristic)
2761         heur_dissector_add("tcp", dissect_dcm_heuristic, proto_dcm);
2762
2763 }
2764 void
2765 proto_register_dcm(void)
2766 {
2767 /* Setup list of header fields  See Section 1.6.1 for details*/
2768     static hf_register_info hf[] = {
2769     { &hf_dcm_pdu, { "PDU Type", "dicom.pdu.type",
2770         FT_UINT8, BASE_HEX, VALS(dcm_pdu_ids), 0, "", HFILL } },
2771     { &hf_dcm_pdu_len, { "PDU Length", "dicom.pdu.len",
2772         FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
2773     { &hf_dcm_pdu_type, { "PDU Detail", "dicom.pdu.detail",
2774         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2775     { &hf_dcm_assoc_item_type, { "Item Type", "dicom.assoc.item.type",
2776         FT_UINT8, BASE_HEX, VALS(dcm_assoc_item_type), 0, "", HFILL } },
2777     { &hf_dcm_assoc_item_len, { "Item Length", "dicom.assoc.item.len",
2778         FT_UINT16, BASE_DEC, NULL, 0, "", HFILL } },
2779     { &hf_dcm_actx, { "Application Context", "dicom.actx",
2780         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2781     { &hf_dcm_pctx_id, { "Presentation Context ID", "dicom.pctx.id",
2782         FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
2783     { &hf_dcm_pctx_result, { "Presentation Context Result", "dicom.pctx.id",
2784         FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
2785     { &hf_dcm_pctx_abss_syntax, { "Abstract Syntax", "dicom.pctx.abss.syntax",
2786         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2787     { &hf_dcm_pctx_xfer_syntax, { "Transfer Syntax", "dicom.pctx.xfer.syntax",
2788         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2789     { &hf_dcm_info_uid, { "Implementation Class UID", "dicom.userinfo.uid",
2790         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2791     { &hf_dcm_info_version, { "Implementation Version", "dicom.userinfo.version",
2792         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
2793     { &hf_dcm_pdu_maxlen, { "Max PDU Length", "dicom.max_pdu_len",
2794         FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
2795     { &hf_dcm_pdv_len, { "PDV Length", "dicom.pdv.len",
2796         FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
2797     { &hf_dcm_pdv_ctx, { "PDV Context", "dicom.pdv.ctx",
2798         FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
2799     { &hf_dcm_pdv_flags, { "PDV Flags", "dicom.pdv.flags",
2800         FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
2801     { &hf_dcm_data_tag, { "Tag", "dicom.data.tag",
2802         FT_BYTES, BASE_HEX, NULL, 0, "", HFILL } },
2803 /*
2804     { &hf_dcm_FIELDABBREV, { "FIELDNAME", "dicom.FIELDABBREV",
2805         FIELDTYPE, FIELDBASE, FIELDCONVERT, BITMASK, "FIELDDESCR", HFILL } },
2806  */
2807     };
2808
2809 /* Setup protocol subtree array */
2810     static gint *ett[] = {
2811             &ett_dcm,
2812             &ett_assoc,
2813             &ett_assoc_actx,
2814             &ett_assoc_pctx,
2815             &ett_assoc_pctx_abss,
2816             &ett_assoc_pctx_xfer,
2817             &ett_assoc_info,
2818             &ett_assoc_info_uid,
2819             &ett_assoc_info_version,
2820             &ett_dcm_data,
2821             &ett_dcm_data_pdv,
2822             &ett_dcm_data_tag
2823     };
2824
2825     module_t *dcm_module;
2826
2827     /* Register the protocol name and description */
2828     proto_dcm = proto_register_protocol("DICOM", "DICOM", "dicom");
2829
2830     /* Required function calls to register the header fields and subtrees used */
2831     proto_register_field_array(proto_dcm, hf, array_length(hf));
2832     proto_register_subtree_array(ett, array_length(ett));
2833
2834     dcm_module = prefs_register_protocol(proto_dcm, dcm_apply_settings);
2835
2836     range_convert_str(&global_dcm_tcp_range, DICOM_DEFAULT_RANGE, 65535);
2837     global_dcm_tcp_range_backup = range_empty();
2838     prefs_register_range_preference(dcm_module, "tcp.port",
2839         "DICOM Ports", "DICOM Ports range", &global_dcm_tcp_range, 65535);
2840
2841     prefs_register_bool_preference(dcm_module, "heuristic",
2842             "Search on any TCP Port (heuristic mode)",
2843             "When enabled, the DICOM dissector will parse all TCP packets "
2844             "not handled by any other dissector and look for an association request. "
2845             "Disabled by default, to preserve resources for the non DICOM community.",
2846             &global_dcm_heuristic);
2847
2848     prefs_register_bool_preference(dcm_module, "header",
2849             "Create Meta Header on Export",
2850             "Create DICOM File Meta Header according to PS 3.10 on export for PDUs. "
2851             "If the cpatured PDV does not contain a SOP Class UID and SOP Instance UID "
2852             "(e.g. for command PDVs), wireshark spefic ones will be created.",
2853             &global_dcm_header);
2854
2855     dicom_eo_tap = register_tap("dicom_eo"); /* DICOM Export Object tap */
2856
2857     register_init_routine(&dcm_init);
2858 }
2859
2860
2861 /* If this dissector uses sub-dissector registration add a registration routine.
2862    This format is required because a script is used to find these routines and
2863    create the code that calls these routines.
2864 */
2865 void
2866 proto_reg_handoff_dcm(void)
2867 {
2868
2869     dcm_handle = new_create_dissector_handle(dissect_dcm_static, proto_dcm);
2870
2871     dcm_apply_settings();       /* Register static and heuristic ports */
2872
2873 }
2874
2875
2876 /*
2877
2878 PDU's
2879 01 ASSOC-RQ
2880  1    1 reserved
2881  2    4 length
2882  6    2 protocol version (0x0 0x1)
2883  8    2 reserved
2884 10   16 dest aetitle
2885 26   16 src  aetitle
2886 42   32 reserved
2887 74    - presentation data value items
2888
2889 02 A-ASSOC-AC
2890     1 reserved
2891     4 length
2892     2 protocol version (0x0 0x1)
2893     2 reserved
2894    16 dest aetitle (not checked)
2895    16 src  aetitle (not checked)
2896    32 reserved
2897     - presentation data value items
2898
2899 03 ASSOC-RJ
2900     1 reserved
2901     4 length (4)
2902     1 reserved
2903     1 result  (1 reject perm, 2 reject transient)
2904     1 source  (1 service user, 2 service provider, 3 service profider)
2905     1 reason
2906         1 == source
2907             1 no reason given
2908             2 application context name not supported
2909             3 calling aetitle not recognized
2910             7 called aetitle not recognized
2911         2 == source
2912             1 no reason given
2913             2 protocol version not supported
2914         3 == source
2915             1 temporary congestion
2916             2 local limit exceeded
2917
2918 04 P-DATA-TF
2919  1  1 reserved
2920  2  4 length
2921     - (1+) presentation data value (PDV) items
2922  6      4 length
2923 10      1 Presentation Context ID (odd ints 1 - 255)
2924         - PDV
2925 11      1 header
2926             0x01 if set, contains Message Command info, else Message Data
2927             0x02 if set, contains last fragment
2928
2929 05 A-RELEASE-RQ
2930     1 reserved
2931     4 length (4)
2932     4 reserved
2933
2934 06 A-RELEASE-RP
2935     1 reserved
2936     4 length (4)
2937     4 reserved
2938
2939 07 A-ABORT
2940     1  reserved
2941     4  length (4)
2942     2  reserved
2943     1  source  (0 = user, 1 = provider)
2944     1  reason  if 1 == source (0 not spec, 1 unrecognized, 2 unexpected 4 unrecognized param, 5 unexpected param, 6 invalid param)
2945
2946
2947
2948 ITEM's
2949 10 Application Context
2950     1 reserved
2951     2 length
2952     - name
2953
2954 20 Presentation Context
2955     1 reserved
2956     2 length
2957     1 Presentation context id
2958     3 reserved
2959     - (1) abstract and (1+) transfer syntax sub-items
2960
2961 21 Presentation Context (Reply)
2962     1 reserved
2963     2 length
2964     1 ID (odd int's 1-255)
2965     1 reserved
2966     1 result (0 accept, 1 user-reject, 2 no-reason, 3 abstract not supported, 4- transfer syntax not supported)
2967     1 reserved
2968     - (1) type 40
2969
2970 30 Abstract syntax
2971     1 reserved
2972     2 length
2973     - name (<= 64)
2974
2975 40 Transfer syntax
2976     1 reserved
2977     2 length
2978     - name (<= 64)
2979
2980 50 user information
2981     1 reserved
2982     2 length
2983     - user data
2984
2985 51 max length
2986     1 reserved
2987     2 length (4)
2988     4 max PDU lengths
2989
2990 From 3.7 Annex D Association Negotiation
2991 ========================================
2992
2993 52 IMPLEMENTATION CLASS UID
2994     1 Item-type 52H
2995     1 Reserved
2996     2 Item-length
2997     n Implementation-class-uid
2998
2999 55 IMPLEMENTATION VERSION NAME
3000     1 Item-type 55H
3001     1 Reserved
3002     2 Item-length
3003     n Implementation-version-name
3004
3005 53 ASYNCHRONOUS OPERATIONS WINDOW
3006     1 Item-type 53H
3007     1 Reserved
3008     2 Item-length
3009     2 Maximum-number-operations-invoked
3010     2 Maximum-number-operations-performed
3011
3012 54 SCP/SCU ROLE SELECTION
3013     1 Item-type 54H
3014     1 Reserved
3015     2 Item-length (n)
3016     2 UID-length (m)
3017     m SOP-class-uid
3018     1 SCU-role
3019       0 - non support of the SCU role
3020       1 - support of the SCU role
3021     1 SCP-role
3022       0 - non support of the SCP role
3023       1 - support of the SCP role.
3024
3025 56 SOP CLASS EXTENDED NEGOTIATION
3026     1 Item-type 56H
3027     1 Reserved
3028     2 Item-Length (n)
3029     2 SOP-class-uid-length (m)
3030     m SOP-class-uid
3031     n-m Service-class-application-information
3032
3033 57 SOP CLASS COMMON EXTENDED NEGOTIATION
3034     1 Item-type 57H
3035     1 Sub-item-version
3036     2 Item-Length
3037     2 SOP-class-uid-length (m)
3038     7-x   SOP-class-uid The SOP Class identifier encoded as a UID as defined in PS 3.5.
3039     (x+1)-(x+2) Service-class-uid-length  The Service-class-uid-length shall be the number of bytes in the Service-class-uid field. It shall be encoded as an unsigned binary number.
3040     (x+3)-y Service-class-uid The Service Class identifier encoded as a UID as defined in PS 3.5.
3041     (y+1)-(y+2) Related-general-sop-class-identification-length The Related-general-sop-class-identification-length shall be the number of bytes in the Related-general-sop-class-identification field. Shall be zero if no Related General SOP Classes are identified.
3042     (y+3)-z Related-general-sop-class-identification  The Related-general-sop-class-identification is a sequence of pairs of length and UID sub-fields.  Each pair of sub-fields shall be formatted in accordance with Table D.3-13.
3043     (z+1)-k Reserved  Reserved for additional fields of the sub-item. Shall be zero-length for Version 0 of Sub-item definition.
3044
3045     Table D.3-13
3046     RELATED-GENERAL-SOP-CLASS-IDENTIFICATION SUB-FIELDS
3047     Bytes Sub-Field Name    Description of Sub-Field
3048     1-2 Related-general-sop-class-uid-length      The Related-general-sop-class-uid-length shall be the number of bytes in the Related-general-sop-class-uid sub-field. It shall be encoded as an unsigned binary number.
3049     3-n Related-general-sop-class-uid The Related General SOP Class identifier encoded as a UID as defined in PS 3.5.
3050
3051 58 User Identity Negotiation
3052     1 Item-type 58H
3053     1 Reserved
3054     2 Item-length
3055     1 User-Identity-Type  Field value shall be in the range 1 to 4 with the following meanings:
3056         1 \96 Username as a string in UTF-8
3057         2 \96 Username as a string in UTF-8 and passcode
3058         3 \96 Kerberos Service ticket
3059         4 \96 SAML Assertion
3060         Other values are reserved for future standardization.
3061     1 Positive-response-requested Field value:
3062         0 - no response requested
3063         1 - positive response requested
3064     2 Primary-field-length  The User-Identity-Length shall contain the length of the User-Identity value.
3065     9-n Primary-field   This field shall convey the user identity, either the username as a series of characters, or the Kerberos Service ticket encoded in accordance with RFC-1510.
3066     n+1-n+2 Secondary-field-length  This field shall be non-zero only if User-Identity-Type has the value 2.  It shall contain the length of the secondary-field.
3067     n+3-m Secondary-field   This field shall be present only if User-Identity-Type has the value 2.  It shall contain the Passcode value.
3068
3069 59 User Identity Negotiation Reply
3070     1 Item-type 59H
3071     1 Reserved
3072     2 Item-length
3073     5-6 Server-response-length  This field shall contain the number of bytes in the Server-response.  May be zero.
3074     7-n Server-response This field shall contain the Kerberos Server ticket, encoded in accordance with RFC-1510, if the User-Identity-Type value in the A-ASSOCIATE-RQ was 3. This field shall contain the SAML response if the User-Identity-Type value in the A-ASSOCIATE-RQ was 4. This field shall be zero length if the value of the User-Identity-Type in the A-ASSOCIATE-RQ was 1 or 2.
3075
3076  */
3077