Document the new Copy Profile button.
[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 <stdio.h>
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <time.h>
36 #include <string.h>
37
38 #include <glib.h>
39 #include <epan/packet.h>
40 #include <epan/addr_resolv.h>
41 #include <epan/strutil.h>
42
43 static int proto_redbackli = -1;
44
45 static int hf_redbackli_seqno = -1;             /* Sequence No */
46 static int hf_redbackli_liid = -1;              /* LI Id */
47 static int hf_redbackli_sessid = -1;            /* Session Id */
48 static int hf_redbackli_label = -1;             /* Label */
49 static int hf_redbackli_acctid = -1;            /* Accounting Session Id */
50 static int hf_redbackli_dir = -1;               /* Direction */
51 static int hf_redbackli_eohpad = -1;            /* End Of Header Padding */
52 static int hf_redbackli_unknownavp = -1;        /* Unknown AVP */
53
54 static int ett_redbackli = -1;
55
56 static dissector_handle_t ip_handle;
57
58 #define RB_AVP_SEQNO    1
59 #define RB_AVP_LIID     2
60 #define RB_AVP_SESSID   3
61 #define RB_AVP_DIR      4
62 #define RB_AVP_LABEL    20
63 #define RB_AVP_ACCTID   40
64 #define RB_AVP_EOH      0
65
66 static const value_string avp_names[] = {
67         {RB_AVP_SEQNO,          "Sequence No"},
68         {RB_AVP_LIID,           "Lawful Intercept Id"},
69         {RB_AVP_SESSID,         "Session Id"},
70         {RB_AVP_LABEL,          "Label"},
71         {RB_AVP_ACCTID,         "Accounting Session Id"},
72         {RB_AVP_DIR,            "Direction"},
73         {RB_AVP_EOH,            "End Of Header"},
74         {0,                     NULL}
75 };
76
77 static void
78 redbackli_dissect_avp(guint8 avptype, guint8 avplen, tvbuff_t *tvb, gint offset, proto_tree *tree)
79 {
80         const char      *avpname;
81         proto_tree      *ti, *st=NULL;
82
83         avpname=val_to_str(avptype, avp_names, "Unknown");
84
85         ti = proto_tree_add_text(tree, tvb, offset, avplen+2, "%s AVP", avpname);
86         st = proto_item_add_subtree(ti, ett_redbackli);
87
88         proto_tree_add_text(st, tvb, offset, 1, "AVP Type: %d", avptype);
89         proto_tree_add_text(st, tvb, offset+1, 1, "AVP Length: %d", avplen);
90
91         if (!avplen)
92                 return;
93
94         /* XXX: ToDo: Validate the length (avplen) of the fixed length fields
95                 before calling proto_tree_add_item().
96                 Note that the field lengths have been validated when 
97                 dissect_avp() is called from redbackli_dissect_heur().
98          */
99                  
100         switch(avptype) {
101                 case(RB_AVP_SEQNO):
102                         proto_tree_add_item(st, hf_redbackli_seqno, tvb,
103                                             offset+2, avplen, FALSE);
104                         break;
105                 case(RB_AVP_LIID):
106                         proto_tree_add_item(st, hf_redbackli_liid, tvb,
107                                             offset+2, avplen, FALSE);
108                         break;
109                 case(RB_AVP_SESSID):
110                         proto_tree_add_item(st, hf_redbackli_sessid, tvb,
111                                             offset+2, avplen, FALSE);
112                         break;
113                 case(RB_AVP_LABEL):
114                         proto_tree_add_item(st, hf_redbackli_label, tvb,
115                                             offset+2, avplen, FALSE);
116                         break;
117                 case(RB_AVP_EOH):
118                         proto_tree_add_item(st, hf_redbackli_eohpad, tvb,
119                                             offset+2, avplen, FALSE);
120                         break;
121                 case(RB_AVP_DIR):
122                         proto_tree_add_item(st, hf_redbackli_dir, tvb,
123                                         offset+2, avplen, FALSE);
124                         break;
125                 case(RB_AVP_ACCTID):
126                         proto_tree_add_item(st, hf_redbackli_acctid, tvb,
127                                             offset+2, avplen, FALSE);
128                         break;
129                 default:
130                         proto_tree_add_item(st, hf_redbackli_unknownavp, tvb,
131                                             offset+2, avplen, FALSE);
132                         break;
133         }
134
135         return;
136 }
137
138 static void
139 redbackli_dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
140 {
141         guint8          avptype, avplen;
142         gint            len, offset=0;
143         gboolean        eoh;
144         proto_tree      *ti, *redbackli_tree=NULL;
145         tvbuff_t        *next_tvb;
146
147         if(check_col(pinfo->cinfo,COL_PROTOCOL))
148                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RBLI");
149
150         if (tree) {
151                 ti = proto_tree_add_item(tree, proto_redbackli,
152                                          tvb, 0, -1, FALSE);
153                 redbackli_tree = proto_item_add_subtree(ti, ett_redbackli);
154         }
155
156         len=tvb_length(tvb);
157         offset=0;
158         eoh=FALSE;
159         while(!eoh && (len > 2)) {
160                 avptype = tvb_get_guint8(tvb, offset+0);
161                 avplen = tvb_get_guint8(tvb, offset+1);
162
163                 if ((len-2) < avplen)           /* AVP Complete ? */
164                         break;
165
166                 if (tree)
167                         redbackli_dissect_avp(avptype, avplen, tvb, offset, redbackli_tree);
168
169                 if (avptype == RB_AVP_EOH)
170                         eoh=TRUE;
171
172                 offset+=2+avplen;
173                 len-=2+avplen;
174         }
175
176         next_tvb = tvb_new_subset_remaining(tvb, offset);
177         call_dissector(ip_handle, next_tvb, pinfo, tree);
178 }
179
180
181 #define REDBACKLI_INTSIZE       6
182 #define REDBACKLI_EOHSIZE       2
183 #define MIN_REDBACKLI_SIZE      (3*REDBACKLI_INTSIZE+REDBACKLI_EOHSIZE)
184
185 static gboolean
186 redbackli_dissect_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
187 {
188         gint            len, offset=0;
189         gboolean        eoh=FALSE;
190         guint8          avptype, avplen;
191         guint32         avpfound=0;
192
193         len=tvb_length(tvb);
194         if (len < MIN_REDBACKLI_SIZE)
195                 return FALSE;
196
197         /*
198          * We scan the possible AVPs and look out for mismatches.
199          * An int AVP needs to be 4 byte long, and the eoh must be 0 or 1
200          * long .. Unknown AVPs also mean not for us ...
201          *
202          */
203         while((len > 2) && !eoh) {
204                 avptype = tvb_get_guint8(tvb, offset+0);
205                 avplen = tvb_get_guint8(tvb, offset+1);
206
207                 switch(avptype) {
208                         case(RB_AVP_SEQNO):
209                         case(RB_AVP_LIID):
210                         case(RB_AVP_SESSID):
211                                 if (avplen != 4)
212                                         return FALSE;
213                                 avpfound|=1<<avptype;
214                                 break;
215                         case(RB_AVP_EOH):
216                                 if (avplen > 1 || offset == 0)
217                                         return FALSE;
218                                 eoh=TRUE;
219                                 break;
220                         case(RB_AVP_LABEL):
221                         case(RB_AVP_DIR):   /* Is this correct? the hf_ originally had FT_UINT8 for DIR */
222                         case(RB_AVP_ACCTID):
223                                 break;
224                         default:
225                                 return FALSE;
226                 }
227                 offset+=2+avplen;
228                 len-=2+avplen;
229         }
230
231         if (!(avpfound & (1<<RB_AVP_SEQNO)))
232                 return FALSE;
233         if (!(avpfound & (1<<RB_AVP_SESSID)))
234                 return FALSE;
235         if (!(avpfound & (1<<RB_AVP_LIID)))
236                 return FALSE;
237
238         redbackli_dissect(tvb, pinfo, tree);
239
240         return TRUE;
241 }
242 void proto_register_redbackli(void) {
243         static hf_register_info hf[] = {
244                 { &hf_redbackli_seqno,
245                         { "Sequence No", "redbackli.seqno", FT_UINT32, BASE_DEC, NULL, 0x0,
246                         NULL, HFILL }},
247                 { &hf_redbackli_liid,
248                         { "Lawful Intercept Id", "redbackli.liid", FT_UINT32, BASE_DEC, NULL, 0x0,
249                         "LI Identifier", HFILL }},
250                 { &hf_redbackli_sessid,
251                         { "Session Id", "redbackli.sessid", FT_UINT32, BASE_DEC, NULL, 0x0,
252                         "Session Identifier", HFILL }},
253                 { &hf_redbackli_dir,
254 #if 0 /* XXX: If one goes by the heuristic then this field can be variable length ??
255          In the absence of any documentation We'll assume that's the case 
256          (even though 'direction' sounds like a fixed length field */
257                         { "Direction", "redbackli.dir", FT_UINT8, BASE_DEC, NULL, 0x0,
258 #endif
259                         { "Direction", "redbackli.dir", FT_BYTES, BASE_NONE, NULL, 0x0,
260                         NULL, HFILL }},
261                 { &hf_redbackli_label,
262                         { "Label", "redbackli.label", FT_STRING, BASE_NONE, NULL, 0x0,
263                         NULL, HFILL }},
264                 { &hf_redbackli_acctid,
265                         { "Acctid", "redbackli.acctid", FT_BYTES, BASE_NONE, NULL, 0x0,
266                         NULL, HFILL }},
267                 { &hf_redbackli_eohpad,
268                         { "End of Header Padding", "redbackli.eohpad", FT_BYTES, BASE_NONE, NULL, 0x0,
269                         NULL, HFILL }},
270                 { &hf_redbackli_unknownavp,
271                         { "Unknown AVP", "redbackli.unknownavp", FT_BYTES, BASE_NONE, NULL, 0x0,
272                         NULL, HFILL }}
273                 };
274
275         static gint *ett[] = {
276                 &ett_redbackli
277         };
278
279         proto_redbackli = proto_register_protocol("Redback Lawful Intercept",
280                                                   "RedbackLI","redbackli");
281
282         proto_register_field_array(proto_redbackli,hf,array_length(hf));
283         proto_register_subtree_array(ett,array_length(ett));
284
285         register_dissector("redbackli", redbackli_dissect, proto_redbackli);
286 }
287
288 void proto_reg_handoff_redbackli(void) {
289         dissector_handle_t redbackli_handle;
290
291         ip_handle = find_dissector("ip");
292
293         redbackli_handle = find_dissector("redbackli");
294         dissector_add_handle("udp.port", redbackli_handle);  /* for 'decode-as' */
295
296         heur_dissector_add("udp", redbackli_dissect_heur, proto_redbackli);
297 }