diff options
author | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
---|---|---|
committer | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
commit | 669db206cb1f270046ad400fff7655e20c63e723 (patch) | |
tree | 4eff24a2e16c8963e497e1fc575f35e6af59bd26 /epan/dissectors/packet-dcm.c | |
parent | ae46c27a38700af669ef907491081f09df6f6b2c (diff) | |
download | wireshark-669db206cb1f270046ad400fff7655e20c63e723.tar.gz wireshark-669db206cb1f270046ad400fff7655e20c63e723.tar.bz2 wireshark-669db206cb1f270046ad400fff7655e20c63e723.zip |
Move dissectors to epan/dissectors directory.
Also move ncp222.py, x11-fields, process-x11-fields.pl,
make-reg-dotc, and make-reg-dotc.py.
Adjust #include lines in files that include packet-*.h
files.
svn path=/trunk/; revision=11410
Diffstat (limited to 'epan/dissectors/packet-dcm.c')
-rw-r--r-- | epan/dissectors/packet-dcm.c | 1234 |
1 files changed, 1234 insertions, 0 deletions
diff --git a/epan/dissectors/packet-dcm.c b/epan/dissectors/packet-dcm.c new file mode 100644 index 0000000000..cb42cf01ce --- /dev/null +++ b/epan/dissectors/packet-dcm.c @@ -0,0 +1,1234 @@ +/* packet-dcm.c + * Routines for DICOM dissection + * Copyright 2003, Rich Coe <Richard.Coe@med.ge.com> + * + * DICOM communication protocol + * http://medical.nema.org/dicom/2003.html + * DICOM Part 8: Network Communication Support for Message Exchange + * + * (NOTE: you need to turn on 'Allow subdissector to desegment TCP streams' + * in Preferences/Protocols/TCP Option menu, in order to view + * DICOM packets correctly. + * This should probably be documented somewhere besides here.) + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* Notes: + * This is my first pass at a Ethereal dissector to display + * DICOM (Digital Imaging and Communications in Medicine) packets. + * + * - It currently displays most of the DICOM packets. + * + * - I've used it to debug Query/Retrieve, Storage, and Echo protocols. + * + * - Not all DICOM tags are currently displayed symbolically. + * Unknown tags are displayed as '(unknown)' + * More known tags might be added in the future. + * If the tag data contains a string, it will be displayed. + * Even if the tag contains Explicit VR, it is not currently used to + * symbolically display the data. Consider this a future enhancement. + * + * - If the DATA PDU has the 'more' bit set, subsequent packets will + * not currently display. Finding out how much 'more' data is coming + * currently requires parsing the entire packet. + * + * - The 'value to string' routines should probably be hash lookups. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include <glib.h> + +#include "isprint.h" + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include <epan/packet.h> +#include <epan/strutil.h> +#include <epan/conversation.h> + +#include "packet-tcp.h" + +/* Initialize the protocol and registered fields */ +static int proto_dcm = -1; +static int hf_dcm_pdu = -1, + hf_dcm_pdu_len = -1, + hf_dcm_pdu_type = -1, + hf_dcm_pdi = -1, + hf_dcm_pdi_name = -1, + hf_dcm_pdi_syntax = -1, + hf_dcm_pctxt = -1, + hf_dcm_pcres = -1, + hf_dcm_pdu_maxlen = -1, + hf_dcm_impl = -1, + hf_dcm_vers = -1, + hf_dcm_data_len = -1, + hf_dcm_data_ctx = -1, + hf_dcm_data_flags = -1, + hf_dcm_data_tag = -1; + +/* Initialize the subtree pointers */ +static gint ett_dcm = -1, ett_assoc = -1, ett_dcm_data = -1; + +static const value_string dcm_pdu_ids[] = { + { 1, "ASSOC Request" }, + { 2, "ASSOC Accept" }, + { 3, "ASSOC Reject" }, + { 4, "Data" }, + { 5, "RELEASE Request" }, + { 6, "RELEASE Response" }, + { 7, "ABORT" }, + { 0, NULL } +}; + +static const value_string dcm_pdi_ids[] = { + { 0x10, "Application Context" }, + { 0x20, "Presentation Context" }, + { 0x21, "Presentation Context Reply" }, + { 0x30, "Abstract syntax" }, + { 0x40, "Transfer syntax" }, + { 0x50, "User Info" }, + { 0x51, "Max Length" }, + { 0, NULL } +}; + +struct dcmContext { + guint8 id; + guint8 result; +}; +struct dcmItem { + struct dcmItem *next, *prev; + guint8 id; /* 0x20 Presentation Context */ + guint8 *abs; /* 0x30 Abstract syntax */ + guint8 *xfer; /* 0x40 Transfer syntax */ + guint8 syntax; +#define DCM_ILE 0x01 /* implicit, little endian */ +#define DCM_EBE 0x02 /* explicit, big endian */ +#define DCM_ELE 0x03 /* explicit, little endian */ +#define DCM_UNK 0xf0 +}; +typedef struct dcmItem dcmItem_t; + +struct dcmState { + dcmItem_t *first, *last; + guint8 pdu; /* protocol data unit */ + guint32 tlen, clen, rlen; /* length: total, current, remaining */ + int partial; /* is a partial packet */ + int coff; /* current offset */ + /* enum { DCM_NONE, DCM_ASSOC, DCM_ }; */ +#define AEEND 16 + guint8 orig[1+AEEND], targ[1+AEEND], resp[1+AEEND], source, result, reason; +}; +typedef struct dcmState dcmState_t; + +struct dcmTag { + int tag; + int dtype; + char *desc; +#define DCM_TSTR 1 +#define DCM_TINT2 2 +#define DCM_TINT4 3 +#define DCM_TFLT 4 +#define DCM_TDBL 5 +#define DCM_TSTAT 6 /* call dcm_rsp2str() on TINT2 */ +#define DCM_TRET 7 +#define DCM_TCMD 8 +}; +typedef struct dcmTag dcmTag_t; + +/* static GMemChunk *dcm_assocs = NULL; */ +static GMemChunk *dcm_pdus = NULL; +static GHashTable *dcm_tagTable = NULL; + +dcmItem_t * lookupCtx(dcmState_t *dd, guint8 ctx); + +static dcmTag_t tagData[] = { + { 0x1, DCM_TRET, "(Ret) Length to End" }, + { 0x2, DCM_TSTR, "Affected Class" }, + { 0x3, DCM_TSTR, "Requested Class" }, + { 0x0010, DCM_TRET, "(Ret) Recognition Code" }, + { 0x0100, DCM_TCMD, "Command Field" }, + { 0x0110, DCM_TINT2, "Message ID" }, + { 0x0120, DCM_TINT2, "Resp Message ID" }, + { 0x0200, DCM_TRET, "(Ret) Initiator" }, + { 0x0300, DCM_TRET, "(Ret) Reciever" }, + { 0x0400, DCM_TRET, "(Ret) Find Location" }, + { 0x0600, DCM_TSTR, "Dest AE" }, + { 0x0700, DCM_TINT2, "Priority" }, + { 0x0800, DCM_TINT2, "Data Set (0x0101 means no data set present)" }, + { 0x0850, DCM_TRET, "(Ret) Num Matches" }, + { 0x0860, DCM_TRET, "(Ret) Resp Seq Num" }, + { 0x0900, DCM_TSTAT, "Status" }, + { 0x0901, DCM_TSTR, "Offending elm(s)" }, + { 0x0902, DCM_TSTR, "Error Comment" }, + { 0x0903, DCM_TINT2, "Error Id" }, + { 0x1000, DCM_TSTR, "Affected Instance UID" }, + { 0x1001, DCM_TSTR, "Requested Instance UID" }, + { 0x1002, DCM_TINT2, "Event Type Id" }, + { 0x1005, DCM_TSTR, "Attr Id List" }, + { 0x1008, DCM_TINT2, "Action Type Id" }, + { 0x1020, DCM_TINT2, "Num Remaining Ops" }, + { 0x1021, DCM_TINT2, "Num Completed Ops" }, + { 0x1022, DCM_TINT2, "Num Failed Ops" }, + { 0x1023, DCM_TINT2, "Num Warning Ops" }, + { 0x1030, DCM_TSTR, "Move Orig AE" }, + { 0x1031, DCM_TINT2, "Move Orig Id" }, + { 0x4000, DCM_TRET, "(Ret) DIALOG Recv'r" }, + { 0x4010, DCM_TRET, "(Ret) Terminal Type" }, + { 0x5010, DCM_TRET, "(Ret) Msg Set ID" }, + { 0x5020, DCM_TRET, "(Ret) End Msg ID" }, + { 0x5110, DCM_TRET, "(Ret) Display Fmt" }, + { 0x5120, DCM_TRET, "(Ret) Page Position ID" }, + { 0x5130, DCM_TRET, "(Ret) Text Fmt ID" }, + { 0x5140, DCM_TRET, "(Ret) Nor/Rev" }, + { 0x5150, DCM_TRET, "(Ret) Add Gray Scale" }, + { 0x5160, DCM_TRET, "(Ret) Borders" }, + { 0x5170, DCM_TRET, "(Ret) Copies" }, + { 0x5180, DCM_TRET, "(Ret) Mag Type" }, + { 0x5190, DCM_TRET, "(Ret) Erase" }, + { 0x51a0, DCM_TRET, "(Ret) Print" }, + { 0x080018, DCM_TSTR, "Image UID" }, + { 0x080020, DCM_TSTR, "Study Date" }, + { 0x080030, DCM_TSTR, "Study Time" }, + { 0x080050, DCM_TSTR, "Acc Num" }, + { 0x080052, DCM_TSTR, "Q/R Level" }, + { 0x080054, DCM_TSTR, "Retrieve AE" }, + { 0x080060, DCM_TSTR, "Modality" }, + { 0x080070, DCM_TSTR, "Manuf" }, + { 0x081030, DCM_TSTR, "Study Desc" }, + { 0x08103e, DCM_TSTR, "Series Desc" }, + { 0x100010, DCM_TSTR, "Patient Name" }, + { 0x100020, DCM_TSTR, "Patient Id" }, + { 0x20000d, DCM_TSTR, "Study UID" }, + { 0x20000e, DCM_TSTR, "Series UID" }, + { 0x200010, DCM_TSTR, "Study Num" }, + { 0x200011, DCM_TSTR, "Series Num" }, + { 0x200012, DCM_TSTR, "Acq Num" }, + { 0x200013, DCM_TSTR, "Image Num" }, + { 0xfffee000, DCM_TRET, "Item Begin" }, + { 0xfffee00d, DCM_TRET, "Item End" }, + { 0xfffee0dd, DCM_TRET, "Sequence End" }, +}; + +static void +dcm_init(void) +{ +#ifdef notdef + if (dcm_assocs) g_mem_chunk_destroy(dcm_assocs); + dcm_assocs = g_mem_chunk_new("dcm_assocs", sizeof(struct dcmState), + 100 * sizeof(struct dcmState), G_ALLOC_AND_FREE); +#endif + if (dcm_pdus) g_mem_chunk_destroy(dcm_pdus); + dcm_pdus = g_mem_chunk_new("dcm_pdus", sizeof(struct dcmItem), + 128 * sizeof(struct dcmItem), G_ALLOC_AND_FREE); + if (NULL == dcm_tagTable) { + unsigned int i; + dcm_tagTable = g_hash_table_new(NULL, NULL); + for (i = 0; i < sizeof(tagData) / sizeof(dcmTag_t); i++) + g_hash_table_insert(dcm_tagTable, (gpointer)tagData[i].tag, + (gpointer) (tagData+i)); + } +} + +dcmState_t * +mkds() +{ + dcmState_t *ds; + + if (NULL == (ds = (dcmState_t *) malloc(sizeof(dcmState_t)))) { + return NULL; + } + ds->pdu = 0; + ds->tlen = ds->rlen = 0; + ds->partial = 0; + memset(ds->orig, 0, sizeof(ds->orig)); + memset(ds->targ, 0, sizeof(ds->targ)); + memset(ds->resp, 0, sizeof(ds->resp)); + ds->first = ds->last = NULL; + return ds; +} + +char * +dcm_pdu2str(guint8 item) +{ + char *s = ""; + switch (item) { + case 1: s = "ASSOC Request"; break; + case 2: s = "ASSOC Accept"; break; + case 3: s = "ASSOC Reject"; break; + case 4: s = "Data"; break; + case 5: s = "RELEASE Request"; break; + case 6: s = "RELEASE Response"; break; + case 7: s = "ABORT"; break; + case 0x10: s = "Application Context"; break; + case 0x20: s = "Presentation Context"; break; + case 0x21: s = "Presentation Context Reply"; break; + case 0x30: s = "Abstract syntax"; break; + case 0x40: s = "Transfer syntax"; break; + case 0x50: s = "User Info"; break; + case 0x51: s = "Max Length"; break; + default: break; + } + return s; +} + +char * +dcm_result2str(guint8 result) +{ + char *s = ""; + switch (result) { + case 1: s = "Reject Permanent"; break; + case 2: s = "Reject Transient"; break; + default: break; + } + return s; +} + +char * +dcm_source2str(guint8 source) +{ + char *s = ""; + switch (source) { + case 1: s = "User"; break; + case 2: s = "Provider (ACSE)"; break; + case 3: s = "Provider (Presentation)"; break; + default: break; + } + return s; +} + +char * +dcm_reason2str(guint8 source, guint8 reason) +{ + char *s = ""; + if (1 == source) switch (reason) { + case 1: s = "No reason"; break; + case 2: s = "App Name not supported"; break; + case 3: s = "calling AET not recognized"; break; + case 7: s = "called AET not recognized"; break; + default: break; + } else if (2 == source) switch (reason) { + case 1: s = "No reason"; break; + case 2: s = "protocol unsupported"; break; + default: break; + } else if (3 == source) switch (reason) { + case 1: s = "temporary congestion"; break; + case 2: s = "local limit exceeded"; break; + default: break; + } + return s; +} + +char * +dcm_abort2str(guint8 reason) +{ + char *s = ""; + switch (reason) { + case 0: s = "not specified"; break; + case 1: s = "unrecoginized"; break; + case 2: s = "unexpected"; break; + case 4: s = "unrecognized parameter"; break; + case 5: s = "unexpected parameter"; break; + case 6: s = "invalid parameter"; break; + default: break; + } + return s; +} + +char * +dcm_PCresult2str(guint8 result) +{ + char *s = ""; + switch (result) { + case 0: s = "accept"; break; + case 1: s = "user-reject"; break; + case 2: s = "no-reason"; break; + case 3: s = "abstract syntax unsupported"; break; + case 4: s = "transfer syntax unsupported"; break; + default: break; + } + return s; +} + +char * +dcm_flags2str(guint8 flags) +{ + char *s = ""; + switch (flags) { + case 0: s = "Data, more Fragments"; break; /* 00 */ + case 1: s = "Command, more Fragments"; break; /* 01 */ + case 2: s = "Data, last Fragment"; break; /* 10 */ + case 3: s = "Command, last Fragment"; break; /* 11 */ + default: break; + } + return s; +} + +char * +dcm_cmd2str(guint16 us) +{ + char *s = ""; + /* there should be a better way to do this */ + switch (us) { + case 0x0001: s = "C-STORE-RQ"; break; + case 0x8001: s = "C-STORE-RSP"; break; + case 0x0010: s = "C-GET-RQ"; break; + case 0x8010: s = "C-GET-RSP"; break; + case 0x0020: s = "C-FIND-RQ"; break; + case 0x8020: s = "C-FIND-RSP"; break; + case 0x0021: s = "C-MOVE-RQ"; break; + case 0x8021: s = "C-MOVE-RSP"; break; + case 0x0030: s = "C-ECHO-RQ"; break; + case 0x8030: s = "C-ECHO-RSP"; break; + case 0x0100: s = "N-EVENT-REPORT-RQ"; break; + case 0x8100: s = "N-EVENT-REPORT-RSP"; break; + case 0x0110: s = "N-GET-RQ"; break; + case 0x8110: s = "N-GET-RSP"; break; + case 0x0120: s = "N-SET-RQ"; break; + case 0x8120: s = "N-SET-RSP"; break; + case 0x0130: s = "N-ACTION-RQ"; break; + case 0x8130: s = "N-ACTION-RSP"; break; + case 0x0140: s = "N-CREATE-RQ"; break; + case 0x8140: s = "N-CREATE-RSP"; break; + case 0x0150: s = "N-DELETE-RQ"; break; + case 0x8150: s = "N-DELETE-RSP"; break; + case 0x0fff: s = "C-CANCEL-RQ"; break; + default: break; + } + return s; +} + +char * +dcm_rsp2str(guint16 us) +{ + char *s = ""; + switch (us) { + case 0x0000: s = "Success"; break; + case 0xa701: + case 0xa702: s = "Refused: Out of Resources"; break; + case 0xa801: s = "Refused: Move Destination unknown"; break; + case 0xa900: s = "Failed: Id does not match Class"; break; + case 0xb000: s = "Warning: operations complete -- One or more Failures"; break; + case 0xfe00: s = "Cancel: operations terminated by Cancel"; break; + case 0xff00: s = "Pending: operations are continuing"; break; + default: break; + } + if (0xC000 == (0xC000 & us)) s = "Failed: Unable to Process"; + return s; +} + +void +dcm_setSyntax(dcmItem_t *di, char *name) +{ + if (NULL == di) return; + di->syntax = 0; + if (0 == *name) return; + /* this would be faster to skip the common parts, and have a FSA to + * find the syntax. + * Absent of coding that, this is in descending order of probability */ + if (0 == strcmp(name, "1.2.840.10008.1.2")) + di->syntax = DCM_ILE; /* implicit little endian */ + else if (0 == strcmp(name, "1.2.840.10008.1.2.1")) + di->syntax = DCM_ELE; /* explicit little endian */ + else if (0 == strcmp(name, "1.2.840.10008.1.2.2")) + di->syntax = DCM_EBE; /* explicit big endian */ + else if (0 == strcmp(name, "1.2.840.113619.5.2")) + di->syntax = DCM_ILE; /* implicit little endian, big endian pixels */ + else if (0 == strcmp(name, "1.2.840.10008.1.2.4.70")) + di->syntax = DCM_ELE; /* explicit little endian, jpeg */ + else if (0 == strncmp(name, "1.2.840.10008.1.2.4", 18)) + di->syntax = DCM_ELE; /* explicit little endian, jpeg */ + else if (0 == strcmp(name, "1.2.840.10008.1.2.1.99")) + di->syntax = DCM_ELE; /* explicit little endian, deflated */ +} + +char * +dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, guint32 len) +{ + static char buf[512+1]; /* bad form ??? */ + const guint8 *vval; + char *p; + guint32 tag, val32; + guint16 val16; + dcmTag_t *dtag; + static dcmTag_t utag = { 0, 0, "(unknown)" }; + + *buf = 0; + if (0 == elm) { + if (DCM_ILE & syntax) + val32 = tvb_get_letohl(tvb, offset); + else val32 = tvb_get_ntohl(tvb, offset); + snprintf(buf, sizeof(buf), "Group Length 0x%x (%d)", val32, val32); + return buf; + } + tag = (grp << 16) | elm; + if (NULL == (dtag = g_hash_table_lookup(dcm_tagTable, (gconstpointer) tag))) + dtag = &utag; + + strcpy(buf, dtag->desc); + p = buf + strlen(buf); + + switch (dtag->dtype) { + case DCM_TSTR: + *p++ = ' '; + vval = tvb_get_ptr(tvb, offset, len); + strncpy(p, vval, len); + p += len; + *p = 0; + break; + case DCM_TINT2: + if (DCM_ILE & syntax) + val16 = tvb_get_letohs(tvb, offset); + else val16 = tvb_get_ntohs(tvb, offset); + sprintf(p, " 0x%x (%d)", val16, val16); + break; + case DCM_TINT4: + if (DCM_ILE & syntax) + val32 = tvb_get_letohl(tvb, offset); + else val32 = tvb_get_ntohl(tvb, offset); + sprintf(p, " 0x%x (%d)", val32, val32); + break; + case DCM_TFLT: { + gfloat valf; + if (DCM_ILE & syntax) + valf = tvb_get_letohieee_float(tvb, offset); + else valf = tvb_get_ntohieee_float(tvb, offset); + sprintf(p, " (%f)", valf); + } break; + case DCM_TDBL: { + gdouble vald; + if (DCM_ILE & syntax) + vald = tvb_get_letohieee_double(tvb, offset); + else vald = tvb_get_ntohieee_double(tvb, offset); + sprintf(p, " (%f)", vald); + } break; + case DCM_TSTAT: /* call dcm_rsp2str() on TINT2 */ + if (DCM_ILE & syntax) + val16 = tvb_get_letohs(tvb, offset); + else val16 = tvb_get_ntohs(tvb, offset); + sprintf(p, " 0x%x '%s'", val16, dcm_rsp2str(val16)); + break; + case DCM_TCMD: /* call dcm_cmd2str() on TINT2 */ + if (DCM_ILE & syntax) + val16 = tvb_get_letohs(tvb, offset); + else val16 = tvb_get_ntohs(tvb, offset); + sprintf(p, " 0x%x '%s'", val16, dcm_cmd2str(val16)); + break; + case DCM_TRET: /* Retired */ + break; + default: { /* try ascii */ + unsigned int i; + + vval = tvb_get_ptr(tvb, offset, len); + i = 0; + *p++ = ' '; + while (i < len && i < 512 && isprint(*(vval+i))) + *p++ = *(vval + i++); + *p = 0; + } break; + } + return buf; +} + +static guint +dcm_get_pdu_len(tvbuff_t *tvb, int offset) +{ + long len; +#if 0 + guint8 pdu_type; +#endif + + len = tvb_get_ntohl(tvb, 2 + offset); +#if 0 + guint8 pdu_type; + pdu_type = tvb_get_guint8(tvb, offset); + if (4 == pdu_type) { + guint8 frag; + /* this is a total swamp. We only know how big this + fragment is and that more are coming. We have to + parse the entire packet to find a clue how much more is + coming. this tries to guess .... */ + frag = tvb_get_guint8(tvb, 11 + offset); + if (0 == (0x2 & frag)) + /* len += tvb_ensure_length_remaining(tvb, offset) - 6; */ + /* there are more fragments */ + len++; + } +#endif + return len + 6; /* add in fixed header part */ +} + +void +dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offset) +{ + proto_tree *dcm_tree; + dcmItem_t *di = NULL; + + dcm_tree = proto_item_add_subtree(ti, ett_assoc); + while (-1 < offset && offset < (int) dcm_data->clen) { + guint8 id, *name, result; + short len; + long mlen; + id = tvb_get_guint8(tvb, offset); + len = tvb_get_ntohs(tvb, 2 + offset); + proto_tree_add_uint_format(dcm_tree, hf_dcm_pdi, tvb, + offset, 4+len, id, "Item 0x%x (%s)", id, dcm_pdu2str(id)); + offset += 4; + switch (id) { + case 0x10: /* App context */ + name = g_malloc(1 + len); + tvb_memcpy(tvb, name, offset, len); + *(name + len) = 0; + proto_tree_add_string(dcm_tree, hf_dcm_pdi_name, tvb, offset, len, name); + g_free(name); + offset += len; + break; + case 0x30: /* Abstract syntax */ + dcm_data->last->abs = name = g_malloc(1 + len); + tvb_memcpy(tvb, name, offset, len); + *(name + len) = 0; + proto_tree_add_string(dcm_tree, hf_dcm_pdi_syntax, tvb, offset, len, name); + offset += len; + break; + case 0x40: /* Transfer syntax */ + dcm_data->last->xfer = name = g_malloc(1 + len); + tvb_memcpy(tvb, name, offset, len); + *(name + len) = 0; + proto_tree_add_string(dcm_tree, hf_dcm_pdi_syntax, tvb, offset, len, name); + dcm_setSyntax(di, name); + offset += len; + break; + case 0x20: /* Presentation context */ + id = tvb_get_guint8(tvb, offset); + di = lookupCtx(dcm_data, id); + if (DCM_UNK == di->syntax) { + di = g_chunk_new(dcmItem_t, dcm_pdus); + di->id = id; + di->next = di->prev = NULL; + if (dcm_data->last) { + dcm_data->last->next = di; + di->prev = dcm_data->last; + dcm_data->last = di; + } else + dcm_data->first = dcm_data->last = di; + } + proto_tree_add_item(dcm_tree, hf_dcm_pctxt, tvb, offset, 1, FALSE); + offset += 4; + break; + case 0x21: /* Presentation context reply */ + id = tvb_get_guint8(tvb, offset); + result = tvb_get_guint8(tvb, 2 + offset); + proto_tree_add_item(dcm_tree, hf_dcm_pctxt, tvb, offset, 1, FALSE); + proto_tree_add_uint_format(dcm_tree, hf_dcm_pcres, tvb, + 2 + offset, 1, + result, "Result 0x%x (%s)", result, dcm_PCresult2str(result)); + offset += len; + break; + case 0x50: /* User Info */ + break; + case 0x51: /* Max length */ + mlen = tvb_get_ntohl(tvb, offset); + proto_tree_add_item(dcm_tree, hf_dcm_pdu_maxlen, tvb, offset, 4, FALSE); + offset += len; + break; + case 0x52: /* UID? */ + name = g_malloc(1 + len); + tvb_memcpy(tvb, name, offset, len); + *(name + len) = 0; + proto_tree_add_string(dcm_tree, hf_dcm_impl, tvb, offset, len, name); + g_free(name); + offset += len; + break; + case 0x55: /* version? */ + name = g_malloc(1 + len); + tvb_memcpy(tvb, name, offset, len); + *(name + len) = 0; + proto_tree_add_string(dcm_tree, hf_dcm_vers, tvb, offset, len, name); + g_free(name); + offset += len; + break; + default: + offset += len; + break; + } + } +} + +dcmItem_t * +lookupCtx(dcmState_t *dd, guint8 ctx) +{ + dcmItem_t *di = dd->first; + static dcmItem_t dunk = { NULL, NULL, -1, + "not found - click on ASSOC Request", + "not found - click on ASSOC Request", DCM_UNK }; + while (di) { + if (ctx == di->id) + break; + di = di->next; + } + return di ? di : &dunk; +} + +/* +04 P-DATA-TF + 1 1 reserved + 2 4 length + - (1+) presentation data value (PDV) items + 6 4 length +10 1 Presentation Context ID (odd ints 1 - 255) + - PDV +11 1 header + 0x01 if set, contains Message Command info, else Message Data + 0x02 if set, contains last fragment + */ +#define D_HEADER 1 +#define D_TAG 2 +#define D_VR 3 +#define D_LEN2 4 +#define D_LEN4 5 +#define D_VALUE 6 + +void +dissect_dcm_data(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb) +{ + int len, offset, toffset, state; + proto_tree *dcm_tree; + dcmItem_t *di; + guint8 ctx, syntax = DCM_UNK; + guint16 grp = 0, elm = 0; + guint32 tlen = 0, nlen; + + dcm_tree = proto_item_add_subtree(ti, ett_dcm_data); + proto_tree_add_item(dcm_tree, hf_dcm_data_len, tvb, 6, 4, FALSE); + ctx = tvb_get_guint8(tvb, 10); + di = lookupCtx(dcm_data, ctx); + /* proto_tree_add_item(dcm_tree, hf_dcm_data_ctx, tvb, 10, 1, FALSE); */ + proto_tree_add_uint_format(dcm_tree, hf_dcm_data_ctx, tvb, 10, 1, + ctx, "Context 0x%x (%s)", ctx, di->xfer); + len = offset = toffset = 11; + state = D_HEADER; + nlen = 1; + while (len + nlen < dcm_data->clen) { + switch (state) { + case D_HEADER: { + guint8 flags; + flags = tvb_get_guint8(tvb, offset); + proto_tree_add_uint_format(dcm_tree, hf_dcm_data_flags, tvb, offset, 1, + flags, "Flags 0x%x (%s)", flags, dcm_flags2str(flags)); + /* proto_tree_add_item(dcm_tree, hf_dcm_data_flags, tvb, offset, 1, FALSE); */ + len++; + offset++; + if (0x1 & flags) + syntax = DCM_ILE; + else if (DCM_UNK == di->syntax) { + const guint8 *val; + tlen = dcm_data->clen - len; + val = tvb_get_ptr(tvb, offset, tlen+8); + proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb, + offset, tlen, val, "(%04x,%04x) %-8x Unparsed data", 0, 0, tlen); + len = dcm_data->clen; /* ends parsing */ + } else + syntax = di->syntax; + state = D_TAG; + nlen = 4; + } break; /* don't fall through -- check length */ + case D_TAG: { + if (DCM_ILE & syntax) { + grp = tvb_get_letohs(tvb, offset); + elm = tvb_get_letohs(tvb, offset+2); + state = (DCM_EBE & syntax) ? D_VR : D_LEN4; /* is Explicit */ + nlen = (DCM_EBE & syntax) ? 2 : 4; /* is Explicit */ + } else { + grp = tvb_get_ntohs(tvb, offset); + elm = tvb_get_ntohs(tvb, offset+2); + state = D_VR; + nlen = 2; + } + toffset = offset; + if (0xfffe == grp) state = D_LEN4; + offset += 4; + len += 4; + } break; /* don't fall through -- check length */ + case D_VR: { + guint8 V, R; + V = tvb_get_guint8(tvb, offset); offset++; + R = tvb_get_guint8(tvb, offset); offset++; + len += 2; + /* 4byte lenghts OB, OW, OF, SQ, UN, UT */ + state = D_LEN2; + nlen = 2; + if ((('O' == V) && ('B' == R || 'W' == R || 'F' == R)) + || (('U' == V) && ('N' == R || 'T' == R)) + || ('S' == V && 'Q' == R)) { + state = D_LEN4; + offset += 2; /* skip 00 (2 bytes) */ + len += 2; + nlen = 4; + } + } break; /* don't fall through -- check length */ + case D_LEN2: { + if (DCM_ILE & syntax) /* is it LE */ + tlen = tvb_get_letohs(tvb, offset); + else + tlen = tvb_get_ntohs(tvb, offset); + offset += 2; + len += 2; + state = D_VALUE; + nlen = tlen; + } break; + case D_LEN4: { + if (DCM_ILE & syntax) /* is it LE */ + tlen = tvb_get_letohl(tvb, offset); + else + tlen = tvb_get_ntohl(tvb, offset); + offset += 4; + len += 4; + state = D_VALUE; + nlen = tlen; + } break; /* don't fall through -- check length */ + case D_VALUE: { + const guint8 *val; + int totlen = (offset - toffset); + if (0xffffffff == tlen || 0xfffe == grp) { + val = tvb_get_ptr(tvb, toffset, totlen); + proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb, + toffset, totlen, val, + "(%04x,%04x) %-8x %s", grp, elm, tlen, + dcm_tag2str(grp, elm, syntax, tvb, offset, 0)); + tlen = 0; + /* } else if (0xfffe == grp) { */ /* need to make a sub-tree here */ + } else { + totlen += tlen; + val = tvb_get_ptr(tvb, toffset, totlen); + proto_tree_add_bytes_format(dcm_tree, hf_dcm_data_tag, tvb, + toffset, totlen, val, + "(%04x,%04x) %-8x %s", grp, elm, tlen, + dcm_tag2str(grp, elm, syntax, tvb, offset, tlen)); + } + len += tlen; + offset += tlen; + state = D_TAG; + nlen = 4; + } break; + } + } +} + +/* + Originator src:srcport dest:destport + Acceptor src:srcport dest:destport + + conn = lookup(src:srcport, dest:destport) + if (!conn) + look at data payload of packet + if no-data return false; + if 01 == *p && *p+10 ... *p+42 <= [ 0x20 .. printable ] + create conn + */ + +static void dissect_dcm_pdu(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree); + +/* Code to actually dissect the packets */ +static gboolean +dissect_dcm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + conversation_t *conv; + guint8 pdu; + guint16 vers; + guint32 len, tlen; + dcmState_t *dcm_data; + + conv = find_conversation(&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, pinfo->destport, 0); + + if (NULL == conv) { + /* No conversation found. + * only look for the first packet of a DICOM conversation. + * if we don't get the first packet, we cannot decode the rest + * of the session. + */ + if (10 > (tlen = tvb_reported_length(tvb))) + return FALSE; /* not long enough */ + if (1 != (pdu = tvb_get_guint8(tvb, 0))) + return FALSE; /* look for the start */ + if (1 != (vers = tvb_get_ntohs(tvb, 6))) + return FALSE; /* not version 1 */ + len = 6 + tvb_get_ntohl(tvb, 2); + if (len < tlen) + return FALSE; /* packet is > decl len */ + + conv = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + if (NULL == (dcm_data = mkds())) + return FALSE; /* internal error */ + conversation_add_proto_data(conv, proto_dcm, dcm_data); + } else { + /* + * conversation exists + */ + dcm_data = conversation_get_proto_data(conv, proto_dcm); + if (NULL == dcm_data) { + /* + * This is not a DICOM conversation + */ + return FALSE; + } + } + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_clear(pinfo->cinfo, COL_PROTOCOL); + + tcp_dissect_pdus(tvb, pinfo, tree, 1, 6, dcm_get_pdu_len, dissect_dcm_pdu); + + return TRUE; +} + +static void +dissect_dcm_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti; + dcmState_t *dcm_data; + proto_tree *dcm_tree; + conversation_t *conv; + char *buf; + int offset = 0; + + if (NULL == (conv = find_conversation(&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, pinfo->destport, 0))) + return; /* OOPS */ + + dcm_data = conversation_get_proto_data(conv, proto_dcm); + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCM"); + +/* This field shows up as the "Info" column in the display; you should make + it, if possible, summarize what's in the packet, so that a user looking + at the list of packets can tell what type of packet it is. See section 1.5 + for more information. + */ + + if (check_col(pinfo->cinfo, COL_INFO)) + col_clear(pinfo->cinfo, COL_INFO); + + dcm_data->pdu = tvb_get_guint8(tvb, 0); + dcm_data->tlen = tvb_get_ntohl(tvb, 2) + 6; + dcm_data->clen = tvb_reported_length(tvb); + /* if (dcm_data->clen < dcm_data->tlen) dcm_data->partial = 1; */ + + switch (dcm_data->pdu) { + case 1: /* ASSOC Request */ + tvb_memcpy(tvb, dcm_data->orig, 10, 16); + tvb_memcpy(tvb, dcm_data->targ, 26, 16); + dcm_data->orig[AEEND] = dcm_data->targ[AEEND] = 0; + buf = g_malloc(128); + snprintf(buf, 128, "DCM ASSOC Request %s <-- %s", + dcm_data->orig, dcm_data->targ); + offset = 74; + break; + case 2: /* ASSOC Accept */ + tvb_memcpy(tvb, dcm_data->resp, 26, 16); + buf = g_malloc(128); + snprintf(buf, 128, "DCM ASSOC Accept %s <-- %s (%s)", + dcm_data->orig, dcm_data->targ, dcm_data->resp); + offset = 74; + break; + case 3: /* ASSOC Reject */ + dcm_data->result = tvb_get_guint8(tvb, 7); + dcm_data->source = tvb_get_guint8(tvb, 8); + dcm_data->reason = tvb_get_guint8(tvb, 9); + buf = g_malloc(128); + snprintf(buf, 128, "DCM ASSOC Reject %s <-- %s %s %s %s", + dcm_data->orig, dcm_data->targ, + dcm_result2str(dcm_data->result), + dcm_source2str(dcm_data->source), + dcm_reason2str(dcm_data->source, dcm_data->reason)); + offset = 10; + break; + case 4: /* DATA */ + offset = 6; + buf = g_malloc(128); + strcpy(buf, "DCM Data"); + break; + case 5: /* RELEASE Request */ + buf = g_malloc(128); + strcpy(buf, "DCM RELEASE Request"); + offset = 6; + break; + case 6: /* RELEASE Response */ + buf = g_malloc(128); + strcpy(buf, "DCM RELEASE Response"); + offset = 6; + break; + case 7: /* ABORT */ + dcm_data->source = tvb_get_guint8(tvb, 8); + dcm_data->reason = tvb_get_guint8(tvb, 9); + buf = g_malloc(128); + snprintf(buf, 128, "DCM ABORT %s <-- %s %s %s", + dcm_data->orig, dcm_data->targ, + (dcm_data->source == 1) ? "USER" : + (dcm_data->source == 2) ? "PROVIDER" : "", + dcm_data->source == 1 ? dcm_abort2str(dcm_data->reason) : ""); + break; + default: + buf = g_malloc(128); + strcpy(buf, "DCM Continuation"); + offset = -1; /* cannot continue parsing */ + break; + } + if (check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, buf); + +/* In the interest of speed, if "tree" is NULL, don't do any work not + necessary to generate protocol tree items. */ + if (tree) { + proto_item *tf; + ti = proto_tree_add_item(tree, proto_dcm, tvb, 0, -1, FALSE); + dcm_tree = proto_item_add_subtree(ti, ett_dcm); + proto_tree_add_uint_format(dcm_tree, hf_dcm_pdu, tvb, 0, dcm_data->tlen, + dcm_data->pdu, "PDU 0x%x (%s)", dcm_data->pdu, + dcm_pdu2str(dcm_data->pdu)); + proto_tree_add_item(dcm_tree, hf_dcm_pdu_len, tvb, 2, 4, FALSE); + + switch (dcm_data->pdu) { + case 1: /* ASSOC Request */ + case 2: /* ASSOC Accept */ + case 3: /* ASSOC Reject */ + case 5: /* RELEASE Request */ + case 6: /* RELEASE Response */ + case 7: /* ABORT */ + tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, 0, dcm_data->tlen, buf); + dissect_dcm_assoc(dcm_data, tf, tvb, offset); + break; + case 4: /* DATA */ + tf = proto_tree_add_string(dcm_tree, hf_dcm_pdu_type, tvb, 0, dcm_data->tlen, buf); + dissect_dcm_data(dcm_data, tf, tvb); + break; + default: + break; + } + +/* Continue adding tree items to process the packet here */ + } + +/* If this protocol has a sub-dissector call it here, see section 1.8 */ +} + + +/* Register the protocol with Ethereal */ + +/* this format is require because a script is used to build the C function + that calls all the protocol registration. +*/ + +void +proto_register_dcm(void) +{ +/* Setup list of header fields See Section 1.6.1 for details*/ +static hf_register_info hf[] = { + { &hf_dcm_pdu, { "PDU", "dcm.pdu", + FT_UINT8, BASE_HEX, VALS(dcm_pdu_ids), 0, "", HFILL } }, + { &hf_dcm_pdu_len, { "PDU LENGTH", "dcm.pdu_len", + FT_UINT32, BASE_HEX, NULL, 0, "", HFILL } }, + { &hf_dcm_pdu_type, { "PDU Detail", "dcm.pdu_detail", + FT_STRING, BASE_NONE, NULL, 0, "", HFILL } }, + { &hf_dcm_pdi, { "Item", "dcm.pdu.pdi", + FT_UINT8, BASE_HEX, VALS(dcm_pdi_ids), 0, "", HFILL } }, + { &hf_dcm_pdi_name, { "Application Context", "dcm.pdi.name", + FT_STRING, BASE_NONE, NULL, 0, "", HFILL } }, + { &hf_dcm_pdi_syntax, { "Abstract Syntax", "dcm.pdi.syntax", + FT_STRING, BASE_NONE, NULL, 0, "", HFILL } }, + { &hf_dcm_pctxt, { "Presentation Context", "dcm.pdi.ctxt", + FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } }, + { &hf_dcm_pcres, { "Presentation Context result", "dcm.pdi.result", + FT_UINT8, BASE_HEX, VALS(dcm_pdi_ids), 0, "", HFILL } }, + { &hf_dcm_pdu_maxlen, { "MAX PDU LENGTH", "dcm.max_pdu_len", + FT_UINT32, BASE_DEC, NULL, 0, "", HFILL } }, + { &hf_dcm_impl, { "Implementation", "dcm.pdi.impl", + FT_STRING, BASE_NONE, NULL, 0, "", HFILL } }, + { &hf_dcm_vers, { "Version", "dcm.pdi.version", + FT_STRING, BASE_NONE, NULL, 0, "", HFILL } }, + { &hf_dcm_data_len, { "DATA LENGTH", "dcm.data.len", + FT_UINT32, BASE_HEX, NULL, 0, "", HFILL } }, + { &hf_dcm_data_ctx, { "Data Context", "dcm.data.ctx", + FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } }, + { &hf_dcm_data_flags, { "Flags", "dcm.data.flags", + FT_UINT8, BASE_HEX, NULL, 0, "", HFILL } }, + { &hf_dcm_data_tag, { "Tag", "dcm.data.tag", + FT_BYTES, BASE_HEX, NULL, 0, "", HFILL } }, +/* + { &hf_dcm_FIELDABBREV, { "FIELDNAME", "dcm.FIELDABBREV", + FIELDTYPE, FIELDBASE, FIELDCONVERT, BITMASK, "FIELDDESCR", HFILL } }, + */ + }; + +/* Setup protocol subtree array */ + static gint *ett[] = { + &ett_dcm, + &ett_assoc, + &ett_dcm_data + }; +/* Register the protocol name and description */ + proto_dcm = proto_register_protocol("DICOM", "dicom", "dcm"); + +/* Required function calls to register the header fields and subtrees used */ + proto_register_field_array(proto_dcm, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + register_init_routine(&dcm_init); +} + + +/* If this dissector uses sub-dissector registration add a registration routine. + This format is required because a script is used to find these routines and + create the code that calls these routines. +*/ +void +proto_reg_handoff_dcm(void) +{ + heur_dissector_add("tcp", dissect_dcm, proto_dcm); +} + +/* + +PDU's +01 ASSOC-RQ + 1 1 reserved + 2 4 length + 6 2 protocol version (0x0 0x1) + 8 2 reserved +10 16 dest aetitle +26 16 src aetitle +42 32 reserved +74 - presentation data value items + +02 A-ASSOC-AC + 1 reserved + 4 length + 2 protocol version (0x0 0x1) + 2 reserved + 16 dest aetitle (not checked) + 16 src aetitle (not checked) + 32 reserved + - presentation data value items + +03 ASSOC-RJ + 1 reserved + 4 length (4) + 1 reserved + 1 result (1 reject perm, 2 reject transient) + 1 source (1 service user, 2 service provider, 3 service profider) + 1 reason + 1 == source + 1 no reason given + 2 application context name not supported + 3 calling aetitle not recognized + 7 called aetitle not recognized + 2 == source + 1 no reason given + 2 protocol version not supported + 3 == source + 1 temporary congestion + 2 local limit exceeded + +04 P-DATA-TF + 1 1 reserved + 2 4 length + - (1+) presentation data value (PDV) items + 6 4 length +10 1 Presentation Context ID (odd ints 1 - 255) + - PDV +11 1 header + 0x01 if set, contains Message Command info, else Message Data + 0x02 if set, contains last fragment + +05 A-RELEASE-RQ + 1 reserved + 4 length (4) + 4 reserved + +06 A-RELEASE-RP + 1 reserved + 4 length (4) + 4 reserved + +07 A-ABORT + 1 reserved + 4 length (4) + 2 reserved + 1 source (0 = user, 1 = provider) + 1 reason if 1 == source (0 not spec, 1 unrecognized, 2 unexpected 4 unrecognized param, 5 unexpected param, 6 invalid param) + + + +ITEM's +10 Application Context + 1 reserved + 2 length + - name + +20 Presentation Context + 1 reserved + 2 length + 1 Presentation context id + 3 reserved + - (1) abstract and (1+) transfer syntax sub-items + +21 Presentation Context (Reply) + 1 reserved + 2 length + 1 ID (odd int's 1-255) + 1 reserved + 1 result (0 accept, 1 user-reject, 2 no-reason, 3 abstract not supported, 4- transfer syntax not supported) + 1 reserved + - (1) type 40 + +30 Abstract syntax + 1 reserved + 2 length + - name (<= 64) + +40 Transfer syntax + 1 reserved + 2 length + - name (<= 64) + +50 user information + 1 reserved + 2 length + - user data + +51 max length + 1 reserved + 2 length (4) + 4 max PDU lengths + */ |