2 * Routines for DICOM dissection
3 * Copyright 2003, Rich Coe <Richard.Coe@med.ge.com>
5 * DICOM communication protocol
6 * http://medical.nema.org/dicom/2003.html
7 * DICOM Part 8: Network Communication Support for Message Exchange
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.)
20 * Wireshark - Network traffic analyzer
21 * By Gerald Combs <gerald@wireshark.org>
22 * Copyright 1998 Gerald Combs
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.
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.
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.
44 * This is my first pass at a Wireshark dissector to display
45 * DICOM (Digital Imaging and Communications in Medicine) packets.
47 * - It currently displays most of the DICOM packets.
49 * - I've used it to debug Query/Retrieve, Storage, and Echo protocols.
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.
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.
62 * - The 'value to string' routines should probably be hash lookups.
65 * - Fixed the heuristic code -- sometimes a conversation already exists
66 * - Fixed the dissect code to display all the tags in the pdu
69 * - fix memory leak when Assoc packet is processed repeatedly in wireshark
71 * - removed unused partial packet flag
73 * - added better support for DICOM VR
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
79 * - remove my goofy attempt at trying to get access to the fragmented packets
80 * (anyone have an idea on how to fix this ???)
82 * - process all the data in the Assoc packet even if display is off
84 * - limit display of data in Assoc packet to defined size of the data even
85 * if reported size is larger
87 * - show the last tag in a packet as [incomplete] if we don't have all the data
89 * - added framework for reporting DICOM async negotiation (not finished)
90 * (I'm not aware of an implementation which currently supports this)
92 * - still need to fix display of continuation packets
104 #include <epan/packet.h>
105 #include <epan/emem.h>
106 #include <epan/strutil.h>
107 #include <epan/conversation.h>
109 #include "packet-tcp.h"
111 /* Initialize the protocol and registered fields */
112 static int proto_dcm = -1;
113 static int hf_dcm_pdu = -1,
115 hf_dcm_pdu_type = -1,
117 hf_dcm_pdi_name = -1,
118 hf_dcm_pdi_syntax = -1,
121 hf_dcm_pdu_maxlen = -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;
130 /* Initialize the subtree pointers */
131 static gint ett_dcm = -1, ett_assoc = -1, ett_dcm_data = -1;
133 static const value_string dcm_pdu_ids[] = {
134 { 1, "ASSOC Request" },
135 { 2, "ASSOC Accept" },
136 { 3, "ASSOC Reject" },
138 { 5, "RELEASE Request" },
139 { 6, "RELEASE Response" },
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" },
160 struct dcmItem *next, *prev;
162 guint8 id; /* 0x20 Presentation Context */
163 const guint8 *abs; /* 0x30 Abstract syntax */
164 char *xfer; /* 0x40 Transfer 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 */
171 typedef struct dcmItem dcmItem_t;
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_ }; */
181 guint8 orig[1+AEEND], targ[1+AEEND], resp[1+AEEND], source, result, reason;
183 typedef struct dcmState dcmState_t;
194 #define DCM_TSTAT 6 /* call dcm_rsp2str() on TINT2 */
197 #define DCM_SQ 9 /* sequence */
198 #define DCM_OTH 10 /* other */
200 typedef struct dcmTag dcmTag_t;
202 static GHashTable *dcm_tagTable = NULL;
204 dcmItem_t * lookupCtx(dcmState_t *dd, guint8 ctx);
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" },
278 if (NULL == dcm_tagTable) {
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));
292 if (NULL == (ds = (dcmState_t *) g_malloc(sizeof(dcmState_t)))) {
296 ds->tlen = ds->rlen = 0;
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;
306 dcm_pdu2str(guint8 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;
330 dcm_result2str(guint8 result)
334 case 1: s = "Reject Permanent"; break;
335 case 2: s = "Reject Transient"; break;
342 dcm_source2str(guint8 source)
346 case 1: s = "User"; break;
347 case 2: s = "Provider (ACSE)"; break;
348 case 3: s = "Provider (Presentation)"; break;
355 dcm_reason2str(guint8 source, guint8 reason)
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;
364 } else if (2 == source) switch (reason) {
365 case 1: s = "No reason"; break;
366 case 2: s = "protocol unsupported"; break;
368 } else if (3 == source) switch (reason) {
369 case 1: s = "temporary congestion"; break;
370 case 2: s = "local limit exceeded"; break;
377 dcm_abort2str(guint8 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;
393 dcm_PCresult2str(guint8 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;
408 dcm_flags2str(guint8 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 */
422 dcm_cmd2str(guint16 us)
425 /* there should be a better way to do this */
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;
456 dcm_rsp2str(guint16 us)
460 case 0x0000: s = "Success"; break;
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;
470 if (0xC000 == (0xF000 & us)) s = "Failed: Unable to Process";
475 dcm_setSyntax(dcmItem_t *di, char *name)
477 if (NULL == di) return;
478 if (di->xfer != NULL)
479 g_free(di->xfer); /* free prev allocated xfer */
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
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 */
503 dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, guint32 len, int vr, int tr)
511 static dcmTag_t utag = { 0, 0, "(unknown)" };
513 #define MAX_BUF_LEN 1024
514 buf=ep_alloc(MAX_BUF_LEN);
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);
523 tag = (grp << 16) | elm;
524 if (NULL == (dtag = g_hash_table_lookup(dcm_tagTable, GUINT_TO_POINTER(tag))))
527 DISSECTOR_ASSERT(MAX_BUF_LEN > strlen(dtag->desc));
529 p+=MIN(MAX_BUF_LEN-(p-buf),
530 g_snprintf(p, MAX_BUF_LEN-(p-buf), "%s", dtag->desc));
532 vval = (guint8*)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));
537 switch (tr > 0 ? tr : dtag->dtype) {
539 default: /* try ascii */
540 vval = (guint8*)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));
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));
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));
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));
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));
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)));
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)));
588 case DCM_SQ: /* Sequence */
589 case DCM_OTH: /* Other BYTE, WORD, ... */
590 case DCM_TRET: /* Retired */
597 dcm_get_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
601 len = tvb_get_ntohl(tvb, 2 + offset);
602 return len + 6; /* add in fixed header part */
606 dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offset)
608 proto_tree *dcm_tree = NULL;
609 dcmItem_t *di = NULL;
610 guint8 id, *name, result;
616 dcm_tree = proto_item_add_subtree(ti, ett_assoc);
617 while (-1 < offset && offset < (int) dcm_data->clen) {
620 id = tvb_get_guint8(tvb, offset);
621 len = tvb_get_ntohs(tvb, 2 + offset);
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));
627 case 0x10: /* App context */
629 proto_tree_add_item(dcm_tree, hf_dcm_pdi_name, tvb, offset, len > 65 ? 65 : len, FALSE);
632 case 0x30: /* Abstract syntax */
634 proto_tree_add_item(dcm_tree, hf_dcm_pdi_syntax, tvb, offset, len > 65 ? 65 : len, FALSE);
637 case 0x40: /* Transfer syntax */
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, (char*)name);
647 case 0x20: /* Presentation context */
648 id = tvb_get_guint8(tvb, offset);
649 di = lookupCtx(dcm_data, id);
651 di = se_alloc(sizeof(struct dcmItem));
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;
662 dcm_data->first = dcm_data->last = di;
665 proto_tree_add_item(dcm_tree, hf_dcm_pctxt, tvb, offset, 1, FALSE);
668 case 0x21: /* Presentation context reply */
669 id = tvb_get_guint8(tvb, offset);
670 result = tvb_get_guint8(tvb, 2 + offset);
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));
679 di = lookupCtx(dcm_data, id);
684 case 0x50: /* User Info */
686 case 0x51: /* Max length */
687 mlen = tvb_get_ntohl(tvb, offset);
689 proto_tree_add_item(dcm_tree, hf_dcm_pdu_maxlen, tvb, offset, 4, FALSE);
694 proto_tree_add_item(dcm_tree, hf_dcm_impl, tvb, offset, len > 65 ? 65 : len, FALSE);
697 case 0x55: /* version */
699 proto_tree_add_item(dcm_tree, hf_dcm_vers, tvb, offset, len > 17 ? 17 : len, FALSE);
702 case 0x53: /* async negotion */
714 lookupCtx(dcmState_t *dd, guint8 ctx)
716 dcmItem_t *di = dd->first;
717 static dcmItem_t dunk = { NULL, NULL, 0, -1,
718 "not found - click on ASSOC Request",
719 "not found - click on ASSOC Request", DCM_UNK };
725 return di ? di : &dunk;
732 - (1+) presentation data value (PDV) items
734 10 1 Presentation Context ID (odd ints 1 - 255)
737 0x01 if set, contains Message Command info, else Message Data
738 0x02 if set, contains last fragment
748 dissect_dcm_data(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb)
750 int len, offset, toffset, state, vr, tr;
751 proto_tree *dcm_tree;
753 guint8 ctx, syntax = DCM_UNK;
754 guint16 grp = 0, elm = 0;
755 guint32 tlen = 0, nlen;
757 dcm_tree = proto_item_add_subtree(ti, ett_dcm_data);
758 proto_tree_add_item(dcm_tree, hf_dcm_data_len, tvb, 6, 4, FALSE);
759 ctx = tvb_get_guint8(tvb, 10);
760 di = lookupCtx(dcm_data, ctx);
762 * XXX - telling the user to "click on ASSOC request" is bogus if we
763 * have already identified the ASSOC request and can connect it to
764 * this mnessage; if clicking on a request prior to this one causes
765 * additional state information to be set up that would affect the
766 * dissection of this request, we should set up that state *at the
767 * time we dissect that request*, if possible, and if clicking on it
768 * doesn't change any state, clicking on the request doesn't convey
769 * any additional information.
771 proto_tree_add_uint_format(dcm_tree, hf_dcm_data_ctx, tvb, 10, 1,
772 ctx, "Context 0x%x (%s)", ctx,
773 di->xfer == NULL ? "not found - click on ASSOC Request" :
775 if (DCM_UNK == di->syntax)
777 len = offset = toffset = 11;
780 while (len + nlen <= dcm_data->tlen && len + nlen <= dcm_data->clen) {
784 flags = tvb_get_guint8(tvb, offset);
785 proto_tree_add_uint_format(dcm_tree, hf_dcm_data_flags, tvb, offset, 1,
786 flags, "Flags 0x%x (%s)", flags, dcm_flags2str(flags));
787 /* proto_tree_add_item(dcm_tree, hf_dcm_data_flags, tvb, offset, 1, FALSE); */
792 else if (DCM_UNK == di->syntax) {
794 tlen = dcm_data->clen - len;
795 val = tvb_get_ptr(tvb, offset, tlen+8);
796 proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
797 offset, tlen, val, "(%04x,%04x) %-8x Unparsed data", 0, 0, tlen);
798 len = dcm_data->clen; /* ends parsing */
803 } break; /* don't fall through -- check length */
806 if (DCM_ILE & syntax) {
807 grp = tvb_get_letohs(tvb, offset);
808 elm = tvb_get_letohs(tvb, offset+2);
809 state = (DCM_EBE & syntax) ? D_VR : D_LEN4; /* is Explicit */
810 nlen = (DCM_EBE & syntax) ? 2 : 4; /* is Explicit */
812 grp = tvb_get_ntohs(tvb, offset);
813 elm = tvb_get_ntohs(tvb, offset+2);
818 if (0xfffe == grp) state = D_LEN4;
821 } break; /* don't fall through -- check length */
825 V = tvb_get_guint8(tvb, offset); offset++;
826 R = tvb_get_guint8(tvb, offset); offset++;
828 /* 4byte lengths OB, OW, OF, SQ, UN, UT */
831 if ((('O' == V) && ('B' == R || 'W' == R || 'F' == R) && (tr = DCM_OTH))
832 || (('U' == V) && ('N' == R || (('T' == R) && (tr = DCM_TSTR))))
833 || ('S' == V && 'Q' == R && (tr = DCM_SQ))) {
835 offset += 2; /* skip 00 (2 bytes) */
838 } else if ('F' == V && 'L' == R) {
840 } else if ('F' == V && 'D' == R) {
842 } else if (('S' == V && 'L' == R) || ('U' == V && 'L' == R)) {
844 } else if (('S' == V && 'S' == R) || ('U' == V && 'S' == R)) {
846 } else if ('A' == V && 'T' == R) {
851 else if (('A' == V && ('E' == R || 'S' == R))
852 || ('C' == V && 'S' == R)
853 || ('D' == V && ('A' == R || 'S' == R || 'T' == R))
854 || ('I' == V && 'S' == R)
855 || ('L' == V && ('O' == R || 'T' == R))
856 || ('P' == V && 'N' == R)
857 || ('S' == V && ('H' == R ||| 'T' == R))
858 || ('T' == V && 'M' == R)
859 || ('U' == V && ('I' == R || 'T' == R)))
862 } break; /* don't fall through -- check length */
864 if (DCM_ILE & syntax) /* is it LE */
865 tlen = tvb_get_letohs(tvb, offset);
867 tlen = tvb_get_ntohs(tvb, offset);
874 if (DCM_ILE & syntax) /* is it LE */
875 tlen = tvb_get_letohl(tvb, offset);
877 tlen = tvb_get_ntohl(tvb, offset);
882 } break; /* don't fall through -- check length */
885 int totlen = (offset - toffset);
886 if (0xffffffff == tlen || 0xfffe == grp) {
887 val = tvb_get_ptr(tvb, toffset, totlen);
888 proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
889 toffset, totlen, val,
890 "(%04x,%04x) %-8x %s", grp, elm, tlen,
891 dcm_tag2str(grp, elm, syntax, tvb, offset, 0, vr, tr));
893 /* } else if (0xfffe == grp) { */ /* need to make a sub-tree here */
896 val = tvb_get_ptr(tvb, toffset, totlen);
897 proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
898 toffset, totlen, val,
899 "(%04x,%04x) %-8x %s", grp, elm, tlen,
900 dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, tr));
909 if (D_VALUE == state) {
911 int totlen = (offset - toffset);
912 val = tvb_get_ptr(tvb, toffset, totlen);
913 proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb,
914 toffset, totlen, val,
915 "(%04x,%04x) %-8x %s [incomplete]", grp, elm, tlen,
916 dcm_tag2str(grp, elm, syntax, tvb, offset, tlen, vr, tr));
921 Originator src:srcport dest:destport
922 Acceptor src:srcport dest:destport
924 conn = lookup(src:srcport, dest:destport)
926 look at data payload of packet
927 if no-data return false;
928 if 01 == *p && *p+10 ... *p+42 <= [ 0x20 .. printable ]
932 static void dissect_dcm_pdu(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree);
934 /* Code to actually dissect the packets */
936 dissect_dcm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
938 conversation_t *conv;
942 dcmState_t *dcm_data = NULL;
944 conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
945 pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
947 if (NULL != conv) /* conversation exists */
948 /* do we have any data for this conversation ? */
949 dcm_data = conversation_get_proto_data(conv, proto_dcm);
951 conv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
952 pinfo->srcport, pinfo->destport, 0);
954 if (NULL == dcm_data) {
955 /* No conversation found.
956 * only look for the first packet of a DICOM conversation.
957 * if we don't get the first packet, we cannot decode the rest
960 if (NULL == (dcm_data = mkds()))
961 return FALSE; /* internal error */
962 if (10 > (tlen = tvb_reported_length(tvb)) /* not long enough */
963 || 1 != (pdu = tvb_get_guint8(tvb, 0)) /* look for the start */
964 || 1 != (vers = tvb_get_ntohs(tvb, 6))) /* not version 1 */
965 dcm_data->valid = FALSE;
967 len = 6 + tvb_get_ntohl(tvb, 2);
969 dcm_data->valid = FALSE; /* packet is > decl len */
972 conversation_add_proto_data(conv, proto_dcm, dcm_data);
975 if (FALSE == dcm_data->valid)
978 if (check_col(pinfo->cinfo, COL_PROTOCOL))
979 col_clear(pinfo->cinfo, COL_PROTOCOL);
981 tcp_dissect_pdus(tvb, pinfo, tree, 1, 6, dcm_get_pdu_len, dissect_dcm_pdu);
987 dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
990 dcmState_t *dcm_data;
991 proto_tree *dcm_tree;
992 conversation_t *conv;
996 if (NULL == (conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
997 pinfo->ptype, pinfo->srcport, pinfo->destport, 0)))
1000 dcm_data = conversation_get_proto_data(conv, proto_dcm);
1002 if (check_col(pinfo->cinfo, COL_PROTOCOL))
1003 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCM");
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.
1011 if (check_col(pinfo->cinfo, COL_INFO))
1012 col_clear(pinfo->cinfo, COL_INFO);
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);
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);
1028 case 2: /* ASSOC Accept */
1029 tvb_memcpy(tvb, dcm_data->resp, 26, 16);
1030 buf = ep_alloc(128);
1031 g_snprintf(buf, 128, "DCM ASSOC Accept %s <-- %s (%s)",
1032 dcm_data->orig, dcm_data->targ, dcm_data->resp);
1035 case 3: /* ASSOC Reject */
1036 dcm_data->result = tvb_get_guint8(tvb, 7);
1037 dcm_data->source = tvb_get_guint8(tvb, 8);
1038 dcm_data->reason = tvb_get_guint8(tvb, 9);
1039 buf = ep_alloc(128);
1040 g_snprintf(buf, 128, "DCM ASSOC Reject %s <-- %s %s %s %s",
1041 dcm_data->orig, dcm_data->targ,
1042 dcm_result2str(dcm_data->result),
1043 dcm_source2str(dcm_data->source),
1044 dcm_reason2str(dcm_data->source, dcm_data->reason));
1051 case 5: /* RELEASE Request */
1052 buf="DCM RELEASE Request";
1055 case 6: /* RELEASE Response */
1056 buf="DCM RELEASE Response";
1060 dcm_data->source = tvb_get_guint8(tvb, 8);
1061 dcm_data->reason = tvb_get_guint8(tvb, 9);
1062 buf = ep_alloc(128);
1063 g_snprintf(buf, 128, "DCM ABORT %s <-- %s %s %s",
1064 dcm_data->orig, dcm_data->targ,
1065 (dcm_data->source == 1) ? "USER" :
1066 (dcm_data->source == 2) ? "PROVIDER" : "",
1067 dcm_data->source == 1 ? dcm_abort2str(dcm_data->reason) : "");
1070 buf="DCM Continuation";
1071 offset = -1; /* cannot continue parsing */
1074 if (check_col(pinfo->cinfo, COL_INFO))
1075 col_set_str(pinfo->cinfo, COL_INFO, buf);
1077 /* In the interest of speed, if "tree" is NULL, don't do any work not
1078 necessary to generate protocol tree items. */
1081 ti = proto_tree_add_item(tree, proto_dcm, tvb, 0, -1, FALSE);
1082 dcm_tree = proto_item_add_subtree(ti, ett_dcm);
1083 proto_tree_add_uint_format(dcm_tree, hf_dcm_pdu, tvb, 0, dcm_data->tlen,
1084 dcm_data->pdu, "PDU 0x%x (%s)", dcm_data->pdu,
1085 dcm_pdu2str(dcm_data->pdu));
1086 proto_tree_add_item(dcm_tree, hf_dcm_pdu_len, tvb, 2, 4, FALSE);
1088 switch (dcm_data->pdu) {
1089 case 1: /* ASSOC Request */
1090 case 2: /* ASSOC Accept */
1091 case 3: /* ASSOC Reject */
1092 case 5: /* RELEASE Request */
1093 case 6: /* RELEASE Response */
1095 tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, 0, dcm_data->tlen, buf);
1096 dissect_dcm_assoc(dcm_data, tf, tvb, offset);
1099 tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, 0, dcm_data->tlen, buf);
1100 dissect_dcm_data(dcm_data, tf, tvb);
1106 /* Continue adding tree items to process the packet here */
1107 } else if (1 == dcm_data->pdu || 2 == dcm_data->pdu) {
1108 dissect_dcm_assoc(dcm_data, NULL, tvb, offset);
1111 /* If this protocol has a sub-dissector call it here, see section 1.8 */
1115 /* Register the protocol with Wireshark */
1117 /* this format is require because a script is used to build the C function
1118 that calls all the protocol registration.
1122 proto_register_dcm(void)
1124 /* Setup list of header fields See Section 1.6.1 for details*/
1125 static hf_register_info hf[] = {
1126 { &hf_dcm_pdu, { "PDU", "dcm.pdu",
1127 FT_UINT8, BASE_HEX, VALS(dcm_pdu_ids), 0, "", HFILL } },
1128 { &hf_dcm_pdu_len, { "PDU LENGTH", "dcm.pdu_len",
1129 FT_UINT32, BASE_HEX, NULL, 0, "", HFILL } },
1130 { &hf_dcm_pdu_type, { "PDU Detail", "dcm.pdu_detail",
1131 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1132 { &hf_dcm_pdi, { "Item", "dcm.pdu.pdi",
1133 FT_UINT8, BASE_HEX, VALS(dcm_pdi_ids), 0, "", HFILL } },
1134 { &hf_dcm_pdi_name, { "Application Context", "dcm.pdi.name",
1135 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1136 { &hf_dcm_pdi_syntax, { "Abstract Syntax", "dcm.pdi.syntax",
1137 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1138 { &hf_dcm_pctxt, { "Presentation Context", "dcm.pdi.ctxt",
1139 FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
1140 { &hf_dcm_pcres, { "Presentation Context result", "dcm.pdi.result",
1141 FT_UINT8, BASE_HEX, VALS(dcm_pdi_ids), 0, "", HFILL } },
1142 { &hf_dcm_pdu_maxlen, { "MAX PDU LENGTH", "dcm.max_pdu_len",
1143 FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } },
1144 { &hf_dcm_impl, { "Implementation", "dcm.pdi.impl",
1145 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1146 { &hf_dcm_vers, { "Version", "dcm.pdi.version",
1147 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1148 { &hf_dcm_async, { "Asynch", "dcm.pdi.async",
1149 FT_STRING, BASE_NONE, NULL, 0, "", HFILL } },
1150 { &hf_dcm_data_len, { "DATA LENGTH", "dcm.data.len",
1151 FT_UINT32, BASE_HEX, NULL, 0, "", HFILL } },
1152 { &hf_dcm_data_ctx, { "Data Context", "dcm.data.ctx",
1153 FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
1154 { &hf_dcm_data_flags, { "Flags", "dcm.data.flags",
1155 FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } },
1156 { &hf_dcm_data_tag, { "Tag", "dcm.data.tag",
1157 FT_BYTES, BASE_HEX, NULL, 0, "", HFILL } },
1159 { &hf_dcm_FIELDABBREV, { "FIELDNAME", "dcm.FIELDABBREV",
1160 FIELDTYPE, FIELDBASE, FIELDCONVERT, BITMASK, "FIELDDESCR", HFILL } },
1164 /* Setup protocol subtree array */
1165 static gint *ett[] = {
1170 /* Register the protocol name and description */
1171 proto_dcm = proto_register_protocol("DICOM", "dicom", "dcm");
1173 /* Required function calls to register the header fields and subtrees used */
1174 proto_register_field_array(proto_dcm, hf, array_length(hf));
1175 proto_register_subtree_array(ett, array_length(ett));
1177 register_init_routine(&dcm_init);
1181 /* If this dissector uses sub-dissector registration add a registration routine.
1182 This format is required because a script is used to find these routines and
1183 create the code that calls these routines.
1186 proto_reg_handoff_dcm(void)
1188 dissector_handle_t dcm_handle;
1190 heur_dissector_add("tcp", dissect_dcm, proto_dcm);
1191 dcm_handle = new_create_dissector_handle(dissect_dcm, proto_dcm);
1192 dissector_add("tcp.port", 104, dcm_handle);
1201 6 2 protocol version (0x0 0x1)
1206 74 - presentation data value items
1211 2 protocol version (0x0 0x1)
1213 16 dest aetitle (not checked)
1214 16 src aetitle (not checked)
1216 - presentation data value items
1222 1 result (1 reject perm, 2 reject transient)
1223 1 source (1 service user, 2 service provider, 3 service profider)
1227 2 application context name not supported
1228 3 calling aetitle not recognized
1229 7 called aetitle not recognized
1232 2 protocol version not supported
1234 1 temporary congestion
1235 2 local limit exceeded
1240 - (1+) presentation data value (PDV) items
1242 10 1 Presentation Context ID (odd ints 1 - 255)
1245 0x01 if set, contains Message Command info, else Message Data
1246 0x02 if set, contains last fragment
1262 1 source (0 = user, 1 = provider)
1263 1 reason if 1 == source (0 not spec, 1 unrecognized, 2 unexpected 4 unrecognized param, 5 unexpected param, 6 invalid param)
1268 10 Application Context
1273 20 Presentation Context
1276 1 Presentation context id
1278 - (1) abstract and (1+) transfer syntax sub-items
1280 21 Presentation Context (Reply)
1283 1 ID (odd int's 1-255)
1285 1 result (0 accept, 1 user-reject, 2 no-reason, 3 abstract not supported, 4- transfer syntax not supported)