Removed some more "statement not reached" warnings.
[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  *
5  * DICOM communication protocol
6  * http://medical.nema.org/dicom/2003.html
7  *   DICOM Part 8: Network Communication Support for Message Exchange
8  *
9  * (NOTE: you need to turn on 'Allow subdissector to desegment TCP streams'
10  *        in Preferences/Protocols/TCP Option menu, in order to view
11  *        DICOM packets correctly.  
12  *        Also, you might have to turn off tcp.check_checksum if tcp
13  *        detects that the checksum is bad - for example, if you're
14  *        capturing on a network interface that does TCP checksum
15  *        offloading and you're capturing outgoing packets.
16  *        This should probably be documented somewhere besides here.)
17  *
18  * $Id$
19  *
20  * Wireshark - Network traffic analyzer
21  * By Gerald Combs <gerald@wireshark.org>
22  * Copyright 1998 Gerald Combs
23  *
24  * This program is free software; you can redistribute it and/or
25  * modify it under the terms of the GNU General Public License
26  * as published by the Free Software Foundation; either version 2
27  * of the License, or (at your option) any later version.
28  * 
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32  * GNU General Public License for more details.
33  * 
34  * You should have received a copy of the GNU General Public License
35  * along with this program; if not, write to the Free Software
36  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37  */
38
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42
43 /* Notes:
44  * This is my first pass at a Wireshark dissector to display
45  * DICOM (Digital Imaging and Communications in Medicine) packets. 
46  * 
47  * - It currently displays most of the DICOM packets.
48  * 
49  * - I've used it to debug Query/Retrieve, Storage, and Echo protocols.
50  *
51  * - Not all DICOM tags are currently displayed symbolically.  
52  *   Unknown tags are displayed as '(unknown)'
53  *   More known tags might be added in the future.
54  *   If the tag data contains a string, it will be displayed.
55  *   Even if the tag contains Explicit VR, it is not currently used to
56  *   symbolically display the data.  Consider this a future enhancement.
57  *
58  * - If the DATA PDU has the 'more' bit set, subsequent packets will
59  *   not currently display.  Finding out how much 'more' data is coming
60  *   currently requires parsing the entire packet.
61  * 
62  * - The 'value to string' routines should probably be hash lookups.
63  *
64  * 9 Nov 2004
65  * - Fixed the heuristic code -- sometimes a conversation already exists
66  * - Fixed the dissect code to display all the tags in the pdu
67  *
68  * 28 Apr 2005
69  * - fix memory leak when Assoc packet is processed repeatedly in wireshark
70  *
71  * - removed unused partial packet flag
72  *
73  * - added better support for DICOM VR
74  *      - sequences
75  *      - report actual VR in packet display, if supplied by xfer syntax 
76  *      - show that we are not displaying entire tag string with '[...]',
77  *        some tags can hold up to 2^32-1 chars
78  *
79  * - remove my goofy attempt at trying to get access to the fragmented packets
80  *   (anyone have an idea on how to fix this ???)
81  *
82  * - process all the data in the Assoc packet even if display is off
83  *
84  * - limit display of data in Assoc packet to defined size of the data even
85  *   if reported size is larger
86  *
87  * - show the last tag in a packet as [incomplete] if we don't have all the data
88  *
89  * - added framework for reporting DICOM async negotiation (not finished)
90  *   (I'm not aware of an implementation which currently supports this)
91  *
92  * - still need to fix display of continuation packets
93  */
94
95 #include <stdio.h>
96 #include <stdlib.h>
97 #include <string.h>
98 #include <ctype.h>
99
100 #include <glib.h>
101
102 #include "isprint.h"
103
104 #include <epan/packet.h>
105 #include <epan/emem.h>
106 #include <epan/strutil.h>
107 #include <epan/conversation.h>
108
109 #include "packet-tcp.h"
110
111 /* Initialize the protocol and registered fields */
112 static int proto_dcm = -1;
113 static int hf_dcm_pdu = -1,
114     hf_dcm_pdu_len = -1,
115     hf_dcm_pdu_type = -1,
116     hf_dcm_pdi = -1,
117     hf_dcm_pdi_name = -1,
118     hf_dcm_pdi_syntax = -1, 
119     hf_dcm_pctxt = -1,
120     hf_dcm_pcres = -1,
121     hf_dcm_pdu_maxlen = -1,
122     hf_dcm_impl = -1,
123     hf_dcm_vers = -1,
124     hf_dcm_async = -1,
125     hf_dcm_data_len = -1,
126     hf_dcm_data_ctx = -1,
127     hf_dcm_data_flags = -1,
128     hf_dcm_data_tag = -1;
129
130 /* Initialize the subtree pointers */
131 static gint ett_dcm = -1, ett_assoc = -1, ett_dcm_data = -1;
132
133 static const value_string dcm_pdu_ids[] = {
134     { 1, "ASSOC Request" },
135     { 2, "ASSOC Accept" },
136     { 3, "ASSOC Reject" },
137     { 4, "Data" },
138     { 5, "RELEASE Request" },
139     { 6, "RELEASE Response" },
140     { 7, "ABORT" },
141     { 0, NULL }
142 };
143
144 static const value_string dcm_pdi_ids[] = {
145     { 0x10, "Application Context" },
146     { 0x20, "Presentation Context" },
147     { 0x21, "Presentation Context Reply" },
148     { 0x30, "Abstract syntax" },
149     { 0x40, "Transfer syntax" },
150     { 0x50, "User Info" },
151     { 0x51, "Max Length" },
152     { 0, NULL }
153 };
154
155 struct dcmContext {
156     guint8 id;
157     guint8 result;
158 };
159 struct dcmItem {
160     struct dcmItem *next, *prev;
161     int valid;
162     guint8 id;          /* 0x20 Presentation Context */
163     const guint8 *abs;  /* 0x30 Abstract syntax */
164     char *xfer;         /* 0x40 Transfer syntax */
165     guint8 syntax;
166 #define DCM_ILE  0x01           /* implicit, little endian */
167 #define DCM_EBE  0x02           /* explicit, big endian */
168 #define DCM_ELE  0x03           /* explicit, little endian */
169 #define DCM_UNK  0xf0
170 };
171 typedef struct dcmItem dcmItem_t;
172
173 struct dcmState {
174     dcmItem_t *first, *last;
175     guint8 pdu;         /* protocol data unit */
176     guint32 tlen, clen, rlen;    /* length: total, current, remaining */
177     int coff;           /* current offset */
178     int valid;          /* this conversation is a dicom conversation */
179     /* enum { DCM_NONE, DCM_ASSOC, DCM_ }; */
180 #define AEEND 16
181     guint8 orig[1+AEEND], targ[1+AEEND], resp[1+AEEND], source, result, reason;
182 };
183 typedef struct dcmState dcmState_t;
184
185 struct dcmTag {
186     int tag;
187     int dtype;
188     const char *desc;
189 #define DCM_TSTR  1
190 #define DCM_TINT2 2
191 #define DCM_TINT4 3
192 #define DCM_TFLT  4
193 #define DCM_TDBL  5
194 #define DCM_TSTAT 6  /* call dcm_rsp2str() on TINT2 */
195 #define DCM_TRET  7
196 #define DCM_TCMD  8
197 #define DCM_SQ    9     /* sequence */
198 #define DCM_OTH   10    /* other */
199 };
200 typedef struct dcmTag dcmTag_t;
201
202 static GHashTable *dcm_tagTable = NULL;
203
204 static dcmItem_t * lookupCtx(dcmState_t *dd, guint8 ctx);
205
206 static dcmTag_t tagData[] = {
207     {  0x1,    DCM_TRET,  "(Ret) Length to End" },
208     {  0x2,    DCM_TSTR,  "Affected Class" },
209     {  0x3,    DCM_TSTR,  "Requested Class" },
210     {  0x0010, DCM_TRET,  "(Ret) Recognition Code" },
211     {  0x0100, DCM_TCMD,  "Command Field" },
212     {  0x0110, DCM_TINT2, "Message ID" },
213     {  0x0120, DCM_TINT2, "Resp Message ID" },
214     {  0x0200, DCM_TRET,  "(Ret) Initiator" },
215     {  0x0300, DCM_TRET,  "(Ret) Reciever" },
216     {  0x0400, DCM_TRET,  "(Ret) Find Location" },
217     {  0x0600, DCM_TSTR,  "Dest AE" },
218     {  0x0700, DCM_TINT2, "Priority" },
219     {  0x0800, DCM_TINT2, "Data Set (0x0101 means no data set present)" },
220     {  0x0850, DCM_TRET,  "(Ret) Num Matches" },
221     {  0x0860, DCM_TRET,  "(Ret) Resp Seq Num" },
222     {  0x0900, DCM_TSTAT, "Status" },
223     {  0x0901, DCM_TSTR,  "Offending elm(s)" },
224     {  0x0902, DCM_TSTR,  "Error Comment" },
225     {  0x0903, DCM_TINT2, "Error Id" },
226     {  0x1000, DCM_TSTR,  "Affected Instance UID" },
227     {  0x1001, DCM_TSTR,  "Requested Instance UID" },
228     {  0x1002, DCM_TINT2, "Event Type Id" },
229     {  0x1005, DCM_TSTR,  "Attr Id List" },
230     {  0x1008, DCM_TINT2, "Action Type Id" },
231     {  0x1020, DCM_TINT2, "Num Remaining Ops" },
232     {  0x1021, DCM_TINT2, "Num Completed Ops" },
233     {  0x1022, DCM_TINT2, "Num Failed Ops" },
234     {  0x1023, DCM_TINT2, "Num Warning Ops" },
235     {  0x1030, DCM_TSTR,  "Move Orig AE" },
236     {  0x1031, DCM_TINT2, "Move Orig Id" },
237     {  0x4000, DCM_TRET,  "(Ret) DIALOG Recv'r" },
238     {  0x4010, DCM_TRET,  "(Ret) Terminal Type" },
239     {  0x5010, DCM_TRET,  "(Ret) Msg Set ID" },
240     {  0x5020, DCM_TRET,  "(Ret) End Msg ID" },
241     {  0x5110, DCM_TRET,  "(Ret) Display Fmt" },
242     {  0x5120, DCM_TRET,  "(Ret) Page Position ID" },
243     {  0x5130, DCM_TRET,  "(Ret) Text Fmt ID" },
244     {  0x5140, DCM_TRET,  "(Ret) Nor/Rev" },
245     {  0x5150, DCM_TRET,  "(Ret) Add Gray Scale" },
246     {  0x5160, DCM_TRET,  "(Ret) Borders" },
247     {  0x5170, DCM_TRET,  "(Ret) Copies" },
248     {  0x5180, DCM_TRET,  "(Ret) Mag Type" },
249     {  0x5190, DCM_TRET,  "(Ret) Erase" },
250     {  0x51a0, DCM_TRET,  "(Ret) Print" },
251     {  0x080018, DCM_TSTR, "Image UID" },
252     {  0x080020, DCM_TSTR, "Study Date" },
253     {  0x080030, DCM_TSTR, "Study Time" },
254     {  0x080050, DCM_TSTR, "Acc Num" },
255     {  0x080052, DCM_TSTR, "Q/R Level" },
256     {  0x080054, DCM_TSTR, "Retrieve AE" },
257     {  0x080060, DCM_TSTR, "Modality" },
258     {  0x080070, DCM_TSTR, "Manuf" },
259     {  0x081030, DCM_TSTR, "Study Desc" },
260     {  0x08103e, DCM_TSTR, "Series Desc" },
261     {  0x100010, DCM_TSTR, "Patient Name" },
262     {  0x100020, DCM_TSTR, "Patient Id" },
263     {  0x20000d, DCM_TSTR, "Study UID" },
264     {  0x20000e, DCM_TSTR, "Series UID" },
265     {  0x200010, DCM_TSTR, "Study Num" },
266     {  0x200011, DCM_TSTR, "Series Num" },
267     {  0x200012, DCM_TSTR, "Acq Num" },
268     {  0x200013, DCM_TSTR, "Image Num" },
269     {  0x7fe00010, DCM_OTH, "Pixels" },
270     {  0xfffee000, DCM_TRET, "Item Begin" },
271     {  0xfffee00d, DCM_TRET, "Item End" },
272     {  0xfffee0dd, DCM_TRET, "Sequence End" },
273 };
274
275 static void
276 dcm_init(void)
277 {
278     if (NULL == dcm_tagTable) {
279         unsigned int i;
280         dcm_tagTable = g_hash_table_new(NULL, NULL);
281         for (i = 0; i < sizeof(tagData) / sizeof(dcmTag_t); i++) 
282             g_hash_table_insert(dcm_tagTable, GINT_TO_POINTER(tagData[i].tag),
283                 (gpointer) (tagData+i));
284     }
285 }
286
287 static dcmState_t *
288 mkds(void)
289 {
290     dcmState_t *ds;
291
292     if (NULL == (ds = (dcmState_t *) g_malloc(sizeof(dcmState_t)))) {
293         return NULL;
294     }
295     ds->pdu = 0;
296     ds->tlen = ds->rlen = 0;
297     ds->valid = TRUE;
298     memset(ds->orig, 0, sizeof(ds->orig));
299     memset(ds->targ, 0, sizeof(ds->targ));
300     memset(ds->resp, 0, sizeof(ds->resp));
301     ds->first = ds->last = NULL;
302     return ds;
303 }
304
305 static const char *
306 dcm_pdu2str(guint8 item)
307 {
308     const char *s = "";
309     switch (item) {
310     case 1: s = "ASSOC Request"; break;
311     case 2: s = "ASSOC Accept"; break;
312     case 3: s = "ASSOC Reject"; break;
313     case 4: s = "Data"; break;
314     case 5: s = "RELEASE Request"; break;
315     case 6: s = "RELEASE Response"; break;
316     case 7: s = "ABORT"; break;
317     case 0x10: s = "Application Context"; break;
318     case 0x20: s = "Presentation Context"; break;
319     case 0x21: s = "Presentation Context Reply"; break;
320     case 0x30: s = "Abstract syntax"; break;
321     case 0x40: s = "Transfer syntax"; break;
322     case 0x50: s = "User Info"; break;
323     case 0x51: s = "Max Length"; break;
324     default: break;
325     }
326     return s;
327 }
328
329 static const char *
330 dcm_result2str(guint8 result)
331 {
332     const char *s = "";
333     switch (result) {
334     case 1:  s = "Reject Permanent"; break;
335     case 2:  s = "Reject Transient"; break;
336     default: break;
337     }
338     return s;
339 }
340
341 static const char *
342 dcm_source2str(guint8 source)
343 {
344     const char *s = "";
345     switch (source) {
346     case 1:  s = "User"; break;
347     case 2:  s = "Provider (ACSE)"; break;
348     case 3:  s = "Provider (Presentation)"; break;
349     default: break;
350     }
351     return s;
352 }
353
354 static const char * 
355 dcm_reason2str(guint8 source, guint8 reason)
356 {
357     const char *s = "";
358     if (1 == source) switch (reason) {
359         case 1:  s = "No reason"; break;
360         case 2:  s = "App Name not supported"; break;
361         case 3:  s = "calling AET not recognized"; break;
362         case 7:  s = "called AET not recognized"; break;
363         default: break;
364     } else if (2 == source) switch (reason) {
365         case 1:  s = "No reason"; break;
366         case 2:  s = "protocol unsupported"; break;
367         default: break;
368     } else if (3 == source) switch (reason) {
369         case 1:  s = "temporary congestion"; break;
370         case 2:  s = "local limit exceeded"; break;
371         default: break;
372     }
373     return s;
374 }
375
376 static const char *
377 dcm_abort2str(guint8 reason)
378 {
379     const char *s = "";
380     switch (reason) {
381     case 0:  s = "not specified"; break;
382     case 1:  s = "unrecognized"; break;
383     case 2:  s = "unexpected"; break;
384     case 4:  s = "unrecognized parameter"; break;
385     case 5:  s = "unexpected parameter"; break;
386     case 6:  s = "invalid parameter"; break;
387     default: break;
388     }
389     return s;
390 }
391
392 static const char *
393 dcm_PCresult2str(guint8 result)
394 {
395     const char *s = "";
396     switch (result) {
397     case 0:  s = "accept"; break;
398     case 1:  s = "user-reject"; break;
399     case 2:  s = "no-reason"; break;
400     case 3:  s = "abstract syntax unsupported"; break;
401     case 4:  s = "transfer syntax unsupported"; break;
402     default: break;
403     }
404     return s;
405 }
406
407 static const char *
408 dcm_flags2str(guint8 flags)
409 {
410     const char *s = "";
411     switch (flags) {
412     case 0:  s = "Data,    more Fragments"; break;      /* 00 */
413     case 1:  s = "Command, more Fragments"; break;      /* 01 */
414     case 2:  s = "Data,    last Fragment";  break;      /* 10 */
415     case 3:  s = "Command, last Fragment";  break;      /* 11 */
416     default: break;
417     }
418     return s;
419 }
420
421 static const char *
422 dcm_cmd2str(guint16 us)
423 {
424     const char *s = "";
425     /* there should be a better way to do this */
426     switch (us) {
427     case 0x0001:  s = "C-STORE-RQ"; break;
428     case 0x8001:  s = "C-STORE-RSP"; break;
429     case 0x0010:  s = "C-GET-RQ"; break;
430     case 0x8010:  s = "C-GET-RSP"; break;
431     case 0x0020:  s = "C-FIND-RQ"; break;
432     case 0x8020:  s = "C-FIND-RSP"; break;
433     case 0x0021:  s = "C-MOVE-RQ"; break;
434     case 0x8021:  s = "C-MOVE-RSP"; break;
435     case 0x0030:  s = "C-ECHO-RQ"; break;
436     case 0x8030:  s = "C-ECHO-RSP"; break;
437     case 0x0100:  s = "N-EVENT-REPORT-RQ"; break;
438     case 0x8100:  s = "N-EVENT-REPORT-RSP"; break;
439     case 0x0110:  s = "N-GET-RQ"; break;
440     case 0x8110:  s = "N-GET-RSP"; break;
441     case 0x0120:  s = "N-SET-RQ"; break;
442     case 0x8120:  s = "N-SET-RSP"; break;
443     case 0x0130:  s = "N-ACTION-RQ"; break;
444     case 0x8130:  s = "N-ACTION-RSP"; break;
445     case 0x0140:  s = "N-CREATE-RQ"; break;
446     case 0x8140:  s = "N-CREATE-RSP"; break;
447     case 0x0150:  s = "N-DELETE-RQ"; break;
448     case 0x8150:  s = "N-DELETE-RSP"; break;
449     case 0x0fff:  s = "C-CANCEL-RQ"; break;
450     default: break;
451     }
452     return s;
453 }
454
455 static const char *
456 dcm_rsp2str(guint16 us)
457 {
458     const char *s = "";
459     switch (us) {
460     case 0x0000:  s = "Success"; break;
461     case 0xa701:
462     case 0xa702:  s = "Refused: Out of Resources"; break;
463     case 0xa801:  s = "Refused: Move Destination unknown"; break;
464     case 0xa900:  s = "Failed:  Id does not match Class"; break;
465     case 0xb000:  s = "Warning: operations complete -- One or more Failures"; break;
466     case 0xfe00:  s = "Cancel:  operations terminated by Cancel"; break;
467     case 0xff00:  s = "Pending: operations are continuing"; break;
468     default: break;
469     }
470     if (0xC000 == (0xF000 & us))  s = "Failed:  Unable to Process"; 
471     return s;
472 }
473
474 static void
475 dcm_setSyntax(dcmItem_t *di, char *name)
476 {
477     if (NULL == di) return;
478     if (di->xfer != NULL)
479         g_free(di->xfer);       /* free prev allocated xfer */
480     di->syntax = 0;
481     di->xfer = g_strdup(name);
482     if (0 == *name) return;
483     /* this would be faster to skip the common parts, and have a FSA to 
484      * find the syntax.
485      * Absent of coding that, this is in descending order of probability */
486     if (0 == strcmp(name, "1.2.840.10008.1.2"))
487         di->syntax = DCM_ILE;    /* implicit little endian */
488     else if (0 == strcmp(name, "1.2.840.10008.1.2.1"))
489         di->syntax = DCM_ELE;    /* explicit little endian */
490     else if (0 == strcmp(name, "1.2.840.10008.1.2.2"))
491         di->syntax = DCM_EBE;    /* explicit big endian */
492     else if (0 == strcmp(name, "1.2.840.113619.5.2"))
493         di->syntax = DCM_ILE;    /* implicit little endian, big endian pixels */
494     else if (0 == strcmp(name, "1.2.840.10008.1.2.4.70"))
495         di->syntax = DCM_ELE;    /* explicit little endian, jpeg */
496     else if (0 == strncmp(name, "1.2.840.10008.1.2.4", 18))
497         di->syntax = DCM_ELE;    /* explicit little endian, jpeg */
498     else if (0 == strcmp(name, "1.2.840.10008.1.2.1.99"))
499         di->syntax = DCM_ELE;    /* explicit little endian, deflated */
500 }
501
502 static char *
503 dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, guint32 len, int vr, int tr)
504 {
505     char *buf;
506     const guint8 *vval;
507     char *p;
508     guint32 tag, val32;
509     guint16 val16;
510     dcmTag_t *dtag;
511     static dcmTag_t utag = { 0, 0, "(unknown)" };
512
513 #define MAX_BUF_LEN 1024
514     buf=ep_alloc(MAX_BUF_LEN);
515     *buf = 0;
516     if (0 == elm) {
517         if (DCM_ILE & syntax) 
518              val32 = tvb_get_letohl(tvb, offset); 
519         else val32 = tvb_get_ntohl(tvb, offset); 
520         g_snprintf(buf, MAX_BUF_LEN, "Group Length 0x%x (%d)", val32, val32);
521         return buf;
522     }
523     tag = (grp << 16) | elm;
524     if (NULL == (dtag = g_hash_table_lookup(dcm_tagTable, GUINT_TO_POINTER(tag))))
525         dtag = &utag;
526
527     DISSECTOR_ASSERT(MAX_BUF_LEN > strlen(dtag->desc));
528     p=buf;
529     p+=MIN(MAX_BUF_LEN-(p-buf),
530            g_snprintf(p, MAX_BUF_LEN-(p-buf), "%s", dtag->desc));
531     if (vr > 0) {
532         vval = tvb_format_text(tvb, vr, 2);
533         p+=MIN(MAX_BUF_LEN-(p-buf),
534                g_snprintf(p, MAX_BUF_LEN-(p-buf), " [%s]", vval));
535     }
536
537     switch (tr > 0 ? tr : dtag->dtype) {
538     case DCM_TSTR:
539     default:            /* try ascii */
540         vval = tvb_format_text(tvb, offset, len);
541         p+=MIN(MAX_BUF_LEN-(p-buf),
542                g_snprintf(p, MAX_BUF_LEN-(p-buf), " %s", vval));
543         break;
544     case DCM_TINT2:
545         if (DCM_ILE & syntax) 
546              val16 = tvb_get_letohs(tvb, offset);
547         else val16 = tvb_get_ntohs(tvb, offset);
548         p+=MIN(MAX_BUF_LEN-(p-buf),
549                g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x (%d)", val16, val16));
550         break;
551     case DCM_TINT4:
552         if (DCM_ILE & syntax) 
553              val32 = tvb_get_letohl(tvb, offset); 
554         else val32 = tvb_get_ntohl(tvb, offset); 
555         p+=MIN(MAX_BUF_LEN-(p-buf),
556                g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x (%d)", val32, val32));
557         break;
558     case DCM_TFLT: {
559         gfloat valf;
560         if (DCM_ILE & syntax) 
561              valf = tvb_get_letohieee_float(tvb, offset); 
562         else valf = tvb_get_ntohieee_float(tvb, offset); 
563         p+=MIN(MAX_BUF_LEN-(p-buf),
564                g_snprintf(p, MAX_BUF_LEN-(p-buf), " (%f)", valf));
565         } break;
566     case DCM_TDBL: {
567         gdouble vald;
568         if (DCM_ILE & syntax) 
569              vald = tvb_get_letohieee_double(tvb, offset); 
570         else vald = tvb_get_ntohieee_double(tvb, offset); 
571         p+=MIN(MAX_BUF_LEN-(p-buf),
572                g_snprintf(p, MAX_BUF_LEN-(p-buf), " (%f)", vald));
573         } break;
574     case DCM_TSTAT: /* call dcm_rsp2str() on TINT2 */
575         if (DCM_ILE & syntax) 
576              val16 = tvb_get_letohs(tvb, offset);
577         else val16 = tvb_get_ntohs(tvb, offset);
578         p+=MIN(MAX_BUF_LEN-(p-buf),
579                g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x '%s'", val16, dcm_rsp2str(val16)));
580         break;
581     case DCM_TCMD:   /* call dcm_cmd2str() on TINT2 */
582         if (DCM_ILE & syntax) 
583              val16 = tvb_get_letohs(tvb, offset);
584         else val16 = tvb_get_ntohs(tvb, offset);
585         p+=MIN(MAX_BUF_LEN-(p-buf),
586                g_snprintf(p, MAX_BUF_LEN-(p-buf), " 0x%x '%s'", val16, dcm_cmd2str(val16)));
587         break;
588     case DCM_SQ:        /* Sequence */
589     case DCM_OTH:       /* Other BYTE, WORD, ... */
590     case DCM_TRET:      /* Retired */
591         break;
592     }
593     return buf;
594 }
595
596 static guint
597 dcm_get_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
598 {
599     guint32 len;
600
601     len = tvb_get_ntohl(tvb, 2 + offset);
602     return len + 6;             /* add in fixed header part */
603 }
604
605 static void 
606 dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offset)
607
608     proto_tree *dcm_tree = NULL;
609     dcmItem_t *di = NULL;
610     guint8 id, *name, result;
611     int reply = 0;
612
613     if (!ti)
614         return;
615
616     dcm_tree = proto_item_add_subtree(ti, ett_assoc);
617     while (-1 < offset && offset < (int) dcm_data->clen) {
618         guint16 len;
619         guint32 mlen;
620         id = tvb_get_guint8(tvb, offset);
621         len = tvb_get_ntohs(tvb, 2 + offset);
622         if (ti)
623             proto_tree_add_uint_format(dcm_tree, hf_dcm_pdi, tvb,
624                 offset, 4+len, id, "Item 0x%x (%s)", id, dcm_pdu2str(id));
625         offset += 4;
626         switch (id) {
627         case 0x10:              /* App context */
628             if (ti)
629                 proto_tree_add_item(dcm_tree, hf_dcm_pdi_name, tvb, offset, len > 65 ? 65 : len, FALSE);
630             offset += len;
631             break;
632         case 0x30:              /* Abstract syntax */
633             if (ti)
634                 proto_tree_add_item(dcm_tree, hf_dcm_pdi_syntax, tvb, offset, len > 65 ? 65 : len, FALSE);
635             offset += len;
636             break;
637         case 0x40:              /* Transfer syntax */
638             if (ti)
639                 proto_tree_add_item(dcm_tree, hf_dcm_pdi_syntax, tvb, offset, len > 65 ? 65 : len, FALSE);
640             if (reply && di && di->valid) {
641                 name = tvb_get_ephemeral_string(tvb, offset, len);
642                 dcm_setSyntax(di, name);
643             }
644             reply = 0;
645             offset += len;
646             break;
647         case 0x20:              /* Presentation context */
648             id = tvb_get_guint8(tvb, offset);
649             di = lookupCtx(dcm_data, id);
650             if (!di->valid) {
651                 di = se_alloc(sizeof(struct dcmItem));
652                 di->id = id;
653                 di->valid = 1;
654                 di->xfer = NULL;
655                 di->syntax = DCM_UNK;
656                 di->next = di->prev = NULL;
657                 if (dcm_data->last) {
658                     dcm_data->last->next = di;
659                     di->prev = dcm_data->last;
660                     dcm_data->last = di;
661                 } else 
662                     dcm_data->first = dcm_data->last = di;
663             }
664             if (ti)
665                 proto_tree_add_item(dcm_tree, hf_dcm_pctxt, tvb, offset, 1, FALSE);
666             offset += 4;
667             break;
668         case 0x21:              /* Presentation context reply */
669             id = tvb_get_guint8(tvb, offset);
670             result = tvb_get_guint8(tvb, 2 + offset);
671             if (ti) {
672                 proto_tree_add_item(dcm_tree, hf_dcm_pctxt, tvb, offset, 1, FALSE);
673                 proto_tree_add_uint_format(dcm_tree, hf_dcm_pcres, tvb, 
674                     2 + offset, 1, result, 
675                     "Result 0x%x (%s)", result, dcm_PCresult2str(result));
676             }
677             if (0 == result) {
678                 reply = 1;
679                 di = lookupCtx(dcm_data, id);
680                 offset += 4;
681             } else
682                 offset += len;
683             break;
684         case 0x50:              /* User Info */
685             break;
686         case 0x51:              /* Max length */
687             mlen = tvb_get_ntohl(tvb, offset);
688             if (ti)
689                 proto_tree_add_item(dcm_tree, hf_dcm_pdu_maxlen, tvb, offset, 4, FALSE);
690             offset += len;
691             break;
692         case 0x52:              /* UID */
693             if (ti)
694                 proto_tree_add_item(dcm_tree, hf_dcm_impl, tvb, offset, len > 65 ? 65 : len, FALSE);
695             offset += len;
696             break;
697         case 0x55:              /* version */
698             if (ti)
699                 proto_tree_add_item(dcm_tree, hf_dcm_vers, tvb, offset, len > 17 ? 17 : len, FALSE);
700             offset += len;
701             break;
702         case 0x53:              /* async negotion */
703             /* hf_dcm_async */
704             offset += len;
705             break;
706         default:
707             offset += len;
708             break;
709         }
710     }
711 }
712
713 static dcmItem_t *
714 lookupCtx(dcmState_t *dd, guint8 ctx)
715 {
716     dcmItem_t *di = dd->first;
717     static char notfound[] = "not found - click on ASSOC Request";
718     static dcmItem_t dunk = { NULL, NULL, 0, -1, notfound, notfound, DCM_UNK };
719     while (di) {
720         if (ctx == di->id)
721             break;
722         di = di->next;
723     }
724     return di ? di : &dunk;
725 }
726
727 /* 
728 04 P-DATA-TF
729  1  1 reserved
730  2  4 length
731     - (1+) presentation data value (PDV) items
732  6      4 length
733 10      1 Presentation Context ID (odd ints 1 - 255)
734         - PDV
735 11      1 header 
736             0x01 if set, contains Message Command info, else Message Data
737             0x02 if set, contains last fragment
738  */
739 #define D_HEADER 1
740 #define D_TAG    2
741 #define D_VR     3
742 #define D_LEN2   4
743 #define D_LEN4   5
744 #define D_VALUE  6
745
746 static void 
747 dissect_dcm_data(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb)
748 {
749     int len, offset, toffset, state, vr = 0, tr = 0;
750     proto_tree *dcm_tree;
751     dcmItem_t *di;
752     guint8 ctx, syntax = DCM_UNK;
753     guint16 grp = 0, elm = 0;
754     guint32 tlen = 0, nlen;
755
756     dcm_tree = proto_item_add_subtree(ti, ett_dcm_data);
757     proto_tree_add_item(dcm_tree, hf_dcm_data_len, tvb, 6, 4, FALSE);
758     ctx = tvb_get_guint8(tvb, 10);
759     di = lookupCtx(dcm_data, ctx);
760     /*
761      * XXX - telling the user to "click on ASSOC request" is bogus if we
762      * have already identified the ASSOC request and can connect it to
763      * this message; if clicking on a request prior to this one causes
764      * additional state information to be set up that would affect the
765      * dissection of this request, we should set up that state *at the
766      * time we dissect that request*, if possible, and if clicking on it
767      * doesn't change any state, clicking on the request doesn't convey
768      * any additional information.
769      */
770     proto_tree_add_uint_format(dcm_tree, hf_dcm_data_ctx, tvb, 10, 1, 
771         ctx, "Context 0x%x (%s)", ctx,
772         di->xfer == NULL ? "not found - click on ASSOC Request" :
773                            di->xfer);
774     if (DCM_UNK == di->syntax)
775         return;
776     len = offset = toffset = 11;
777     state = D_HEADER;
778     nlen = 1;
779     while (len + nlen <= dcm_data->tlen && len + nlen <= dcm_data->clen) {
780     switch (state) {
781     case D_HEADER: {
782         guint8 flags;
783         flags = tvb_get_guint8(tvb, offset);
784         proto_tree_add_uint_format(dcm_tree, hf_dcm_data_flags, tvb, offset, 1, 
785             flags, "Flags 0x%x (%s)", flags, dcm_flags2str(flags));
786         /* proto_tree_add_item(dcm_tree, hf_dcm_data_flags, tvb, offset, 1, FALSE); */
787         len++;
788         offset++;
789         if (0x1 & flags) 
790             syntax = DCM_ILE;
791         else if (DCM_UNK == di->syntax) {
792             const guint8 *val;
793             tlen = dcm_data->clen - len;
794             val = tvb_get_ptr(tvb, offset, tlen+8);
795             proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
796                 offset, tlen, val, "(%04x,%04x) %-8x Unparsed data", 0, 0, tlen);
797             len = dcm_data->clen;      /* ends parsing */
798         } else
799             syntax = di->syntax;
800         state = D_TAG;
801         nlen = 4;
802         } break;                /* don't fall through -- check length */
803     case D_TAG: {
804         vr = tr = 0;
805         if (DCM_ILE & syntax) {
806             grp = tvb_get_letohs(tvb, offset);
807             elm = tvb_get_letohs(tvb, offset+2);
808             state = (DCM_EBE & syntax) ? D_VR : D_LEN4;  /* is Explicit */
809             nlen  = (DCM_EBE & syntax) ? 2 : 4;  /* is Explicit */
810         } else {
811             grp = tvb_get_ntohs(tvb, offset);
812             elm = tvb_get_ntohs(tvb, offset+2);
813             state = D_VR;
814             nlen = 2;
815         }
816         toffset = offset;
817         if (0xfffe == grp) state = D_LEN4;
818         offset += 4;
819         len += 4;
820         } break;                /* don't fall through -- check length */
821     case D_VR:  {
822         guint8 V, R;
823         vr = offset;
824         V = tvb_get_guint8(tvb, offset); offset++;
825         R = tvb_get_guint8(tvb, offset); offset++;
826         len += 2;
827         /* 4byte lengths OB, OW, OF, SQ, UN, UT */
828         state = D_LEN2;
829         nlen = 2;
830         if ((('O' == V) && ('B' == R || 'W' == R || 'F' == R) && (tr = DCM_OTH))
831             || (('U' == V) && ('N' == R || (('T' == R) && (tr = DCM_TSTR))))
832             || ('S' == V && 'Q' == R && (tr = DCM_SQ))) {
833             state = D_LEN4;
834             offset += 2;        /* skip 00 (2 bytes) */
835             len += 2;
836             nlen = 4;
837         } else if ('F' == V && 'L' == R) {
838             tr = DCM_TFLT;
839         } else if ('F' == V && 'D' == R) {
840             tr = DCM_TDBL;
841         } else if (('S' == V && 'L' == R) || ('U' == V && 'L' == R)) {
842             tr = DCM_TINT4;
843         } else if (('S' == V && 'S' == R) || ('U' == V && 'S' == R)) {
844             tr = DCM_TINT2;
845         } else if ('A' == V && 'T' == R) {
846             tr = DCM_OTH;
847         } else
848             tr = DCM_TSTR;
849 /* 
850         else if (('A' == V && ('E' == R || 'S' == R))
851             || ('C' == V && 'S' == R)
852             || ('D' == V && ('A' == R || 'S' == R || 'T' == R))
853             || ('I' == V && 'S' == R)
854             || ('L' == V && ('O' == R || 'T' == R))
855             || ('P' == V && 'N' == R)
856             || ('S' == V && ('H' == R ||| 'T' == R))
857             || ('T' == V && 'M' == R)
858             || ('U' == V && ('I' == R || 'T' == R)))
859             tr = DCM_TSTR;
860  */
861         } break;                /* don't fall through -- check length */
862     case D_LEN2: {
863         if (DCM_ILE & syntax)   /* is it LE */
864             tlen = tvb_get_letohs(tvb, offset); 
865         else
866             tlen = tvb_get_ntohs(tvb, offset); 
867         offset += 2;
868         len += 2;
869         state = D_VALUE;
870         nlen = tlen;
871         } break;
872     case D_LEN4: {
873         if (DCM_ILE & syntax)   /* is it LE */
874             tlen = tvb_get_letohl(tvb, offset); 
875         else
876             tlen = tvb_get_ntohl(tvb, offset); 
877         offset += 4;
878         len += 4;
879         state = D_VALUE;
880         nlen = tlen;
881         } break;                /* don't fall through -- check length */
882     case D_VALUE: {
883         const guint8 *val;
884         int totlen = (offset - toffset);
885         if (0xffffffff == tlen || 0xfffe == grp) {
886             val = tvb_get_ptr(tvb, toffset, totlen);
887             proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb, 
888                 toffset, totlen, val, 
889                 "(%04x,%04x) %-8x %s", grp, elm, tlen, 
890                     dcm_tag2str(grp, elm, syntax, tvb, offset, 0, vr, tr));
891             tlen = 0;
892         /* } else if (0xfffe == grp) { */ /* need to make a sub-tree here */
893         } else {
894             totlen += tlen;
895             val = tvb_get_ptr(tvb, toffset, totlen);
896             proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb, 
897                 toffset, totlen, val, 
898                 "(%04x,%04x) %-8x %s", grp, elm, tlen, 
899                     dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, tr));
900         }
901         len += tlen;
902         offset += tlen;
903         state = D_TAG;
904         nlen = 4;
905         } break;
906     }
907     }
908     if (D_VALUE == state) {
909         const guint8 *val;
910         int totlen = (offset - toffset);
911         val = tvb_get_ptr(tvb, toffset, totlen);
912         proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
913             toffset, totlen, val,
914             "(%04x,%04x) %-8x %s [incomplete]", grp, elm, tlen, 
915                 dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, tr));
916     }
917 }
918
919 /* 
920      Originator src:srcport dest:destport
921      Acceptor   src:srcport dest:destport
922
923      conn = lookup(src:srcport, dest:destport) 
924      if (!conn)
925          look at data payload of packet
926          if no-data return false;
927          if 01 == *p && *p+10 ... *p+42 <= [ 0x20 .. printable ]
928             create conn
929  */
930
931 static void dissect_dcm_pdu(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree);
932
933 /* Code to actually dissect the packets */
934 static gboolean
935 dissect_dcm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
936 {
937     conversation_t *conv;
938     guint8 pdu;
939     guint16 vers;
940     guint32 len, tlen;
941     dcmState_t *dcm_data = NULL;
942
943     conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
944         pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
945
946     if (NULL != conv)   /* conversation exists */
947                         /* do we have any data for this conversation ? */
948         dcm_data = conversation_get_proto_data(conv, proto_dcm);
949     else
950         conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
951             pinfo->srcport, pinfo->destport, 0);
952
953     if (NULL == dcm_data) {
954         /* No conversation found.
955          * only look for the first packet of a DICOM conversation.
956          * if we don't get the first packet, we cannot decode the rest
957          * of the session.
958          */
959         if (NULL == (dcm_data = mkds()))
960             return FALSE;       /* internal error */
961         if (10 > (tlen = tvb_reported_length(tvb))     /* not long enough */
962             || 1 != (pdu = tvb_get_guint8(tvb, 0))     /* look for the start */
963             || 1 != (vers = tvb_get_ntohs(tvb, 6)))    /* not version 1 */
964             dcm_data->valid = FALSE;            
965         else {
966             len = 6 + tvb_get_ntohl(tvb, 2);
967             if (len < tlen)
968                 dcm_data->valid = FALSE;        /* packet is > decl len */
969         }
970
971         conversation_add_proto_data(conv, proto_dcm, dcm_data);
972     }
973
974     if (FALSE == dcm_data->valid)
975         return FALSE;
976
977     if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
978         col_clear(pinfo->cinfo, COL_PROTOCOL);
979
980     tcp_dissect_pdus(tvb, pinfo, tree, 1, 6, dcm_get_pdu_len, dissect_dcm_pdu);
981
982     return TRUE;
983 }
984
985 static void
986 dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
987 {
988     proto_item *ti;
989     dcmState_t *dcm_data;
990     proto_tree *dcm_tree;
991     conversation_t *conv;
992     char *buf;
993     const char *info_str = NULL;
994     int offset = 0;
995
996     if (NULL == (conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
997         pinfo->ptype, pinfo->srcport, pinfo->destport, 0)))
998         return;  /* OOPS */
999
1000     dcm_data = conversation_get_proto_data(conv, proto_dcm);
1001
1002     if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
1003         col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCM");
1004     
1005 /* This field shows up as the "Info" column in the display; you should make
1006    it, if possible, summarize what's in the packet, so that a user looking
1007    at the list of packets can tell what type of packet it is. See section 1.5
1008    for more information.
1009    */
1010
1011     if (check_col(pinfo->cinfo, COL_INFO)) 
1012         col_clear(pinfo->cinfo, COL_INFO);
1013
1014     dcm_data->pdu = tvb_get_guint8(tvb, 0);
1015     dcm_data->tlen = tvb_get_ntohl(tvb, 2) + 6;
1016     dcm_data->clen = tvb_reported_length(tvb);
1017
1018     switch (dcm_data->pdu) {
1019     case 1:                                     /* ASSOC Request */
1020         tvb_memcpy(tvb, dcm_data->orig, 10, 16);
1021         tvb_memcpy(tvb, dcm_data->targ, 26, 16);
1022         dcm_data->orig[AEEND] = dcm_data->targ[AEEND] = 0;
1023         buf = ep_alloc(128);
1024         g_snprintf(buf, 128, "DCM ASSOC Request %s <-- %s",
1025             dcm_data->orig, dcm_data->targ);
1026         info_str = buf;
1027         offset = 74;
1028         break;
1029     case 2:                             /* ASSOC Accept */
1030         tvb_memcpy(tvb, dcm_data->resp, 26, 16);
1031         buf = ep_alloc(128);
1032         g_snprintf(buf, 128, "DCM ASSOC Accept %s <-- %s (%s)",
1033             dcm_data->orig, dcm_data->targ, dcm_data->resp);
1034         info_str = buf;
1035         offset = 74; 
1036         break;
1037     case 3:                                     /* ASSOC Reject */
1038         dcm_data->result = tvb_get_guint8(tvb, 7);
1039         dcm_data->source = tvb_get_guint8(tvb, 8);
1040         dcm_data->reason = tvb_get_guint8(tvb, 9);
1041         buf = ep_alloc(128);
1042         g_snprintf(buf, 128, "DCM ASSOC Reject %s <-- %s %s %s %s",
1043             dcm_data->orig, dcm_data->targ,
1044             dcm_result2str(dcm_data->result),
1045             dcm_source2str(dcm_data->source),
1046             dcm_reason2str(dcm_data->source, dcm_data->reason));
1047         info_str = buf;
1048         offset = 10;
1049         break;
1050     case 4:                                     /* DATA */
1051         offset = 6; 
1052         info_str="DCM Data";
1053         break;
1054     case 5:                                     /* RELEASE Request */
1055         info_str="DCM RELEASE Request";
1056         offset = 6; 
1057         break;
1058     case 6:                                     /* RELEASE Response */
1059         info_str="DCM RELEASE Response";
1060         offset = 6; 
1061         break;
1062     case 7:                                     /* ABORT */
1063         dcm_data->source = tvb_get_guint8(tvb, 8);
1064         dcm_data->reason = tvb_get_guint8(tvb, 9);
1065         buf = ep_alloc(128);
1066         g_snprintf(buf, 128, "DCM ABORT %s <-- %s %s %s", 
1067             dcm_data->orig, dcm_data->targ,
1068             (dcm_data->source == 1) ? "USER" :
1069                 (dcm_data->source == 2) ? "PROVIDER" : "",
1070             dcm_data->source == 1 ? dcm_abort2str(dcm_data->reason) : "");
1071         info_str = buf;
1072         break;
1073     default:
1074         info_str="DCM Continuation";
1075         offset = -1;                            /* cannot continue parsing */
1076         break;
1077     }
1078     if (check_col(pinfo->cinfo, COL_INFO)) 
1079         col_add_str(pinfo->cinfo, COL_INFO, info_str);
1080
1081 /* In the interest of speed, if "tree" is NULL, don't do any work not
1082    necessary to generate protocol tree items. */
1083     if (tree) {
1084     proto_item *tf;
1085     ti = proto_tree_add_item(tree, proto_dcm, tvb, 0, -1, FALSE);
1086     dcm_tree = proto_item_add_subtree(ti, ett_dcm);
1087     proto_tree_add_uint_format(dcm_tree, hf_dcm_pdu, tvb, 0, dcm_data->tlen, 
1088         dcm_data->pdu, "PDU 0x%x (%s)", dcm_data->pdu, 
1089         dcm_pdu2str(dcm_data->pdu));
1090     proto_tree_add_item(dcm_tree, hf_dcm_pdu_len, tvb, 2, 4, FALSE);
1091
1092     switch (dcm_data->pdu) {
1093     case 1:                                     /* ASSOC Request */
1094     case 2:                                     /* ASSOC Accept */
1095     case 3:                                     /* ASSOC Reject */
1096     case 5:                                     /* RELEASE Request */
1097     case 6:                                     /* RELEASE Response */
1098     case 7:                                     /* ABORT */
1099         tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, 0, dcm_data->tlen, info_str);
1100         dissect_dcm_assoc(dcm_data, tf, tvb, offset);
1101         break;
1102     case 4:                                     /* DATA */
1103         tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, 0, dcm_data->tlen, info_str);
1104         dissect_dcm_data(dcm_data, tf, tvb);
1105         break;
1106     default:
1107         break;
1108     }
1109
1110 /* Continue adding tree items to process the packet here */
1111     } else if (1 == dcm_data->pdu || 2 == dcm_data->pdu) {
1112         dissect_dcm_assoc(dcm_data, NULL, tvb, offset);
1113     }
1114
1115 /* If this protocol has a sub-dissector call it here, see section 1.8 */
1116 }
1117
1118
1119 /* Register the protocol with Wireshark */
1120
1121 /* this format is require because a script is used to build the C function
1122    that calls all the protocol registration.
1123 */
1124
1125 void
1126 proto_register_dcm(void)
1127 {                 
1128 /* Setup list of header fields  See Section 1.6.1 for details*/
1129 static hf_register_info hf[] = {
1130     { &hf_dcm_pdu, { "PDU", "dcm.pdu",
1131         FT_UINT8, BASE_HEX, VALS(dcm_pdu_ids), 0, "", HFILL } },
1132     { &hf_dcm_pdu_len, { "PDU LENGTH", "dcm.pdu_len",
1133         FT_UINT32, BASE_HEX, NULL, 0, "", HFILL } },
1134     { &hf_dcm_pdu_type, { "PDU Detail", "dcm.pdu_detail",
1135         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1136     { &hf_dcm_pdi, { "Item", "dcm.pdu.pdi",
1137         FT_UINT8, BASE_HEX, VALS(dcm_pdi_ids), 0, "", HFILL } },
1138     { &hf_dcm_pdi_name, { "Application Context", "dcm.pdi.name",
1139         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1140     { &hf_dcm_pdi_syntax, { "Abstract Syntax", "dcm.pdi.syntax",
1141         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1142     { &hf_dcm_pctxt, { "Presentation Context", "dcm.pdi.ctxt",
1143         FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
1144     { &hf_dcm_pcres, { "Presentation Context result", "dcm.pdi.result",
1145         FT_UINT8, BASE_HEX, VALS(dcm_pdi_ids), 0, "", HFILL } },
1146     { &hf_dcm_pdu_maxlen, { "MAX PDU LENGTH", "dcm.max_pdu_len",
1147         FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
1148     { &hf_dcm_impl, { "Implementation", "dcm.pdi.impl",
1149         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1150     { &hf_dcm_vers, { "Version", "dcm.pdi.version",
1151         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1152     { &hf_dcm_async, { "Asynch", "dcm.pdi.async",
1153         FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1154     { &hf_dcm_data_len, { "DATA LENGTH", "dcm.data.len",
1155         FT_UINT32, BASE_HEX, NULL, 0, "", HFILL } },
1156     { &hf_dcm_data_ctx, { "Data Context", "dcm.data.ctx",
1157         FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
1158     { &hf_dcm_data_flags, { "Flags", "dcm.data.flags",
1159         FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
1160     { &hf_dcm_data_tag, { "Tag", "dcm.data.tag",
1161         FT_BYTES, BASE_HEX, NULL, 0, "", HFILL } },
1162 /*
1163     { &hf_dcm_FIELDABBREV, { "FIELDNAME", "dcm.FIELDABBREV",
1164         FIELDTYPE, FIELDBASE, FIELDCONVERT, BITMASK, "FIELDDESCR", HFILL } },
1165  */
1166     };
1167
1168 /* Setup protocol subtree array */
1169     static gint *ett[] = {
1170             &ett_dcm,
1171             &ett_assoc,
1172             &ett_dcm_data
1173     };
1174 /* Register the protocol name and description */
1175     proto_dcm = proto_register_protocol("DICOM", "dicom", "dcm");
1176
1177 /* Required function calls to register the header fields and subtrees used */
1178     proto_register_field_array(proto_dcm, hf, array_length(hf));
1179     proto_register_subtree_array(ett, array_length(ett));
1180
1181     register_init_routine(&dcm_init);
1182 }
1183
1184
1185 /* If this dissector uses sub-dissector registration add a registration routine.
1186    This format is required because a script is used to find these routines and
1187    create the code that calls these routines.
1188 */
1189 void
1190 proto_reg_handoff_dcm(void)
1191 {
1192     dissector_handle_t dcm_handle;
1193
1194     heur_dissector_add("tcp", dissect_dcm, proto_dcm);
1195     dcm_handle = new_create_dissector_handle(dissect_dcm, proto_dcm);
1196     dissector_add("tcp.port", 104, dcm_handle);
1197 }
1198
1199 /* 
1200
1201 PDU's
1202 01 ASSOC-RQ
1203  1    1 reserved
1204  2    4 length
1205  6    2 protocol version (0x0 0x1)
1206  8    2 reserved
1207 10   16 dest aetitle
1208 26   16 src  aetitle
1209 42   32 reserved 
1210 74    - presentation data value items
1211
1212 02 A-ASSOC-AC
1213     1 reserved
1214     4 length
1215     2 protocol version (0x0 0x1)
1216     2 reserved
1217    16 dest aetitle (not checked)
1218    16 src  aetitle (not checked)
1219    32 reserved 
1220     - presentation data value items
1221
1222 03 ASSOC-RJ
1223     1 reserved
1224     4 length (4)
1225     1 reserved
1226     1 result  (1 reject perm, 2 reject transient)
1227     1 source  (1 service user, 2 service provider, 3 service profider)
1228     1 reason
1229         1 == source 
1230             1 no reason given
1231             2 application context name not supported
1232             3 calling aetitle not recognized
1233             7 called aetitle not recognized
1234         2 == source
1235             1 no reason given
1236             2 protocol version not supported
1237         3 == source
1238             1 temporary congestion
1239             2 local limit exceeded
1240
1241 04 P-DATA-TF
1242  1  1 reserved
1243  2  4 length
1244     - (1+) presentation data value (PDV) items
1245  6      4 length
1246 10      1 Presentation Context ID (odd ints 1 - 255)
1247         - PDV
1248 11      1 header 
1249             0x01 if set, contains Message Command info, else Message Data
1250             0x02 if set, contains last fragment
1251
1252 05 A-RELEASE-RQ
1253     1 reserved
1254     4 length (4)
1255     4 reserved
1256
1257 06 A-RELEASE-RP
1258     1 reserved
1259     4 length (4)
1260     4 reserved
1261
1262 07 A-ABORT
1263     1  reserved
1264     4  length (4)
1265     2  reserved
1266     1  source  (0 = user, 1 = provider)
1267     1  reason  if 1 == source (0 not spec, 1 unrecognized, 2 unexpected 4 unrecognized param, 5 unexpected param, 6 invalid param)
1268
1269
1270
1271 ITEM's
1272 10 Application Context
1273     1 reserved
1274     2 length
1275     - name
1276
1277 20 Presentation Context
1278     1 reserved
1279     2 length
1280     1 Presentation context id
1281     3 reserved
1282     - (1) abstract and (1+) transfer syntax sub-items
1283
1284 21 Presentation Context (Reply)
1285     1 reserved
1286     2 length
1287     1 ID (odd int's 1-255)
1288     1 reserved
1289     1 result (0 accept, 1 user-reject, 2 no-reason, 3 abstract not supported, 4- transfer syntax not supported)
1290     1 reserved
1291     - (1) type 40
1292
1293 30 Abstract syntax 
1294     1 reserved
1295     2 length
1296     - name (<= 64)
1297
1298 40 Transfer syntax
1299     1 reserved
1300     2 length
1301     - name (<= 64)
1302
1303 50 user information
1304     1 reserved
1305     2 length
1306     - user data
1307
1308 51 max length
1309     1 reserved
1310     2 length (4)
1311     4 max PDU lengths
1312  */