Get the build going.
[obnox/wireshark/wip.git] / gtk / firewall_dlg.c
1 /* firewall_rules_dlg.c
2  *
3  * $Id$
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19
20 /*
21  * Generate firewall ACL rules based on packet addresses and ports.
22  * For directional rules, an outside interface is assumed.
23  *
24  * There may be better ways to present the information, e.g. all rules
25  * in one huge text window, or some sort of tree view.
26  */
27
28 /*
29  * To add a new product, add syntax functions modify the products[] array.
30  *
31  * To add a new syntax function, add its prototype above the products[]
32  * array, and add the function below with all the others.
33  */
34
35 /* Copied from ssl-dlg.c */
36
37 #include "config.h"
38
39 #include <gtk/gtk.h>
40
41 #include <stdio.h>
42 #include <string.h>
43
44 #ifdef HAVE_FCNTL_H
45 #include <fcntl.h>
46 #endif
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51
52 #include <ctype.h>
53
54 #include <gtk/main.h>
55 #include <gtk/dlg_utils.h>
56 #include <gtk/file_dlg.h>
57 #include <gtk/keys.h>
58 #include <globals.h>
59 #include <alert_box.h>
60 #include <simple_dialog.h>
61 #include <epan/dissectors/packet-ipv6.h>
62 #include <epan/prefs.h>
63 #include <epan/addr_resolv.h>
64 #include <epan/charsets.h>
65 #include <util.h>
66 #include <gtk/gui_utils.h>
67 #include <epan/epan_dissect.h>
68 #include <epan/filesystem.h>
69 #include <gtk/compat_macros.h>
70 #include <epan/ipproto.h>
71 #include <gtk/font_utils.h>
72 #include <wiretap/file_util.h>
73
74 #include "firewall_dlg.h"
75
76 #define MAX_RULE_LEN 200
77
78 /* Rule types */
79 typedef enum {
80   RT_NONE,
81   RT_MAC_SRC,
82   RT_MAC_DST,
83   RT_IPv4_SRC,
84   RT_IPv4_DST,
85   RT_PORT_SRC,
86   RT_PORT_DST,
87   RT_IPv4_PORT_SRC,
88   RT_IPv4_PORT_DST,
89   NUM_RULE_TYPES
90 } rule_type_t;
91
92
93 /* Copied from packet_info struct */
94 typedef struct _rule_info_t {
95   gint product;
96   address dl_src;
97   address dl_dst;
98   address net_src;
99   address net_dst;
100   port_type ptype;
101   guint32 srcport;
102   guint32 destport;
103   GtkWidget *text;
104   GtkWidget *filter_om;
105   GtkWidget *deny_cb;
106   GtkWidget *inbound_cb;
107   GtkWidget *firewall_save_as_w;
108   gboolean inbound;
109   gboolean deny;
110   rule_type_t rule_type;
111 } rule_info_t;
112
113 /* Syntax function prototypes */
114 typedef void (*syntax_func)(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
115
116 static void sf_dummy(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
117
118 static void sf_ipfw_mac(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
119 static void sf_netfilter_mac(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
120
121 static void sf_ios_std_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
122 static void sf_ios_ext_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
123 static void sf_ipfilter_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
124 static void sf_ipfw_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
125 static void sf_netfilter_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
126 static void sf_pf_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
127 /* XXX - Can you addresses-only filters using WFW/netsh? */
128
129 static void sf_ios_ext_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
130 static void sf_ipfilter_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
131 static void sf_ipfw_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
132 static void sf_netfilter_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
133 static void sf_pf_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
134 static void sf_netsh_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
135
136 static void sf_ios_ext_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
137 static void sf_ipfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
138 static void sf_ipfw_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
139 static void sf_netfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
140 static void sf_pf_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
141 static void sf_netsh_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
142
143 typedef struct _fw_product_t {
144   gchar *name;
145   gchar *comment_pfx;
146   syntax_func mac_func;
147   syntax_func ipv4_func;
148   syntax_func port_func;
149   syntax_func ipv4_port_func;
150   gboolean does_inbound;
151 } fw_product;
152
153 fw_product products[] = {
154     { "Cisco IOS (standard)", "!", NULL, sf_ios_std_ipv4, NULL, NULL, FALSE },
155     { "Cisco IOS (extended)", "!",
156         NULL, sf_ios_ext_ipv4, sf_ios_ext_port, sf_ios_ext_ipv4_port, TRUE },
157     { "IP Filter (ipfilter)", "#",
158         NULL, sf_ipfilter_ipv4, sf_ipfilter_port, sf_ipfilter_ipv4_port, TRUE },
159     { "IPFirewall (ipfw)", "#",
160         sf_ipfw_mac, sf_ipfw_ipv4, sf_ipfw_port, sf_ipfw_ipv4_port, TRUE },
161     { "Netfilter (iptables)", "#",
162         sf_netfilter_mac, sf_netfilter_ipv4, sf_netfilter_port,
163         sf_netfilter_ipv4_port, TRUE },
164     { "Packet Filter (pf)", "#",
165         NULL, sf_pf_ipv4, sf_pf_port, sf_pf_ipv4_port, TRUE },
166     { "Windows Firewall (netsh)", "#",
167         NULL, NULL, sf_netsh_port, sf_netsh_ipv4_port, FALSE }
168 };
169 #define NUM_PRODS (sizeof(products) / sizeof(fw_product))
170
171
172 static void select_product(GtkWidget * win, gpointer data);
173 static void select_filter(GtkWidget * win, gpointer data);
174 static void toggle_inbound(GtkToggleButton *t, gpointer data);
175 static void toggle_deny(GtkToggleButton *t, gpointer data);
176 static void set_rule_text(rule_info_t *rule_info);
177 static void firewall_destroy_cb(GtkWidget * win, gpointer data);
178 static void firewall_copy_cmd_cb(GtkWidget * w, gpointer data);
179 static void firewall_save_as_cmd_cb(GtkWidget * w, gpointer data);
180 static void firewall_save_as_ok_cb(GtkWidget * w, gpointer fs);
181 static void firewall_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
182
183 #define WS_RULE_INFO_KEY "rule_info_key"
184
185 /* List of "rule_info_t" structures for all rule windows. */
186 static GList *rule_infos;
187
188 /* Remove a "rule_info_t" structure from the list. */
189 static void
190 forget_rule_info(rule_info_t *rule_info)
191 {
192   rule_infos = g_list_remove(rule_infos, rule_info);
193 }
194
195 void
196 firewall_rule_cb(GtkWidget *w _U_, gpointer data _U_)
197 {
198     GtkWidget   *rule_w, *vbox, *txt_scrollw, *text;
199     GtkWidget   *label, *product_om, *menu, *menu_item;
200     GtkWidget   *hbox, *button_hbox, *button;
201     GtkTooltips *tooltips;
202     rule_info_t *rule_info;
203     packet_info *pinfo = &cfile.edt->pi;
204     guint i;
205
206     rule_info = g_new0(rule_info_t, 1);
207     COPY_ADDRESS(&(rule_info->dl_src), &(pinfo->dl_src));
208     COPY_ADDRESS(&(rule_info->dl_dst), &(pinfo->dl_dst));
209     COPY_ADDRESS(&(rule_info->net_src), &(pinfo->net_src));
210     COPY_ADDRESS(&(rule_info->net_dst), &(pinfo->net_dst));
211     rule_info->ptype = pinfo->ptype;
212     rule_info->srcport = pinfo->srcport;
213     rule_info->destport = pinfo->destport;
214     rule_info->inbound = TRUE;
215     rule_info->deny = TRUE;
216     rule_info->product = 0;
217
218     rule_w = dlg_window_new("Firewall ACL Rules");
219
220     gtk_widget_set_name(rule_w, "Firewall ACL rule window");
221     gtk_container_border_width(GTK_CONTAINER(rule_w), 6);
222
223     /* setup the container */
224     tooltips = gtk_tooltips_new ();
225
226     vbox = gtk_vbox_new(FALSE, 6);
227     gtk_container_add(GTK_CONTAINER(rule_w), vbox);
228
229     /* rule type selectors hbox */
230     hbox = gtk_hbox_new(FALSE, 1);
231     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
232
233     /* product selector */
234     label = gtk_label_new("Product");
235     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
236
237     product_om = gtk_option_menu_new();
238     menu = gtk_menu_new();
239     for (i = 0; i < NUM_PRODS; i++) {
240         menu_item = gtk_menu_item_new_with_label(products[i].name);
241         SIGNAL_CONNECT(menu_item, "activate", select_product, GUINT_TO_POINTER(i));
242         OBJECT_SET_DATA(menu_item, WS_RULE_INFO_KEY, rule_info);
243         gtk_menu_append(GTK_MENU(menu), menu_item);
244         /* if (i == 0)
245             gtk_menu_item_select(GTK_MENU_ITEM(menu_item)); */
246     }
247     gtk_option_menu_set_menu(GTK_OPTION_MENU(product_om), menu);
248     gtk_box_pack_start(GTK_BOX(hbox), product_om, FALSE, FALSE, 5);
249
250     /* type selector */
251     label = gtk_label_new("Filter");
252     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
253
254     rule_info->filter_om = gtk_option_menu_new();
255     gtk_box_pack_start(GTK_BOX(hbox), rule_info->filter_om, FALSE, FALSE, 5);
256
257     /* inbound selector */
258     rule_info->inbound_cb = gtk_check_button_new_with_label("Inbound");
259     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rule_info->inbound_cb),
260         rule_info->inbound);
261     gtk_box_pack_start(GTK_BOX(hbox), rule_info->inbound_cb, FALSE, FALSE, 10);
262     SIGNAL_CONNECT(rule_info->inbound_cb, "toggled", toggle_inbound, rule_info);
263
264     /* deny selector */
265     rule_info->deny_cb = gtk_check_button_new_with_label("Deny");
266     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rule_info->deny_cb),
267         rule_info->deny);
268     gtk_box_pack_start(GTK_BOX(hbox), rule_info->deny_cb, FALSE, FALSE, 10);
269     SIGNAL_CONNECT(rule_info->deny_cb, "toggled", toggle_deny, rule_info);
270
271     /* create a scrolled window for the text */
272     txt_scrollw = scrolled_window_new(NULL, NULL);
273 #if GTK_MAJOR_VERSION >= 2
274     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
275                                         GTK_SHADOW_IN);
276 #endif
277     gtk_box_pack_start(GTK_BOX(vbox), txt_scrollw, TRUE, TRUE, 0);
278
279     /* create a text box */
280 #if GTK_MAJOR_VERSION < 2
281     text = gtk_text_new(NULL, NULL);
282     gtk_text_set_editable(GTK_TEXT(text), FALSE);
283 #else
284     text = gtk_text_view_new();
285     gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
286 #endif
287     gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
288     rule_info->text = text;
289
290     /* button hbox */
291     button_hbox = gtk_hbutton_box_new();
292     gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
293     gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
294     gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
295
296     /* Create Copy Button */
297     button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_COPY);
298     SIGNAL_CONNECT(button, "clicked", firewall_copy_cmd_cb, rule_info);
299     gtk_tooltips_set_tip (tooltips, button, "Copy rule to clipboard ", NULL);
300     gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
301
302     /* Create Save As Button */
303     button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
304     SIGNAL_CONNECT(button, "clicked", firewall_save_as_cmd_cb, rule_info);
305     gtk_tooltips_set_tip (tooltips, button, "Save the rule as currently displayed ", NULL);
306     gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
307
308     /* Create Close Button */
309     button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
310     gtk_tooltips_set_tip (tooltips, button,
311         "Close the dialog", NULL);
312     gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
313     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
314
315     window_set_cancel_button(rule_w, button, window_cancel_button_cb);
316
317     /* Tuck away the rule_info object into the window */
318     OBJECT_SET_DATA(rule_w, WS_RULE_INFO_KEY, rule_info);
319
320     SIGNAL_CONNECT(rule_w, "delete_event", window_delete_event_cb, NULL);
321     SIGNAL_CONNECT(rule_w, "destroy", firewall_destroy_cb, NULL);
322
323     /* Make sure this widget gets destroyed if we quit the main loop,
324        so that if we exit, we clean up any temporary files we have
325        for "Follow SSL Stream" windows. */
326     gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(rule_w));
327
328     gtk_widget_show_all(rule_w);
329     window_present(rule_w);
330     gtk_option_menu_set_history(GTK_OPTION_MENU(product_om), 0);
331     select_product(rule_w, GUINT_TO_POINTER(0));
332 }
333
334 /* Set the current product. */
335 #define ADD_TO_FILTER_MENU(rt) \
336         menu_item = gtk_menu_item_new_with_label(name); \
337         SIGNAL_CONNECT(menu_item, "activate", select_filter, \
338             GUINT_TO_POINTER(rt)); \
339         OBJECT_SET_DATA(menu_item, WS_RULE_INFO_KEY, rule_info); \
340         gtk_menu_append(GTK_MENU(menu), menu_item); \
341         if (! rt_set) { \
342             rt_set = TRUE; \
343             first_mi = menu_item; \
344             rule_type = rt; \
345         }
346
347 #define NAME_TCP_UDP (rule_info->ptype == PT_TCP ? "TCP" : "UDP")
348 static void
349 select_product(GtkWidget *w, gpointer data)
350 {
351     guint prod = GPOINTER_TO_UINT(data);
352     rule_info_t *rule_info;
353     gchar name[MAX_RULE_LEN], addr_str[MAX_RULE_LEN];
354     address *addr;
355     GtkWidget *menu, *menu_item, *first_mi = NULL;
356     rule_type_t rule_type = RT_NONE;
357     gboolean rt_set = FALSE, sensitive = FALSE;
358
359     rule_info = OBJECT_GET_DATA(w, WS_RULE_INFO_KEY);
360
361     if (prod >= NUM_PRODS || !rule_info)
362         return;
363
364     rule_info->product = prod;
365
366     /* Clear the menu */
367     gtk_option_menu_remove_menu(GTK_OPTION_MENU(rule_info->filter_om));
368     menu = gtk_menu_new();
369     gtk_option_menu_set_menu(GTK_OPTION_MENU(rule_info->filter_om), menu);
370
371     /* Fill in valid menu items.   */
372     if (products[prod].mac_func && rule_info->dl_src.type == AT_ETHER) {
373         addr = &(rule_info->dl_src);
374         address_to_str_buf(addr, name, MAX_RULE_LEN);
375         ADD_TO_FILTER_MENU(RT_MAC_SRC);
376
377         addr = &(rule_info->dl_dst);
378         address_to_str_buf(addr, name, MAX_RULE_LEN);
379         ADD_TO_FILTER_MENU(RT_MAC_DST);
380     }
381
382     if (products[prod].ipv4_func && rule_info->net_src.type == AT_IPv4) {
383         addr = &(rule_info->net_src);
384         address_to_str_buf(addr, name, MAX_RULE_LEN);
385         ADD_TO_FILTER_MENU(RT_IPv4_SRC);
386
387         addr = &(rule_info->net_dst);
388         address_to_str_buf(addr, name, MAX_RULE_LEN);
389         ADD_TO_FILTER_MENU(RT_IPv4_DST);
390     }
391
392     if (products[prod].port_func && (rule_info->ptype == PT_TCP || rule_info->ptype == PT_UDP)) {
393         g_snprintf(name, MAX_RULE_LEN, "%s port %u", NAME_TCP_UDP,
394             rule_info->srcport);
395         ADD_TO_FILTER_MENU(RT_PORT_SRC);
396         if (rule_info->srcport != rule_info->destport) {
397             g_snprintf(name, MAX_RULE_LEN, "%s port %u", NAME_TCP_UDP,
398                 rule_info->destport);
399             ADD_TO_FILTER_MENU(RT_PORT_DST);
400         }
401     }
402
403     if (products[prod].ipv4_port_func && rule_info->net_src.type == AT_IPv4 &&
404             (rule_info->ptype == PT_TCP || rule_info->ptype == PT_UDP)) {
405         addr = &(rule_info->net_src);
406         address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
407         g_snprintf(name, MAX_RULE_LEN, "%s + %s port %u", addr_str,
408             NAME_TCP_UDP, rule_info->srcport);
409         ADD_TO_FILTER_MENU(RT_IPv4_PORT_SRC);
410
411         addr = &(rule_info->net_dst);
412         address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
413         g_snprintf(name, MAX_RULE_LEN, "%s + %s port %u", addr_str,
414             NAME_TCP_UDP, rule_info->destport);
415         ADD_TO_FILTER_MENU(RT_IPv4_PORT_DST);
416         sensitive = TRUE;
417     }
418
419     if (rule_type != RT_NONE) {
420         gtk_widget_show_all(rule_info->filter_om);
421         gtk_option_menu_set_history(GTK_OPTION_MENU(rule_info->filter_om), 0);
422         sensitive = TRUE;
423     }
424     gtk_widget_set_sensitive(rule_info->filter_om, sensitive);
425     gtk_widget_set_sensitive(rule_info->inbound_cb,
426         products[prod].does_inbound && sensitive);
427     gtk_widget_set_sensitive(rule_info->deny_cb, sensitive);
428
429     select_filter(w, GUINT_TO_POINTER(rule_type));
430 }
431
432 /* Set the current product. */
433 static void
434 select_filter(GtkWidget *w, gpointer data)
435 {
436     rule_type_t cur_type = GPOINTER_TO_UINT(data);
437     rule_info_t *rule_info;
438
439     rule_info = OBJECT_GET_DATA(w, WS_RULE_INFO_KEY);
440
441     if (cur_type >= NUM_RULE_TYPES || !rule_info)
442         return;
443
444     rule_info->rule_type = cur_type;
445
446     set_rule_text(rule_info);
447 }
448
449 /* Set inbound/outbound */
450 static void
451 toggle_inbound(GtkToggleButton *t, gpointer data)
452 {
453     rule_info_t *rule_info = (rule_info_t *) data;
454
455     rule_info->inbound = gtk_toggle_button_get_active(t);
456
457     set_rule_text(rule_info);
458 }
459
460 /* Set deny/allow. */
461 static void
462 toggle_deny(GtkToggleButton *t, gpointer data)
463 {
464     rule_info_t *rule_info = (rule_info_t *) data;
465
466     rule_info->deny = gtk_toggle_button_get_active(t);
467
468     set_rule_text(rule_info);
469 }
470
471 /* Set the rule text */
472 #define DL_ADDR (rt == RT_MAC_SRC ? &(rule_info->dl_src) : &(rule_info->dl_dst))
473 #define NET_ADDR (rt == RT_IPv4_SRC ? &(rule_info->net_src) : &(rule_info->net_dst))
474 #define NET_PORT (rt == RT_PORT_SRC ? rule_info->srcport : rule_info->destport)
475 static void
476 set_rule_text(rule_info_t *rule_info) {
477     GString *rtxt = g_string_new("");
478     gchar addr_str[MAX_RULE_LEN];
479     rule_type_t rt = rule_info->rule_type;
480     guint prod = rule_info->product;
481     address *addr = NULL;
482     guint32 port = 0;
483     syntax_func rt_func = NULL;
484
485 #if GTK_MAJOR_VERSION < 2
486     gint txtlen;
487 #else
488     GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(rule_info->text));
489 #endif
490
491     if (prod < NUM_PRODS) {
492         g_string_sprintf(rtxt, "%s %s\n", products[prod].comment_pfx, products[prod].name);
493         switch(rt) {
494             case RT_NONE:
495                 g_string_sprintfa(rtxt, "%s Not supported", products[prod].comment_pfx);
496                 rt_func = sf_dummy;
497                 break;
498             case RT_MAC_SRC:
499             case RT_MAC_DST:
500                 addr = DL_ADDR;
501                 address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
502                 rt_func = products[prod].mac_func;
503                 break;
504             case RT_IPv4_SRC:
505             case RT_IPv4_DST:
506                 addr = NET_ADDR;
507                 address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
508                 rt_func = products[prod].ipv4_func;
509                 break;
510             case RT_PORT_SRC:
511             case RT_PORT_DST:
512                 port = NET_PORT;
513                 rt_func = products[prod].port_func;
514                 break;
515             case RT_IPv4_PORT_SRC:
516             case RT_IPv4_PORT_DST:
517                 addr = NET_ADDR;
518                 address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
519                 port = NET_PORT;
520                 rt_func = products[prod].ipv4_port_func;
521                 break;
522             default:
523                 break;
524         }
525     }
526
527     if (rt_func) {
528         rt_func(rtxt, addr_str, port, rule_info->ptype, rule_info->inbound, rule_info->deny);
529     } else {
530         g_string_sprintfa(rtxt, "ERROR: Unable to create rule");
531     }
532
533 #if GTK_MAJOR_VERSION < 2
534     txtlen = gtk_text_get_length(GTK_TEXT(rule_info->text));
535     if (txtlen > 0) {
536         gtk_text_set_point(GTK_TEXT(rule_info->text), 0);
537         gtk_text_forward_delete(GTK_TEXT(rule_info->text), txtlen);
538     }
539     gtk_text_insert(GTK_TEXT(rule_info->text), user_font_get_regular(),
540         NULL, NULL, rtxt->str, rtxt->len);
541 #else
542     gtk_text_buffer_set_text(buf, rtxt->str, rtxt->len);
543 #endif
544
545     g_string_free(rtxt, TRUE);
546 }
547
548
549 /* Rule text functions */
550 /* Dummy */
551 static void sf_dummy(GString *rtxt _U_, gchar *addr _U_, guint32 port _U_, port_type ptype _U_, gboolean inbound _U_, gboolean deny _U_) {
552 }
553
554 /* MAC */
555 #define IPFW_DENY (deny ? "deny" : "allow")
556 #define IPFW_IN (inbound ? "in" : "out")
557 static void sf_ipfw_mac(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
558     g_string_sprintfa(rtxt, "add %s MAC %s any %s",
559         IPFW_DENY, addr, IPFW_IN);
560 }
561
562 #define NF_DROP (deny ? "DROP" : "ACCEPT")
563 #define NF_INPUT (inbound ? "INPUT" : "OUTPUT")
564 static void sf_netfilter_mac(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
565     g_string_sprintfa(rtxt, "iptables -A %s --mac-source %s -j %s",
566         NF_INPUT, addr, NF_DROP);
567 }
568
569 /* IPv4 */
570 #define IOS_DENY (deny ? "deny" : "permit")
571 static void sf_ios_std_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound _U_, gboolean deny) {
572     g_string_sprintfa(rtxt, "access-list NUMBER %s host %s", IOS_DENY, addr);
573 }
574
575 static void sf_ios_ext_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
576     if (inbound)
577         g_string_sprintfa(rtxt, "access-list NUMBER %s ip host %s any", IOS_DENY, addr);
578     else
579         g_string_sprintfa(rtxt, "access-list NUMBER %s ip any host %s", IOS_DENY, addr);
580 }
581
582 #define IPFILTER_DENY (deny ? "block" : "pass")
583 #define IPFILTER_IN (inbound ? "in" : "out")
584 static void sf_ipfilter_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
585     g_string_sprintfa(rtxt, "%s %s on le0 from %s to any",
586         IPFILTER_DENY, IPFILTER_IN, addr);
587 }
588
589 static void sf_ipfw_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
590     g_string_sprintfa(rtxt, "add %s ip from %s to any %s",
591         IPFW_DENY, addr, IPFW_IN);
592 }
593
594 static void sf_netfilter_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
595     g_string_sprintfa(rtxt, "iptables -A %s -i eth0 -d %s/32 -j %s",
596         NF_INPUT, addr, NF_DROP);
597 }
598
599 #define PF_DENY (deny ? "block" : "pass")
600 #define PF_IN (inbound ? "in" : "out")
601 static void sf_pf_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
602     g_string_sprintfa(rtxt, "%s %s quick on $ext_if from %s to any",
603         PF_DENY, PF_IN, addr);
604 }
605
606 /* Port */
607 #define RT_TCP_UDP (ptype == PT_TCP ? "tcp" : "udp")
608 static void sf_ios_ext_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound _U_, gboolean deny) {
609     g_string_sprintfa(rtxt, "access-list NUMBER %s %s any any eq %u",
610         IOS_DENY, RT_TCP_UDP, port);
611 }
612
613 static void sf_ipfilter_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype _U_, gboolean inbound, gboolean deny) {
614     g_string_sprintfa(rtxt, "%s %s on le0 proto %s from any to any port = %u",
615         IPFILTER_DENY, IPFILTER_IN, RT_TCP_UDP, port);
616 }
617
618 static void sf_ipfw_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
619     g_string_sprintfa(rtxt, "add %s %s from any to any %u %s",
620         IPFW_DENY, RT_TCP_UDP, port, IPFW_IN);
621 }
622
623 static void sf_netfilter_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
624     g_string_sprintfa(rtxt, "iptables -A %s -p %s --destination-port %u -j %s",
625             NF_INPUT, RT_TCP_UDP, port, NF_DROP);
626 }
627
628 static void sf_pf_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
629     g_string_sprintfa(rtxt, "%s %s quick on $ext_if proto %s from any to any port %u",
630         PF_DENY, PF_IN, RT_TCP_UDP, port);
631 }
632
633 #define NETSH_DENY (deny ? "DISABLE" : "ENABLE")
634 static void sf_netsh_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound _U_, gboolean deny) {
635     g_string_sprintfa(rtxt, "add portopening %s %u Wireshark %s",
636         RT_TCP_UDP, port, NETSH_DENY);
637 }
638
639 /* IPv4 + port */
640 static void sf_ios_ext_ipv4_port(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
641     if (inbound)
642         g_string_sprintfa(rtxt, "access-list NUMBER %s %s host %s any eq %u", IOS_DENY, RT_TCP_UDP, addr, port);
643     else
644         g_string_sprintfa(rtxt, "access-list NUMBER %s %s any host %s eq %u", IOS_DENY, RT_TCP_UDP, addr, port);
645 }
646
647 static void sf_ipfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
648     if (inbound)
649         g_string_sprintfa(rtxt, "%s %s on le0 proto %s from %s to any port = %u",
650             IPFILTER_DENY, IPFILTER_IN, RT_TCP_UDP, addr, port);
651     else
652         g_string_sprintfa(rtxt, "%s %s on le0 proto %s from any to %s port = %u",
653             IPFILTER_DENY, IPFILTER_IN, RT_TCP_UDP, addr, port);
654 }
655
656 static void sf_ipfw_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
657     g_string_sprintfa(rtxt, "add %s %s from %s to any %u %s",
658         IPFW_DENY, RT_TCP_UDP, addr, port, IPFW_IN);
659 }
660
661 static void sf_pf_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
662     g_string_sprintfa(rtxt, "%s %s quick on $ext_if proto %s from %s to any port %u",
663         PF_DENY, PF_IN, RT_TCP_UDP, addr, port);
664 }
665
666 static void sf_netfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
667     g_string_sprintfa(rtxt, "iptables -A %s -p %s -d %s/32 --destination-port %u -j %s",
668         NF_INPUT, RT_TCP_UDP, addr, port, NF_DROP);
669 }
670
671 static void sf_netsh_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound _U_, gboolean deny) {
672     g_string_sprintfa(rtxt, "add portopening %s %u Wireshark %s %s",
673         RT_TCP_UDP, port, NETSH_DENY, addr);
674 }
675
676 /* The destroy call back has the responsibility of
677  * unlinking the temporary file
678  * and freeing the filter_out_filter */
679 static void
680 firewall_destroy_cb(GtkWidget *w, gpointer data _U_)
681 {
682     rule_info_t *rule_info;
683
684     rule_info = OBJECT_GET_DATA(w, WS_RULE_INFO_KEY);
685     forget_rule_info(rule_info);
686
687     g_free(rule_info);
688 }
689
690 static void
691 firewall_copy_cmd_cb(GtkWidget *w _U_, gpointer data)
692 {
693     rule_info_t *rule_info = data;
694
695 #if GTK_MAJOR_VERSION >= 2
696     GtkTextIter start, end;
697     GtkTextBuffer *buf;
698 #endif
699
700 #if GTK_MAJOR_VERSION < 2
701     gtk_editable_select_region(GTK_EDITABLE(rule_info->text), 0, -1);
702     gtk_editable_copy_clipboard(GTK_EDITABLE(rule_info->text));
703 #else
704     buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(rule_info->text));
705     gtk_text_buffer_get_start_iter(buf, &start);
706     gtk_text_buffer_get_end_iter(buf, &end);
707     gtk_text_buffer_move_mark_by_name(buf, "insert", &start);
708     gtk_text_buffer_move_mark_by_name(buf, "selection_bound", &end);
709     gtk_text_buffer_copy_clipboard(buf, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
710 #endif
711 }
712
713 /*
714  * Keep a static pointer to the current "Save SSL Follow Stream As" window, if
715  * any, so that if somebody tries to do "Save"
716  * while there's already a "Save SSL Follow Stream" window up, we just pop
717  * up the existing one, rather than creating a new one.
718  */
719 static void
720 firewall_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
721 {
722     GtkWidget           *new_win;
723     rule_info_t *rule_info = data;
724
725     if (rule_info->firewall_save_as_w != NULL) {
726         /* There's already a dialog box; reactivate it. */
727         reactivate_window(rule_info->firewall_save_as_w);
728         return;
729     }
730
731     new_win = file_selection_new("Wireshark: Save Firewall ACL Rule",
732                                  FILE_SELECTION_SAVE);
733     rule_info->firewall_save_as_w = new_win;
734
735     /* Tuck away the rule_info object into the window */
736     OBJECT_SET_DATA(new_win, WS_RULE_INFO_KEY, rule_info);
737
738     SIGNAL_CONNECT(new_win, "destroy", firewall_save_as_destroy_cb, rule_info);
739
740 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
741     if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
742     {
743         firewall_save_as_ok_cb(new_win, new_win);
744     } else {
745         window_destroy(new_win);
746     }
747 #else
748     /* Connect the ok_button to file_save_as_ok_cb function and pass along a
749        pointer to the file selection box widget */
750     SIGNAL_CONNECT(GTK_FILE_SELECTION(new_win)->ok_button,
751         "clicked", firewall_save_as_ok_cb, new_win);
752
753     window_set_cancel_button(new_win,
754         GTK_FILE_SELECTION(new_win)->cancel_button, window_cancel_button_cb);
755
756     gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
757
758     SIGNAL_CONNECT(new_win, "delete_event", window_delete_event_cb, NULL);
759
760     gtk_widget_show_all(new_win);
761     window_present(new_win);
762 #endif
763 }
764
765
766 static void
767 firewall_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
768 {
769     gchar       *to_name, *rule;
770     rule_info_t *rule_info;
771     FILE        *fh;
772     gchar       *dirname;
773
774 #if GTK_MAJOR_VERSION >= 2
775     GtkTextIter start, end;
776     GtkTextBuffer *buf;
777 #endif
778
779 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
780     to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
781 #else
782     to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
783 #endif
784
785     /* Perhaps the user specified a directory instead of a file.
786        Check whether they did. */
787     if (test_for_directory(to_name) == EISDIR) {
788         /* It's a directory - set the file selection box to display that
789            directory, and leave the selection box displayed. */
790         set_last_open_dir(to_name);
791         g_free(to_name);
792         file_selection_set_current_folder(fs, get_last_open_dir());
793         return;
794     }
795
796     rule_info = OBJECT_GET_DATA(fs, WS_RULE_INFO_KEY);
797     fh = eth_fopen(to_name, "w");
798     if (fh == NULL) {
799         open_failure_alert_box(to_name, errno, TRUE);
800         g_free(to_name);
801         return;
802     }
803
804 #if GTK_MAJOR_VERSION < 2
805     rule = gtk_editable_get_chars(GTK_EDITABLE(rule_info->text), 0, -1);
806 #else
807     buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(rule_info->text));
808     gtk_text_buffer_get_start_iter(buf, &start);
809     gtk_text_buffer_get_end_iter(buf, &end);
810     rule = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
811 #endif
812
813     fputs(rule, fh);
814     fclose(fh);
815 #if GTK_MAJOR_VERSION < 2 /* XXX - Does this apply for gtk_text_buffer_get_text () as well? */
816     g_free(rule);
817 #endif
818
819     gtk_widget_hide(GTK_WIDGET(fs));
820     window_destroy(GTK_WIDGET(fs));
821
822     /* Save the directory name for future file dialogs. */
823     dirname = get_dirname(to_name);  /* Overwrites to_name */
824     set_last_open_dir(dirname);
825     g_free(to_name);
826 }
827
828 static void
829 firewall_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
830 {
831     rule_info_t *rule_info = data;
832
833     /* Note that we no longer have a dialog box. */
834     rule_info->firewall_save_as_w = NULL;
835 }