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