Skinny: fix dissector registration for SSL
[metze/wireshark/wip.git] / epan / dissectors / packet-skinny.c.in
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  * This program is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU General Public License
22  * as published by the Free Software Foundation; either version 2
23  * of the License, or (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  * GNU General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License
31  * along with this program; if not, write to the Free Software
32  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33  */
34
35 /* [[[cog
36 #
37 # Using Cog.py Inplace Code Generator
38 #
39 # Dependencies:
40 # - python
41 # - cog.py: http://nedbatchelder.com/code/cog/
42 # - python.xml
43 # - python.xml.sax
44 #
45 cog.out('/*\n')
46 cog.out(' * Generated Automatically Using (from wireshark base directory):\n')
47 cog.out(' *   cog.py -D xmlfile=tools/SkinnyProtocolOptimized.xml -d -c -o epan/dissectors/packet-skinny.c epan/dissectors/packet-skinny.c.in\n')
48 cog.out(' */\n')
49 ]]]*/
50
51 /*[[[end]]]*/
52
53 /* c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil
54  * vi: set shiftwidth=2 tabstop=2 expandtab:
55  * :indentSize=2:tabSize=2:noTabs=true:
56  */
57
58 #include "config.h"
59
60 #include <epan/packet.h>
61 #include <epan/prefs.h>
62 #include <epan/tap.h>
63 #include <epan/ptvcursor.h>
64
65 #include "packet-rtp.h"
66 #include "packet-tcp.h"
67 #include "packet-ssl.h"
68 #include "packet-skinny.h"
69
70 void proto_register_skinny(void);
71 void proto_reg_handoff_skinny(void);
72
73
74 #define TCP_PORT_SKINNY 2000
75 #define SSL_PORT_SKINNY 2443 /* IANA assigned to PowerClient Central Storage Facility */
76
77 #define BASIC_MSG_TYPE 0x00
78 #define V10_MSG_TYPE 0x0A
79 #define V11_MSG_TYPE 0x0B
80 #define V15_MSG_TYPE 0x0F
81 #define V16_MSG_TYPE 0x10
82 #define V17_MSG_TYPE 0x11
83 #define V18_MSG_TYPE 0x12
84 #define V19_MSG_TYPE 0x13
85 #define V20_MSG_TYPE 0x14
86 #define V21_MSG_TYPE 0x15
87 #define V22_MSG_TYPE 0x16
88
89 static const value_string header_version[] = {
90   { BASIC_MSG_TYPE, "Basic" },
91   { V10_MSG_TYPE,   "V10" },
92   { V11_MSG_TYPE,   "V11" },
93   { V15_MSG_TYPE,   "V15" },
94   { V16_MSG_TYPE,   "V16" },
95   { V17_MSG_TYPE,   "V17" },
96   { V18_MSG_TYPE,   "V18" },
97   { V19_MSG_TYPE,   "V19" },
98   { V20_MSG_TYPE,   "V20" },
99   { V21_MSG_TYPE,   "V21" },
100   { V22_MSG_TYPE,   "V22" },
101   { 0             , NULL }
102 };
103
104 /* Declare MessageId */
105 /* [[[cog
106 import sys
107 sys.path.append('tools/')
108
109 import parse_xml2skinny_dissector as xml2skinny
110 global skinny
111 global message_dissector_functions
112
113 message_dissector_functions = ''
114 skinny = xml2skinny.xml2obj(xmlfile)
115
116 cog.out('static const value_string  message_id[] = {\n')
117 for message in skinny.message:
118     message_dissector_functions += '%s' %message.dissect()
119     cog.out('  { %s, "%s" },\n' %(message.opcode, message.name.replace('Message','')))
120 cog.out('  {0     , NULL}\n')
121 cog.out('};\n')
122 cog.out('static value_string_ext message_id_ext = VALUE_STRING_EXT_INIT(message_id);\n')
123 ]]]*/
124 /*[[[end]]]*/
125
126
127 /* Declare Enums and Defines */
128 /* [[[cog
129 for enum in skinny.enum:
130     name = enum.name[0].upper() + enum.name[1:]
131     if enum.define == "yes":
132         for entries in enum.entries:
133             for entry in sorted(entries.entry, key=lambda x: int(x['value'],0)):
134                 if entries.type is not None:
135                     cog.out('#define {0:38} 0x{1:05x} /* {2} */\n' .format(entry.name.upper(), int(entry.value,0), entries.type))
136                 else:
137                     cog.out('#define {0:38} 0x{1:05x}\n' .format(entry.name.upper(), int(entry.value,0)))
138         cog.out('\n')
139     cog.out('static const value_string %s[] = {\n' %(name))
140     for entries in enum.entries:
141         for entry in sorted(entries.entry, key=lambda x: int(x['value'],0)):
142             if enum.define == "yes":
143                 cog.out('  { %s, "%s" },\n' %(entry.name.upper(), entry.text))
144             else:
145                 cog.out('  { 0x%05x, "%s" },\n' %(int(entry.value,0), entry.text))
146     cog.out('  { 0x00000, NULL }\n')
147     cog.out('};\n')
148     cog.out('static value_string_ext %s_ext = VALUE_STRING_EXT_INIT(%s);\n\n' %(name, name))
149 ]]]*/
150 /*[[[end]]]*/
151
152 /* Staticly Declared Variables */
153 static int proto_skinny                 = -1;
154 static int hf_skinny_messageId          = -1;
155 static int hf_skinny_data_length        = -1;
156 static int hf_skinny_hdr_version        = -1;
157 static int hf_skinny_xmlData            = -1;
158 static int hf_skinny_ipv4or6            = -1;
159
160 /* [[[cog
161 for key in sorted(xml2skinny.fieldsArray.keys()):
162     cog.out('static int hf_skinny_%s = -1;\n' %key)
163 ]]]*/
164 /*[[[end]]]*/
165
166 static dissector_table_t media_type_dissector_table;
167
168 /* Initialize the subtree pointers */
169 static gint ett_skinny          = -1;
170 static gint ett_skinny_tree     = -1;
171
172 /* desegmentation of SCCP */
173 static gboolean skinny_desegment = TRUE;
174
175 /* tap register id */
176 static int skinny_tap = -1;
177
178 /* skinny protocol tap info */
179 #define MAX_SKINNY_MESSAGES_IN_PACKET 10
180 static skinny_info_t pi_arr[MAX_SKINNY_MESSAGES_IN_PACKET];
181 static int pi_current = 0;
182 static skinny_info_t *si;
183
184 dissector_handle_t skinny_handle;
185
186 /* Get the length of a single SCCP PDU */
187 static guint
188 get_skinny_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
189 {
190   guint32 hdr_data_length;
191
192   /* Get the length of the SCCP packet. */
193   hdr_data_length = tvb_get_letohl(tvb, offset);
194
195   /* That length doesn't include the length of the header itself. */
196   return hdr_data_length + 8;
197 }
198
199 static void
200 dissect_skinny_xml(ptvcursor_t *cursor, int hfindex, packet_info *pinfo, guint32 length, guint32 maxlength)
201 {
202   proto_item         *item       = NULL;
203   proto_tree         *subtree    = NULL;
204   dissector_handle_t handle      = NULL;
205   proto_tree         *tree       = ptvcursor_tree(cursor);
206   guint32            offset      = ptvcursor_current_offset(cursor);
207   tvbuff_t           *tvb        = ptvcursor_tvbuff(cursor);
208   tvbuff_t           *next_tvb;
209
210   if (length == 0) {
211     length = tvb_strnlen(tvb, offset, -1);
212   }
213   if (length >= maxlength) {
214     length = maxlength;
215   }
216
217   ptvcursor_add_no_advance(cursor, hfindex, length, ENC_ASCII|ENC_NA);
218
219   item = proto_tree_add_item(tree, hf_skinny_xmlData, tvb, offset, length, ENC_ASCII|ENC_NA);
220   subtree = proto_item_add_subtree(item, 0);
221   next_tvb = tvb_new_subset(tvb, offset, length, -1);
222   handle = dissector_get_string_handle(media_type_dissector_table, "text/xml");
223   if (handle != NULL) {
224     call_dissector(handle, next_tvb, pinfo, subtree);
225   }
226   ptvcursor_advance(cursor, maxlength);
227 }
228
229 static void
230 dissect_skinny_ipv4or6(ptvcursor_t *cursor, int hfindex_ipv4, int hfindex_ipv6, packet_info *pinfo)
231 {
232   address            src_addr;
233   guint32            ipversion   = 0;
234   guint32            offset      = ptvcursor_current_offset(cursor);
235   tvbuff_t           *tvb        = ptvcursor_tvbuff(cursor);
236   guint32            hdr_version = tvb_get_letohl(tvb, 4);
237   gboolean           is_video    = FALSE;
238
239   /* ProtocolVersion > 18 include and extra field to declare IPv4 (0) / IPv6 (1) */
240   if (hdr_version >= V17_MSG_TYPE) {
241     ipversion = tvb_get_letohl(tvb, offset);
242     ptvcursor_add(cursor, hf_skinny_ipv4or6, 4, ENC_LITTLE_ENDIAN);
243   }
244   if (ipversion == IPADDRTYPE_IPV4) {
245     guint32 ip_address;
246     src_addr.type = AT_IPv4;
247     src_addr.len = 4;
248     src_addr.data = (guint8 *)&ip_address;
249     ip_address = tvb_get_ipv4(tvb, offset);
250     rtp_add_address(pinfo, &src_addr, tvb_get_letohl(tvb, offset), 0, "Skinny", pinfo->fd->num, is_video, NULL);
251     ptvcursor_add(cursor, hfindex_ipv4, 4, ENC_BIG_ENDIAN);
252     if (hdr_version >= V17_MSG_TYPE) {
253       /* skip over the extra room for ipv6 addresses */
254       ptvcursor_advance(cursor, 12);
255     }
256   } else if (ipversion == IPADDRTYPE_IPV6 || ipversion == IPADDRTYPE_IPV4_V6) {
257     struct e_in6_addr IPv6;
258     src_addr.type = AT_IPv6;
259     src_addr.len = 16;
260     src_addr.data = (guint8 *)&IPv6;
261     tvb_get_ipv6(tvb, offset, &IPv6);
262     rtp_add_address(pinfo, &src_addr, tvb_get_letohl(tvb, offset), 0, "Skinny", pinfo->fd->num, is_video, NULL);
263     ptvcursor_add(cursor, hfindex_ipv6, 16, ENC_NA);
264   } else {
265     /* Invalid : skip over ipv6 space completely */
266     ptvcursor_advance(cursor, 16);
267   }
268 }
269
270 /**
271  * 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
272  */
273 static void
274 dissect_skinny_displayLabel(ptvcursor_t *cursor, int hfindex, gint length)
275 {
276   proto_item    *item             = NULL;
277   proto_tree    *tree             = ptvcursor_tree(cursor);
278   guint32       offset            = ptvcursor_current_offset(cursor);
279   tvbuff_t      *tvb              = ptvcursor_tvbuff(cursor);
280   wmem_strbuf_t *wmem_new         = NULL;
281   gchar         *disp_string      = NULL;
282   const gchar   *replacestr       = NULL;
283   gboolean      show_replaced_str = FALSE;
284   gint          x                 = 0;
285
286   if (length == 0) {
287     length = tvb_strnlen(tvb, offset, -1);
288     if (length == -1) {
289       /* did not find end of string */
290       length = tvb_captured_length_remaining(tvb, offset);
291     }
292   }
293
294   item = proto_tree_add_item(tree, hfindex, tvb, offset, length, ENC_ASCII | ENC_NA);
295
296   wmem_new = wmem_strbuf_sized_new(wmem_packet_scope(), length + 1, 0);
297   disp_string = (gchar*) wmem_alloc(wmem_packet_scope(), length + 1);
298   disp_string[length] = '\0';
299   tvb_memcpy(tvb, (void*)disp_string, offset, length);
300
301   for (x = 0; x < length && disp_string[x] != '\0'; x++) {
302     replacestr = NULL;
303     if (x + 1 < length) {
304       if (disp_string[x] == '\36') {
305         replacestr = try_val_to_str_ext(disp_string[x + 1], &DisplayLabels_36_ext);
306       } else if (disp_string[x] == '\200') {
307         replacestr = try_val_to_str_ext(disp_string[x + 1], &DisplayLabels_200_ext);
308       }
309     }
310     if (replacestr) {
311       x++;        /* swallow replaced characters */
312       wmem_strbuf_append(wmem_new, replacestr);
313       show_replaced_str = TRUE;
314     } else {
315       wmem_strbuf_append_c(wmem_new, disp_string[x]);
316     }
317   }
318   if (show_replaced_str) {
319     proto_item_append_text(item, " => \"%s\"" , wmem_strbuf_get_str(wmem_new));
320   }
321   ptvcursor_advance(cursor, length);
322 }
323
324 /*** Messages Handlers ***/
325
326 /* [[[cog
327 cog.out(message_dissector_functions)
328 ]]]*/
329 /*[[[end]]]*/
330
331 /* Messages Handler Array */
332 /* [[[cog
333 cog.out('typedef void (*message_handler) (ptvcursor_t * cursor, packet_info *pinfo);\n')
334 cog.out('static const struct opcode2handler {\n')
335 cog.out('  guint16 opcode;\n');
336 cog.out('  message_handler handler;\n');
337 cog.out('  const char *name;\n');
338 cog.out('} skinny_opcode2handler[] = {\n')
339 for message in skinny.message:
340     cog.out('  {%-6s, %-47s, "%s"},\n' %(message.opcode, message.gen_handler(), message.name))
341 cog.out('};\n')
342 ]]]*/
343 /*[[[end]]]*/
344
345 /* Dissect a single SCCP PDU */
346 static int dissect_skinny_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
347 {
348   guint    offset   = 0;
349   /*gboolean is_video = FALSE;*/    /* FIX ME: need to indicate video or not */
350   ptvcursor_t* cursor;
351
352   /* Header fields */
353   guint32  hdr_data_length;
354   guint32  hdr_version;
355   guint32  data_messageid;
356   guint16  i;
357
358   /* Set up structures we will need to add the protocol subtree and manage it */
359   proto_tree *skinny_tree = NULL;
360   proto_item *ti = NULL;
361
362   /* Initialization */
363   /*
364   hdr_data_length = tvb_get_letohl(tvb, offset);
365   hdr_version     = tvb_get_letohl(tvb, offset+4);
366   data_messageid  = tvb_get_letohl(tvb, offset+8);
367   */
368   hdr_data_length = tvb_get_letohl(tvb, 0);
369   hdr_version     = tvb_get_letohl(tvb, 4);
370   data_messageid  = tvb_get_letohl(tvb, 8);
371
372   /* Initialise stat info for passing to tap */
373   pi_current++;
374   if (pi_current == MAX_SKINNY_MESSAGES_IN_PACKET)
375   {
376     /* Overwrite info in first struct if run out of space... */
377     pi_current = 0;
378   }
379   si = &pi_arr[pi_current];
380   si->messId = data_messageid;
381   si->messageName = val_to_str_ext(data_messageid, &message_id_ext, "0x%08X (Unknown)");
382   si->callId = 0;
383   si->lineId = 0;
384   si->passThruId = 0;
385   si->callState = 0;
386   g_free(si->callingParty);
387   si->callingParty = NULL;
388   g_free(si->calledParty);
389   si->calledParty = NULL;
390   si->openreceiveStatus = 0;
391   si->startmediatransmisionStatus = 0;
392
393   /* In the interest of speed, if "tree" is NULL, don't do any work not
394    * necessary to generate protocol tree items.
395    */
396   if (tree) {
397     ti = proto_tree_add_item(tree, proto_skinny, tvb, offset, hdr_data_length+8, ENC_NA);
398     skinny_tree = proto_item_add_subtree(ti, ett_skinny);
399     proto_tree_add_uint(skinny_tree, hf_skinny_data_length, tvb, offset, 4, hdr_data_length);
400     proto_tree_add_uint(skinny_tree, hf_skinny_hdr_version, tvb, offset+4, 4, hdr_version);
401   }
402
403   col_add_fstr(pinfo->cinfo, COL_INFO,"%s ", si->messageName);
404   col_set_fence(pinfo->cinfo, COL_INFO);
405
406   /*offset += 8;*/
407   /*cursor = ptvcursor_new(skinny_tree, tvb, offset);*/
408
409   proto_tree_add_uint(skinny_tree, hf_skinny_messageId, tvb,offset+8, 4, data_messageid );
410   /*ptvcursor_add(cursor, hf_skinny_messageId, 4, data_messageid);*/
411
412   offset += 12;
413   cursor = ptvcursor_new(skinny_tree, tvb, offset);
414
415   for (i = 0; i < sizeof(skinny_opcode2handler)/sizeof(struct opcode2handler) ; i++) {
416     if (skinny_opcode2handler[i].opcode == data_messageid && skinny_opcode2handler[i].handler) {
417       skinny_opcode2handler[i].handler(cursor, pinfo);
418     }
419   }
420   ptvcursor_free(cursor);
421
422   tap_queue_packet(skinny_tap, pinfo, si);
423   return tvb_captured_length(tvb);
424 }
425
426 /* Code to actually dissect the packets */
427 static int
428 dissect_skinny(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
429 {
430   /* The general structure of a packet: {IP-Header|TCP-Header|n*SKINNY}
431    * SKINNY-Packet: {Header(Size, Reserved)|Data(MessageID, Message-Data)}
432    */
433   /* Header fields */
434   guint32 hdr_data_length;
435   guint32 hdr_version;
436
437   /* check, if this is really an SKINNY packet, they start with a length + 0 */
438
439   if (tvb_captured_length(tvb) < 8)
440   {
441     return 0;
442   }
443   /* get relevant header information */
444   hdr_data_length = tvb_get_letohl(tvb, 0);
445   hdr_version     = tvb_get_letohl(tvb, 4);
446
447   /*  data_size       = MIN(8+hdr_data_length, tvb_length(tvb)) - 0xC; */
448
449   if (
450       (hdr_data_length < 4) ||
451       ((hdr_version != BASIC_MSG_TYPE) &&
452        (hdr_version != V10_MSG_TYPE) &&
453        (hdr_version != V11_MSG_TYPE) &&
454        (hdr_version != V15_MSG_TYPE) &&
455        (hdr_version != V16_MSG_TYPE) &&
456        (hdr_version != V17_MSG_TYPE) &&
457        (hdr_version != V18_MSG_TYPE) &&
458        (hdr_version != V19_MSG_TYPE) &&
459        (hdr_version != V20_MSG_TYPE) &&
460        (hdr_version != V21_MSG_TYPE) &&
461        (hdr_version != V22_MSG_TYPE))
462      )
463   {
464       /* Not an SKINNY packet, just happened to use the same port */
465       return 0;
466   }
467
468   /* Make entries in Protocol column and Info column on summary display */
469   col_set_str(pinfo->cinfo, COL_PROTOCOL, "SKINNY");
470
471   col_set_str(pinfo->cinfo, COL_INFO, "Skinny Client Control Protocol");
472
473   tcp_dissect_pdus(tvb, pinfo, tree, skinny_desegment, 4, get_skinny_pdu_len, dissect_skinny_pdu, data);
474
475   return tvb_captured_length(tvb);
476 }
477
478 /* Register the protocol with Wireshark */
479 void
480 proto_register_skinny(void)
481 {
482   /* Setup list of header fields */
483   static hf_register_info hf[] = {
484     { &hf_skinny_data_length,
485       {
486         "Data length", "skinny.data_length", FT_UINT32, BASE_DEC, NULL, 0x0,
487         "Number of bytes in the data portion.", HFILL }},
488     { &hf_skinny_hdr_version,
489       {
490         "Header version", "skinny.hdr_version", FT_UINT32, BASE_HEX, VALS(header_version), 0x0,
491         NULL, HFILL }},
492     { &hf_skinny_messageId,
493       {
494         "Message ID", "skinny.messageId", FT_UINT32, BASE_DEC|BASE_EXT_STRING, &message_id_ext, 0x0,
495         NULL, HFILL }},
496     { &hf_skinny_xmlData,
497       {
498         "XML data", "skinny.xmlData", FT_STRING, BASE_NONE, NULL, 0x0,
499         NULL,  HFILL }},
500     { &hf_skinny_ipv4or6,
501       {
502         "IPv4or6", "skinny.ipv4or6", FT_UINT32, BASE_DEC|BASE_EXT_STRING, &IpAddrType_ext, 0x0,
503         NULL, HFILL }},
504     /* [[[cog
505     for valuestr in sorted(xml2skinny.fieldsArray.values()):
506       cog.out('%s' %valuestr)
507     ]]]*/
508     /*[[[end]]]*/
509   };
510
511   /* Setup protocol subtree array */
512   static gint *ett[] = {
513     &ett_skinny,
514     &ett_skinny_tree,
515   };
516
517   module_t *skinny_module;
518
519   /* Register the protocol name and description */
520   proto_skinny = proto_register_protocol("Skinny Client Control Protocol",
521                                          "SKINNY", "skinny");
522
523   /* Required function calls to register the header fields and subtrees used */
524   proto_register_field_array(proto_skinny, hf, array_length(hf));
525   proto_register_subtree_array(ett, array_length(ett));
526
527   skinny_module = prefs_register_protocol(proto_skinny, NULL);
528   prefs_register_bool_preference(skinny_module, "desegment",
529     "Reassemble SCCP messages spanning multiple TCP segments",
530     "Whether the SCCP dissector should reassemble messages spanning multiple TCP segments."
531     " To use this option, you must also enable"
532     " \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
533     &skinny_desegment);
534
535   skinny_handle = new_register_dissector("skinny", dissect_skinny, proto_skinny);
536
537   skinny_tap = register_tap("skinny");
538 }
539
540 void
541 proto_reg_handoff_skinny(void)
542 {
543   /* Skinny content type and internet media type used by other dissectors are the same */
544   media_type_dissector_table = find_dissector_table("media_type");
545   dissector_add_uint("tcp.port", TCP_PORT_SKINNY, skinny_handle);
546   ssl_dissector_add(SSL_PORT_SKINNY, "skinny", TRUE);
547 }
548
549 /*
550  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
551  *
552  * Local variables:
553  * c-basic-offset: 2
554  * tab-width: 2
555  * indent-tabs-mode: nil
556  * End:
557  *
558  * vi: set shiftwidth=2 tabstop=2 expandtab:
559  * :indentSize=2:tabSize=2:noTabs=true:
560  */