diff options
-rw-r--r-- | epan/dissectors/packet-dcm.c | 152 |
1 files changed, 92 insertions, 60 deletions
diff --git a/epan/dissectors/packet-dcm.c b/epan/dissectors/packet-dcm.c index 58a6ce5f9c..32fca4c9bf 100644 --- a/epan/dissectors/packet-dcm.c +++ b/epan/dissectors/packet-dcm.c @@ -9,6 +9,10 @@ * (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. + * Also, you might have to turn off tcp.check_checksum if tcp + * detects that the checksum is bad - for example, if you're + * capturing on a network interface that does TCP checksum + * offloading and you're capturing outgoing packets. * This should probably be documented somewhere besides here.) * * $Id$ @@ -57,6 +61,9 @@ * * - The 'value to string' routines should probably be hash lookups. * + * 9 Nov 2004 + * - Fixed the heuristic code -- sometimes a conversation already exists + * - Fixed the dissect code to display all the tags in the pdu */ #include <stdio.h> @@ -127,6 +134,7 @@ struct dcmContext { }; struct dcmItem { struct dcmItem *next, *prev; + int valid; guint8 id; /* 0x20 Presentation Context */ guint8 *abs; /* 0x30 Abstract syntax */ guint8 *xfer; /* 0x40 Transfer syntax */ @@ -144,6 +152,7 @@ struct dcmState { guint32 tlen, clen, rlen; /* length: total, current, remaining */ int partial; /* is a partial packet */ int coff; /* current offset */ + int valid; /* this conversation is a dicom conversation */ /* enum { DCM_NONE, DCM_ASSOC, DCM_ }; */ #define AEEND 16 guint8 orig[1+AEEND], targ[1+AEEND], resp[1+AEEND], source, result, reason; @@ -270,6 +279,7 @@ mkds(void) ds->pdu = 0; ds->tlen = ds->rlen = 0; ds->partial = 0; + ds->valid = TRUE; memset(ds->orig, 0, sizeof(ds->orig)); memset(ds->targ, 0, sizeof(ds->targ)); memset(ds->resp, 0, sizeof(ds->resp)); @@ -442,7 +452,7 @@ dcm_rsp2str(guint16 us) case 0xff00: s = "Pending: operations are continuing"; break; default: break; } - if (0xC000 == (0xC000 & us)) s = "Failed: Unable to Process"; + if (0xC000 == (0xF000 & us)) s = "Failed: Unable to Process"; return s; } @@ -451,6 +461,7 @@ dcm_setSyntax(dcmItem_t *di, char *name) { if (NULL == di) return; di->syntax = 0; + di->xfer = name; if (0 == *name) return; /* this would be faster to skip the common parts, and have a FSA to * find the syntax. @@ -476,10 +487,12 @@ dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, { static char buf[512+1]; /* bad form ??? */ const guint8 *vval; + size_t vval_len; char *p; guint32 tag, val32; guint16 val16; dcmTag_t *dtag; + size_t pl; static dcmTag_t utag = { 0, 0, "(unknown)" }; *buf = 0; @@ -495,13 +508,17 @@ dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, dtag = &utag; strcpy(buf, dtag->desc); + g_assert(sizeof(buf) >= strlen(buf)); + pl = sizeof(buf) - strlen(buf); p = buf + strlen(buf); switch (dtag->dtype) { case DCM_TSTR: + default: /* try ascii */ *p++ = ' '; - vval = tvb_get_ptr(tvb, offset, len); - strncpy(p, vval, len); + vval = tvb_format_text(tvb, offset, len); + vval_len = strlen(vval); + strncpy(p, vval, vval_len > pl ? pl : vval_len); p += len; *p = 0; break; @@ -545,16 +562,6 @@ dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, 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; } @@ -562,26 +569,42 @@ dcm_tag2str(guint16 grp, guint16 elm, guint8 syntax, tvbuff_t *tvb, int offset, static guint dcm_get_pdu_len(tvbuff_t *tvb, int offset) { - long len; + guint32 len; #if 0 guint8 pdu_type; + guint len_rem; #endif len = tvb_get_ntohl(tvb, 2 + offset); #if 0 - guint8 pdu_type; pdu_type = tvb_get_guint8(tvb, offset); + len_rem = tvb_ensure_length_remaining(tvb, offset); + /* + * XXX - a get_pdu_len() routine *CANNOT* scan forward arbitrarily + * long into a packet, as it might run past the end of the tvbuff + * and cause an exception. It *must* be able to compute the PDU + * length from the first N bytes of the PDU, for some value of N. + * + * If there's fragmentation in DICOM, the TCP reassembly should be + * done at the *fragment* level - i.e., do reassembly of a *fragment* + * at the TCP layer - and reassembly of the fragments should be done + * in the DICOM dissector. + */ 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++; + /* old swamp */ + frag = tvb_get_guint8(tvb, 11 + offset); + while (0 == (0x2 & frag)) { /* there are more fragments */ + guint32 nlen; + if (len_rem < len + 6 + 12) { + len += 12; /* fixed part of next pdu */ + break; + } + len += 6; + nlen = tvb_get_ntohl(tvb, 2 + offset + len); + frag = tvb_get_guint8(tvb, 11 + offset + len); + len += nlen; + } } #endif return len + 6; /* add in fixed header part */ @@ -592,12 +615,13 @@ dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offse { proto_tree *dcm_tree; dcmItem_t *di = NULL; + guint8 id, *name, result; + int reply = 0; 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; + guint16 len; + guint32 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, @@ -620,19 +644,21 @@ dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offse offset += len; break; case 0x40: /* Transfer syntax */ - dcm_data->last->xfer = name = g_malloc(1 + len); + 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); + if (reply && di && di->valid) dcm_setSyntax(di, name); + reply = 0; offset += len; break; case 0x20: /* Presentation context */ id = tvb_get_guint8(tvb, offset); di = lookupCtx(dcm_data, id); - if (DCM_UNK == di->syntax) { + if (!di->valid) { di = g_chunk_new(dcmItem_t, dcm_pdus); di->id = id; + di->valid = 1; di->next = di->prev = NULL; if (dcm_data->last) { dcm_data->last->next = di; @@ -649,9 +675,14 @@ dissect_dcm_assoc(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb, int offse 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; + 2 + offset, 1, result, + "Result 0x%x (%s)", result, dcm_PCresult2str(result)); + if (0 == result) { + reply = 1; + di = lookupCtx(dcm_data, id); + offset += 4; + } else + offset += len; break; case 0x50: /* User Info */ break; @@ -687,7 +718,7 @@ dcmItem_t * lookupCtx(dcmState_t *dd, guint8 ctx) { dcmItem_t *di = dd->first; - static dcmItem_t dunk = { NULL, NULL, -1, + static dcmItem_t dunk = { NULL, NULL, 0, -1, "not found - click on ASSOC Request", "not found - click on ASSOC Request", DCM_UNK }; while (di) { @@ -737,7 +768,7 @@ dissect_dcm_data(dcmState_t *dcm_data, proto_item *ti, tvbuff_t *tvb) len = offset = toffset = 11; state = D_HEADER; nlen = 1; - while (len + nlen < dcm_data->clen) { + while (len + nlen <= dcm_data->clen) { switch (state) { case D_HEADER: { guint8 flags; @@ -865,45 +896,42 @@ dissect_dcm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) guint8 pdu; guint16 vers; guint32 len, tlen; - dcmState_t *dcm_data; + dcmState_t *dcm_data = NULL; conv = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0); - if (NULL == conv) { + if (NULL != conv) /* conversation exists */ + /* do we have any data for this conversation ? */ + dcm_data = conversation_get_proto_data(conv, proto_dcm); + else + conv = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype, + pinfo->srcport, pinfo->destport, 0); + + if (NULL == dcm_data) { /* 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 */ + if (10 > (tlen = tvb_reported_length(tvb)) /* not long enough */ + || 1 != (pdu = tvb_get_guint8(tvb, 0)) /* look for the start */ + || 1 != (vers = tvb_get_ntohs(tvb, 6))) /* not version 1 */ + dcm_data->valid = FALSE; + else { + len = 6 + tvb_get_ntohl(tvb, 2); + if (len < tlen) + dcm_data->valid = FALSE; /* packet is > decl len */ + } + 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 (FALSE == dcm_data->valid) + return FALSE; + if (check_col(pinfo->cinfo, COL_PROTOCOL)) col_clear(pinfo->cinfo, COL_PROTOCOL); @@ -1115,7 +1143,11 @@ static hf_register_info hf[] = { void proto_reg_handoff_dcm(void) { + dissector_handle_t dcm_handle; + heur_dissector_add("tcp", dissect_dcm, proto_dcm); + dcm_handle = new_create_dissector_handle(dissect_dcm, proto_dcm); + dissector_add("tcp.port", 104, dcm_handle); } /* |