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