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