2 * Routines for Financial Information eXchange (FIX) Protocol dissection
3 * Copyright 2000, PC Drew <drewpc@ibsncentral.com>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * Documentation: http://www.fixprotocol.org/
26 * Fields and messages from http://www.quickfixengine.org/ and http://sourceforge.net/projects/quickfix/files/ xml
37 #include <epan/packet.h>
38 #include <epan/expert.h>
39 #include <epan/prefs.h>
40 #include <epan/conversation.h>
42 #include "packet-tcp.h"
43 #include "packet-ssl.h"
45 typedef struct _fix_parameter
{
53 /* Initialize the protocol and registered fields */
54 static int proto_fix
= -1;
56 /* desegmentation of fix */
57 static gboolean fix_desegment
= TRUE
;
59 /* Initialize the subtree pointers */
60 static gint ett_fix
= -1;
61 static gint ett_unknow
= -1;
62 static gint ett_badfield
= -1;
63 static gint ett_checksum
= -1;
65 static expert_field ei_fix_checksum_bad
= EI_INIT
;
67 static int hf_fix_data
= -1; /* continuation data */
68 static int hf_fix_checksum_good
= -1;
69 static int hf_fix_checksum_bad
= -1;
70 static int hf_fix_field_value
= -1;
71 static int hf_fix_field_tag
= -1;
73 static dissector_handle_t fix_handle
;
75 static range_t
*global_fix_tcp_range
= NULL
;
76 static range_t
*fix_tcp_range
= NULL
;
79 #define MARKER_TAG "8=FIX"
82 static int fix_marker(tvbuff_t
*tvb
, int offset
)
84 return tvb_strneql(tvb
, offset
, MARKER_TAG
, MARKER_LEN
);
88 * Fields and messages generated from http://www.quickfixengine.org/ xml (slightly modified)
91 #include "packet-fix.h"
93 static void dissect_fix_init(void) {
94 /* TODO load xml def for private field */
95 /* TODO check that fix_fields is really sorted */
101 int lower
= 0, upper
= array_length(fix_fields
) -1;
102 while (lower
<= upper
) {
103 int middle
= (lower
+ upper
) / 2;
104 int res
= fix_fields
[middle
].tag
;
107 } else if (res
== key
) {
116 /* Code to actually dissect the packets */
117 static int fix_next_header(tvbuff_t
*tvb
, int offset
)
119 /* try to resync to the next start */
120 guint min_len
= tvb_length_remaining(tvb
, offset
);
121 const guint8
*data
= tvb_get_string(wmem_packet_scope(), tvb
, offset
, min_len
);
122 const guint8
*start
= data
;
124 while ((start
= strstr(start
, "\0018"))) {
125 min_len
= (guint
) (start
+1 -data
);
126 /* if remaining length < 6 return and let the next desegment round
129 if (tvb_length_remaining(tvb
, min_len
+ offset
) < MARKER_LEN
)
131 if (!fix_marker(tvb
, min_len
+offset
) )
138 /* ----------------------------------------------
139 Format: name=value\001
141 static fix_parameter
*fix_param(tvbuff_t
*tvb
, int offset
)
143 static fix_parameter ret
;
146 ret
.ctrla_offset
= tvb_find_guint8(tvb
, offset
, -1, 0x01);
147 if (ret
.ctrla_offset
== -1) {
151 ret
.field_len
= ret
.ctrla_offset
- offset
+ 1;
152 equals
= tvb_find_guint8(tvb
, offset
, ret
.field_len
, '=');
157 ret
.value_offset
= equals
+ 1;
158 ret
.tag_len
= ret
.value_offset
- offset
- 1;
159 ret
.value_len
= ret
.ctrla_offset
- ret
.value_offset
;
163 /* ---------------------------------------------- */
164 static int fix_header_len(tvbuff_t
*tvb
, int offset
)
166 int base_offset
, ctrla_offset
;
171 base_offset
= offset
;
173 /* get at least the fix version: 8=FIX.x.x */
174 if (fix_marker(tvb
, offset
) != 0) {
175 return fix_next_header(tvb
, offset
);
179 ctrla_offset
= tvb_find_guint8(tvb
, offset
, -1, 0x01);
180 if (ctrla_offset
== -1) {
181 /* it should be there, (minimum size is big enough)
182 * if not maybe it's not really
183 * a FIX packet but it's too late to bail out.
185 return fix_next_header(tvb
, offset
+MARKER_LEN
) +MARKER_LEN
;
187 offset
= ctrla_offset
+ 1;
190 if (!(tag
= fix_param(tvb
, offset
)) || tvb_strneql(tvb
, offset
, "9=", 2)) {
191 /* not a tag or not the BodyLength tag, give up */
192 return fix_next_header(tvb
, offset
);
195 value
= tvb_get_string(wmem_packet_scope(), tvb
, tag
->value_offset
, tag
->value_len
);
196 /* Fix version, msg type, length and checksum aren't in body length.
197 * If the packet is big enough find the checksum
199 size
= atoi(value
) +tag
->ctrla_offset
- base_offset
+1;
200 if (tvb_length_remaining(tvb
, base_offset
) > size
+4) {
201 /* 10= should be there */
202 offset
= base_offset
+size
;
203 if (tvb_strneql(tvb
, offset
, "10=", 3) != 0) {
204 /* No? bogus packet, try to find the next header */
205 return fix_next_header(tvb
, base_offset
+MARKER_LEN
) +MARKER_LEN
;
207 ctrla_offset
= tvb_find_guint8(tvb
, offset
, -1, 0x01);
208 if (ctrla_offset
== -1) {
209 /* assume checksum is 7 bytes 10=xxx\01 */
212 return size
+ctrla_offset
-offset
+1;
216 /* assume checksum is 7 bytes 10=xxx\01 */
220 /* ---------------------------------------------- */
222 dissect_fix_packet(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
224 /* Set up structures needed to add the protocol subtree and manage it */
226 proto_tree
*fix_tree
;
229 int field_offset
, ctrla_offset
;
234 const char *msg_type
;
236 /* Make entries in Protocol column and Info column on summary display */
237 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "FIX");
238 col_clear(pinfo
->cinfo
, COL_INFO
);
240 /* get at least the fix version: 8=FIX.x.x */
241 if (fix_marker(tvb
, 0) != 0) {
242 /* not a fix packet start but it's a fix packet */
243 col_set_str(pinfo
->cinfo
, COL_INFO
, "[FIX continuation]");
244 ti
= proto_tree_add_item(tree
, proto_fix
, tvb
, 0, -1, ENC_NA
);
245 fix_tree
= proto_item_add_subtree(ti
, ett_fix
);
246 proto_tree_add_item(fix_tree
, hf_fix_data
, tvb
, 0, -1, ENC_NA
);
247 return tvb_length(tvb
);
250 pdu_len
= tvb_reported_length(tvb
);
251 ti
= proto_tree_add_item(tree
, proto_fix
, tvb
, 0, -1, ENC_NA
);
252 fix_tree
= proto_item_add_subtree(ti
, ett_fix
);
255 ctrla_offset
= tvb_find_guint8(tvb
, offset
, -1, 0x01);
256 if (ctrla_offset
== -1) {
257 return tvb_length(tvb
);
259 offset
= ctrla_offset
+ 1;
262 ctrla_offset
= tvb_find_guint8(tvb
, offset
, -1, 0x01);
263 if (ctrla_offset
== -1) {
264 return tvb_length(tvb
);
266 offset
= ctrla_offset
+ 1;
269 if (!(tag
= fix_param(tvb
, offset
)) || tag
->value_len
< 1) {
270 return tvb_length(tvb
);
273 value
= tvb_get_string(wmem_packet_scope(), tvb
, tag
->value_offset
, tag
->value_len
);
274 msg_type
= str_to_str(value
, messages_val
, "FIX Message (%s)");
275 col_add_str(pinfo
->cinfo
, COL_INFO
, msg_type
);
277 /* In the interest of speed, if "tree" is NULL, don't do any work not
278 * necessary to generate protocol tree items.
282 while(field_offset
< pdu_len
&& (tag
= fix_param(tvb
, field_offset
)) ) {
285 if (tag
->tag_len
< 1) {
286 field_offset
= tag
->ctrla_offset
+ 1;
290 tag_str
= tvb_get_string(wmem_packet_scope(), tvb
, field_offset
, tag
->tag_len
);
291 tag_value
= atoi(tag_str
);
292 if (tag
->value_len
< 1) {
293 proto_tree
*field_tree
;
294 /* XXX - put an error indication here. It's too late
295 to return FALSE; we've already started dissecting,
296 and if a heuristic dissector starts dissecting
297 (either updating the columns or creating a protocol
298 tree) and then gives up, it leaves crud behind that
299 messes up other dissectors that might process the
301 ti
= proto_tree_add_text(fix_tree
, tvb
, field_offset
, tag
->field_len
, "%i: <missing value>", tag_value
);
302 field_tree
= proto_item_add_subtree(ti
, ett_badfield
);
303 proto_tree_add_uint(field_tree
, hf_fix_field_tag
, tvb
, field_offset
, tag
->tag_len
, tag_value
);
304 field_offset
= tag
->ctrla_offset
+ 1;
308 /* fix_fields array is sorted by tag_value */
310 if ((i
= tag_search(tag_value
)) >= 0) {
314 value
= tvb_get_string(wmem_packet_scope(), tvb
, tag
->value_offset
, tag
->value_len
);
316 if (fix_fields
[i
].table
) {
318 switch (fix_fields
[i
].type
) {
319 case 1: /* strings */
320 proto_tree_add_string_format_value(fix_tree
, fix_fields
[i
].hf_id
, tvb
, field_offset
, tag
->field_len
, value
,
321 "%s (%s)", value
, str_to_str(value
, (string_string
*)fix_fields
[i
].table
, "unknown %s"));
324 proto_tree_add_string_format_value(fix_tree
, fix_fields
[i
].hf_id
, tvb
, field_offset
, tag
->field_len
, value
,
325 "%s (%s)", value
, val_to_str(*value
, (value_string
*)fix_fields
[i
].table
, "unknown %d"));
328 proto_tree_add_string_format_value(fix_tree
, fix_fields
[i
].hf_id
, tvb
, field_offset
, tag
->field_len
, value
,
329 "%s (%s)", value
, val_to_str(atoi(value
), (value_string
*)fix_fields
[i
].table
, "unknown %d"));
341 proto_tree
*checksum_tree
;
343 const guint8
*sum_data
= tvb_get_ptr(tvb
, 0, field_offset
);
347 for (j
= 0; j
< field_offset
; j
++, sum_data
++) {
350 sum_ok
= (atoi(value
) == sum
);
352 item
= proto_tree_add_string_format_value(fix_tree
, fix_fields
[i
].hf_id
, tvb
, field_offset
, tag
->field_len
,
353 value
, "%s [correct]", value
);
356 item
= proto_tree_add_string_format_value(fix_tree
, fix_fields
[i
].hf_id
, tvb
, field_offset
, tag
->field_len
,
357 value
, "%s [incorrect should be %d]", value
, sum
);
359 checksum_tree
= proto_item_add_subtree(item
, ett_checksum
);
360 item
= proto_tree_add_boolean(checksum_tree
, hf_fix_checksum_good
, tvb
, field_offset
, tag
->field_len
, sum_ok
);
361 PROTO_ITEM_SET_GENERATED(item
);
362 item
= proto_tree_add_boolean(checksum_tree
, hf_fix_checksum_bad
, tvb
, field_offset
, tag
->field_len
, !sum_ok
);
363 PROTO_ITEM_SET_GENERATED(item
);
365 expert_add_info(pinfo
, item
, &ei_fix_checksum_bad
);
369 proto_tree_add_string(fix_tree
, fix_fields
[i
].hf_id
, tvb
, field_offset
, tag
->field_len
, value
);
375 proto_tree
*field_tree
;
377 /* XXX - it could be -1 if the tag isn't a number */
378 ti
= proto_tree_add_text(fix_tree
, tvb
, field_offset
, tag
->field_len
, "%i: %s", tag_value
, value
);
379 field_tree
= proto_item_add_subtree(ti
, ett_unknow
);
380 proto_tree_add_uint(field_tree
, hf_fix_field_tag
, tvb
, field_offset
, tag
->tag_len
, tag_value
);
381 proto_tree_add_item(field_tree
, hf_fix_field_value
, tvb
, tag
->value_offset
, tag
->value_len
, ENC_ASCII
|ENC_NA
);
384 field_offset
= tag
->ctrla_offset
+ 1;
388 return tvb_length(tvb
);
392 get_fix_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
)
396 fix_len
= fix_header_len(tvb
, offset
);
400 /* ------------------------------------
401 fixed-length part isn't really a constant but if we assume it's at least:
407 it should catch all 9= size
410 #define FIX_MIN_LEN 24
413 dissect_fix_pdus(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
415 tcp_dissect_pdus(tvb
, pinfo
, tree
, fix_desegment
, FIX_MIN_LEN
,
416 get_fix_pdu_len
, dissect_fix_packet
, data
);
418 return tvb_length(tvb
);
422 dissect_fix(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
424 return dissect_fix_pdus(tvb
, pinfo
, tree
, data
);
427 /* Code to actually dissect the packets */
429 dissect_fix_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
431 conversation_t
*conv
;
433 /* get at least the fix version: 8=FIX.x.x */
434 if (fix_marker(tvb
, 0) != 0) {
435 /* not a fix packet */
439 conv
= find_or_create_conversation(pinfo
);
440 conversation_set_dissector(conv
, fix_handle
);
442 dissect_fix_pdus(tvb
, pinfo
, tree
, data
);
446 /* Register the protocol with Wireshark */
447 static void fix_prefs(void)
449 dissector_delete_uint_range("tcp.port", fix_tcp_range
, fix_handle
);
450 g_free(fix_tcp_range
);
451 fix_tcp_range
= range_copy(global_fix_tcp_range
);
452 dissector_add_uint_range("tcp.port", fix_tcp_range
, fix_handle
);
455 /* this format is require because a script is used to build the C function
456 that calls all the protocol registration.
460 proto_register_fix(void)
462 static hf_register_info hf
[] = {
464 { "Continuation Data", "fix.data", FT_BYTES
, BASE_NONE
, NULL
, 0x00,
469 { "Field Tag", "fix.field.tag", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
470 "Field length.", HFILL
}},
472 { &hf_fix_field_value
,
473 { "Field Value", "fix.field.value", FT_STRING
, BASE_NONE
, NULL
, 0x0,
476 { &hf_fix_checksum_good
,
477 { "Good Checksum", "fix.checksum_good", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
478 "True: checksum matches packet content; False: doesn't match content or not checked", HFILL
}},
480 { &hf_fix_checksum_bad
,
481 { "Bad Checksum", "fix.checksum_bad", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
482 "True: checksum doesn't match packet content; False: matches content or not checked", HFILL
}},
485 /* Setup protocol subtree array */
486 static gint
*ett
[] = {
493 static ei_register_info ei
[] = {
494 { &ei_fix_checksum_bad
, { "fix.checksum_bad.expert", PI_CHECKSUM
, PI_ERROR
, "Bad checksum", EXPFILL
}},
497 module_t
*fix_module
;
498 expert_module_t
* expert_fix
;
500 /* register re-init routine */
501 register_init_routine(&dissect_fix_init
);
503 /* Register the protocol name and description */
504 proto_fix
= proto_register_protocol("Financial Information eXchange Protocol",
507 /* Allow dissector to find be found by name. */
508 fix_handle
= new_register_dissector("fix", dissect_fix
, proto_fix
);
510 proto_register_field_array(proto_fix
, hf
, array_length(hf
));
511 proto_register_field_array(proto_fix
, hf_FIX
, array_length(hf_FIX
));
512 proto_register_subtree_array(ett
, array_length(ett
));
513 expert_fix
= expert_register_protocol(proto_fix
);
514 expert_register_field_array(expert_fix
, ei
, array_length(ei
));
516 fix_module
= prefs_register_protocol(proto_fix
, fix_prefs
);
517 prefs_register_bool_preference(fix_module
, "desegment",
518 "Reassemble FIX messages spanning multiple TCP segments",
519 "Whether the FIX dissector should reassemble messages spanning multiple TCP segments."
520 " To use this option, you must also enable"
521 " \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
524 prefs_register_range_preference(fix_module
, "tcp.port", "TCP Ports", "TCP Ports range", &global_fix_tcp_range
, 65535);
526 fix_tcp_range
= range_empty();
531 proto_reg_handoff_fix(void)
533 /* Let the tcp dissector know that we're interested in traffic */
534 heur_dissector_add("tcp", dissect_fix_heur
, proto_fix
);
535 /* Register a fix handle to "tcp.port" to be able to do 'decode-as' */
536 dissector_add_handle("tcp.port", fix_handle
);