#include <string.h> and/or #include <stdio.h> not needed.
[obnox/wireshark/wip.git] / epan / dissectors / packet-nasdaq-soup.c
1 /* packet-nasdaq-soup.c
2  * Routines for NASDAQ SOUP 2.0 Protocol dissection
3  * Copyright 2007,2008 Didier Gautheron <dgautheron@magic.fr>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  *
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.
20  *
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24  *
25  * Documentation: http://www.nasdaqtrader.com/Trader.aspx?id=DPSpecs
26  * ex:
27  * http://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/souptcp.pdf
28  */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <epan/packet.h>
35 #include <epan/prefs.h>
36
37 #include "packet-tcp.h"
38
39 static const value_string message_types_val[] = {
40       { 'S', "Sequenced Data" },
41       { 'R', "Client Heartbeat" },
42       { 'H', "Server Heartbeat" },
43       { '+' , "Debug Packet" },
44       { 'A', "Login Accepted" },
45       { 'J', "Login Rejected" },
46       { 'L', "Login Request" },
47       { 'U', "Unsequenced Data" },
48       { 'O', "Logout Request" },
49       { 0, NULL }
50 };
51
52 static const value_string reject_code_val[] = {
53       { 'A', "Not authorized" },
54       { 'S', "Session not available" },
55       { 0, NULL }
56 };
57
58 /* Initialize the protocol and registered fields */
59 static int proto_nasdaq_soup = -1;
60 static dissector_handle_t nasdaq_soup_handle;
61 static dissector_handle_t nasdaq_itch_handle;
62
63 /* desegmentation of Nasdaq Soup */
64 static gboolean nasdaq_soup_desegment = TRUE;
65
66 static range_t *global_nasdaq_soup_tcp_range = NULL;
67 static range_t *nasdaq_soup_tcp_range = NULL;
68
69 /* Initialize the subtree pointers */
70 static gint ett_nasdaq_soup = -1;
71
72 static int hf_nasdaq_soup_packet_type = -1; 
73 static int hf_nasdaq_soup_message = -1; 
74 static int hf_nasdaq_soup_text = -1; 
75 static int hf_nasdaq_soup_packet_eol = -1; 
76 static int hf_nasdaq_soup_username = -1; 
77 static int hf_nasdaq_soup_password = -1; 
78 static int hf_nasdaq_soup_session = -1; 
79 static int hf_nasdaq_soup_seq_number = -1; 
80 static int hf_nasdaq_soup_reject_code = -1; 
81
82 static void
83 dissect_nasdaq_soup_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, proto_tree *tree, int offset, int linelen)
84 {
85     guint8   nasdaq_soup_type;
86     tvbuff_t *new_tvb = NULL;
87
88     nasdaq_soup_type = tvb_get_guint8(tvb, offset);
89     proto_tree_add_item(tree, hf_nasdaq_soup_packet_type, tvb, offset, 1, FALSE);
90     offset++;
91
92     switch (nasdaq_soup_type) {
93     case '+': /* debug msg */
94         proto_tree_add_item(tree, hf_nasdaq_soup_text, tvb, offset, linelen -1, FALSE);
95         offset += linelen -1;
96         break;
97     case 'A': /* login accept */
98         proto_tree_add_item(tree, hf_nasdaq_soup_session, tvb, offset, 10, FALSE);
99         offset += 10;
100
101         proto_tree_add_item(tree, hf_nasdaq_soup_seq_number, tvb, offset, 10, FALSE);
102         offset += 10;
103         break;
104     case 'J': /* login reject */
105         proto_tree_add_item(tree, hf_nasdaq_soup_reject_code, tvb, offset, 1, FALSE);
106         offset++;
107         break;
108
109     case 'U': /* unsequenced data packed */
110     case 'S': /* sequenced data packed */
111         if (linelen > 1 && nasdaq_itch_handle) {
112             new_tvb = tvb_new_subset(tvb, offset,linelen -1,linelen -1);
113         } else {
114             proto_tree_add_item(tree, hf_nasdaq_soup_message, tvb, offset, linelen -1, FALSE);
115         }
116         offset += linelen -1;
117         break;
118
119     case 'L': /* login request */
120         proto_tree_add_item(tree, hf_nasdaq_soup_username, tvb, offset, 6, FALSE);
121         offset += 6;
122
123         proto_tree_add_item(tree, hf_nasdaq_soup_password, tvb, offset, 10, FALSE);
124         offset += 10;
125
126         proto_tree_add_item(tree, hf_nasdaq_soup_session, tvb, offset, 10, FALSE);
127         offset += 10;
128
129         proto_tree_add_item(tree, hf_nasdaq_soup_seq_number, tvb, offset, 10, FALSE);
130         offset += 10;
131         break;
132
133     case 'H': /* server heartbeat */
134     case 'O': /* logout request */
135     case 'R': /* client heartbeat */
136         /* no payload */
137         break;
138     default:
139         /* unknown */
140         proto_tree_add_item(tree, hf_nasdaq_soup_message, tvb, offset, linelen -1, FALSE);
141         offset += linelen -1;
142         break;
143     }
144
145     proto_tree_add_item(tree, hf_nasdaq_soup_packet_eol, tvb, offset, 1, FALSE);
146     if (new_tvb) {
147         call_dissector(nasdaq_itch_handle, new_tvb, pinfo, parent_tree);
148     }
149     return;
150 }
151
152 /* ---------------------------- */
153 static void
154 dissect_nasdaq_soup(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
155 {
156     proto_item *ti;
157     proto_tree *nasdaq_soup_tree = NULL;
158     guint8 nasdaq_soup_type;
159     int  linelen;
160     gint next_offset;
161     int  offset = 0;
162     gint col_info;
163     gint counter = 0;
164
165     col_info = check_col(pinfo->cinfo, COL_INFO);
166     while (tvb_offset_exists(tvb, offset)) {
167       /* there's only a \n no \r */
168       linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, nasdaq_soup_desegment && pinfo->can_desegment);
169       if (linelen == -1) {
170         /*
171          * We didn't find a line ending, and we're doing desegmentation;
172          * tell the TCP dissector where the data for this message starts
173          * in the data it handed us, and tell it we need one more byte
174          * (we may need more, but we'll try again if what we get next
175          * isn't enough), and return.
176          */
177         pinfo->desegment_offset = offset;
178         pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
179         return;
180       }
181
182       nasdaq_soup_type = tvb_get_guint8(tvb, offset);
183       if (counter == 0) {
184         col_set_str(pinfo->cinfo, COL_PROTOCOL, "Nasdaq-SOUP");
185         if (col_info)
186             col_clear(pinfo->cinfo, COL_INFO);
187       }
188       if (col_info ) {
189         if (counter) {
190           col_append_str(pinfo->cinfo, COL_INFO, "; ");
191           col_set_fence(pinfo->cinfo, COL_INFO);
192         }
193         col_append_str(pinfo->cinfo, COL_INFO, val_to_str(nasdaq_soup_type, message_types_val, "Unknown packet type (0x%02x)"));
194       }
195       counter++;
196       if (tree) {
197           ti = proto_tree_add_item(tree, proto_nasdaq_soup, tvb, offset, linelen +1, FALSE);
198           nasdaq_soup_tree = proto_item_add_subtree(ti, ett_nasdaq_soup);
199       }
200       dissect_nasdaq_soup_packet(tvb, pinfo, tree, nasdaq_soup_tree, offset, linelen);
201       offset = next_offset;
202     }
203 }
204
205 /* Register the protocol with Wireshark */
206 static void range_delete_nasdaq_soup_tcp_callback(guint32 port) {
207     dissector_delete("tcp.port", port, nasdaq_soup_handle);
208 }
209
210 static void range_add_nasdaq_soup_tcp_callback(guint32 port) {
211     dissector_add("tcp.port", port, nasdaq_soup_handle);
212 }
213
214 static void nasdaq_soup_prefs(void) 
215 {
216     range_foreach(nasdaq_soup_tcp_range, range_delete_nasdaq_soup_tcp_callback);
217     g_free(nasdaq_soup_tcp_range);
218     nasdaq_soup_tcp_range = range_copy(global_nasdaq_soup_tcp_range);
219     range_foreach(nasdaq_soup_tcp_range, range_add_nasdaq_soup_tcp_callback);
220 }
221
222 void
223 proto_register_nasdaq_soup(void)
224 {
225
226 /* Setup list of header fields  See Section 1.6.1 for details*/
227     static hf_register_info hf[] = {
228
229     { &hf_nasdaq_soup_packet_type, 
230       { "Packet Type",       "nasdaq-soup.packet_type",
231         FT_UINT8, BASE_DEC, VALS(message_types_val), 0x0,
232         NULL, HFILL }},
233
234     { &hf_nasdaq_soup_reject_code,
235       { "Login Reject Code", "nasdaq-soup.reject_code",
236         FT_UINT8, BASE_DEC, VALS(reject_code_val), 0x0,
237         NULL, HFILL }},
238
239     { &hf_nasdaq_soup_message,
240       { "Message",           "nasdaq-soup.message",
241         FT_STRING, BASE_NONE, NULL, 0x0,
242         NULL, HFILL }},
243
244     { &hf_nasdaq_soup_text,
245       { "Debug Text",        "nasdaq-soup.text",
246         FT_STRING, BASE_NONE, NULL, 0x0,
247         NULL, HFILL }},
248
249     { &hf_nasdaq_soup_username,
250       { "User Name",         "nasdaq-soup.username",
251         FT_STRING, BASE_NONE, NULL, 0x0,
252         NULL, HFILL }},
253
254     { &hf_nasdaq_soup_password,
255       { "Password",          "nasdaq-soup.password",
256         FT_STRING, BASE_NONE, NULL, 0x0,
257         NULL, HFILL }},
258
259     { &hf_nasdaq_soup_session,
260       { "Session",           "nasdaq-soup.session",
261         FT_STRING, BASE_NONE, NULL, 0x0,
262         "Session ID", HFILL }},
263
264     { &hf_nasdaq_soup_seq_number,
265       { "Sequence number",   "nasdaq-soup.seq_number",
266         FT_STRING, BASE_NONE, NULL, 0x0,
267         NULL, HFILL }},
268
269     { &hf_nasdaq_soup_packet_eol,
270       { "End Of Packet",     "nasdaq-soup.packet_eol",
271         FT_STRING, BASE_NONE, NULL, 0x0,
272         NULL, HFILL }}
273     };
274
275     /* Setup protocol subtree array */
276     static gint *ett[] = {
277         &ett_nasdaq_soup
278     };
279
280     module_t *nasdaq_soup_module;
281
282     /* Register the protocol name and description */
283     proto_nasdaq_soup = proto_register_protocol("Nasdaq-SoupTCP version 2.0","NASDAQ-SOUP", "nasdaq_soup");
284
285     /* Required function calls to register the header fields and subtrees used */
286     proto_register_field_array(proto_nasdaq_soup, hf, array_length(hf));
287     proto_register_subtree_array(ett, array_length(ett));
288
289     nasdaq_soup_module = prefs_register_protocol(proto_nasdaq_soup, nasdaq_soup_prefs);
290     prefs_register_bool_preference(nasdaq_soup_module, "desegment",
291         "Reassemble Nasdaq-SoupTCP messages spanning multiple TCP segments",
292         "Whether the Nasdaq-SoupTCP dissector should reassemble messages spanning multiple TCP segments.",
293         &nasdaq_soup_desegment);
294
295     prefs_register_range_preference(nasdaq_soup_module, "tcp.port", "TCP Ports", "TCP Ports range", &global_nasdaq_soup_tcp_range, 65535);
296
297     nasdaq_soup_tcp_range = range_empty();
298 }
299
300 /* If this dissector uses sub-dissector registration add a registration routine.
301    This format is required because a script is used to find these routines and
302    create the code that calls these routines.
303 */
304 void
305 proto_reg_handoff_nasdaq_soup(void)
306 {
307     nasdaq_soup_handle = create_dissector_handle(dissect_nasdaq_soup, proto_nasdaq_soup);
308     nasdaq_itch_handle = find_dissector("nasdaq-itch");
309     dissector_add_handle("tcp.port", nasdaq_soup_handle); /* for "decode-as" */
310 }
311