#include <emem.h> not req'd
[obnox/wireshark/wip.git] / epan / dissectors / packet-lsc.c
1 /* packet-lsc.c
2  * Routines for Pegasus LSC packet disassembly
3  * Copyright 2006, Sean Sheedy <seansh@users.sourceforge.net>
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
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include <glib.h>
35
36 #include <epan/packet.h>
37 #include <epan/prefs.h>
38
39 #include <packet-tcp.h>
40
41 /* Forward declaration we need below */
42 void proto_reg_handoff_lsc(void);
43
44 #define LSC_PAUSE        0x01
45 #define LSC_RESUME       0x02
46 #define LSC_STATUS       0x03
47 #define LSC_RESET        0x04
48 #define LSC_JUMP         0x05
49 #define LSC_PLAY         0x06
50 #define LSC_DONE         0x40
51 #define LSC_PAUSE_REPLY  0x81
52 #define LSC_RESUME_REPLY 0x82
53 #define LSC_STATUS_REPLY 0x83
54 #define LSC_RESET_REPLY  0x84
55 #define LSC_JUMP_REPLY   0x85
56 #define LSC_PLAY_REPLY   0x86
57
58 #define isReply(o) ((o) & 0x80)
59
60 static const value_string op_code_vals[] = {
61   { LSC_PAUSE,          "LSC_PAUSE" },
62   { LSC_RESUME,         "LSC_RESUME" },
63   { LSC_STATUS,         "LSC_STATUS" },
64   { LSC_RESET,          "LSC_RESET" },
65   { LSC_JUMP,           "LSC_JUMP" },
66   { LSC_PLAY,           "LSC_PLAY" },
67   { LSC_DONE,           "LSC_DONE" },
68   { LSC_PAUSE_REPLY,    "LSC_PAUSE_REPLY" },
69   { LSC_RESUME_REPLY,   "LSC_RESUME_REPLY" },
70   { LSC_STATUS_REPLY,   "LSC_STATUS_REPLY" },
71   { LSC_RESET_REPLY,    "LSC_RESET_REPLY" },
72   { LSC_JUMP_REPLY,     "LSC_JUMP_REPLY" },
73   { LSC_PLAY_REPLY,     "LSC_PLAY_REPLY" },
74   { 0,                  NULL }
75 };
76
77 #define LSC_OPCODE_LEN   3              /* Length to find op code */
78 #define LSC_MIN_LEN      8              /* Minimum packet length */
79 /* Length of each packet type */
80 #define LSC_PAUSE_LEN   12
81 #define LSC_RESUME_LEN  16
82 #define LSC_STATUS_LEN   8
83 #define LSC_RESET_LEN    8
84 #define LSC_JUMP_LEN    20
85 #define LSC_PLAY_LEN    20
86 #define LSC_REPLY_LEN   17
87
88 static const value_string status_code_vals[] = {
89   { 0x00,       "LSC_OK" },
90   { 0x10,       "LSC_BAD_REQUEST" },
91   { 0x11,       "LSC_BAD_STREAM" },
92   { 0x12,       "LSC_WRONG_STATE" },
93   { 0x13,       "LSC_UNKNOWN" },
94   { 0x14,       "LSC_NO_PERMISSION" },
95   { 0x15,       "LSC_BAD_PARAM" },
96   { 0x16,       "LSC_NO_IMPLEMENT" },
97   { 0x17,       "LSC_NO_MEMORY" },
98   { 0x18,       "LSC_IMP_LIMIT" },
99   { 0x19,       "LSC_TRANSIENT" },
100   { 0x1a,       "LSC_NO_RESOURCES" },
101   { 0x20,       "LSC_SERVER_ERROR" },
102   { 0x21,       "LSC_SERVER_FAILURE" },
103   { 0x30,       "LSC_BAD_SCALE" },
104   { 0x31,       "LSC_BAD_START" },
105   { 0x32,       "LSC_BAD_STOP" },
106   { 0x40,       "LSC_MPEG_DELIVERY" },
107   { 0,          NULL }
108 };
109
110 static const value_string mode_vals[] = {
111   { 0x00,       "O   - Open Mode" },
112   { 0x01,       "P   - Pause Mode" },
113   { 0x02,       "ST  - Search Transport" },
114   { 0x03,       "T   - Transport" },
115   { 0x04,       "TP  - Transport Pause" },
116   { 0x05,       "STP - Search Transport Pause" },
117   { 0x06,       "PST - Pause Search Transport" },
118   { 0x07,       "EOS - End of Stream" },
119   { 0,          NULL }
120 };
121
122 /* Initialize the protocol and registered fields */
123 static int proto_lsc = -1;
124 static int hf_lsc_version = -1;
125 static int hf_lsc_trans_id = -1;
126 static int hf_lsc_op_code = -1;
127 static int hf_lsc_status_code = -1;
128 static int hf_lsc_stream_handle = -1;
129 static int hf_lsc_start_npt = -1;
130 static int hf_lsc_stop_npt = -1;
131 static int hf_lsc_current_npt = -1;
132 static int hf_lsc_scale_num = -1;
133 static int hf_lsc_scale_denom = -1;
134 static int hf_lsc_mode = -1;
135
136 /* Preferences */
137 static guint global_lsc_port = 0;
138 static guint lsc_port = 0;
139
140
141 /* Initialize the subtree pointers */
142 static gint ett_lsc = -1;
143
144 /* Code to actually dissect the packets */
145 static void
146 dissect_lsc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
147 {
148   proto_item *ti;
149   proto_tree *lsc_tree;
150   guint8 op_code;
151   guint32 stream;
152   guint expected_len;
153
154   /* Protocol is LSC, packet summary is not yet known */
155   if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
156     col_set_str(pinfo->cinfo, COL_PROTOCOL, "LSC");
157   if (check_col(pinfo->cinfo, COL_INFO))
158     col_clear(pinfo->cinfo, COL_INFO);
159
160   /* Too little data? */
161   if (tvb->length < LSC_MIN_LEN)
162   {
163     if (check_col(pinfo->cinfo, COL_INFO))
164       col_set_str(pinfo->cinfo, COL_INFO, "[Too short]");
165     return;
166   }
167
168   /* Get the op code */
169   op_code = tvb_get_guint8(tvb, 2);
170   /* And the stream handle */
171   stream = tvb_get_ntohl(tvb, 4);
172
173   /* Check the data length against what we actually received */
174   switch (op_code)
175     {
176       case LSC_PAUSE:
177         expected_len = LSC_PAUSE_LEN;
178         break;
179       case LSC_RESUME:
180         expected_len = LSC_RESUME_LEN;
181         break;
182       case LSC_STATUS:
183         expected_len = LSC_STATUS_LEN;
184         break;
185       case LSC_RESET:
186         expected_len = LSC_RESET_LEN;
187         break;
188       case LSC_JUMP:
189         expected_len = LSC_JUMP_LEN;
190         break;
191       case LSC_PLAY:
192         expected_len = LSC_PLAY_LEN;
193         break;
194       case LSC_DONE:
195       case LSC_PAUSE_REPLY:
196       case LSC_RESUME_REPLY:
197       case LSC_STATUS_REPLY:
198       case LSC_RESET_REPLY:
199       case LSC_JUMP_REPLY:
200       case LSC_PLAY_REPLY:
201         expected_len = LSC_REPLY_LEN;
202         break;
203       default:
204         /* Unrecognized op code */
205         expected_len = LSC_MIN_LEN;
206         break;
207     }
208
209   /* Display the op code in the summary */
210   if (check_col(pinfo->cinfo, COL_INFO)) {
211     col_add_fstr(pinfo->cinfo, COL_INFO, "%s, session %.8u",
212                  val_to_str(op_code, op_code_vals, "Unknown op code (0x%x)"),
213                  stream);
214
215     if (tvb->length < expected_len)
216       col_append_str(pinfo->cinfo, COL_INFO, " [Too short]");
217     else if (tvb->length > expected_len)
218       col_append_str(pinfo->cinfo, COL_INFO, " [Too long]");
219   }
220
221   if (tree) {
222     /* Create display subtree for the protocol */
223     ti = proto_tree_add_item(tree, proto_lsc, tvb, 0, -1, FALSE);
224     lsc_tree = proto_item_add_subtree(ti, ett_lsc);
225
226     /* Add already fetched items to the tree */
227     proto_tree_add_uint(lsc_tree, hf_lsc_op_code, tvb, 2, 1, op_code);
228     proto_tree_add_uint_format_value(lsc_tree, hf_lsc_stream_handle, tvb, 4, 4,
229                                      stream, "%.8u", stream);
230
231     /* Add rest of LSC header */
232     proto_tree_add_uint(lsc_tree, hf_lsc_version, tvb, 0, 1,
233                         tvb_get_guint8(tvb, 0));
234     proto_tree_add_uint(lsc_tree, hf_lsc_trans_id, tvb, 1, 1,
235                         tvb_get_guint8(tvb, 1));
236
237     /* Only replies contain a status code */
238     if (isReply(op_code))
239       proto_tree_add_uint(lsc_tree, hf_lsc_status_code, tvb, 3, 1,
240                           tvb_get_guint8(tvb, 3));
241
242     /* Add op code specific parts */
243     switch (op_code)
244       {
245         case LSC_PAUSE:
246           proto_tree_add_int(lsc_tree, hf_lsc_stop_npt, tvb, 8, 4,
247                              tvb_get_ntohl(tvb, 8));
248           break;
249         case LSC_RESUME:
250           proto_tree_add_int(lsc_tree, hf_lsc_start_npt, tvb, 8, 4,
251                              tvb_get_ntohl(tvb, 8));
252           proto_tree_add_int(lsc_tree, hf_lsc_scale_num, tvb, 12, 2,
253                              tvb_get_ntohs(tvb, 12));
254           proto_tree_add_uint(lsc_tree, hf_lsc_scale_denom, tvb, 14, 2,
255                               tvb_get_ntohs(tvb, 14));
256           break;
257         case LSC_JUMP:
258         case LSC_PLAY:
259           proto_tree_add_int(lsc_tree, hf_lsc_start_npt, tvb, 8, 4,
260                              tvb_get_ntohl(tvb, 8));
261           proto_tree_add_int(lsc_tree, hf_lsc_stop_npt, tvb, 12, 4,
262                              tvb_get_ntohl(tvb, 12));
263           proto_tree_add_int(lsc_tree, hf_lsc_scale_num, tvb, 16, 2,
264                              tvb_get_ntohs(tvb, 16));
265           proto_tree_add_uint(lsc_tree, hf_lsc_scale_denom, tvb, 18, 2,
266                               tvb_get_ntohs(tvb, 18));
267           break;
268         case LSC_DONE:
269         case LSC_PAUSE_REPLY:
270         case LSC_RESUME_REPLY:
271         case LSC_STATUS_REPLY:
272         case LSC_RESET_REPLY:
273         case LSC_JUMP_REPLY:
274         case LSC_PLAY_REPLY:
275           proto_tree_add_int(lsc_tree, hf_lsc_current_npt, tvb, 8, 4,
276                              tvb_get_ntohl(tvb, 8));
277           proto_tree_add_int(lsc_tree, hf_lsc_scale_num, tvb, 12, 2,
278                              tvb_get_ntohs(tvb, 12));
279           proto_tree_add_uint(lsc_tree, hf_lsc_scale_denom, tvb, 14, 2,
280                               tvb_get_ntohs(tvb, 14));
281           proto_tree_add_uint(lsc_tree, hf_lsc_mode, tvb, 16, 1,
282                               tvb_get_guint8(tvb, 16));
283           break;
284         default:
285           break;
286       }
287   }
288 }
289
290 /* Decode LSC over UDP */
291 static void
292 dissect_lsc_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
293 {
294   dissect_lsc_common(tvb, pinfo, tree);
295 }
296
297 /* Determine length of LSC message */
298 static guint
299 get_lsc_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
300 {
301   guint8 op_code;
302   guint pdu_len;
303
304   /* Get the op code */
305   op_code = tvb_get_guint8(tvb, offset + 2);
306
307   switch (op_code)
308     {
309       case LSC_PAUSE:
310         pdu_len = LSC_PAUSE_LEN;
311         break;
312       case LSC_RESUME:
313         pdu_len = LSC_RESUME_LEN;
314         break;
315       case LSC_STATUS:
316         pdu_len = LSC_STATUS_LEN;
317         break;
318       case LSC_RESET:
319         pdu_len = LSC_RESET_LEN;
320         break;
321       case LSC_JUMP:
322         pdu_len = LSC_JUMP_LEN;
323         break;
324       case LSC_PLAY:
325         pdu_len = LSC_PLAY_LEN;
326         break;
327       case LSC_DONE:
328       case LSC_PAUSE_REPLY:
329       case LSC_RESUME_REPLY:
330       case LSC_STATUS_REPLY:
331       case LSC_RESET_REPLY:
332       case LSC_JUMP_REPLY:
333       case LSC_PLAY_REPLY:
334         pdu_len = LSC_REPLY_LEN;
335         break;
336       default:
337         /* Unrecognized op code */
338         pdu_len = LSC_OPCODE_LEN;
339         break;
340     }
341
342   return pdu_len;
343 }
344
345 /* Decode LSC over TCP */
346 static void
347 dissect_lsc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
348 {
349   tcp_dissect_pdus(tvb, pinfo, tree, TRUE, LSC_OPCODE_LEN, get_lsc_pdu_len,
350                    dissect_lsc_common);
351 }
352
353 /* Register the protocol with Wireshark */
354 void
355 proto_register_lsc(void)
356 {                 
357   module_t *lsc_module;
358
359   /* Setup list of header fields */
360   static hf_register_info hf[] = {
361     { &hf_lsc_version,
362       { "Version", "lsc.version",
363         FT_UINT8, BASE_DEC, NULL, 0,          
364         "Version of the Pegasus LSC protocol", HFILL }
365     },
366     { &hf_lsc_trans_id,
367       { "Transaction ID", "lsc.trans_id",
368         FT_UINT8, BASE_DEC, NULL, 0,          
369         "Transaction ID", HFILL }
370     },
371     { &hf_lsc_op_code,
372       { "Op Code", "lsc.op_code",
373         FT_UINT8, BASE_HEX, VALS(op_code_vals), 0,          
374         "Operation Code", HFILL }
375     },
376     { &hf_lsc_status_code,
377       { "Status Code", "lsc.status_code",
378         FT_UINT8, BASE_HEX, VALS(status_code_vals), 0,          
379         "Status Code", HFILL }
380     },
381     { &hf_lsc_stream_handle,
382       { "Stream Handle", "lsc.stream_handle",
383         FT_UINT32, BASE_DEC, NULL, 0,          
384         "Stream identification handle", HFILL }
385     },
386     { &hf_lsc_start_npt,
387       { "Start NPT", "lsc.start_npt",
388         FT_INT32, BASE_DEC, NULL, 0,          
389         "Start Time (milliseconds)", HFILL }
390     },
391     { &hf_lsc_stop_npt,
392       { "Stop NPT", "lsc.stop_npt",
393         FT_INT32, BASE_DEC, NULL, 0,          
394         "Stop Time (milliseconds)", HFILL }
395     },
396     { &hf_lsc_current_npt,
397       { "Current NPT", "lsc.current_npt",
398         FT_INT32, BASE_DEC, NULL, 0,          
399         "Current Time (milliseconds)", HFILL }
400     },
401     { &hf_lsc_scale_num,
402       { "Scale Numerator", "lsc.scale_num",
403         FT_INT16, BASE_DEC, NULL, 0,          
404         "Scale Numerator", HFILL }
405     },
406     { &hf_lsc_scale_denom,
407       { "Scale Denominator", "lsc.scale_denum",
408         FT_UINT16, BASE_DEC, NULL, 0,          
409         "Scale Denominator", HFILL }
410     },
411     { &hf_lsc_mode,
412       { "Server Mode", "lsc.mode",
413         FT_UINT8, BASE_HEX, VALS(mode_vals), 0,          
414         "Current Server Mode", HFILL }
415     }
416   };
417
418   /* Setup protocol subtree array */
419   static gint *ett[] = {
420     &ett_lsc,
421   };
422
423   /* Register the protocol name and description */
424   proto_lsc = proto_register_protocol("Pegasus Lightweight Stream Control",
425                                       "LSC", "lsc");
426
427   /* Required function calls to register the header fields and subtrees used */
428   proto_register_field_array(proto_lsc, hf, array_length(hf));
429   proto_register_subtree_array(ett, array_length(ett));
430
431   /* Register preferences module */       
432   lsc_module = prefs_register_protocol(proto_lsc, proto_reg_handoff_lsc);
433      
434   /* Register preferences */        
435   prefs_register_uint_preference(lsc_module, "port",
436                             "LSC Port",
437                             "Set the TCP or UDP port for Pegasus LSC messages",
438                             10, &global_lsc_port);
439 }
440
441 void
442 proto_reg_handoff_lsc(void)
443 {
444   dissector_handle_t lsc_udp_handle;
445   dissector_handle_t lsc_tcp_handle;
446
447   lsc_udp_handle = create_dissector_handle(dissect_lsc_udp, proto_lsc);
448   lsc_tcp_handle = create_dissector_handle(dissect_lsc_tcp, proto_lsc);
449
450   /* Set the port number */
451   lsc_port = global_lsc_port;
452
453   dissector_add("udp.port", lsc_port, lsc_udp_handle);
454   dissector_add("tcp.port", lsc_port, lsc_tcp_handle);
455 }