#include <string.h> and/or #include <stdio.h> not needed.
[obnox/wireshark/wip.git] / epan / dissectors / packet-redbackli.c
1 /* packet-redbackli.c
2  *
3  * $Id$
4  *
5  * Redback Lawful Intercept Packet dissector
6  *
7  * Copyright 2008 Florian Lohoff <flo[AT]rfc822.org>
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald[AT]wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <time.h>
35
36 #include <glib.h>
37 #include <epan/packet.h>
38 #include <epan/addr_resolv.h>
39 #include <epan/strutil.h>
40
41 static int proto_redbackli = -1;
42
43 static int hf_redbackli_seqno = -1;             /* Sequence No */
44 static int hf_redbackli_liid = -1;              /* LI Id */
45 static int hf_redbackli_sessid = -1;            /* Session Id */
46 static int hf_redbackli_label = -1;             /* Label */
47 static int hf_redbackli_acctid = -1;            /* Accounting Session Id */
48 static int hf_redbackli_dir = -1;               /* Direction */
49 static int hf_redbackli_eohpad = -1;            /* End Of Header Padding */
50 static int hf_redbackli_unknownavp = -1;        /* Unknown AVP */
51
52 static int ett_redbackli = -1;
53
54 static dissector_handle_t ip_handle;
55
56 #define RB_AVP_SEQNO    1
57 #define RB_AVP_LIID     2
58 #define RB_AVP_SESSID   3
59 #define RB_AVP_DIR      4
60 #define RB_AVP_LABEL    20
61 #define RB_AVP_ACCTID   40
62 #define RB_AVP_EOH      0
63
64 static const value_string avp_names[] = {
65         {RB_AVP_SEQNO,          "Sequence No"},
66         {RB_AVP_LIID,           "Lawful Intercept Id"},
67         {RB_AVP_SESSID,         "Session Id"},
68         {RB_AVP_LABEL,          "Label"},
69         {RB_AVP_ACCTID,         "Accounting Session Id"},
70         {RB_AVP_DIR,            "Direction"},
71         {RB_AVP_EOH,            "End Of Header"},
72         {0,                     NULL}
73 };
74
75 static void
76 redbackli_dissect_avp(guint8 avptype, guint8 avplen, tvbuff_t *tvb, gint offset, proto_tree *tree)
77 {
78         const char      *avpname;
79         proto_tree      *ti, *st=NULL;
80
81         avpname=val_to_str(avptype, avp_names, "Unknown");
82
83         ti = proto_tree_add_text(tree, tvb, offset, avplen+2, "%s AVP", avpname);
84         st = proto_item_add_subtree(ti, ett_redbackli);
85
86         proto_tree_add_text(st, tvb, offset, 1, "AVP Type: %d", avptype);
87         proto_tree_add_text(st, tvb, offset+1, 1, "AVP Length: %d", avplen);
88
89         if (!avplen)
90                 return;
91
92         /* XXX: ToDo: Validate the length (avplen) of the fixed length fields
93                 before calling proto_tree_add_item().
94                 Note that the field lengths have been validated when 
95                 dissect_avp() is called from redbackli_dissect_heur().
96          */
97                  
98         switch(avptype) {
99                 case(RB_AVP_SEQNO):
100                         proto_tree_add_item(st, hf_redbackli_seqno, tvb,
101                                             offset+2, avplen, FALSE);
102                         break;
103                 case(RB_AVP_LIID):
104                         proto_tree_add_item(st, hf_redbackli_liid, tvb,
105                                             offset+2, avplen, FALSE);
106                         break;
107                 case(RB_AVP_SESSID):
108                         proto_tree_add_item(st, hf_redbackli_sessid, tvb,
109                                             offset+2, avplen, FALSE);
110                         break;
111                 case(RB_AVP_LABEL):
112                         proto_tree_add_item(st, hf_redbackli_label, tvb,
113                                             offset+2, avplen, FALSE);
114                         break;
115                 case(RB_AVP_EOH):
116                         proto_tree_add_item(st, hf_redbackli_eohpad, tvb,
117                                             offset+2, avplen, FALSE);
118                         break;
119                 case(RB_AVP_DIR):
120                         proto_tree_add_item(st, hf_redbackli_dir, tvb,
121                                         offset+2, avplen, FALSE);
122                         break;
123                 case(RB_AVP_ACCTID):
124                         proto_tree_add_item(st, hf_redbackli_acctid, tvb,
125                                             offset+2, avplen, FALSE);
126                         break;
127                 default:
128                         proto_tree_add_item(st, hf_redbackli_unknownavp, tvb,
129                                             offset+2, avplen, FALSE);
130                         break;
131         }
132
133         return;
134 }
135
136 static void
137 redbackli_dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
138 {
139         guint8          avptype, avplen;
140         gint            len, offset=0;
141         gboolean        eoh;
142         proto_tree      *ti, *redbackli_tree=NULL;
143         tvbuff_t        *next_tvb;
144
145         col_set_str(pinfo->cinfo,COL_PROTOCOL,"RBLI");
146
147         if (tree) {
148                 ti = proto_tree_add_item(tree, proto_redbackli,
149                                          tvb, 0, -1, FALSE);
150                 redbackli_tree = proto_item_add_subtree(ti, ett_redbackli);
151         }
152
153         len=tvb_length(tvb);
154         offset=0;
155         eoh=FALSE;
156         while(!eoh && (len > 2)) {
157                 avptype = tvb_get_guint8(tvb, offset+0);
158                 avplen = tvb_get_guint8(tvb, offset+1);
159
160                 if ((len-2) < avplen)           /* AVP Complete ? */
161                         break;
162
163                 if (tree)
164                         redbackli_dissect_avp(avptype, avplen, tvb, offset, redbackli_tree);
165
166                 if (avptype == RB_AVP_EOH)
167                         eoh=TRUE;
168
169                 offset+=2+avplen;
170                 len-=2+avplen;
171         }
172
173         next_tvb = tvb_new_subset_remaining(tvb, offset);
174         call_dissector(ip_handle, next_tvb, pinfo, tree);
175 }
176
177
178 #define REDBACKLI_INTSIZE       6
179 #define REDBACKLI_EOHSIZE       2
180 #define MIN_REDBACKLI_SIZE      (3*REDBACKLI_INTSIZE+REDBACKLI_EOHSIZE)
181
182 static gboolean
183 redbackli_dissect_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
184 {
185         gint            len, offset=0;
186         gboolean        eoh=FALSE;
187         guint8          avptype, avplen;
188         guint32         avpfound=0;
189
190         len=tvb_length(tvb);
191         if (len < MIN_REDBACKLI_SIZE)
192                 return FALSE;
193
194         /*
195          * We scan the possible AVPs and look out for mismatches.
196          * An int AVP needs to be 4 byte long, and the eoh must be 0 or 1
197          * long .. Unknown AVPs also mean not for us ...
198          *
199          */
200         while((len > 2) && !eoh) {
201                 avptype = tvb_get_guint8(tvb, offset+0);
202                 avplen = tvb_get_guint8(tvb, offset+1);
203
204                 switch(avptype) {
205                         case(RB_AVP_SEQNO):
206                         case(RB_AVP_LIID):
207                         case(RB_AVP_SESSID):
208                                 if (avplen != 4)
209                                         return FALSE;
210                                 avpfound|=1<<avptype;
211                                 break;
212                         case(RB_AVP_EOH):
213                                 if (avplen > 1 || offset == 0)
214                                         return FALSE;
215                                 eoh=TRUE;
216                                 break;
217                         case(RB_AVP_LABEL):
218                         case(RB_AVP_DIR):   /* Is this correct? the hf_ originally had FT_UINT8 for DIR */
219                         case(RB_AVP_ACCTID):
220                                 break;
221                         default:
222                                 return FALSE;
223                 }
224                 offset+=2+avplen;
225                 len-=2+avplen;
226         }
227
228         if (!(avpfound & (1<<RB_AVP_SEQNO)))
229                 return FALSE;
230         if (!(avpfound & (1<<RB_AVP_SESSID)))
231                 return FALSE;
232         if (!(avpfound & (1<<RB_AVP_LIID)))
233                 return FALSE;
234
235         redbackli_dissect(tvb, pinfo, tree);
236
237         return TRUE;
238 }
239 void proto_register_redbackli(void) {
240         static hf_register_info hf[] = {
241                 { &hf_redbackli_seqno,
242                         { "Sequence No", "redbackli.seqno", FT_UINT32, BASE_DEC, NULL, 0x0,
243                         NULL, HFILL }},
244                 { &hf_redbackli_liid,
245                         { "Lawful Intercept Id", "redbackli.liid", FT_UINT32, BASE_DEC, NULL, 0x0,
246                         "LI Identifier", HFILL }},
247                 { &hf_redbackli_sessid,
248                         { "Session Id", "redbackli.sessid", FT_UINT32, BASE_DEC, NULL, 0x0,
249                         "Session Identifier", HFILL }},
250                 { &hf_redbackli_dir,
251 #if 0 /* XXX: If one goes by the heuristic then this field can be variable length ??
252          In the absence of any documentation We'll assume that's the case 
253          (even though 'direction' sounds like a fixed length field */
254                         { "Direction", "redbackli.dir", FT_UINT8, BASE_DEC, NULL, 0x0,
255 #endif
256                         { "Direction", "redbackli.dir", FT_BYTES, BASE_NONE, NULL, 0x0,
257                         NULL, HFILL }},
258                 { &hf_redbackli_label,
259                         { "Label", "redbackli.label", FT_STRING, BASE_NONE, NULL, 0x0,
260                         NULL, HFILL }},
261                 { &hf_redbackli_acctid,
262                         { "Acctid", "redbackli.acctid", FT_BYTES, BASE_NONE, NULL, 0x0,
263                         NULL, HFILL }},
264                 { &hf_redbackli_eohpad,
265                         { "End of Header Padding", "redbackli.eohpad", FT_BYTES, BASE_NONE, NULL, 0x0,
266                         NULL, HFILL }},
267                 { &hf_redbackli_unknownavp,
268                         { "Unknown AVP", "redbackli.unknownavp", FT_BYTES, BASE_NONE, NULL, 0x0,
269                         NULL, HFILL }}
270                 };
271
272         static gint *ett[] = {
273                 &ett_redbackli
274         };
275
276         proto_redbackli = proto_register_protocol("Redback Lawful Intercept",
277                                                   "RedbackLI","redbackli");
278
279         proto_register_field_array(proto_redbackli,hf,array_length(hf));
280         proto_register_subtree_array(ett,array_length(ett));
281
282         register_dissector("redbackli", redbackli_dissect, proto_redbackli);
283 }
284
285 void proto_reg_handoff_redbackli(void) {
286         dissector_handle_t redbackli_handle;
287
288         ip_handle = find_dissector("ip");
289
290         redbackli_handle = find_dissector("redbackli");
291         dissector_add_handle("udp.port", redbackli_handle);  /* for 'decode-as' */
292
293         heur_dissector_add("udp", redbackli_dissect_heur, proto_redbackli);
294 }