name change
[obnox/wireshark/wip.git] / epan / dfilter / dfilter.c
1 /*
2  * $Id$
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 2001 Gerald Combs
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "dfilter-int.h"
31 #include "syntax-tree.h"
32 #include "gencode.h"
33 #include "semcheck.h"
34 #include "dfvm.h"
35 #include <epan/epan_dissect.h>
36 #include "dfilter.h"
37
38 #define DFILTER_TOKEN_ID_OFFSET 1
39
40 /* Global error message space for dfilter_compile errors */
41 static gchar dfilter_error_msg_buf[1024];
42 gchar *dfilter_error_msg;       /* NULL when no error resulted */
43
44 /* From scanner.c */
45 void    df_scanner_text(const char *text);
46 void    df_scanner_cleanup(void);
47 int     df_lex(void);
48
49 /* Holds the singular instance of our Lemon parser object */
50 static void*    ParserObj = NULL;
51
52 void
53 dfilter_fail(const char *format, ...)
54 {
55         va_list args;
56
57         /* If we've already reported one error, don't overwite it */
58         if (dfilter_error_msg != NULL)
59                 return;
60
61         va_start(args, format);
62
63         g_vsnprintf(dfilter_error_msg_buf, sizeof(dfilter_error_msg_buf),
64                         format, args);
65         dfilter_error_msg = dfilter_error_msg_buf;
66         va_end(args);
67 }
68
69
70 /* Initialize the dfilter module */
71 void
72 dfilter_init(void)
73 {
74         if (ParserObj) {
75                 g_message("I expected ParserObj to be NULL\n");
76                 /* Free the Lemon Parser object */
77                 DfilterFree(ParserObj, g_free);
78         }
79         /* Allocate an instance of our Lemon-based parser */
80         ParserObj = DfilterAlloc(g_malloc);
81
82 /* Enable parser tracing by defining AM_CFLAGS
83  * so that it contains "-DDFTRACE".
84  */
85 #ifdef DFTRACE
86         /* Trace parser */
87         DfilterTrace(stdout, "lemon> ");
88 #endif
89
90         /* Initialize the syntax-tree sub-sub-system */
91         sttype_init();
92 }
93
94 /* Clean-up the dfilter module */
95 void
96 dfilter_cleanup(void)
97 {
98         /* Free the Lemon Parser object */
99         if (ParserObj) {
100                 DfilterFree(ParserObj, g_free);
101         }
102
103         /* Clean up the syntax-tree sub-sub-system */
104         sttype_cleanup();
105 }
106
107 static dfilter_t*
108 dfilter_new(void)
109 {
110         dfilter_t       *df;
111
112         df = g_new(dfilter_t, 1);
113         df->insns = NULL;
114
115         return df;
116 }
117
118 /* Given a GPtrArray of instructions (dfvm_insn_t),
119  * free them. */
120 static void
121 free_insns(GPtrArray *insns)
122 {
123         unsigned int    i;
124         dfvm_insn_t     *insn;
125
126         for (i = 0; i < insns->len; i++) {
127                 insn = g_ptr_array_index(insns, i);
128                 dfvm_insn_free(insn);
129         }
130 }
131
132 void
133 dfilter_free(dfilter_t *df)
134 {
135         if (df->insns) {
136                 free_insns(df->insns);
137         }
138
139         if (df->interesting_fields) {
140                 g_free(df->interesting_fields);
141         }
142
143         g_free(df->registers);
144         g_free(df->attempted_load);
145         g_free(df);
146 }
147
148
149 static dfwork_t*
150 dfwork_new(void)
151 {
152         dfwork_t        *dfw;
153
154         dfw = g_new(dfwork_t, 1);
155
156         dfw->st_root = NULL;
157         dfw->syntax_error = FALSE;
158         dfw->insns = NULL;
159         dfw->loaded_fields = NULL;
160         dfw->interesting_fields = NULL;
161         dfw->next_insn_id = 0;
162         dfw->next_register = 0;
163
164         return dfw;
165 }
166
167 static void
168 dfwork_free(dfwork_t *dfw)
169 {
170         if (dfw->st_root) {
171                 stnode_free(dfw->st_root);
172         }
173
174         if (dfw->loaded_fields) {
175                 g_hash_table_destroy(dfw->loaded_fields);
176         }
177
178         if (dfw->interesting_fields) {
179                 g_hash_table_destroy(dfw->interesting_fields);
180         }
181
182         if (dfw->insns) {
183                 free_insns(dfw->insns);
184         }
185
186
187         g_free(dfw);
188 }
189
190
191 gboolean
192 dfilter_compile(const gchar *text, dfilter_t **dfp)
193 {
194         int             token;
195         dfilter_t       *dfilter;
196         dfwork_t        *dfw;
197         gboolean failure = FALSE;
198
199         dfilter_error_msg = NULL;
200
201         dfw = dfwork_new();
202
203         df_scanner_text(text);
204
205         while (1) {
206                 df_lval = stnode_new(STTYPE_UNINITIALIZED, NULL);
207                 token = df_lex();
208
209                 /* Check for scanner failure */
210                 if (token == SCAN_FAILED) {
211                         failure = TRUE;
212                         break;
213                 }
214
215                 /* Check for end-of-input */
216                 if (token == 0) {
217                         break;
218                 }
219
220                 /* Give the token to the parser */
221                 Dfilter(ParserObj, token, df_lval, dfw);
222                 /* We've used the stnode_t, so we don't want to free it */
223                 df_lval = NULL;
224
225                 if (dfw->syntax_error) {
226                         failure = TRUE;
227                         break;
228                 }
229         } /* while (1) */
230
231         /* If we created an stnode_t but didn't use it, free it; the
232          * parser doesn't know about it and won't free it for us. */
233         if (df_lval) {
234                 stnode_free(df_lval);
235                 df_lval = NULL;
236         }
237
238         /* Tell the parser that we have reached the end of input; that
239          * way, it'll reset its state for the next compile.  (We want
240          * to do that even if we got a syntax error, to make sure the
241          * parser state is cleaned up; we don't create a new parser
242          * object when we start a new parse, and don't destroy it when
243          * the parse finishes.) */
244         Dfilter(ParserObj, 0, NULL, dfw);
245
246         /* One last check for syntax error (after EOF) */
247         if (dfw->syntax_error)
248                 failure = TRUE;
249
250         /* Reset flex */
251         df_scanner_cleanup();
252
253         if (failure)
254                 goto FAILURE;
255
256         /* Success, but was it an empty filter? If so, discard
257          * it and set *dfp to NULL */
258         if (dfw->st_root == NULL) {
259                 *dfp = NULL;
260         }
261         else {
262
263                 /* Check semantics and do necessary type conversion*/
264                 if (!dfw_semcheck(dfw)) {
265                         goto FAILURE;
266                 }
267
268                 /* Create bytecode */
269                 dfw_gencode(dfw);
270
271                 /* Tuck away the bytecode in the dfilter_t */
272                 dfilter = dfilter_new();
273                 dfilter->insns = dfw->insns;
274                 dfw->insns = NULL;
275                 dfilter->interesting_fields = dfw_interesting_fields(dfw,
276                         &dfilter->num_interesting_fields);
277
278                 /* Initialize run-time space */
279                 dfilter->num_registers = dfw->next_register;
280                 dfilter->registers = g_new0(GList*, dfilter->num_registers);
281                 dfilter->attempted_load = g_new0(gboolean, dfilter->num_registers);
282
283                 /* And give it to the user. */
284                 *dfp = dfilter;
285         }
286         /* SUCCESS */
287         dfwork_free(dfw);
288         return TRUE;
289
290 FAILURE:
291         if (dfw) {
292                 dfwork_free(dfw);
293         }
294         dfilter_fail("Unable to parse filter string \"%s\".", text);
295         *dfp = NULL;
296         return FALSE;
297
298 }
299
300
301 gboolean
302 dfilter_apply(dfilter_t *df, proto_tree *tree)
303 {
304         return dfvm_apply(df, tree);
305 }
306
307 gboolean
308 dfilter_apply_edt(dfilter_t *df, epan_dissect_t* edt)
309 {
310         return dfvm_apply(df, edt->tree);
311 }
312
313
314 void
315 dfilter_prime_proto_tree(const dfilter_t *df, proto_tree *tree)
316 {
317     int i;
318
319     for (i = 0; i < df->num_interesting_fields; i++) {
320         proto_tree_prime_hfid(tree, df->interesting_fields[i]);
321     }
322 }
323
324
325 void
326 dfilter_dump(dfilter_t *df)
327 {
328         dfvm_dump(stdout, df->insns);
329 }