Get rid of some more g_assert*()'s in the dissectors. There are a few
[obnox/wireshark/wip.git] / epan / dissectors / packet-ipdc.c
1 /* packet-ipdc.c
2  * Routines for IP Device Control (SS7 over IP) dissection
3  * Copyright Lucent Technologies 2004
4  * Josh Bailey <joshbailey@lucent.com> and Ruud Linders <ruud@lucent.com>
5  *
6  * Using IPDC spec 0.20.2
7  *
8  * $Id$
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1998 Gerald Combs
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  * 
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <glib.h>
38 #include <math.h>
39
40 #include <epan/packet.h>
41 #include <epan/emem.h>
42 #include "packet-ipdc.h"
43 #include "packet-tcp.h"
44 #include <epan/ipproto.h>
45 #include <epan/prefs.h>
46
47 static int proto_ipdc = -1;
48 static int hf_ipdc_nr = -1;
49 static int hf_ipdc_ns = -1;
50 static int hf_ipdc_payload_len = -1;
51 static int hf_ipdc_protocol_id = -1;
52 static int hf_ipdc_trans_id_size = -1;
53 static int hf_ipdc_trans_id = -1;
54 static int hf_ipdc_message_code = -1;
55
56 static gint ett_ipdc = -1;
57 static gint ett_ipdc_tag = -1;
58
59 static gboolean ipdc_desegment = TRUE;
60 static guint ipdc_port_pref = TCP_PORT_IPDC;
61 static gboolean new_packet = FALSE;
62
63 static dissector_handle_t q931_handle;
64
65 void proto_reg_handoff_ipdc(void);
66
67
68 static guint
69 get_ipdc_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
70 {
71         /* lower 10 bits only */
72         guint raw_len = (tvb_get_ntohs(tvb,offset+2) & 0x03FF);
73  
74         return raw_len + 4;
75 }
76
77 static void
78 dissect_ipdc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
79 {
80         proto_item *ti;
81         proto_tree *ipdc_tree;
82         proto_item *ipdc_tag;
83         proto_tree *tag_tree;
84         tvbuff_t *q931_tvb;
85
86         const char *des;
87         const char *enum_val = "";
88         char tmp_tag_text[IPDC_STR_LEN + 1];
89         const value_string *val_ptr;
90         guint32 type;
91         guint len;
92         guint i;
93         guint status;
94         gshort tag;
95         guint32 tmp_tag;
96
97         gshort nr = tvb_get_guint8(tvb,0);
98         gshort ns = tvb_get_guint8(tvb,1);
99         guint payload_len = get_ipdc_pdu_len(pinfo,tvb,0);
100
101         gshort protocol_id;
102         gshort trans_id_size;
103         guint32 trans_id;
104         guint16 message_code;
105         guint16 offset;
106
107         /* display IPDC protocol ID */
108         if (check_col(pinfo->cinfo, COL_PROTOCOL))
109                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPDC");
110
111         /* short frame... */
112         if (payload_len < 4)
113                 return;
114
115         /* clear info column and display send/receive sequence numbers */
116         if (check_col(pinfo->cinfo, COL_INFO)) {
117                 if (new_packet == TRUE) {
118                         col_clear(pinfo->cinfo, COL_INFO);
119                         new_packet = FALSE;
120                 }
121                 col_append_fstr(pinfo->cinfo, COL_INFO, "r=%u s=%u ",
122                 nr, ns);
123         }
124
125         if (payload_len == 4) {
126                 if (!tree)
127                         return;
128
129                 ti = proto_tree_add_item(tree, proto_ipdc, tvb, 0, -1, FALSE);
130                 ipdc_tree = proto_item_add_subtree(ti, ett_ipdc);
131                 proto_tree_add_item(ipdc_tree, hf_ipdc_nr, tvb, 0, 1, nr);
132                 proto_tree_add_item(ipdc_tree, hf_ipdc_ns, tvb, 1, 1, ns);
133                 proto_tree_add_uint(ipdc_tree, hf_ipdc_payload_len, tvb, 2, 2,
134                         payload_len);
135
136                 return;
137         }
138
139         /* IPDC tags present - display message code and trans. ID */
140         protocol_id = tvb_get_guint8(tvb,4);
141         trans_id_size = TRANS_ID_SIZE_IPDC; /* tvb_get_guint8(tvb,5); */
142         trans_id = tvb_get_ntohl(tvb,6);
143         message_code = tvb_get_ntohs(tvb,6+trans_id_size);
144         offset = 6 + trans_id_size + 2; /* past message_code */
145
146         if (check_col(pinfo->cinfo, COL_INFO))
147                 col_append_fstr(pinfo->cinfo, COL_INFO,
148                         "TID=%x %s ",
149                         trans_id,
150                         val_to_str(message_code, message_code_vals,
151                         TEXT_UNDEFINED));
152
153         if (!tree)
154                 return;
155
156         ti = proto_tree_add_item(tree, proto_ipdc, tvb, 0, -1, FALSE);
157         ipdc_tree = proto_item_add_subtree(ti, ett_ipdc);
158
159         proto_tree_add_item(ipdc_tree, hf_ipdc_nr, tvb, 0, 1, nr);
160         proto_tree_add_item(ipdc_tree, hf_ipdc_ns, tvb, 1, 1, ns);
161         proto_tree_add_uint(ipdc_tree, hf_ipdc_payload_len, tvb,
162                 2, 2, payload_len);
163
164         proto_tree_add_item(ipdc_tree, hf_ipdc_protocol_id, tvb,
165                 4, 1, protocol_id);
166         proto_tree_add_item(ipdc_tree, hf_ipdc_trans_id_size, tvb,
167                 5, 1, trans_id_size);
168         proto_tree_add_item(ipdc_tree, hf_ipdc_trans_id, tvb,
169                 6, trans_id_size, trans_id);
170         proto_tree_add_item(ipdc_tree, hf_ipdc_message_code, tvb,
171                 6 + trans_id_size + 1, 1, message_code);
172
173         ipdc_tag = proto_tree_add_text(ipdc_tree, tvb, offset,
174         payload_len - offset, "IPDC tags");
175         tag_tree = proto_item_add_subtree(ipdc_tag, ett_ipdc_tag);
176
177         /* iterate through tags. first byte is tag, second is length,
178            in bytes, following is tag data. tag of 0x0 should be
179            end of tags. */
180         for (;;) {
181                 tag = tvb_get_guint8(tvb, offset);
182
183                 if (tag == 0x0) {
184                         if (offset == payload_len - 1) {
185                                 proto_tree_add_text(tag_tree, tvb,
186                                 offset, 1, "end of tags");
187                         } else {
188                                 proto_tree_add_text(tag_tree, tvb,
189                                 offset, 1, "data trailing end of tags");
190                         }
191
192                         break;
193                 }
194
195                 len = tvb_get_guint8(tvb,offset+1);
196                 des = val_to_str(tag, tag_description, TEXT_UNDEFINED);
197                 /* lookup tag type */
198                 for (i = 0; (ipdc_tag_types[i].tag != tag &&
199                         ipdc_tag_types[i].type != IPDC_UNKNOWN); i++)
200                 ;
201                 type = ipdc_tag_types[i].type;
202
203                 tmp_tag = 0;
204
205                 switch (type) {
206                         /* simple IPDC_ASCII strings */
207                         case IPDC_ASCII:
208                                 DISSECTOR_ASSERT(len<=IPDC_STR_LEN);
209                                 tvb_memcpy(tvb, tmp_tag_text, offset+2, len);
210                                 tmp_tag_text[len] = 0;
211                                 proto_tree_add_text(tag_tree, tvb, offset,
212                                 len + 2, "0x%2.2x: %s: %s", tag, des,
213                                 tmp_tag_text);
214                         break;
215
216                         /* unsigned integers, or bytes */
217                         case IPDC_UINT:
218                         case IPDC_BYTE:
219                                 for (i = 0; i < len; i++) 
220                                         tmp_tag += tvb_get_guint8(tvb,
221                                                 offset + 2 + i) * (guint32)
222                                                         pow(256, len - (i + 1));
223
224                                 if (len == 1)
225                                         enum_val =
226                                                 val_to_str(IPDC_TAG(tag) +
227                                                 tmp_tag,
228                                                 tag_enum_type, TEXT_UNDEFINED);
229
230                                 if (len == 1 &&
231                                         strcmp(enum_val, TEXT_UNDEFINED) != 0) {
232                                         proto_tree_add_text(tag_tree, tvb,
233                                                 offset, len + 2,
234                                                 "0x%2.2x: %s: %s",
235                                                 tag, des, enum_val);
236                                 } else {
237                                         proto_tree_add_text(tag_tree, tvb,
238                                                 offset, len + 2,
239                                                 "0x%2.2x: %s: %u",
240                                                 tag, des, tmp_tag);
241                                 }
242                         break;
243
244                         /* IP addresses */
245                         case IPDC_IPA:
246                                 switch (len) {
247                                         case 4:
248                                                 g_snprintf(tmp_tag_text,
249                                                 IPDC_STR_LEN,
250                                                 "%u.%u.%u.%u",
251                                                 tvb_get_guint8(tvb, offset + 2),
252                                                 tvb_get_guint8(tvb, offset + 3),
253                                                 tvb_get_guint8(tvb, offset + 4),
254                                                 tvb_get_guint8(tvb, offset + 5)
255                                                 );
256                                         break;
257
258                                         case 6:
259                                                 g_snprintf(tmp_tag_text,
260                                                 IPDC_STR_LEN,
261                                                 "%u.%u.%u.%u:%u",
262                                                 tvb_get_guint8(tvb, offset + 2),
263                                                 tvb_get_guint8(tvb, offset + 3),
264                                                 tvb_get_guint8(tvb, offset + 4),
265                                                 tvb_get_guint8(tvb, offset + 5),
266                                                 tvb_get_ntohs(tvb, offset + 6));
267                                         break;
268
269                                         default:
270                                                 g_snprintf(tmp_tag_text,
271                                                 IPDC_STR_LEN,
272                                                 "Invalid IP address length %u",
273                                                  len);
274                                 }
275
276                                 proto_tree_add_text(tag_tree, tvb,
277                                         offset, len + 2,
278                                         "0x%2.2x: %s: %s",
279                                         tag, des, tmp_tag_text);
280                         break;
281
282                         /* Line status arrays */
283                         case IPDC_LINESTATUS:
284                         case IPDC_CHANNELSTATUS:
285                                 proto_tree_add_text(tag_tree, tvb, offset,
286                                 len + 2, "0x%2.2x: %s", tag, des);
287                                 val_ptr = (type == IPDC_LINESTATUS) ?
288                                         line_status_vals : channel_status_vals;
289
290                                 for (i = 0; i < len; i++) {
291                                         status = tvb_get_guint8(tvb,offset+2+i);
292
293                                         proto_tree_add_text(tag_tree, tvb,
294                                                 offset + 2 + i, 1, 
295                                                 " %.2u: %.2x (%s)",
296                                                 i + 1, status,
297                                                 val_to_str(status,
298                                                 val_ptr,
299                                                 TEXT_UNDEFINED));
300                                 }
301                         break;
302
303                         case IPDC_Q931:
304                                 q931_tvb =
305                                         tvb_new_subset(tvb, offset+2, len, len);
306                                 call_dissector(q931_handle,q931_tvb,pinfo,tree);
307                         break;
308
309                         case IPDC_ENCTYPE:
310                                 proto_tree_add_text(tag_tree, tvb,
311                                         offset, len + 2,
312                                         "0x%2.2x: %s: %s",
313                                         tag, des, val_to_str(
314                                         tvb_get_guint8(tvb,offset+2),
315                                         encoding_type_vals,
316                                         TEXT_UNDEFINED));
317
318                                 if (len == 2) {
319                                         proto_tree_add_text(tag_tree, tvb,
320                                                 offset, len + 2,
321                                                 "0x%2.2x: %s: %u",
322                                                 tag, des,
323                                                 tvb_get_guint8(tvb,offset+3));
324                                 }
325                         break;
326                                         
327                         /* default */
328                         default:
329                                 proto_tree_add_text(tag_tree, tvb, offset,
330                                 len + 2, "0x%2.2x: %s", tag, des);
331                 } /* switch */
332
333                 offset += len + 2;
334         }
335 }
336
337 static void
338 dissect_ipdc_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
339 {
340         dissect_ipdc_common(tvb, pinfo, tree);
341 }
342
343 static void
344 dissect_ipdc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
345 {
346         new_packet = TRUE;
347         tcp_dissect_pdus(tvb, pinfo, tree, ipdc_desegment, 4,
348                 get_ipdc_pdu_len, dissect_ipdc_tcp_pdu);
349 }
350
351 void
352 proto_register_ipdc(void)
353 {                 
354
355         static hf_register_info hf[] = {
356                 { &hf_ipdc_nr,
357                         { "N(r)",       "ipdc.nr",
358                         FT_UINT8, BASE_DEC, NULL, 0x0,
359                         "Receive sequence number", HFILL }
360                 },
361
362                 { &hf_ipdc_ns,
363                         { "N(s)",       "ipdc.ns",
364                         FT_UINT8, BASE_DEC, NULL, 0x0, 
365                         "Transmit sequence number", HFILL }
366                 },
367
368                 { &hf_ipdc_payload_len,
369                         { "Payload length",     "ipdc.length",
370                         FT_UINT16, BASE_DEC, NULL, 0x0,
371                         "Payload length", HFILL }
372                 },
373
374                 { &hf_ipdc_protocol_id,
375                         { "Protocol ID",        "ipdc.protocol_id",
376                         FT_UINT8, BASE_HEX, NULL, 0x0,
377                         "Protocol ID", HFILL }
378                 },
379
380                 { &hf_ipdc_trans_id_size,
381                         { "Transaction ID size",        "ipdc.trans_id_size",
382                         FT_UINT8, BASE_DEC, NULL, 0x0,
383                         "Transaction ID size", HFILL }
384                 },
385
386                 { &hf_ipdc_trans_id,
387                         { "Transaction ID",     "ipdc.trans_id",
388                         FT_BYTES, BASE_HEX, NULL, 0x0,
389                         "Transaction ID", HFILL }
390                 },
391
392                 { &hf_ipdc_message_code,
393                         { "Message code",       "ipdc.message_code",
394                         FT_UINT16, BASE_HEX, VALS(message_code_vals), 0x0,
395                         "Message Code", HFILL }
396                 },
397         };
398
399         static gint *ett[] = {
400                 &ett_ipdc,
401                 &ett_ipdc_tag,
402         };
403
404         module_t *ipdc_module;
405
406         proto_ipdc = proto_register_protocol("IP Device Control (SS7 over IP)",
407             "IPDC", "ipdc");
408         proto_register_field_array(proto_ipdc, hf, array_length(hf));
409         proto_register_subtree_array(ett, array_length(ett));
410
411         ipdc_module = prefs_register_protocol(proto_ipdc, proto_reg_handoff_ipdc);
412         prefs_register_bool_preference(ipdc_module, "desegment_ipdc_messages",
413                 "Reassemble IPDC messages spanning multiple TCP segments",
414                 "Whether the IPDC dissector should reassemble messages spanning multiple TCP segments."
415                 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
416                 &ipdc_desegment);
417         prefs_register_uint_preference(ipdc_module, "tcp.port",
418                 "IPDC monitoring port",
419                 "Set the IPDC monitoring port", 10,
420                 &ipdc_port_pref);
421 }
422
423 void
424 proto_reg_handoff_ipdc(void)
425 {
426         static guint last_ipdc_port_pref = 0;
427         static dissector_handle_t ipdc_tcp_handle = NULL;
428
429         if (ipdc_tcp_handle) {
430                 dissector_delete("tcp.port", last_ipdc_port_pref,
431                         ipdc_tcp_handle);
432         } else {
433                 ipdc_tcp_handle = 
434                         create_dissector_handle(dissect_ipdc_tcp, proto_ipdc);
435                 q931_handle = find_dissector("q931");
436         }
437
438         last_ipdc_port_pref = ipdc_port_pref;
439         dissector_add("tcp.port", ipdc_port_pref, ipdc_tcp_handle);
440 }