/src/wireshark/epan/dissectors/packet-soupbintcp.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-soupbintcp.c |
2 | | * Routines for SoupBinTCP 3.0 protocol dissection |
3 | | * Copyright 2013 David Arnold <davida@pobox.com> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | /* |
13 | | * SoupBinTCP is a framing protocol published and used by NASDAQ to |
14 | | * encapsulate both market data (ITCH) and order entry (OUCH) |
15 | | * protocols. It is derived from the original SOUP protocol, which |
16 | | * was ASCII-based, and relied on an EOL indicator as a message |
17 | | * boundary. |
18 | | * |
19 | | * SoupBinTCP was introduced with OUCH-4.0 / ITCH-4.0 when those |
20 | | * protocols also switched to using a binary representation for |
21 | | * numerical values. |
22 | | * |
23 | | * The SOUP/SoupBinTCP protocols are also commonly used by other |
24 | | * financial exchanges, although frequently they are more SOUP-like |
25 | | * than exactly the same. This dissector doesn't attempt to support |
26 | | * any other SOUP-like variants; I think it's probably better to have |
27 | | * separate (if similar) dissectors for them. |
28 | | * |
29 | | * The only really complexity in the protocol is the message sequence |
30 | | * numbering. See the comments below for an explanation of how it is |
31 | | * handled. |
32 | | * |
33 | | * Specifications are available from NASDAQ's website, although the |
34 | | * links to find them tend to move around over time. At the time of |
35 | | * writing the correct URL is: |
36 | | * |
37 | | * http://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/soupbintcp.pdf |
38 | | * |
39 | | */ |
40 | | |
41 | | #include "config.h" |
42 | | |
43 | | #include <stdlib.h> |
44 | | |
45 | | #include <epan/packet.h> |
46 | | #include <epan/prefs.h> |
47 | | #include <epan/proto_data.h> |
48 | | #include <epan/expert.h> |
49 | | |
50 | | #include <wsutil/strtoi.h> |
51 | | |
52 | | /* For tcp_dissect_pdus() */ |
53 | | #include "packet-tcp.h" |
54 | | |
55 | | void proto_register_soupbintcp(void); |
56 | | void proto_reg_handoff_soupbintcp(void); |
57 | | |
58 | | /** Session data stored in the conversation */ |
59 | | struct conv_data { |
60 | | /** Next expected sequence number |
61 | | * |
62 | | * Set by the Login Accepted packet, and then updated for each |
63 | | * subsequent Sequenced Data packet during dissection. */ |
64 | | unsigned next_seq; |
65 | | }; |
66 | | |
67 | | |
68 | | /** Per-PDU data, stored in the frame's private data pointer */ |
69 | | struct pdu_data { |
70 | | /** Sequence number for this PDU */ |
71 | | unsigned seq_num; |
72 | | }; |
73 | | |
74 | | |
75 | | /** Packet names, indexed by message type code value */ |
76 | | static const value_string pkt_type_val[] = { |
77 | | { '+', "Debug Packet" }, |
78 | | { 'A', "Login Accepted" }, |
79 | | { 'H', "Server Heartbeat" }, |
80 | | { 'J', "Login Rejected" }, |
81 | | { 'L', "Login Request" }, |
82 | | { 'O', "Logout Request" }, |
83 | | { 'R', "Client Heartbeat" }, |
84 | | { 'S', "Sequenced Data" }, |
85 | | { 'U', "Unsequenced Data" }, |
86 | | { 'Z', "End of Session" }, |
87 | | { 0, NULL } |
88 | | }; |
89 | | |
90 | | |
91 | | /** Login reject reasons, indexed by code value */ |
92 | | static const value_string reject_code_val[] = { |
93 | | { 'A', "Not authorized" }, |
94 | | { 'S', "Session not available" }, |
95 | | { 0, NULL } |
96 | | }; |
97 | | |
98 | | |
99 | | /* Initialize the protocol and registered fields */ |
100 | | static int proto_soupbintcp; |
101 | | static dissector_handle_t soupbintcp_handle; |
102 | | static heur_dissector_list_t heur_subdissector_list; |
103 | | |
104 | | /* Preferences */ |
105 | | static bool soupbintcp_desegment = true; |
106 | | |
107 | | /* Initialize the subtree pointers */ |
108 | | static int ett_soupbintcp; |
109 | | |
110 | | /* Header field formatting */ |
111 | | static int hf_soupbintcp_packet_length; |
112 | | static int hf_soupbintcp_packet_type; |
113 | | static int hf_soupbintcp_message; |
114 | | static int hf_soupbintcp_text; |
115 | | static int hf_soupbintcp_username; |
116 | | static int hf_soupbintcp_password; |
117 | | static int hf_soupbintcp_session; |
118 | | static int hf_soupbintcp_seq_num; |
119 | | static int hf_soupbintcp_next_seq_num; |
120 | | static int hf_soupbintcp_req_seq_num; |
121 | | static int hf_soupbintcp_reject_code; |
122 | | |
123 | | static expert_field ei_soupbintcp_next_seq_num_invalid; |
124 | | static expert_field ei_soupbintcp_req_seq_num_invalid; |
125 | | |
126 | | /** Dissector for SoupBinTCP messages */ |
127 | | static void |
128 | | dissect_soupbintcp_common( |
129 | | tvbuff_t *tvb, |
130 | | packet_info *pinfo, |
131 | | proto_tree *tree) |
132 | 0 | { |
133 | 0 | struct conv_data *conv_data; |
134 | 0 | struct pdu_data *pdu_data; |
135 | 0 | const char *pkt_name; |
136 | 0 | int32_t seq_num; |
137 | 0 | bool seq_num_valid; |
138 | 0 | proto_item *ti; |
139 | 0 | proto_tree *soupbintcp_tree = NULL; |
140 | 0 | conversation_t *conv = NULL; |
141 | 0 | uint16_t expected_len; |
142 | 0 | uint8_t pkt_type; |
143 | 0 | int offset = 0; |
144 | 0 | unsigned this_seq = 0, next_seq = 0, key; |
145 | 0 | heur_dtbl_entry_t *hdtbl_entry; |
146 | 0 | proto_item *pi; |
147 | | |
148 | | /* Record the start of the packet to use as a sequence number key */ |
149 | 0 | key = (unsigned)tvb_raw_offset(tvb); |
150 | | |
151 | | /* Get the 16-bit big-endian SOUP packet length */ |
152 | 0 | expected_len = tvb_get_ntohs(tvb, 0); |
153 | | |
154 | | /* Get the 1-byte SOUP message type */ |
155 | 0 | pkt_type = tvb_get_uint8(tvb, 2); |
156 | | |
157 | | /* Since we use the packet name a few times, get and save that value */ |
158 | 0 | pkt_name = val_to_str(pkt_type, pkt_type_val, "Unknown (%u)"); |
159 | | |
160 | | /* Set the protocol name in the summary display */ |
161 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "SoupBinTCP"); |
162 | | |
163 | | /* Set the packet name in the info column */ |
164 | 0 | col_add_str(pinfo->cinfo, COL_INFO, pkt_name); |
165 | | |
166 | | /* Sequence number tracking |
167 | | * |
168 | | * SOUP does not number packets from client to server (the server |
169 | | * acknowledges all important messages, so the client should use |
170 | | * the acks to figure out if the server received the message, and |
171 | | * otherwise resend it). |
172 | | * |
173 | | * Packets from server to client are numbered, but it's implicit. |
174 | | * The Login Accept packet contains the next sequence number that |
175 | | * the server will send, and the client needs to count the |
176 | | * Sequenced Data packets that it receives to know what their |
177 | | * sequence numbers are. |
178 | | * |
179 | | * So, we grab the next sequence number from the Login Acceptance |
180 | | * packet, and save it in a conversation_t we associate with the |
181 | | * TCP session. Then, for each Sequenced Data packet we receive, |
182 | | * the first time it's processed (when PINFO_FD_VISITED() is |
183 | | * false), we write it into the PDU's frame's private data pointer |
184 | | * and increment the saved sequence number (in the conversation_t). |
185 | | * |
186 | | * If the visited flag is true, then we've dissected this packet |
187 | | * already, and so we can fetch the sequence number from the |
188 | | * frame's private data area. |
189 | | * |
190 | | * In either case, if there's any problem, we report zero as the |
191 | | * sequence number, and try to continue dissecting. */ |
192 | | |
193 | | /* If first dissection of Login Accept, save sequence number */ |
194 | 0 | if (pkt_type == 'A' && !PINFO_FD_VISITED(pinfo)) { |
195 | 0 | ws_strtou32(tvb_get_string_enc(pinfo->pool, tvb, 13, 20, ENC_ASCII), |
196 | 0 | NULL, &next_seq); |
197 | | |
198 | | /* Create new conversation for this session */ |
199 | 0 | conv = conversation_new(pinfo->num, |
200 | 0 | &pinfo->src, |
201 | 0 | &pinfo->dst, |
202 | 0 | conversation_pt_to_conversation_type(pinfo->ptype), |
203 | 0 | pinfo->srcport, |
204 | 0 | pinfo->destport, |
205 | 0 | 0); |
206 | | |
207 | | /* Store starting sequence number for session's packets */ |
208 | 0 | conv_data = wmem_new(wmem_file_scope(), struct conv_data); |
209 | 0 | conv_data->next_seq = next_seq; |
210 | 0 | conversation_add_proto_data(conv, proto_soupbintcp, conv_data); |
211 | 0 | } |
212 | | |
213 | | /* Handle sequence numbering for a Sequenced Data packet */ |
214 | 0 | if (pkt_type == 'S') { |
215 | 0 | if (!PINFO_FD_VISITED(pinfo)) { |
216 | | /* Get next expected sequence number from conversation */ |
217 | 0 | conv = find_conversation_pinfo(pinfo, 0); |
218 | 0 | if (!conv) { |
219 | 0 | this_seq = 0; |
220 | 0 | } else { |
221 | 0 | conv_data = (struct conv_data *)conversation_get_proto_data(conv, |
222 | 0 | proto_soupbintcp); |
223 | 0 | if (conv_data) { |
224 | 0 | this_seq = conv_data->next_seq++; |
225 | 0 | } else { |
226 | 0 | this_seq = 0; |
227 | 0 | } |
228 | |
|
229 | 0 | pdu_data = wmem_new(wmem_file_scope(), struct pdu_data); |
230 | 0 | pdu_data->seq_num = this_seq; |
231 | 0 | p_add_proto_data(wmem_file_scope(), pinfo, proto_soupbintcp, key, pdu_data); |
232 | 0 | } |
233 | 0 | } else { |
234 | 0 | pdu_data = (struct pdu_data *)p_get_proto_data(wmem_file_scope(), pinfo, proto_soupbintcp, key); |
235 | 0 | if (pdu_data) { |
236 | 0 | this_seq = pdu_data->seq_num; |
237 | 0 | } else { |
238 | 0 | this_seq = 0; |
239 | 0 | } |
240 | 0 | } |
241 | |
|
242 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, ", SeqNum = %u", this_seq); |
243 | 0 | } |
244 | |
|
245 | 0 | if (tree) { |
246 | | /* Create sub-tree for SoupBinTCP details */ |
247 | 0 | ti = proto_tree_add_item(tree, |
248 | 0 | proto_soupbintcp, |
249 | 0 | tvb, 0, -1, ENC_NA); |
250 | |
|
251 | 0 | soupbintcp_tree = proto_item_add_subtree(ti, ett_soupbintcp); |
252 | | |
253 | | /* Append the packet name to the sub-tree item */ |
254 | 0 | proto_item_append_text(ti, ", %s", pkt_name); |
255 | | |
256 | | /* Length */ |
257 | 0 | proto_tree_add_item(soupbintcp_tree, |
258 | 0 | hf_soupbintcp_packet_length, |
259 | 0 | tvb, offset, 2, ENC_BIG_ENDIAN); |
260 | 0 | offset += 2; |
261 | | |
262 | | /* Type */ |
263 | 0 | proto_tree_add_item(soupbintcp_tree, |
264 | 0 | hf_soupbintcp_packet_type, |
265 | 0 | tvb, offset, 1, ENC_ASCII); |
266 | 0 | offset += 1; |
267 | |
|
268 | 0 | switch (pkt_type) { |
269 | 0 | case '+': /* Debug Message */ |
270 | 0 | proto_tree_add_item(soupbintcp_tree, |
271 | 0 | hf_soupbintcp_text, |
272 | 0 | tvb, offset, expected_len - 1, ENC_ASCII); |
273 | 0 | break; |
274 | | |
275 | 0 | case 'A': /* Login Accept */ |
276 | 0 | proto_tree_add_item(soupbintcp_tree, |
277 | 0 | hf_soupbintcp_session, |
278 | 0 | tvb, offset, 10, ENC_ASCII); |
279 | 0 | offset += 10; |
280 | |
|
281 | 0 | seq_num_valid = ws_strtoi32(tvb_get_string_enc(pinfo->pool, |
282 | 0 | tvb, offset, 20, ENC_ASCII), NULL, &seq_num); |
283 | 0 | pi = proto_tree_add_string_format_value(soupbintcp_tree, |
284 | 0 | hf_soupbintcp_next_seq_num, |
285 | 0 | tvb, offset, 20, |
286 | 0 | "X", "%d", seq_num); |
287 | 0 | if (!seq_num_valid) |
288 | 0 | expert_add_info(pinfo, pi, &ei_soupbintcp_next_seq_num_invalid); |
289 | |
|
290 | 0 | break; |
291 | | |
292 | 0 | case 'J': /* Login Reject */ |
293 | 0 | proto_tree_add_item(soupbintcp_tree, |
294 | 0 | hf_soupbintcp_reject_code, |
295 | 0 | tvb, offset, 1, ENC_ASCII); |
296 | 0 | break; |
297 | | |
298 | 0 | case 'U': /* Unsequenced Data */ |
299 | | /* Display handled by sub-dissector */ |
300 | 0 | break; |
301 | | |
302 | 0 | case 'S': /* Sequenced Data */ |
303 | 0 | proto_item_append_text(ti, ", SeqNum=%u", this_seq); |
304 | 0 | proto_tree_add_string_format_value(soupbintcp_tree, |
305 | 0 | hf_soupbintcp_seq_num, |
306 | 0 | tvb, offset, 0, |
307 | 0 | "X", |
308 | 0 | "%u (Calculated)", |
309 | 0 | this_seq); |
310 | | |
311 | | /* Display handled by sub-dissector */ |
312 | 0 | break; |
313 | | |
314 | 0 | case 'L': /* Login Request */ |
315 | 0 | proto_tree_add_item(soupbintcp_tree, |
316 | 0 | hf_soupbintcp_username, |
317 | 0 | tvb, offset, 6, ENC_ASCII); |
318 | 0 | offset += 6; |
319 | |
|
320 | 0 | proto_tree_add_item(soupbintcp_tree, |
321 | 0 | hf_soupbintcp_password, |
322 | 0 | tvb, offset, 10, ENC_ASCII); |
323 | 0 | offset += 10; |
324 | |
|
325 | 0 | proto_tree_add_item(soupbintcp_tree, |
326 | 0 | hf_soupbintcp_session, |
327 | 0 | tvb, offset, 10, ENC_ASCII); |
328 | 0 | offset += 10; |
329 | |
|
330 | 0 | seq_num_valid = ws_strtoi32(tvb_get_string_enc(pinfo->pool, |
331 | 0 | tvb, offset, 20, ENC_ASCII), NULL, &seq_num); |
332 | 0 | pi = proto_tree_add_string_format_value(soupbintcp_tree, |
333 | 0 | hf_soupbintcp_req_seq_num, |
334 | 0 | tvb, offset, 20, |
335 | 0 | "X", "%d", seq_num); |
336 | 0 | if (!seq_num_valid) |
337 | 0 | expert_add_info(pinfo, pi, &ei_soupbintcp_req_seq_num_invalid); |
338 | |
|
339 | 0 | break; |
340 | | |
341 | 0 | case 'H': /* Server Heartbeat */ |
342 | 0 | break; |
343 | | |
344 | 0 | case 'O': /* Logout Request */ |
345 | 0 | break; |
346 | | |
347 | 0 | case 'R': /* Client Heartbeat */ |
348 | 0 | break; |
349 | | |
350 | 0 | case 'Z': /* End of Session */ |
351 | 0 | break; |
352 | | |
353 | 0 | default: |
354 | | /* Unknown */ |
355 | 0 | proto_tree_add_item(tree, |
356 | 0 | hf_soupbintcp_message, |
357 | 0 | tvb, offset, -1, ENC_NA); |
358 | 0 | break; |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | | /* Call sub-dissector for encapsulated data */ |
363 | 0 | if (pkt_type == 'S' || pkt_type == 'U') { |
364 | 0 | tvbuff_t *sub_tvb; |
365 | | |
366 | | /* Sub-dissector tvb starts at 3 (length (2) + pkt_type (1)) */ |
367 | 0 | sub_tvb = tvb_new_subset_remaining(tvb, 3); |
368 | | |
369 | | /* Otherwise, try heuristic dissectors */ |
370 | 0 | if (dissector_try_heuristic(heur_subdissector_list, |
371 | 0 | sub_tvb, |
372 | 0 | pinfo, |
373 | 0 | tree, |
374 | 0 | &hdtbl_entry, |
375 | 0 | NULL)) { |
376 | 0 | return; |
377 | 0 | } |
378 | | |
379 | | /* Otherwise, give up, and just print the bytes in hex */ |
380 | 0 | if (tree) { |
381 | 0 | proto_tree_add_item(soupbintcp_tree, |
382 | 0 | hf_soupbintcp_message, |
383 | 0 | sub_tvb, 0, -1, |
384 | 0 | ENC_NA); |
385 | 0 | } |
386 | 0 | } |
387 | 0 | } |
388 | | |
389 | | |
390 | | /** Return the size of the PDU in @p tvb, starting at @p offset */ |
391 | | static unsigned |
392 | | get_soupbintcp_pdu_len( |
393 | | packet_info *pinfo _U_, |
394 | | tvbuff_t *tvb, |
395 | | int offset, |
396 | | void *data _U_) |
397 | 0 | { |
398 | | /* Determine the length of the PDU using the SOUP header's 16-bit |
399 | | big-endian length (at offset zero). We're guaranteed to get at |
400 | | least two bytes here because we told tcp_dissect_pdus() that we |
401 | | needed them. Add 2 to the retrieved value, because the SOUP |
402 | | length doesn't include the length field itself. */ |
403 | 0 | return (unsigned)tvb_get_ntohs(tvb, offset) + 2; |
404 | 0 | } |
405 | | |
406 | | |
407 | | /** Dissect a possibly-reassembled TCP PDU */ |
408 | | static int |
409 | | dissect_soupbintcp_tcp_pdu( |
410 | | tvbuff_t *tvb, |
411 | | packet_info *pinfo, |
412 | | proto_tree *tree, |
413 | | void *data _U_) |
414 | 0 | { |
415 | 0 | dissect_soupbintcp_common(tvb, pinfo, tree); |
416 | 0 | return tvb_captured_length(tvb); |
417 | 0 | } |
418 | | |
419 | | |
420 | | /** Dissect a TCP segment containing SoupBinTCP data */ |
421 | | static int |
422 | | dissect_soupbintcp_tcp( |
423 | | tvbuff_t *tvb, |
424 | | packet_info *pinfo, |
425 | | proto_tree *tree, |
426 | | void *data) |
427 | 0 | { |
428 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, |
429 | 0 | soupbintcp_desegment, 2, |
430 | 0 | get_soupbintcp_pdu_len, |
431 | 0 | dissect_soupbintcp_tcp_pdu, data); |
432 | 0 | return tvb_captured_length(tvb); |
433 | 0 | } |
434 | | |
435 | | void |
436 | | proto_register_soupbintcp(void) |
437 | 14 | { |
438 | 14 | expert_module_t* expert_soupbinttcp; |
439 | | |
440 | 14 | static hf_register_info hf[] = { |
441 | | |
442 | 14 | { &hf_soupbintcp_packet_length, |
443 | 14 | { "Packet Length", "soupbintcp.packet_length", |
444 | 14 | FT_UINT16, BASE_DEC, NULL, 0x0, |
445 | 14 | "Packet length, in bytes, NOT including these two bytes.", |
446 | 14 | HFILL }}, |
447 | | |
448 | 14 | { &hf_soupbintcp_packet_type, |
449 | 14 | { "Packet Type", "soupbintcp.packet_type", |
450 | 14 | FT_CHAR, BASE_HEX, VALS(pkt_type_val), 0x0, |
451 | 14 | "Message type code", |
452 | 14 | HFILL }}, |
453 | | |
454 | 14 | { &hf_soupbintcp_reject_code, |
455 | 14 | { "Login Reject Code", "soupbintcp.reject_code", |
456 | 14 | FT_CHAR, BASE_HEX, VALS(reject_code_val), 0x0, |
457 | 14 | "Login reject reason code", |
458 | 14 | HFILL }}, |
459 | | |
460 | 14 | { &hf_soupbintcp_message, |
461 | 14 | { "Message", "soupbintcp.message", |
462 | 14 | FT_BYTES, BASE_NONE, NULL, 0x0, |
463 | 14 | "Content of SoupBinTCP frame", |
464 | 14 | HFILL }}, |
465 | | |
466 | 14 | { &hf_soupbintcp_text, |
467 | 14 | { "Debug Text", "soupbintcp.text", |
468 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
469 | 14 | "Free-form, human-readable text", |
470 | 14 | HFILL }}, |
471 | | |
472 | 14 | { &hf_soupbintcp_username, |
473 | 14 | { "User Name", "soupbintcp.username", |
474 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
475 | 14 | "User's login name", |
476 | 14 | HFILL }}, |
477 | | |
478 | 14 | { &hf_soupbintcp_password, |
479 | 14 | { "Password", "soupbintcp.password", |
480 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
481 | 14 | "User's login password", |
482 | 14 | HFILL }}, |
483 | | |
484 | 14 | { &hf_soupbintcp_session, |
485 | 14 | { "Session", "soupbintcp.session", |
486 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
487 | 14 | "Session identifier, or send all spaces to log into the currently " |
488 | 14 | "active session", |
489 | 14 | HFILL }}, |
490 | | |
491 | 14 | { &hf_soupbintcp_seq_num, |
492 | 14 | { "Sequence number", "soupbintcp.seq_num", |
493 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
494 | 14 | "Calculated sequence number for this message", |
495 | 14 | HFILL }}, |
496 | | |
497 | 14 | { &hf_soupbintcp_next_seq_num, |
498 | 14 | { "Next sequence number", "soupbintcp.next_seq_num", |
499 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
500 | 14 | "Sequence number of next Sequenced Data message to be delivered", |
501 | 14 | HFILL }}, |
502 | | |
503 | 14 | { &hf_soupbintcp_req_seq_num, |
504 | 14 | { "Requested sequence number", "soupbintcp.req_seq_num", |
505 | 14 | FT_STRING, BASE_NONE, NULL, 0x0, |
506 | 14 | "Request to begin (re)transmission of Sequenced Data at this " |
507 | 14 | "sequence number, or, if zero, to begin transmission with the " |
508 | 14 | "next message generated", |
509 | 14 | HFILL }} |
510 | 14 | }; |
511 | | |
512 | 14 | static int *ett[] = { |
513 | 14 | &ett_soupbintcp |
514 | 14 | }; |
515 | | |
516 | 14 | static ei_register_info ei[] = { |
517 | 14 | { &ei_soupbintcp_req_seq_num_invalid, { "soupbintcp.req_seq_num.invalid", PI_MALFORMED, PI_ERROR, |
518 | 14 | "Sequence number of next Sequenced Data message to be delivered is an invalid string", EXPFILL }}, |
519 | 14 | { &ei_soupbintcp_next_seq_num_invalid, { "soupbintcp.next_seq_num.invalid", PI_MALFORMED, PI_ERROR, |
520 | 14 | "Request to begin (re)transmission is an invalid string", EXPFILL }} |
521 | 14 | }; |
522 | | |
523 | 14 | module_t *soupbintcp_module; |
524 | | |
525 | 14 | proto_soupbintcp = proto_register_protocol("SoupBinTCP", "SoupBinTCP", "soupbintcp"); |
526 | 14 | soupbintcp_handle = register_dissector("soupbintcp", dissect_soupbintcp_tcp, proto_soupbintcp); |
527 | | |
528 | 14 | proto_register_field_array(proto_soupbintcp, hf, array_length(hf)); |
529 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
530 | | |
531 | 14 | soupbintcp_module = prefs_register_protocol(proto_soupbintcp, NULL); |
532 | | |
533 | 14 | prefs_register_bool_preference( |
534 | 14 | soupbintcp_module, |
535 | 14 | "desegment", |
536 | 14 | "Reassemble SoupBinTCP messages spanning multiple TCP segments", |
537 | 14 | "Whether the SoupBinTCP dissector should reassemble messages " |
538 | 14 | "spanning multiple TCP segments.", |
539 | 14 | &soupbintcp_desegment); |
540 | | |
541 | 14 | heur_subdissector_list = register_heur_dissector_list_with_description("soupbintcp", "SoupBinTCP encapsulated data", proto_soupbintcp); |
542 | | |
543 | 14 | expert_soupbinttcp = expert_register_protocol(proto_soupbintcp); |
544 | 14 | expert_register_field_array(expert_soupbinttcp, ei, array_length(ei)); |
545 | 14 | } |
546 | | |
547 | | |
548 | | void |
549 | | proto_reg_handoff_soupbintcp(void) |
550 | 14 | { |
551 | 14 | dissector_add_uint_range_with_preference("tcp.port", "", soupbintcp_handle); |
552 | 14 | } |
553 | | |
554 | | |
555 | | /* |
556 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
557 | | * |
558 | | * Local variables: |
559 | | * c-basic-offset: 4 |
560 | | * tab-width: 8 |
561 | | * indent-tabs-mode: nil |
562 | | * End: |
563 | | * |
564 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
565 | | * :indentSize=4:tabSize=8:noTabs=true: |
566 | | */ |