2 * Routines for Financial Information eXchange (FIX) Protocol dissection
3 * Copyright 2000, PC Drew <drewpc@ibsncentral.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
11 * Documentation: http://www.fixprotocol.org/
12 * Fields and messages from http://www.quickfixengine.org/ and http://sourceforge.net/projects/quickfix/files/ xml
20 #include <epan/packet.h>
21 #include <epan/expert.h>
22 #include <epan/prefs.h>
24 #include <wsutil/strtoi.h>
26 #include "packet-tcp.h"
27 #include "packet-tls.h"
29 void proto_register_fix(void);
30 void proto_reg_handoff_fix(void);
32 typedef struct _fix_parameter
{
40 /* Initialize the protocol and registered fields */
43 /* desegmentation of fix */
44 static bool fix_desegment
= true;
46 /* Initialize the subtree pointers */
48 static int ett_unknown
;
49 static int ett_badfield
;
50 static int ett_checksum
;
52 static expert_field ei_fix_checksum_bad
;
53 static expert_field ei_fix_missing_field
;
54 static expert_field ei_fix_tag_invalid
;
55 static expert_field ei_fix_field_invalid
;
57 static int hf_fix_data
; /* continuation data */
58 static int hf_fix_checksum_good
;
59 static int hf_fix_checksum_bad
;
60 static int hf_fix_field_value
;
61 static int hf_fix_field_tag
;
63 static dissector_handle_t fix_handle
;
66 #define MARKER_TAG "8=FIX"
69 static int fix_marker(tvbuff_t
*tvb
, int offset
)
71 return tvb_strneql(tvb
, offset
, MARKER_TAG
, MARKER_LEN
);
75 * Fields and messages generated from http://www.quickfixengine.org/ xml (slightly modified)
78 #include "packet-fix.h"
80 static void dissect_fix_init(void) {
81 /* TODO load xml def for private field */
82 /* TODO check that fix_fields is really sorted */
86 fix_field_tag_compar(const void *v_needle
, const void *v_entry
)
88 int key
= *(const int *)v_needle
;
89 int entry_tag
= ((const fix_field
*)v_entry
)->tag
;
90 return key
> entry_tag
? 1 : (key
< entry_tag
? -1 : 0);
93 /* Code to actually dissect the packets */
94 static int fix_next_header(tvbuff_t
*tvb
, int offset
)
96 /* try to resync to the next start */
97 unsigned min_len
= tvb_captured_length_remaining(tvb
, offset
);
98 const uint8_t *data
= tvb_get_string_enc(wmem_packet_scope(), tvb
, offset
, min_len
, ENC_ASCII
);
99 const uint8_t *start
= data
;
101 while ((start
= strstr(start
, "\0018"))) {
102 min_len
= (unsigned) (start
+1 -data
);
103 /* if remaining length < 6 return and let the next desegment round
106 if (tvb_reported_length_remaining(tvb
, min_len
+ offset
) < MARKER_LEN
)
108 if (!fix_marker(tvb
, min_len
+offset
) )
115 /* ----------------------------------------------
116 Format: name=value\001
118 static fix_parameter
*fix_param(tvbuff_t
*tvb
, int offset
)
120 static fix_parameter ret
;
123 ret
.ctrla_offset
= tvb_find_uint8(tvb
, offset
, -1, 0x01);
124 if (ret
.ctrla_offset
== -1) {
128 ret
.field_len
= ret
.ctrla_offset
- offset
+ 1;
129 equals
= tvb_find_uint8(tvb
, offset
, ret
.field_len
, '=');
134 ret
.value_offset
= equals
+ 1;
135 ret
.tag_len
= ret
.value_offset
- offset
- 1;
136 ret
.value_len
= ret
.ctrla_offset
- ret
.value_offset
;
140 /* ---------------------------------------------- */
141 static int fix_header_len(tvbuff_t
*tvb
, int offset
)
143 int base_offset
, ctrla_offset
;
148 base_offset
= offset
;
150 /* get at least the fix version: 8=FIX.x.x */
151 if (fix_marker(tvb
, offset
) != 0) {
152 return fix_next_header(tvb
, offset
);
156 ctrla_offset
= tvb_find_uint8(tvb
, offset
, -1, 0x01);
157 if (ctrla_offset
== -1) {
158 /* it should be there, (minimum size is big enough)
159 * if not maybe it's not really
160 * a FIX packet but it's too late to bail out.
162 return fix_next_header(tvb
, offset
+MARKER_LEN
) +MARKER_LEN
;
164 offset
= ctrla_offset
+ 1;
167 if (!(tag
= fix_param(tvb
, offset
)) || tvb_strneql(tvb
, offset
, "9=", 2)) {
168 /* not a tag or not the BodyLength tag, give up */
169 return fix_next_header(tvb
, offset
);
172 if (!ws_strtoi32(tvb_get_string_enc(wmem_packet_scope(), tvb
, tag
->value_offset
,
173 tag
->value_len
, ENC_ASCII
), NULL
, &value
))
174 return fix_next_header(tvb
, base_offset
+MARKER_LEN
) +MARKER_LEN
;
175 /* Fix version, msg type, length and checksum aren't in body length.
176 * If the packet is big enough find the checksum
178 size
= value
+ tag
->ctrla_offset
- base_offset
+ 1;
179 if (tvb_reported_length_remaining(tvb
, base_offset
) > size
+4) {
180 /* 10= should be there */
181 offset
= base_offset
+size
;
182 if (tvb_strneql(tvb
, offset
, "10=", 3) != 0) {
183 /* No? bogus packet, try to find the next header */
184 return fix_next_header(tvb
, base_offset
+MARKER_LEN
) +MARKER_LEN
;
186 ctrla_offset
= tvb_find_uint8(tvb
, offset
, -1, 0x01);
187 if (ctrla_offset
== -1) {
188 /* assume checksum is 7 bytes 10=xxx\01 */
191 return size
+ctrla_offset
-offset
+1;
195 /* assume checksum is 7 bytes 10=xxx\01 */
199 /* ---------------------------------------------- */
201 dissect_fix_packet(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
203 /* Set up structures needed to add the protocol subtree and manage it */
205 proto_tree
*fix_tree
;
208 int field_offset
, ctrla_offset
;
215 const char *msg_type
;
217 /* Make entries in Protocol column and Info column on summary display */
218 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "FIX");
219 col_clear(pinfo
->cinfo
, COL_INFO
);
221 /* get at least the fix version: 8=FIX.x.x */
222 if (fix_marker(tvb
, 0) != 0) {
223 /* not a fix packet start but it's a fix packet */
224 col_set_str(pinfo
->cinfo
, COL_INFO
, "[FIX continuation]");
225 ti
= proto_tree_add_item(tree
, proto_fix
, tvb
, 0, -1, ENC_NA
);
226 fix_tree
= proto_item_add_subtree(ti
, ett_fix
);
227 proto_tree_add_item(fix_tree
, hf_fix_data
, tvb
, 0, -1, ENC_NA
);
228 return tvb_captured_length(tvb
);
231 pdu_len
= tvb_reported_length(tvb
);
232 ti
= proto_tree_add_item(tree
, proto_fix
, tvb
, 0, -1, ENC_NA
);
233 fix_tree
= proto_item_add_subtree(ti
, ett_fix
);
236 ctrla_offset
= tvb_find_uint8(tvb
, offset
, -1, 0x01);
237 if (ctrla_offset
== -1) {
238 expert_add_info_format(pinfo
, ti
, &ei_fix_missing_field
, "Missing BeginString field");
239 return tvb_captured_length(tvb
);
241 offset
= ctrla_offset
+ 1;
244 ctrla_offset
= tvb_find_uint8(tvb
, offset
, -1, 0x01);
245 if (ctrla_offset
== -1) {
246 expert_add_info_format(pinfo
, ti
, &ei_fix_missing_field
, "Missing BodyLength field");
247 return tvb_captured_length(tvb
);
249 offset
= ctrla_offset
+ 1;
252 if (!(tag
= fix_param(tvb
, offset
)) || tag
->value_len
< 1) {
253 expert_add_info_format(pinfo
, ti
, &ei_fix_missing_field
, "Missing MsgType field");
254 return tvb_captured_length(tvb
);
257 /* In the interest of speed, if "tree" is NULL, don't do any work not
258 * necessary to generate protocol tree items.
262 while(field_offset
< pdu_len
&& (tag
= fix_param(tvb
, field_offset
)) ) {
263 const fix_field
*field
;
265 if (tag
->tag_len
< 1) {
266 field_offset
= tag
->ctrla_offset
+ 1;
270 if (!ws_strtou32(tvb_get_string_enc(pinfo
->pool
, tvb
, field_offset
, tag
->tag_len
, ENC_ASCII
),
272 proto_tree_add_expert(fix_tree
, pinfo
, &ei_fix_tag_invalid
, tvb
, field_offset
, tag
->tag_len
);
275 if (tag
->value_len
< 1) {
276 proto_tree
*field_tree
;
277 /* XXX - put an error indication here. It's too late
278 to return false; we've already started dissecting,
279 and if a heuristic dissector starts dissecting
280 (either updating the columns or creating a protocol
281 tree) and then gives up, it leaves crud behind that
282 messes up other dissectors that might process the
284 field_tree
= proto_tree_add_subtree_format(fix_tree
, tvb
, field_offset
, tag
->field_len
, ett_badfield
, NULL
, "%i: <missing value>", tag_value
);
285 proto_tree_add_uint(field_tree
, hf_fix_field_tag
, tvb
, field_offset
, tag
->tag_len
, tag_value
);
286 field_offset
= tag
->ctrla_offset
+ 1;
290 /* fix_fields array is sorted by tag_value */
291 field
= bsearch(&tag_value
, fix_fields
, array_length(fix_fields
), sizeof *fix_fields
, fix_field_tag_compar
);
293 value
= tvb_get_string_enc(pinfo
->pool
, tvb
, tag
->value_offset
, tag
->value_len
, ENC_ASCII
);
294 ivalue_valid
= ws_strtoi32(value
, NULL
, &ivalue
);
296 int hf
= fix_hf
[field
- fix_fields
];
300 switch (field
->type
) {
301 case 1: /* strings */
302 proto_tree_add_string_format_value(fix_tree
, hf
, tvb
, field_offset
, tag
->field_len
, value
,
303 "%s (%s)", value
, str_to_str(value
, (const string_string
*)field
->table
, "unknown %s"));
304 if (tag_value
== 35) {
305 /* Make message type part of the Info column */
306 msg_type
= str_to_str(value
, messages_val
, "FIX Message (%s)");
307 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ", ", msg_type
);
308 col_set_fence(pinfo
->cinfo
, COL_INFO
);
312 proto_tree_add_string_format_value(fix_tree
, hf
, tvb
, field_offset
, tag
->field_len
, value
,
313 "%s (%s)", value
, val_to_str(*value
, (const value_string
*)field
->table
, "unknown %d"));
317 proto_tree_add_string_format_value(fix_tree
, hf
, tvb
, field_offset
, tag
->field_len
, value
,
318 "%s (%s)", value
, val_to_str(ivalue
, (const value_string
*)field
->table
, "unknown %d"));
320 pi
= proto_tree_add_string(fix_tree
, hf
, tvb
, field_offset
, tag
->field_len
, value
);
321 expert_add_info_format(pinfo
, pi
, &ei_fix_field_invalid
, "Invalid string %s for fix field tag %i", value
, field
->tag
);
334 proto_tree
*checksum_tree
;
336 const uint8_t *sum_data
= tvb_get_ptr(tvb
, 0, field_offset
);
340 for (j
= 0; j
< field_offset
; j
++, sum_data
++) {
343 sum_ok
= (ivalue
== sum
);
345 item
= proto_tree_add_string_format_value(fix_tree
, hf
, tvb
, field_offset
, tag
->field_len
,
346 value
, "%s [correct]", value
);
349 item
= proto_tree_add_string_format_value(fix_tree
, hf
, tvb
, field_offset
, tag
->field_len
,
350 value
, "%s [incorrect should be %d]", value
, sum
);
352 checksum_tree
= proto_item_add_subtree(item
, ett_checksum
);
353 item
= proto_tree_add_boolean(checksum_tree
, hf_fix_checksum_good
, tvb
, field_offset
, tag
->field_len
, sum_ok
);
354 proto_item_set_generated(item
);
355 item
= proto_tree_add_boolean(checksum_tree
, hf_fix_checksum_bad
, tvb
, field_offset
, tag
->field_len
, !sum_ok
);
356 proto_item_set_generated(item
);
358 expert_add_info(pinfo
, item
, &ei_fix_checksum_bad
);
362 proto_tree_add_string(fix_tree
, hf
, tvb
, field_offset
, tag
->field_len
, value
);
368 proto_tree
*field_tree
;
370 /* XXX - it could be -1 if the tag isn't a number */
371 field_tree
= proto_tree_add_subtree_format(fix_tree
, tvb
, field_offset
, tag
->field_len
, ett_unknown
, NULL
,
372 "%i: %s", tag_value
, value
);
373 proto_tree_add_uint(field_tree
, hf_fix_field_tag
, tvb
, field_offset
, tag
->tag_len
, tag_value
);
374 proto_tree_add_item(field_tree
, hf_fix_field_value
, tvb
, tag
->value_offset
, tag
->value_len
, ENC_ASCII
);
377 field_offset
= tag
->ctrla_offset
+ 1;
379 return tvb_captured_length(tvb
);
383 get_fix_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
)
387 fix_len
= fix_header_len(tvb
, offset
);
391 /* ------------------------------------
392 fixed-length part isn't really a constant but if we assume it's at least:
398 it should catch all 9= size
401 #define FIX_MIN_LEN 24
404 dissect_fix_pdus(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
406 tcp_dissect_pdus(tvb
, pinfo
, tree
, fix_desegment
, FIX_MIN_LEN
,
407 get_fix_pdu_len
, dissect_fix_packet
, data
);
409 return tvb_captured_length(tvb
);
413 dissect_fix(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
415 return dissect_fix_pdus(tvb
, pinfo
, tree
, data
);
418 /* Code to actually dissect the packets */
420 dissect_fix_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
422 conversation_t
*conv
;
424 /* get at least the fix version: 8=FIX.x.x */
425 if (fix_marker(tvb
, 0) != 0) {
426 /* not a fix packet */
430 conv
= find_or_create_conversation(pinfo
);
431 conversation_set_dissector(conv
, fix_handle
);
433 dissect_fix_pdus(tvb
, pinfo
, tree
, data
);
438 dissect_fix_heur_ssl(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
440 struct tlsinfo
*tlsinfo
= (struct tlsinfo
*)data
;
441 /* get at least the fix version: 8=FIX.x.x */
442 if (fix_marker(tvb
, 0) != 0) {
443 /* not a fix packet */
447 dissect_fix_pdus(tvb
, pinfo
, tree
, data
);
448 *(tlsinfo
->app_handle
) = fix_handle
;
452 /* this format is require because a script is used to build the C function
453 that calls all the protocol registration.
457 proto_register_fix(void)
459 static hf_register_info hf
[] = {
461 { "Continuation Data", "fix.data", FT_BYTES
, BASE_NONE
, NULL
, 0x00,
466 { "Field Tag", "fix.field.tag", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
467 "Field length.", HFILL
}},
469 { &hf_fix_field_value
,
470 { "Field Value", "fix.field.value", FT_STRING
, BASE_NONE
, NULL
, 0x0,
473 { &hf_fix_checksum_good
,
474 { "Good Checksum", "fix.checksum_good", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
475 "True: checksum matches packet content; False: doesn't match content or not checked", HFILL
}},
477 { &hf_fix_checksum_bad
,
478 { "Bad Checksum", "fix.checksum_bad", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
479 "True: checksum doesn't match packet content; False: matches content or not checked", HFILL
}},
482 /* Setup protocol subtree array */
483 static int *ett
[] = {
490 static ei_register_info ei
[] = {
491 { &ei_fix_checksum_bad
, { "fix.checksum_bad.expert", PI_CHECKSUM
, PI_ERROR
, "Bad checksum", EXPFILL
}},
492 { &ei_fix_missing_field
, { "fix.missing_field", PI_MALFORMED
, PI_ERROR
, "Missing mandatory field", EXPFILL
}},
493 { &ei_fix_tag_invalid
, { "fix.tag.invalid", PI_MALFORMED
, PI_ERROR
, "Invalid Tag", EXPFILL
}},
494 { &ei_fix_field_invalid
, { "fix.invalid_integer_string", PI_MALFORMED
, PI_ERROR
, "Invalid integer string", 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", "FIX", "fix");
506 /* Allow dissector to find be found by name. */
507 fix_handle
= register_dissector("fix", dissect_fix
, proto_fix
);
509 proto_register_field_array(proto_fix
, hf
, array_length(hf
));
510 proto_register_field_array(proto_fix
, hf_FIX
, array_length(hf_FIX
));
511 proto_register_subtree_array(ett
, array_length(ett
));
512 expert_fix
= expert_register_protocol(proto_fix
);
513 expert_register_field_array(expert_fix
, ei
, array_length(ei
));
515 fix_module
= prefs_register_protocol(proto_fix
, NULL
);
516 prefs_register_bool_preference(fix_module
, "desegment",
517 "Reassemble FIX messages spanning multiple TCP segments",
518 "Whether the FIX dissector should reassemble messages spanning multiple TCP segments."
519 " To use this option, you must also enable"
520 " \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
526 proto_reg_handoff_fix(void)
528 /* Let the tcp dissector know that we're interested in traffic */
529 heur_dissector_add("tcp", dissect_fix_heur
, "FIX over TCP", "fix_tcp", proto_fix
, HEURISTIC_ENABLE
);
530 heur_dissector_add("tls", dissect_fix_heur_ssl
, "FIX over TLS", "fix_tls", proto_fix
, HEURISTIC_ENABLE
);
531 dissector_add_uint_range_with_preference("tcp.port", "", fix_handle
);
535 * Editor modelines - https://www.wireshark.org/tools/modelines.html
540 * indent-tabs-mode: nil
543 * vi: set shiftwidth=4 tabstop=8 expandtab:
544 * :indentSize=4:tabSize=8:noTabs=true: