remove GTK1 code
[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     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
274                                         GTK_SHADOW_IN);
275     gtk_box_pack_start(GTK_BOX(vbox), txt_scrollw, TRUE, TRUE, 0);
276
277     /* create a text box */
278     text = gtk_text_view_new();
279     gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
280     gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
281     rule_info->text = text;
282
283     /* button hbox */
284     button_hbox = gtk_hbutton_box_new();
285     gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
286     gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
287     gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
288
289     /* Create Copy Button */
290     button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_COPY);
291     SIGNAL_CONNECT(button, "clicked", firewall_copy_cmd_cb, rule_info);
292     gtk_tooltips_set_tip (tooltips, button, "Copy rule to clipboard ", NULL);
293     gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
294
295     /* Create Save As Button */
296     button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
297     SIGNAL_CONNECT(button, "clicked", firewall_save_as_cmd_cb, rule_info);
298     gtk_tooltips_set_tip (tooltips, button, "Save the rule as currently displayed ", NULL);
299     gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
300
301     /* Create Close Button */
302     button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
303     gtk_tooltips_set_tip (tooltips, button,
304         "Close the dialog", NULL);
305     gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
306     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
307
308     window_set_cancel_button(rule_w, button, window_cancel_button_cb);
309
310     /* Tuck away the rule_info object into the window */
311     OBJECT_SET_DATA(rule_w, WS_RULE_INFO_KEY, rule_info);
312
313     SIGNAL_CONNECT(rule_w, "delete_event", window_delete_event_cb, NULL);
314     SIGNAL_CONNECT(rule_w, "destroy", firewall_destroy_cb, NULL);
315
316     /* Make sure this widget gets destroyed if we quit the main loop,
317        so that if we exit, we clean up any temporary files we have
318        for "Follow SSL Stream" windows. */
319     gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(rule_w));
320
321     gtk_widget_show_all(rule_w);
322     window_present(rule_w);
323     gtk_option_menu_set_history(GTK_OPTION_MENU(product_om), 0);
324     select_product(rule_w, GUINT_TO_POINTER(0));
325 }
326
327 /* Set the current product. */
328 #define ADD_TO_FILTER_MENU(rt) \
329         menu_item = gtk_menu_item_new_with_label(name); \
330         SIGNAL_CONNECT(menu_item, "activate", select_filter, \
331             GUINT_TO_POINTER(rt)); \
332         OBJECT_SET_DATA(menu_item, WS_RULE_INFO_KEY, rule_info); \
333         gtk_menu_append(GTK_MENU(menu), menu_item); \
334         if (! rt_set) { \
335             rt_set = TRUE; \
336             first_mi = menu_item; \
337             rule_type = rt; \
338         }
339
340 #define NAME_TCP_UDP (rule_info->ptype == PT_TCP ? "TCP" : "UDP")
341 static void
342 select_product(GtkWidget *w, gpointer data)
343 {
344     guint prod = GPOINTER_TO_UINT(data);
345     rule_info_t *rule_info;
346     gchar name[MAX_RULE_LEN], addr_str[MAX_RULE_LEN];
347     address *addr;
348     GtkWidget *menu, *menu_item, *first_mi = NULL;
349     rule_type_t rule_type = RT_NONE;
350     gboolean rt_set = FALSE, sensitive = FALSE;
351
352     rule_info = OBJECT_GET_DATA(w, WS_RULE_INFO_KEY);
353
354     if (prod >= NUM_PRODS || !rule_info)
355         return;
356
357     rule_info->product = prod;
358
359     /* Clear the menu */
360     gtk_option_menu_remove_menu(GTK_OPTION_MENU(rule_info->filter_om));
361     menu = gtk_menu_new();
362     gtk_option_menu_set_menu(GTK_OPTION_MENU(rule_info->filter_om), menu);
363
364     /* Fill in valid menu items.   */
365     if (products[prod].mac_func && rule_info->dl_src.type == AT_ETHER) {
366         addr = &(rule_info->dl_src);
367         address_to_str_buf(addr, name, MAX_RULE_LEN);
368         ADD_TO_FILTER_MENU(RT_MAC_SRC);
369
370         addr = &(rule_info->dl_dst);
371         address_to_str_buf(addr, name, MAX_RULE_LEN);
372         ADD_TO_FILTER_MENU(RT_MAC_DST);
373     }
374
375     if (products[prod].ipv4_func && rule_info->net_src.type == AT_IPv4) {
376         addr = &(rule_info->net_src);
377         address_to_str_buf(addr, name, MAX_RULE_LEN);
378         ADD_TO_FILTER_MENU(RT_IPv4_SRC);
379
380         addr = &(rule_info->net_dst);
381         address_to_str_buf(addr, name, MAX_RULE_LEN);
382         ADD_TO_FILTER_MENU(RT_IPv4_DST);
383     }
384
385     if (products[prod].port_func && (rule_info->ptype == PT_TCP || rule_info->ptype == PT_UDP)) {
386         g_snprintf(name, MAX_RULE_LEN, "%s port %u", NAME_TCP_UDP,
387             rule_info->srcport);
388         ADD_TO_FILTER_MENU(RT_PORT_SRC);
389         if (rule_info->srcport != rule_info->destport) {
390             g_snprintf(name, MAX_RULE_LEN, "%s port %u", NAME_TCP_UDP,
391                 rule_info->destport);
392             ADD_TO_FILTER_MENU(RT_PORT_DST);
393         }
394     }
395
396     if (products[prod].ipv4_port_func && rule_info->net_src.type == AT_IPv4 &&
397             (rule_info->ptype == PT_TCP || rule_info->ptype == PT_UDP)) {
398         addr = &(rule_info->net_src);
399         address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
400         g_snprintf(name, MAX_RULE_LEN, "%s + %s port %u", addr_str,
401             NAME_TCP_UDP, rule_info->srcport);
402         ADD_TO_FILTER_MENU(RT_IPv4_PORT_SRC);
403
404         addr = &(rule_info->net_dst);
405         address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
406         g_snprintf(name, MAX_RULE_LEN, "%s + %s port %u", addr_str,
407             NAME_TCP_UDP, rule_info->destport);
408         ADD_TO_FILTER_MENU(RT_IPv4_PORT_DST);
409         sensitive = TRUE;
410     }
411
412     if (rule_type != RT_NONE) {
413         gtk_widget_show_all(rule_info->filter_om);
414         gtk_option_menu_set_history(GTK_OPTION_MENU(rule_info->filter_om), 0);
415         sensitive = TRUE;
416     }
417     gtk_widget_set_sensitive(rule_info->filter_om, sensitive);
418     gtk_widget_set_sensitive(rule_info->inbound_cb,
419         products[prod].does_inbound && sensitive);
420     gtk_widget_set_sensitive(rule_info->deny_cb, sensitive);
421
422     select_filter(w, GUINT_TO_POINTER(rule_type));
423 }
424
425 /* Set the current product. */
426 static void
427 select_filter(GtkWidget *w, gpointer data)
428 {
429     rule_type_t cur_type = GPOINTER_TO_UINT(data);
430     rule_info_t *rule_info;
431
432     rule_info = OBJECT_GET_DATA(w, WS_RULE_INFO_KEY);
433
434     if (cur_type >= NUM_RULE_TYPES || !rule_info)
435         return;
436
437     rule_info->rule_type = cur_type;
438
439     set_rule_text(rule_info);
440 }
441
442 /* Set inbound/outbound */
443 static void
444 toggle_inbound(GtkToggleButton *t, gpointer data)
445 {
446     rule_info_t *rule_info = (rule_info_t *) data;
447
448     rule_info->inbound = gtk_toggle_button_get_active(t);
449
450     set_rule_text(rule_info);
451 }
452
453 /* Set deny/allow. */
454 static void
455 toggle_deny(GtkToggleButton *t, gpointer data)
456 {
457     rule_info_t *rule_info = (rule_info_t *) data;
458
459     rule_info->deny = gtk_toggle_button_get_active(t);
460
461     set_rule_text(rule_info);
462 }
463
464 /* Set the rule text */
465 #define DL_ADDR (rt == RT_MAC_SRC ? &(rule_info->dl_src) : &(rule_info->dl_dst))
466 #define NET_ADDR (rt == RT_IPv4_SRC ? &(rule_info->net_src) : &(rule_info->net_dst))
467 #define NET_PORT (rt == RT_PORT_SRC ? rule_info->srcport : rule_info->destport)
468 static void
469 set_rule_text(rule_info_t *rule_info) {
470     GString *rtxt = g_string_new("");
471     gchar addr_str[MAX_RULE_LEN];
472     rule_type_t rt = rule_info->rule_type;
473     guint prod = rule_info->product;
474     address *addr = NULL;
475     guint32 port = 0;
476     syntax_func rt_func = NULL;
477
478     GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(rule_info->text));
479
480     if (prod < NUM_PRODS) {
481         g_string_sprintf(rtxt, "%s %s\n", products[prod].comment_pfx, products[prod].name);
482         switch(rt) {
483             case RT_NONE:
484                 g_string_sprintfa(rtxt, "%s Not supported", products[prod].comment_pfx);
485                 rt_func = sf_dummy;
486                 break;
487             case RT_MAC_SRC:
488             case RT_MAC_DST:
489                 addr = DL_ADDR;
490                 address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
491                 rt_func = products[prod].mac_func;
492                 break;
493             case RT_IPv4_SRC:
494             case RT_IPv4_DST:
495                 addr = NET_ADDR;
496                 address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
497                 rt_func = products[prod].ipv4_func;
498                 break;
499             case RT_PORT_SRC:
500             case RT_PORT_DST:
501                 port = NET_PORT;
502                 rt_func = products[prod].port_func;
503                 break;
504             case RT_IPv4_PORT_SRC:
505             case RT_IPv4_PORT_DST:
506                 addr = NET_ADDR;
507                 address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
508                 port = NET_PORT;
509                 rt_func = products[prod].ipv4_port_func;
510                 break;
511             default:
512                 break;
513         }
514     }
515
516     if (rt_func) {
517         rt_func(rtxt, addr_str, port, rule_info->ptype, rule_info->inbound, rule_info->deny);
518     } else {
519         g_string_sprintfa(rtxt, "ERROR: Unable to create rule");
520     }
521
522     gtk_text_buffer_set_text(buf, rtxt->str, rtxt->len);
523
524     g_string_free(rtxt, TRUE);
525 }
526
527
528 /* Rule text functions */
529 /* Dummy */
530 static void sf_dummy(GString *rtxt _U_, gchar *addr _U_, guint32 port _U_, port_type ptype _U_, gboolean inbound _U_, gboolean deny _U_) {
531 }
532
533 /* MAC */
534 #define IPFW_DENY (deny ? "deny" : "allow")
535 #define IPFW_IN (inbound ? "in" : "out")
536 static void sf_ipfw_mac(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
537     g_string_sprintfa(rtxt, "add %s MAC %s any %s",
538         IPFW_DENY, addr, IPFW_IN);
539 }
540
541 #define NF_DROP (deny ? "DROP" : "ACCEPT")
542 #define NF_INPUT (inbound ? "INPUT" : "OUTPUT")
543 static void sf_netfilter_mac(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
544     g_string_sprintfa(rtxt, "iptables -A %s --mac-source %s -j %s",
545         NF_INPUT, addr, NF_DROP);
546 }
547
548 /* IPv4 */
549 #define IOS_DENY (deny ? "deny" : "permit")
550 static void sf_ios_std_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound _U_, gboolean deny) {
551     g_string_sprintfa(rtxt, "access-list NUMBER %s host %s", IOS_DENY, addr);
552 }
553
554 static void sf_ios_ext_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
555     if (inbound)
556         g_string_sprintfa(rtxt, "access-list NUMBER %s ip host %s any", IOS_DENY, addr);
557     else
558         g_string_sprintfa(rtxt, "access-list NUMBER %s ip any host %s", IOS_DENY, addr);
559 }
560
561 #define IPFILTER_DENY (deny ? "block" : "pass")
562 #define IPFILTER_IN (inbound ? "in" : "out")
563 static void sf_ipfilter_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
564     g_string_sprintfa(rtxt, "%s %s on le0 from %s to any",
565         IPFILTER_DENY, IPFILTER_IN, addr);
566 }
567
568 static void sf_ipfw_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
569     g_string_sprintfa(rtxt, "add %s ip from %s to any %s",
570         IPFW_DENY, addr, IPFW_IN);
571 }
572
573 static void sf_netfilter_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
574     g_string_sprintfa(rtxt, "iptables -A %s -i eth0 -d %s/32 -j %s",
575         NF_INPUT, addr, NF_DROP);
576 }
577
578 #define PF_DENY (deny ? "block" : "pass")
579 #define PF_IN (inbound ? "in" : "out")
580 static void sf_pf_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
581     g_string_sprintfa(rtxt, "%s %s quick on $ext_if from %s to any",
582         PF_DENY, PF_IN, addr);
583 }
584
585 /* Port */
586 #define RT_TCP_UDP (ptype == PT_TCP ? "tcp" : "udp")
587 static void sf_ios_ext_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound _U_, gboolean deny) {
588     g_string_sprintfa(rtxt, "access-list NUMBER %s %s any any eq %u",
589         IOS_DENY, RT_TCP_UDP, port);
590 }
591
592 static void sf_ipfilter_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype _U_, gboolean inbound, gboolean deny) {
593     g_string_sprintfa(rtxt, "%s %s on le0 proto %s from any to any port = %u",
594         IPFILTER_DENY, IPFILTER_IN, RT_TCP_UDP, port);
595 }
596
597 static void sf_ipfw_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
598     g_string_sprintfa(rtxt, "add %s %s from any to any %u %s",
599         IPFW_DENY, RT_TCP_UDP, port, IPFW_IN);
600 }
601
602 static void sf_netfilter_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
603     g_string_sprintfa(rtxt, "iptables -A %s -p %s --destination-port %u -j %s",
604             NF_INPUT, RT_TCP_UDP, port, NF_DROP);
605 }
606
607 static void sf_pf_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
608     g_string_sprintfa(rtxt, "%s %s quick on $ext_if proto %s from any to any port %u",
609         PF_DENY, PF_IN, RT_TCP_UDP, port);
610 }
611
612 #define NETSH_DENY (deny ? "DISABLE" : "ENABLE")
613 static void sf_netsh_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound _U_, gboolean deny) {
614     g_string_sprintfa(rtxt, "add portopening %s %u Wireshark %s",
615         RT_TCP_UDP, port, NETSH_DENY);
616 }
617
618 /* IPv4 + port */
619 static void sf_ios_ext_ipv4_port(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
620     if (inbound)
621         g_string_sprintfa(rtxt, "access-list NUMBER %s %s host %s any eq %u", IOS_DENY, RT_TCP_UDP, addr, port);
622     else
623         g_string_sprintfa(rtxt, "access-list NUMBER %s %s any host %s eq %u", IOS_DENY, RT_TCP_UDP, addr, port);
624 }
625
626 static void sf_ipfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
627     if (inbound)
628         g_string_sprintfa(rtxt, "%s %s on le0 proto %s from %s to any port = %u",
629             IPFILTER_DENY, IPFILTER_IN, RT_TCP_UDP, addr, port);
630     else
631         g_string_sprintfa(rtxt, "%s %s on le0 proto %s from any to %s port = %u",
632             IPFILTER_DENY, IPFILTER_IN, RT_TCP_UDP, addr, port);
633 }
634
635 static void sf_ipfw_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
636     g_string_sprintfa(rtxt, "add %s %s from %s to any %u %s",
637         IPFW_DENY, RT_TCP_UDP, addr, port, IPFW_IN);
638 }
639
640 static void sf_pf_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
641     g_string_sprintfa(rtxt, "%s %s quick on $ext_if proto %s from %s to any port %u",
642         PF_DENY, PF_IN, RT_TCP_UDP, addr, port);
643 }
644
645 static void sf_netfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
646     g_string_sprintfa(rtxt, "iptables -A %s -p %s -d %s/32 --destination-port %u -j %s",
647         NF_INPUT, RT_TCP_UDP, addr, port, NF_DROP);
648 }
649
650 static void sf_netsh_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound _U_, gboolean deny) {
651     g_string_sprintfa(rtxt, "add portopening %s %u Wireshark %s %s",
652         RT_TCP_UDP, port, NETSH_DENY, addr);
653 }
654
655 /* The destroy call back has the responsibility of
656  * unlinking the temporary file
657  * and freeing the filter_out_filter */
658 static void
659 firewall_destroy_cb(GtkWidget *w, gpointer data _U_)
660 {
661     rule_info_t *rule_info;
662
663     rule_info = OBJECT_GET_DATA(w, WS_RULE_INFO_KEY);
664     forget_rule_info(rule_info);
665
666     g_free(rule_info);
667 }
668
669 static void
670 firewall_copy_cmd_cb(GtkWidget *w _U_, gpointer data)
671 {
672     rule_info_t *rule_info = data;
673
674     GtkTextIter start, end;
675     GtkTextBuffer *buf;
676
677     buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(rule_info->text));
678     gtk_text_buffer_get_start_iter(buf, &start);
679     gtk_text_buffer_get_end_iter(buf, &end);
680     gtk_text_buffer_move_mark_by_name(buf, "insert", &start);
681     gtk_text_buffer_move_mark_by_name(buf, "selection_bound", &end);
682     gtk_text_buffer_copy_clipboard(buf, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
683 }
684
685 /*
686  * Keep a static pointer to the current "Save SSL Follow Stream As" window, if
687  * any, so that if somebody tries to do "Save"
688  * while there's already a "Save SSL Follow Stream" window up, we just pop
689  * up the existing one, rather than creating a new one.
690  */
691 static void
692 firewall_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
693 {
694     GtkWidget           *new_win;
695     rule_info_t *rule_info = data;
696
697     if (rule_info->firewall_save_as_w != NULL) {
698         /* There's already a dialog box; reactivate it. */
699         reactivate_window(rule_info->firewall_save_as_w);
700         return;
701     }
702
703     new_win = file_selection_new("Wireshark: Save Firewall ACL Rule",
704                                  FILE_SELECTION_SAVE);
705     rule_info->firewall_save_as_w = new_win;
706
707     /* Tuck away the rule_info object into the window */
708     OBJECT_SET_DATA(new_win, WS_RULE_INFO_KEY, rule_info);
709
710     SIGNAL_CONNECT(new_win, "destroy", firewall_save_as_destroy_cb, rule_info);
711
712 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
713     if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
714     {
715         firewall_save_as_ok_cb(new_win, new_win);
716     } else {
717         window_destroy(new_win);
718     }
719 #else
720     /* Connect the ok_button to file_save_as_ok_cb function and pass along a
721        pointer to the file selection box widget */
722     SIGNAL_CONNECT(GTK_FILE_SELECTION(new_win)->ok_button,
723         "clicked", firewall_save_as_ok_cb, new_win);
724
725     window_set_cancel_button(new_win,
726         GTK_FILE_SELECTION(new_win)->cancel_button, window_cancel_button_cb);
727
728     gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
729
730     SIGNAL_CONNECT(new_win, "delete_event", window_delete_event_cb, NULL);
731
732     gtk_widget_show_all(new_win);
733     window_present(new_win);
734 #endif
735 }
736
737
738 static void
739 firewall_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
740 {
741     gchar       *to_name, *rule;
742     rule_info_t *rule_info;
743     FILE        *fh;
744     gchar       *dirname;
745
746     GtkTextIter start, end;
747     GtkTextBuffer *buf;
748
749 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
750     to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
751 #else
752     to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
753 #endif
754
755     /* Perhaps the user specified a directory instead of a file.
756        Check whether they did. */
757     if (test_for_directory(to_name) == EISDIR) {
758         /* It's a directory - set the file selection box to display that
759            directory, and leave the selection box displayed. */
760         set_last_open_dir(to_name);
761         g_free(to_name);
762         file_selection_set_current_folder(fs, get_last_open_dir());
763         return;
764     }
765
766     rule_info = OBJECT_GET_DATA(fs, WS_RULE_INFO_KEY);
767     fh = eth_fopen(to_name, "w");
768     if (fh == NULL) {
769         open_failure_alert_box(to_name, errno, TRUE);
770         g_free(to_name);
771         return;
772     }
773
774     buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(rule_info->text));
775     gtk_text_buffer_get_start_iter(buf, &start);
776     gtk_text_buffer_get_end_iter(buf, &end);
777     rule = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
778
779     fputs(rule, fh);
780     fclose(fh);
781
782     gtk_widget_hide(GTK_WIDGET(fs));
783     window_destroy(GTK_WIDGET(fs));
784
785     /* Save the directory name for future file dialogs. */
786     dirname = get_dirname(to_name);  /* Overwrites to_name */
787     set_last_open_dir(dirname);
788     g_free(to_name);
789 }
790
791 static void
792 firewall_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
793 {
794     rule_info_t *rule_info = data;
795
796     /* Note that we no longer have a dialog box. */
797     rule_info->firewall_save_as_w = NULL;
798 }