1/* Do not modify this file. Changes will be overwritten */
2/* Generated Automatically                              */
3/* packet-skinny.c                                      */
4
5/* packet-skinny.c
6 * Dissector for the Skinny Client Control Protocol
7 *   (The "D-Channel"-Protocol for Cisco Systems' IP-Phones)
8 *
9 * Author: Diederik de Groot <ddegroot@user.sf.net>, Copyright 2014
10 * Rewritten to support newer skinny protocolversions (V0-V22)
11 * Based on previous versions/contributions:
12 *  - Joerg Mayer <jmayer@loplof.de>, Copyright 2001
13 *  - Paul E. Erkkila (pee@erkkila.org) - fleshed out the decode
14 *    skeleton to report values for most message/message fields.
15 *    Much help from Guy Harris on figuring out the wireshark api.
16 *  - packet-aim.c by Ralf Hoelzer <ralf@well.com>, Copyright 2000
17 *  - Wireshark - Network traffic analyzer,
18 *    By Gerald Combs <gerald@wireshark.org>, Copyright 1998
19 *
20 * SPDX-License-Identifier: GPL-2.0-or-later
21 */
22
23/* [[[cog
24#
25# Using Cog.py Inplace Code Generator
26#
27# Dependencies:
28# - python2.x
29# - cog.py: (pip install cogapp / http://nedbatchelder.com/code/cog/)
30# - python.xml
31# - python.xml.sax
32#
33cog.out('/*\n')
34cog.out(' * Generated Automatically Using (from wireshark base directory):\n')
35cog.out(' *   cog.py -D xmlfile=tools/SkinnyProtocolOptimized.xml -d -c -o epan/dissectors/packet-skinny.c epan/dissectors/packet-skinny.c.in\n')
36cog.out(' */\n')
37/*]]]*/
38/*[[[end]]]*/
39
40/* c-basic-offset: 2; tab-width: 8; indent-tabs-mode: nil
41 * vi: set shiftwidth=2 tabstop=8 expandtab:
42 * :indentSize=2:tabSize=8:noTabs=true:
43 */
44
45
46#include "config.h"
47
48#include <epan/packet.h>
49#include <epan/prefs.h>
50#include <epan/conversation.h>
51#include <epan/wmem_scopes.h>
52#include <epan/to_str.h>
53#include <epan/reassemble.h>
54#include <epan/tap.h>
55#include <epan/ptvcursor.h>
56
57#include "packet-rtp.h"
58#include "packet-tcp.h"
59#include "packet-tls.h"
60#include "packet-skinny.h"
61
62/* un-comment the following as well as this line in conversation.c, to enable debug printing */
63/* #define DEBUG_CONVERSATION */
64#include "conversation_debug.h"
65
66void proto_register_skinny(void);
67void proto_reg_handoff_skinny(void);
68
69#define TCP_PORT_SKINNY 2000 /* Not IANA registered */
70#define SSL_PORT_SKINNY 2443 /* IANA assigned to PowerClient Central Storage Facility */
71
72#define BASIC_MSG_TYPE 0x00
73#define V10_MSG_TYPE 0x0A
74#define V11_MSG_TYPE 0x0B
75#define V15_MSG_TYPE 0x0F
76#define V16_MSG_TYPE 0x10
77#define V17_MSG_TYPE 0x11
78#define V18_MSG_TYPE 0x12
79#define V19_MSG_TYPE 0x13
80#define V20_MSG_TYPE 0x14
81#define V21_MSG_TYPE 0x15
82#define V22_MSG_TYPE 0x16
83
84static const value_string header_version[] = {
85  { BASIC_MSG_TYPE, "Basic" },
86  { V10_MSG_TYPE,   "V10" },
87  { V11_MSG_TYPE,   "V11" },
88  { V15_MSG_TYPE,   "V15" },
89  { V16_MSG_TYPE,   "V16" },
90  { V17_MSG_TYPE,   "V17" },
91  { V18_MSG_TYPE,   "V18" },
92  { V19_MSG_TYPE,   "V19" },
93  { V20_MSG_TYPE,   "V20" },
94  { V21_MSG_TYPE,   "V21" },
95  { V22_MSG_TYPE,   "V22" },
96  { 0             , NULL }
97};
98
99/* Declare MessageId */
100/* [[[cog
101import sys
102sys.path.append('tools/')
103
104import parse_xml2skinny_dissector as xml2skinny
105global skinny
106global message_dissector_functions
107
108message_dissector_functions = ''
109skinny = xml2skinny.xml2obj(xmlfile)
110
111cog.out('static const value_string message_id[] = {\n')
112for message in skinny.message:
113    message_dissector_functions += '%s' %message.dissect()
114    cog.out('  { %s, "%s" },\n' %(message.opcode, message.name.replace('Message','')))
115cog.out('  {0     , NULL}\n')
116cog.out('};\n')
117cog.out('static value_string_ext message_id_ext = VALUE_STRING_EXT_INIT(message_id);\n')
118/*]]]*/
119/*[[[end]]]*/
120
121/* Declare Enums and Defines */
122/* [[[cog
123for enum in skinny.enum:
124    name = enum.name[0].upper() + enum.name[1:]
125    if enum.define == "yes":
126        for entries in enum.entries:
127            for entry in sorted(entries.entry, key=lambda x: int(x['value'],0)):
128                if entries.type is not None:
129                    cog.out('#define {0:38} 0x{1:05x} /* {2} */\n' .format(entry.name.upper(), int(entry.value,0), entries.type))
130                else:
131                    cog.out('#define {0:38} 0x{1:05x}\n' .format(entry.name.upper(), int(entry.value,0)))
132        cog.out('\n')
133    cog.out('static const value_string %s[] = {\n' %(name))
134    for entries in enum.entries:
135        for entry in sorted(entries.entry, key=lambda x: int(x['value'],0)):
136            if enum.define == "yes":
137                cog.out('  { %s, "%s" },\n' %(entry.name.upper(), entry.text))
138            else:
139                cog.out('  { 0x%05x, "%s" },\n' %(int(entry.value,0), entry.text))
140    cog.out('  { 0x00000, NULL }\n')
141    cog.out('};\n')
142    cog.out('static value_string_ext %s_ext = VALUE_STRING_EXT_INIT(%s);\n\n' %(name, name))
143/*]]]*/
144/*[[[end]]]*/
145
146/* Staticly Declared Variables */
147static int proto_skinny                 = -1;
148static int hf_skinny_messageId          = -1;
149static int hf_skinny_data_length        = -1;
150static int hf_skinny_hdr_version        = -1;
151static int hf_skinny_xmlData            = -1;
152static int hf_skinny_ipv4or6            = -1;
153static int hf_skinny_response_in        = -1;
154static int hf_skinny_response_to        = -1;
155static int hf_skinny_response_time      = -1;
156
157/* [[[cog
158for key in sorted(xml2skinny.fieldsArray.keys()):
159    cog.out('static int hf_skinny_%s = -1;\n' %key)
160]]]*/
161/*[[[end]]]*/
162
163static dissector_handle_t xml_handle;
164
165/* Initialize the subtree pointers */
166static gint ett_skinny          = -1;
167static gint ett_skinny_tree     = -1;
168
169/* preference globals */
170static gboolean global_skinny_desegment = TRUE;
171
172/* tap register id */
173static int skinny_tap = -1;
174
175/* skinny protocol tap info */
176#define MAX_SKINNY_MESSAGES_IN_PACKET 10
177static skinny_info_t pi_arr[MAX_SKINNY_MESSAGES_IN_PACKET];
178static int pi_current = 0;
179static skinny_info_t *si;
180
181dissector_handle_t skinny_handle;
182
183/* Get the length of a single SKINNY PDU */
184static guint
185get_skinny_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
186{
187  guint32 hdr_data_length;
188
189  /* Get the length of the SKINNY packet. */
190  hdr_data_length = tvb_get_letohl(tvb, offset);
191
192  /* That length doesn't include the length of the header itself. */
193  return hdr_data_length + 8;
194}
195
196static void
197dissect_skinny_xml(ptvcursor_t *cursor, int hfindex, packet_info *pinfo, guint32 length, guint32 maxlength)
198{
199  proto_item         *item       = NULL;
200  proto_tree         *subtree    = NULL;
201  proto_tree         *tree       = ptvcursor_tree(cursor);
202  guint32            offset      = ptvcursor_current_offset(cursor);
203  tvbuff_t           *tvb        = ptvcursor_tvbuff(cursor);
204  tvbuff_t           *next_tvb;
205
206  if (length == 0) {
207    length = tvb_strnlen(tvb, offset, -1);
208  }
209  if (length >= maxlength) {
210    length = maxlength;
211  }
212
213  ptvcursor_add_no_advance(cursor, hfindex, length, ENC_ASCII|ENC_NA);
214
215  item = proto_tree_add_item(tree, hf_skinny_xmlData, tvb, offset, length, ENC_ASCII|ENC_NA);
216  subtree = proto_item_add_subtree(item, 0);
217  next_tvb = tvb_new_subset_length_caplen(tvb, offset, length, -1);
218  if (xml_handle != NULL) {
219    call_dissector(xml_handle, next_tvb, pinfo, subtree);
220  }
221  ptvcursor_advance(cursor, maxlength);
222}
223
224static void
225dissect_skinny_ipv4or6(ptvcursor_t *cursor, int hfindex_ipv4, int hfindex_ipv6)
226{
227  guint32            ipversion   = 0;
228  guint32            offset      = ptvcursor_current_offset(cursor);
229  tvbuff_t           *tvb        = ptvcursor_tvbuff(cursor);
230  guint32            hdr_version = tvb_get_letohl(tvb, 4);
231
232  /* ProtocolVersion > 18 include and extra field to declare IPv4 (0) / IPv6 (1) */
233  if (hdr_version >= V17_MSG_TYPE) {
234    ipversion = tvb_get_letohl(tvb, offset);
235    ptvcursor_add(cursor, hf_skinny_ipv4or6, 4, ENC_LITTLE_ENDIAN);
236  }
237  if (ipversion == IPADDRTYPE_IPV4) {
238    ptvcursor_add(cursor, hfindex_ipv4, 4, ENC_BIG_ENDIAN);
239    if (hdr_version >= V17_MSG_TYPE) {
240      /* skip over the extra room for ipv6 addresses */
241      ptvcursor_advance(cursor, 12);
242    }
243  } else if (ipversion == IPADDRTYPE_IPV6 || ipversion == IPADDRTYPE_IPV4_V6) {
244    ptvcursor_add(cursor, hfindex_ipv6, 16, ENC_NA);
245  } else {
246    /* Invalid : skip over ipv6 space completely */
247    ptvcursor_advance(cursor, 16);
248  }
249}
250
251/* Reads address to provided variable */
252static void
253read_skinny_ipv4or6(ptvcursor_t *cursor, address *media_addr)
254{
255  guint32            ipversion   = IPADDRTYPE_IPV4;
256  guint32            offset      = ptvcursor_current_offset(cursor);
257  guint32            offset2     = 0;
258  tvbuff_t           *tvb        = ptvcursor_tvbuff(cursor);
259  guint32            hdr_version = tvb_get_letohl(tvb, 4);
260
261  /* ProtocolVersion > 18 include and extra field to declare IPv4 (0) / IPv6 (1) */
262  if (hdr_version >= V17_MSG_TYPE) {
263    ipversion = tvb_get_letohl(tvb, offset);
264    offset2 = 4;
265  }
266  if (ipversion == IPADDRTYPE_IPV4) {
267    set_address_tvb(media_addr, AT_IPv4, 4, tvb, offset+offset2);
268  } else if (ipversion == IPADDRTYPE_IPV6 || ipversion == IPADDRTYPE_IPV4_V6) {
269    set_address_tvb(media_addr, AT_IPv6, 16, tvb, offset+offset2);
270  } else {
271    clear_address(media_addr);
272  }
273}
274
275/**
276 * Parse a displayLabel string and check if it is using any embedded labels, if so lookup the label and add a user readable translation to the item_tree
277 */
278static void
279dissect_skinny_displayLabel(ptvcursor_t *cursor, packet_info *pinfo, int hfindex, gint length)
280{
281  proto_item    *item             = NULL;
282  proto_tree    *tree             = ptvcursor_tree(cursor);
283  guint32       offset            = ptvcursor_current_offset(cursor);
284  tvbuff_t      *tvb              = ptvcursor_tvbuff(cursor);
285  wmem_strbuf_t *wmem_new         = NULL;
286  gchar         *disp_string      = NULL;
287  const gchar   *replacestr       = NULL;
288  gboolean      show_replaced_str = FALSE;
289  gint          x                 = 0;
290
291  if (length == 0) {
292    length = tvb_strnlen(tvb, offset, -1);
293    if (length == -1) {
294      /* did not find end of string */
295      length = tvb_captured_length_remaining(tvb, offset);
296    }
297  }
298
299  item = proto_tree_add_item(tree, hfindex, tvb, offset, length, ENC_ASCII | ENC_NA);
300
301  wmem_new = wmem_strbuf_sized_new(pinfo->pool, length + 1, 0);
302  disp_string = (gchar*) wmem_alloc(pinfo->pool, length + 1);
303  disp_string[length] = '\0';
304  tvb_memcpy(tvb, (void*)disp_string, offset, length);
305
306  for (x = 0; x < length && disp_string[x] != '\0'; x++) {
307    replacestr = NULL;
308    if (x + 1 < length) {
309      if (disp_string[x] == '\36') {
310        replacestr = try_val_to_str_ext(disp_string[x + 1], &DisplayLabels_36_ext);
311      } else if (disp_string[x] == '\200') {
312        replacestr = try_val_to_str_ext(disp_string[x + 1], &DisplayLabels_200_ext);
313      }
314    }
315    if (replacestr) {
316      x++;        /* swallow replaced characters */
317      wmem_strbuf_append(wmem_new, replacestr);
318      show_replaced_str = TRUE;
319    } else {
320      wmem_strbuf_append_c(wmem_new, disp_string[x]);
321    }
322  }
323  if (show_replaced_str) {
324    si->additionalInfo = g_strdup_printf("\"%s\"", wmem_strbuf_get_str(wmem_new));
325    proto_item_append_text(item, " => \"%s\"" , wmem_strbuf_get_str(wmem_new));
326  }
327  ptvcursor_advance(cursor, length);
328}
329
330/*** Request / Response helper functions */
331static void skinny_reqrep_add_request(ptvcursor_t *cursor, packet_info * pinfo, skinny_conv_info_t * skinny_conv, const int request_key)
332{
333  proto_tree *tree = ptvcursor_tree(cursor);
334  tvbuff_t *tvb = ptvcursor_tvbuff(cursor);
335  skinny_req_resp_t *req_resp = NULL;
336
337  if (!PINFO_FD_VISITED(pinfo)) {
338    req_resp = wmem_new0(wmem_file_scope(), skinny_req_resp_t);
339    req_resp->request_frame = pinfo->num;
340    req_resp->response_frame = 0;
341    req_resp->request_time = pinfo->abs_ts;
342    wmem_map_insert(skinny_conv->pending_req_resp, GINT_TO_POINTER(request_key), (void *)req_resp);
343    DPRINT(("SKINNY: setup_request: frame=%d add key=%d to map\n", pinfo->num, request_key));
344  }
345
346  req_resp = (skinny_req_resp_t *) wmem_map_lookup(skinny_conv->requests, GUINT_TO_POINTER(pinfo->num));
347  if (req_resp && req_resp->response_frame) {
348    DPRINT(("SKINNY: show request in tree: frame/key=%d\n", pinfo->num));
349    proto_item *it;
350    it = proto_tree_add_uint(tree, hf_skinny_response_in, tvb, 0, 0, req_resp->response_frame);
351    proto_item_set_generated(it);
352  } else {
353    DPRINT(("SKINNY: no request found for frame/key=%d\n", pinfo->num));
354  }
355}
356
357
358static void skinny_reqrep_add_response(ptvcursor_t *cursor, packet_info * pinfo, skinny_conv_info_t * skinny_conv, const int request_key)
359{
360  proto_tree *tree = ptvcursor_tree(cursor);
361  tvbuff_t *tvb = ptvcursor_tvbuff(cursor);
362  skinny_req_resp_t *req_resp = NULL;
363
364  if (!PINFO_FD_VISITED(pinfo)) {
365    req_resp = (skinny_req_resp_t *) wmem_map_remove(skinny_conv->pending_req_resp, GINT_TO_POINTER(request_key));
366    if (req_resp) {
367      DPRINT(("SKINNY: match request:%d with response:%d for key=%d\n", req_resp->request_frame, pinfo->num, request_key));
368      req_resp->response_frame = pinfo->num;
369      wmem_map_insert(skinny_conv->requests, GUINT_TO_POINTER(req_resp->request_frame), (void *)req_resp);
370      wmem_map_insert(skinny_conv->responses, GUINT_TO_POINTER(pinfo->num), (void *)req_resp);
371    } else {
372      DPRINT(("SKINNY: no match found for response frame=%d and key=%d\n", pinfo->num, request_key));
373    }
374  }
375
376  req_resp = (skinny_req_resp_t *) wmem_map_lookup(skinny_conv->responses, GUINT_TO_POINTER(pinfo->num));
377  if (req_resp && req_resp->request_frame) {
378    DPRINT(("SKINNY: show response in tree: frame/key=%d\n", pinfo->num));
379    proto_item *it;
380    nstime_t ns;
381    it = proto_tree_add_uint(tree, hf_skinny_response_to, tvb, 0, 0, req_resp->request_frame);
382    proto_item_set_generated(it);
383
384    nstime_delta(&ns, &pinfo->abs_ts, &req_resp->request_time);
385    it = proto_tree_add_time(tree, hf_skinny_response_time, tvb, 0, 0, &ns);
386    proto_item_set_generated(it);
387  } else {
388    DPRINT(("SKINNY: no response found for frame/key=%d\n", pinfo->num));
389  }
390}
391
392/*** Messages Handlers ***/
393/* [[[cog
394cog.out(message_dissector_functions)
395]]]*/
396/*[[[end]]]*/
397
398typedef void (*message_handler) (ptvcursor_t * cursor, packet_info *pinfo, skinny_conv_info_t * skinny_conv);
399
400typedef struct _skinny_opcode_map_t {
401  guint32 opcode;
402  message_handler handler;
403  skinny_message_type_t type;
404  const char *name;
405} skinny_opcode_map_t;
406
407/* Messages Handler Array */
408/* [[[cog
409cog.out('static const skinny_opcode_map_t skinny_opcode_map[] = {\n')
410for message in skinny.message:
411    msg_type = "SKINNY_MSGTYPE_EVENT"
412    if message.msgtype == "request":
413	msg_type = "SKINNY_MSGTYPE_REQUEST"
414    if message.msgtype == "response" and message.request is not None:
415	msg_type = "SKINNY_MSGTYPE_RESPONSE"
416    cog.out('  {%-6s, %-47s, %-24s, "%s"},\n' %(message.opcode, message.gen_handler(), msg_type, message.name))
417cog.out('};\n')
418]]]*/
419/*[[[end]]]*/
420
421/* Dissect a single SKINNY PDU */
422static int dissect_skinny_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
423{
424  guint    offset   = 0;
425  /*gboolean is_video = FALSE;*/    /* FIX ME: need to indicate video or not */
426  ptvcursor_t* cursor;
427  conversation_t *conversation;
428  skinny_conv_info_t *skinny_conv;
429  const skinny_opcode_map_t *opcode_entry = NULL;
430
431  /* Header fields */
432  guint32  hdr_data_length;
433  guint32  hdr_version;
434  guint32  hdr_opcode;
435  guint16  i;
436
437  /* Set up structures we will need to add the protocol subtree and manage it */
438  proto_tree *skinny_tree = NULL;
439  proto_item *ti = NULL;
440
441  /* Initialization */
442  hdr_data_length = tvb_get_letohl(tvb, 0);
443  hdr_version     = tvb_get_letohl(tvb, 4);
444  hdr_opcode      = tvb_get_letohl(tvb, 8);
445
446  for (i = 0; i < sizeof(skinny_opcode_map)/sizeof(skinny_opcode_map_t) ; i++) {
447    if (skinny_opcode_map[i].opcode == hdr_opcode) {
448      opcode_entry = &skinny_opcode_map[i];
449    }
450  }
451
452  conversation = find_or_create_conversation(pinfo);
453  skinny_conv = (skinny_conv_info_t *)conversation_get_proto_data(conversation, proto_skinny);
454  if (!skinny_conv) {
455    skinny_conv = wmem_new0(wmem_file_scope(), skinny_conv_info_t);
456    //skinny_conv->pending_req_resp = wmem_map_new(wmem_file_scope(), wmem_str_hash, g_str_equal);
457    skinny_conv->pending_req_resp = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
458    skinny_conv->requests = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
459    skinny_conv->responses = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
460    skinny_conv->lineId = -1;
461    skinny_conv->mtype = SKINNY_MSGTYPE_EVENT;
462    conversation_add_proto_data(conversation, proto_skinny, skinny_conv);
463  }
464
465  /* Initialise stat info for passing to tap */
466  /* WIP: will be (partially) replaced in favor of conversionation, dependents: ui/voip_calls.c */
467  pi_current++;
468  if (pi_current == MAX_SKINNY_MESSAGES_IN_PACKET)
469  {
470    /* Overwrite info in first struct if run out of space... */
471    pi_current = 0;
472  }
473  si = &pi_arr[pi_current];
474  si->messId = hdr_opcode;
475  si->messageName = val_to_str_ext(hdr_opcode, &message_id_ext, "0x%08X (Unknown)");
476  si->callId = 0;
477  si->lineId = 0;
478  si->passThroughPartyId = 0;
479  si->callState = 0;
480  g_free(si->callingParty);
481  si->callingParty = NULL;
482  g_free(si->calledParty);
483  si->calledParty = NULL;
484  si->mediaReceptionStatus = -1;
485  si->mediaTransmissionStatus = -1;
486  si->multimediaReceptionStatus = -1;
487  si->multimediaTransmissionStatus = -1;
488  si->multicastReceptionStatus = -1;
489  g_free(si->additionalInfo);
490  si->additionalInfo = NULL;
491
492  col_add_fstr(pinfo->cinfo, COL_INFO,"%s ", si->messageName);
493  col_set_fence(pinfo->cinfo, COL_INFO);
494
495  if (tree) {
496    ti = proto_tree_add_item(tree, proto_skinny, tvb, offset, hdr_data_length+8, ENC_NA);
497    skinny_tree = proto_item_add_subtree(ti, ett_skinny);
498  }
499
500  if (opcode_entry && opcode_entry->type != SKINNY_MSGTYPE_EVENT) {
501    skinny_conv->mtype = opcode_entry->type;
502    if (opcode_entry->type == SKINNY_MSGTYPE_REQUEST) {
503      col_set_str(pinfo->cinfo, COL_PROTOCOL, "SKINNY/REQ");
504    } else {
505      col_set_str(pinfo->cinfo, COL_PROTOCOL, "SKINNY/RESP");
506    }
507  }
508
509  if (skinny_tree) {
510    proto_tree_add_uint(skinny_tree, hf_skinny_data_length, tvb, offset  , 4, hdr_data_length);
511    proto_tree_add_uint(skinny_tree, hf_skinny_hdr_version, tvb, offset+4, 4, hdr_version);
512    proto_tree_add_uint(skinny_tree, hf_skinny_messageId,   tvb, offset+8, 4, hdr_opcode );
513  }
514  offset += 12;
515
516  cursor = ptvcursor_new(pinfo->pool, skinny_tree, tvb, offset);
517  if (opcode_entry && opcode_entry->handler) {
518    opcode_entry->handler(cursor, pinfo, skinny_conv);
519  }
520  ptvcursor_free(cursor);
521
522  tap_queue_packet(skinny_tap, pinfo, si);
523
524  return tvb_captured_length(tvb);
525}
526
527/* Code to actually dissect the packets */
528static int
529dissect_skinny(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
530{
531  /* The general structure of a packet: {IP-Header|TCP-Header|n*SKINNY}
532   * SKINNY-Packet: {Header(Size, Reserved)|Data(MessageID, Message-Data)}
533   */
534  /* Header fields */
535  guint32 hdr_data_length;
536  guint32 hdr_version;
537
538  /* check, if this is really an SKINNY packet, they start with a length + 0 */
539
540  if (tvb_captured_length(tvb) < 8)
541  {
542    return 0;
543  }
544  /* get relevant header information */
545  hdr_data_length = tvb_get_letohl(tvb, 0);
546  hdr_version     = tvb_get_letohl(tvb, 4);
547
548  /*  data_size       = MIN(8+hdr_data_length, tvb_length(tvb)) - 0xC; */
549
550  if (
551      (hdr_data_length < 4) ||
552      ((hdr_version != BASIC_MSG_TYPE) &&
553       (hdr_version != V10_MSG_TYPE) &&
554       (hdr_version != V11_MSG_TYPE) &&
555       (hdr_version != V15_MSG_TYPE) &&
556       (hdr_version != V16_MSG_TYPE) &&
557       (hdr_version != V17_MSG_TYPE) &&
558       (hdr_version != V18_MSG_TYPE) &&
559       (hdr_version != V19_MSG_TYPE) &&
560       (hdr_version != V20_MSG_TYPE) &&
561       (hdr_version != V21_MSG_TYPE) &&
562       (hdr_version != V22_MSG_TYPE))
563     )
564  {
565      /* Not an SKINNY packet, just happened to use the same port */
566      return 0;
567  }
568
569  /* Make entries in Protocol column and Info column on summary display */
570  col_set_str(pinfo->cinfo, COL_PROTOCOL, "SKINNY");
571
572  col_set_str(pinfo->cinfo, COL_INFO, "Skinny Client Control Protocol");
573
574  tcp_dissect_pdus(tvb, pinfo, tree, global_skinny_desegment, 4, get_skinny_pdu_len, dissect_skinny_pdu, data);
575
576  return tvb_captured_length(tvb);
577}
578
579/* Register the protocol with Wireshark */
580void
581proto_register_skinny(void)
582{
583  /* Setup list of header fields */
584  static hf_register_info hf[] = {
585    { &hf_skinny_data_length,
586      {
587        "Data length", "skinny.data_length", FT_UINT32, BASE_DEC, NULL, 0x0,
588        "Number of bytes in the data portion.", HFILL }},
589    { &hf_skinny_hdr_version,
590      {
591        "Header version", "skinny.hdr_version", FT_UINT32, BASE_HEX, VALS(header_version), 0x0,
592        NULL, HFILL }},
593    { &hf_skinny_messageId,
594      {
595        "Message ID", "skinny.messageId", FT_UINT32, BASE_DEC|BASE_EXT_STRING, &message_id_ext, 0x0,
596        NULL, HFILL }},
597    { &hf_skinny_xmlData,
598      {
599        "XML data", "skinny.xmlData", FT_STRING, BASE_NONE, NULL, 0x0,
600        NULL,  HFILL }},
601    { &hf_skinny_ipv4or6,
602      {
603        "IPv4or6", "skinny.ipv4or6", FT_UINT32, BASE_DEC|BASE_EXT_STRING, &IpAddrType_ext, 0x0,
604        NULL, HFILL }},
605    { &hf_skinny_response_in,
606      {
607        "Response In", "skinny.response_in", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
608        "The response to this SKINNY request is in this frame", HFILL }},
609    { &hf_skinny_response_to,
610      {
611        "Request In", "skinny.response_to", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
612        "This is a response to the SKINNY request in this frame", HFILL }},
613    { &hf_skinny_response_time,
614      {
615        "Response Time", "skinny.response_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
616        "The time between the Call and the Reply", HFILL }},
617    /* [[[cog
618    for valuestr in sorted(xml2skinny.fieldsArray.values()):
619      cog.out('%s' %valuestr)
620    ]]]*/
621    /*[[[end]]]*/
622  };
623
624  /* Setup protocol subtree array */
625  static gint *ett[] = {
626    &ett_skinny,
627    &ett_skinny_tree,
628  };
629
630  module_t *skinny_module;
631
632  /* Register the protocol name and description */
633  proto_skinny = proto_register_protocol("Skinny Client Control Protocol",
634                                         "SKINNY", "skinny");
635
636  /* Required function calls to register the header fields and subtrees used */
637  proto_register_field_array(proto_skinny, hf, array_length(hf));
638  proto_register_subtree_array(ett, array_length(ett));
639
640  skinny_module = prefs_register_protocol(proto_skinny, NULL);
641  prefs_register_bool_preference(skinny_module, "desegment",
642    "Reassemble SKINNY messages spanning multiple TCP segments",
643    "Whether the SKINNY dissector should reassemble messages spanning multiple TCP segments."
644    " To use this option, you must also enable"
645    " \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
646    &global_skinny_desegment);
647
648  skinny_handle = register_dissector("skinny", dissect_skinny, proto_skinny);
649
650  skinny_tap = register_tap("skinny");
651}
652
653void
654proto_reg_handoff_skinny(void)
655{
656  /* Skinny content type and internet media type used by other dissectors are the same */
657  xml_handle = find_dissector_add_dependency("xml", proto_skinny);
658  dissector_add_uint_with_preference("tcp.port", TCP_PORT_SKINNY, skinny_handle);
659  ssl_dissector_add(SSL_PORT_SKINNY, skinny_handle);
660}
661
662/*
663 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
664 *
665 * Local variables:
666 * c-basic-offset: 2
667 * tab-width: 8
668 * indent-tabs-mode: nil
669 * End:
670 *
671 * vi: set shiftwidth=2 tabstop=8 expandtab:
672 * :indentSize=2:tabSize=8:noTabs=true:
673 */
674