summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bin/net_applet489
1 files changed, 489 insertions, 0 deletions
diff --git a/bin/net_applet b/bin/net_applet
new file mode 100644
index 0000000..a0c9efe
--- /dev/null
+++ b/bin/net_applet
@@ -0,0 +1,489 @@
+#!/usr/bin/perl
+
+use strict;
+use lib qw(/usr/lib/libDrakX);
+use c;
+use common;
+use standalone;
+use network::network;
+use network::tools;
+use run_program;
+use mygtk2 qw(gtknew);
+use dbus_object;
+use network::ifw;
+use network::monitor;
+use detect_devices;
+
+use Gtk2::TrayIcon;
+use Gtk2::NotificationBubble;
+
+use ugtk2 qw(:create :helpers :wrappers :dialogs);
+
+my $onstartupfile = "$ENV{HOME}/.net_applet";
+shouldStart() or die "$onstartupfile should be set to TRUE or use net_applet --force";
+#- Allow multiple instances, but only one per user:
+is_running('net_applet') and die "net_applet already running\n";
+
+my ($eventbox, $img, $bubble);
+my ($current_state, $current_interface, $menu, $wireless_device, $wireless_menu, $timeout, $update_timeout);
+add_icon_path("/usr/share/libDrakX/pixmaps/");
+
+my $net = {};
+my $watched_interface;
+
+my %pixbufs =
+ (
+ firewall => gtkcreate_pixbuf('/usr/lib/libDrakX/icons/drakfirewall.png'),
+ firewall_icon => gtkcreate_pixbuf('/usr/lib/libDrakX/icons/drakfirewall.png')->scale_simple(24, 24, 'hyper'),
+ state => { map { $_ => gtkcreate_pixbuf($_) } qw(connected disconnected) },
+ link_level => { map {
+ $_ => gtkcreate_pixbuf('wifi-' . sprintf('%03d', $_) . '.png')->scale_simple(24, 24, 'hyper');
+ } qw(20 40 60 80 100) },
+ keyring => gtkcreate_pixbuf("/usr/share/pixmaps/keyring-small.png")->scale_simple(24, 24, 'hyper'), #- provided by usermode, required by drakxtools
+ );
+my %wireless_networks;
+my %tooltips =
+ (
+ connected => N_("Network is up on interface %s"),
+ disconnected =>
+ #-PO: keep the "Configure Network" substring synced with the "Configure Network" message below
+ N_("Network is down on interface %s. Click on \"Configure Network\""),
+ notconfigured => N_("You do not have any configured Internet connection.
+Run the \"%s\" assistant from the Mandriva Linux Control Center", N("Set up a new network interface (LAN, ISDN, ADSL, ...)")),
+ );
+
+my %actions = (
+ 'upNetwork' => { name => sub { N("Connect %s", $_[0]) }, launch => sub { network::tools::start_interface($_[0], 1) } },
+ 'downNetwork' => { name => sub { N("Disconnect %s", $_[0]) }, launch => sub { network::tools::stop_interface($_[0], 1) } },
+ 'monitorNetwork' => { name => N("Monitor Network"), launch => \&run_net_monitor },
+ 'monitorIFW' => { name => N("Interactive Firewall"), launch => \&run_drakids },
+ 'wireless' => { name => N("Manage wireless networks"), launch => sub { run_drakroam() } },
+ 'confNetwork' => { name => N("Configure Network"), launch => sub { system("/usr/sbin/drakconnect --skip-wizard &") } },
+ 'chooseInterface' => {
+ name => N("Watched interface"),
+ choices => sub { N("Auto-detect"), sort keys %{$net->{ifcfg}} },
+ choice_selected => sub { $watched_interface ? $_[0] eq $watched_interface : $_[0] eq N("Auto-detect") },
+ launch => sub {
+ $watched_interface = $_[0] eq N("Auto-detect") ? undef : $_[0];
+ checkNetworkForce();
+ }
+ },
+ 'setInterface' => {
+ name => N("Active interfaces"),
+ use_checkbox => 1,
+ choices => sub { sort keys %{$net->{ifcfg}} },
+ choice_selected => sub {
+ my ($is_up, $_gw) = network::tools::get_interface_status($_[0]);
+ $is_up;
+ },
+ launch => sub {
+ my ($is_up, $_gw) = network::tools::get_interface_status($_[0]);
+ if ($is_up) {
+ network::tools::stop_interface($_[0], 1);
+ } else {
+ network::tools::start_interface($_[0], 1);
+ }
+ checkNetworkForce();
+ }
+ },
+ 'chooseProfile' => {
+ name => N("Profiles"),
+ choices => sub { network::network::netprofile_list() },
+ choice_selected => sub { $_[0] eq $net->{PROFILE} },
+ launch => sub {
+ require run_program;
+ $net->{PROFILE} = $_[0];
+ run_program::raw({ detach => 1 }, network::tools::wrap_command_for_root('/sbin/set-netprofile', $net->{PROFILE}));
+ }
+ },
+ 'help' => { name => N("Get Online Help"), launch => sub { system("drakhelp --id internet-connection &") } },
+ 'quit' => { name => N("Quit"), launch => \&mainQuit },
+ );
+
+gtkadd(my $icon = Gtk2::TrayIcon->new("Net_Applet"),
+ gtkadd($eventbox = Gtk2::EventBox->new,
+ gtkpack($img = Gtk2::Image->new)
+ )
+ );
+$icon->show_all;
+
+my ($dbus, $monitor, $ifw, $interactive_cb, @attacks_queue, $ifw_alert);
+eval { $dbus = dbus_object::system_bus() };
+eval { $monitor = network::monitor->new($dbus) } if $dbus;
+eval {
+ $ifw = network::ifw->new($dbus, sub {
+ my ($_con, $msg) = @_;
+ my $member = $msg->get_member;
+ if ($member eq 'Attack') {
+ handle_attack($msg->get_args_list);
+ } elsif ($member eq 'Init') {
+ $ifw->attach_object;
+ checkNetworkForce();
+ } elsif ($member eq 'AlertAck') {
+ $ifw_alert = 0;
+ }
+ });
+} if $dbus;
+
+$bubble = Gtk2::NotificationBubble->new;
+$bubble->attach($icon);
+$bubble->signal_connect(timeout => sub {
+ set_verdict($attacks_queue[0], \&apply_verdict_ignore);
+});
+$bubble->signal_connect(clicked => sub {
+ $bubble->hide;
+ eval { $ifw->send_alert_ack };
+ $ifw_alert = 0;
+ update_tray_icon();
+ ask_attack_verdict($attacks_queue[0]);
+});
+
+$eventbox->signal_connect(button_press_event => sub {
+ $_[1]->button == 1 and ($ifw_alert ? run_drakids() : run_net_monitor());
+ $_[1]->button == 3 && $menu and $menu->popup(undef, undef, undef, undef, $_[1]->button, $_[1]->time);
+});
+
+checkNetworkForce();
+cronNetwork();
+get_unprocessed_attacks();
+
+$SIG{HUP} = sub {
+ print "received SIGHUP, reloading network configuration\n";
+ checkNetworkForce();
+};
+
+Gtk2->main;
+
+ugtk2::exit(0);
+
+sub is_running {
+ my ($name) = @_;
+ any {
+ my ($ppid, $pid, $n) = /^\s*(\d+)\s+(\d+)\s+(.*)/;
+ $pid != $$ && $n eq $name;
+ } `ps -o '%P %p %c' -u $ENV{USER}`;
+}
+sub shouldStart() {
+ my ($opt) = @ARGV;
+ if ($opt eq '--force' || $opt eq '-f') {
+ return 1;
+ }
+ return getAutoStart();
+}
+sub run_net_monitor() {
+ run_program::raw({ detach => 1 }, '/usr/sbin/net_monitor', '--defaultintf', $current_interface) unless is_running('net_monitor');
+}
+sub run_drakroam {
+ my ($o_ap) = @_;
+ run_program::raw({ detach => 1 }, '/usr/sbin/drakroam', if_($o_ap, "--ap=$o_ap")) unless is_running('drakroam');
+}
+sub run_drakids() {
+ $ifw_alert = 0;
+ if (is_running('drakids')) {
+ eval { $ifw->send_manage_request };
+ } else {
+ run_program::raw({ detach => 1 }, '/usr/sbin/drakids');
+ }
+}
+sub generate_wireless_menuitem {
+ my ($wnet, $ap) = @_;
+ $wnet->{menuitem} = Gtk2::CheckMenuItem->new;
+ $wnet->{menuitem}->set_draw_as_radio(1);
+ $wnet->{menuitem}->add(gtkpack_(gtkshow(Gtk2::HBox->new),
+ 1, gtkset_alignment($wnet->{ssid_label} = Gtk2::Label->new, 0, 0),
+ 0, $wnet->{keyring_image} = Gtk2::Image->new_from_pixbuf($pixbufs{keyring}),
+ 0, $wnet->{level_image} = Gtk2::Image->new));
+ $wnet->{activate} = $wnet->{menuitem}->signal_connect('activate' => sub {
+ if (exists $wnet->{id}) {
+ eval { $monitor->select_network($wnet->{id}) };
+ $@ and err_dialog(N("Interactive Firewall"), N("Unable to contact daemon"));
+ } else {
+ run_drakroam($ap);
+ }
+ checkNetworkForce();
+ });
+ undef $current_state; #- force menu redraw
+}
+sub update_wireless_item {
+ my ($wnet, $ap_address) = @_;
+ $wnet->{ssid_label}->set_text($wnet->{essid} || "[$ap_address]");
+ $wnet->{keyring_image}->visible(to_bool($wnet->{flags}));
+ $wnet->{level_image}->set_from_pixbuf($pixbufs{link_level}{$wnet->{approx_level}});
+
+ $wnet->{menuitem}->signal_handler_block($wnet->{activate});
+ $wnet->{menuitem}->set_active($wnet->{current});
+ $wnet->{menuitem}->signal_handler_unblock($wnet->{activate});
+}
+sub checkWireless() {
+ $wireless_device or return;
+ my ($networks) = network::monitor::list_wireless($monitor, $wireless_device);
+ foreach (keys %$networks) {
+ my $wnet = $wireless_networks{$_} ||= {};
+ put_in_hash($wnet, $networks->{$_});
+ exists $wnet->{menuitem} or generate_wireless_menuitem($wnet, $_);
+ update_wireless_item($wnet, $_);
+ }
+ $wireless_networks{$_}{menuitem}->visible(exists $networks->{$_}) foreach keys %wireless_networks;
+}
+sub checkNetwork() {
+ my ($gw_intf, $_is_up, $gw_address) = $watched_interface ?
+ ($watched_interface, network::tools::get_interface_status($watched_interface)) :
+ network::tools::get_internet_connection($net);
+ go2State($gw_address ? 'connected' : $gw_intf ? 'disconnected' : 'notconfigured', $gw_intf);
+}
+sub checkNetworkForce() {
+ $net = {};
+ network::network::read_net_conf($net);
+ undef $current_state;
+ $wireless_device = detect_devices::get_wireless_interface();
+ checkWireless();
+ checkNetwork();
+}
+sub cronNetwork() {
+ my $i;
+ $timeout = Glib::Timeout->add(2000, sub {
+ checkWireless() if !($i++%30);
+ checkNetwork();
+ 1;
+ });
+}
+sub go2State {
+ my ($state_type, $interface) = @_;
+ if ($current_state ne $state_type || $current_interface ne $interface) {
+ $current_state = $state_type;
+ $current_interface = $interface;
+ $wireless_device = detect_devices::get_wireless_interface();
+ if ($menu) {
+ if (my $m = $wireless_menu && $wireless_menu->get_submenu) {
+ $_->{menuitem}->get_parent and $m->remove($_->{menuitem}) foreach values %wireless_networks;
+ }
+ $menu->destroy;
+ }
+ $menu = generate_menu($interface);
+ }
+}
+sub update_tray_icon() {
+ if (!$ifw_alert || $img->get_storage_type ne 'pixbuf') {
+ my $pixbuf;
+ if ($current_state eq 'connected') {
+ if (detect_devices::is_wireless_interface($current_interface)) {
+ my $wnet = find { $_->{current} } values %wireless_networks;
+ $pixbuf = $pixbufs{link_level}{$wnet->{approx_level}} if $wnet;
+ }
+ $pixbuf ||= $pixbufs{state}{connected};
+ } else {
+ $pixbuf = $pixbufs{state}{disconnected};
+ }
+ $img->set_from_pixbuf($pixbuf);
+ } else {
+ $img->set_from_stock('gtk-dialog-warning', 'small-toolbar');
+ }
+}
+sub generate_menu {
+ my ($interface) = @_;
+
+ update_tray_icon();
+ gtkset_tip(Gtk2::Tooltips->new, $eventbox, formatAlaTeX(sprintf(translate($tooltips{$current_state}), $interface)));
+
+ my $menu = Gtk2::Menu->new;
+ my $create_item = sub {
+ my ($action) = @_;
+ my $name = ref($actions{$action}{name}) eq 'CODE' ? $actions{$action}{name}->($interface) : $actions{$action}{name};
+ my $launch = $actions{$action}{launch};
+ my @choices = exists $actions{$action}{choices} ? $actions{$action}{choices}->() : ();
+ my $w;
+ if (@choices == 0) {
+ $w = gtksignal_connect(gtkshow(Gtk2::MenuItem->new_with_label($name)), activate => sub { $launch->($interface) });
+ } elsif (@choices > 1) {
+ my $selected = $actions{$action}{choice_selected};
+ my $format = $actions{$action}{format_choice};
+ $w = gtkshow(create_menu($name, map {
+ my $choice = $_;
+ my $w = gtkshow(gtkset_active(Gtk2::CheckMenuItem->new_with_label($format ? $format->($choice) : $choice), $selected->($choice)));
+ gtksignal_connect($w, activate => sub { $launch->($choice) });
+ $w->set_draw_as_radio(!$actions{$action}{use_checkbox});
+ $w;
+ } $actions{$action}{choices}->()));
+ }
+ #- don't add submenu if only one choice exists
+ $w;
+ };
+
+ my (@settings);
+ my $interactive;
+ eval { $interactive = $ifw->get_interactive };
+
+ if ($current_state eq 'connected') {
+ $menu->append($create_item->($_)) foreach qw(downNetwork monitorNetwork);
+ } elsif ($current_state eq 'disconnected') {
+ $menu->append($create_item->('upNetwork'));
+ }
+ $menu->append($create_item->('monitorIFW')) if $current_state ne 'notconfigured' && defined $interactive;
+
+ $menu->append($create_item->('confNetwork'));
+
+ if ($current_state ne 'notconfigured') {
+ $menu->append($create_item->('wireless')) if $wireless_device;
+ push @settings, $create_item->('chooseInterface');
+ }
+
+ push @settings, $create_item->('chooseProfile');
+ if (defined $interactive) {
+ $interactive_cb = gtkshow(gtksignal_connect(gtkset_active(Gtk2::CheckMenuItem->new_with_label(N("Interactive Firewall automatic mode")),
+ !$interactive),
+ toggled => sub { eval { $ifw->set_interactive(to_bool(!$_[0]->get_active)) } }));
+ push @settings, $interactive_cb;
+ }
+ push @settings, gtkshow(gtksignal_connect(gtkset_active(Gtk2::CheckMenuItem->new_with_label(N("Always launch on startup")), getAutoStart()),
+ toggled => sub { setAutoStart(uc(bool2text($_[0]->get_active))) }));
+
+ $menu->append(gtkshow(Gtk2::SeparatorMenuItem->new));
+ $wireless_device and $menu->append(gtkshow($wireless_menu = create_menu(N("Wireless networks"),
+ map { $_->{menuitem} } values %wireless_networks)));
+ if (my $set = $current_state ne 'notconfigured' && $create_item->('setInterface')) { $menu->append($set) }
+ $menu->append(gtkshow(create_menu(N("Settings"), grep { $_ } @settings)));
+ $menu->append(gtkshow(Gtk2::SeparatorMenuItem->new));
+ $menu->append($create_item->('help'));
+ $menu->append($create_item->('quit'));
+ $menu;
+}
+sub mainQuit() {
+ Glib::Source->remove($timeout) if $timeout;
+ Glib::Source->remove($update_timeout) if $update_timeout;
+ Gtk2->main_quit;
+}
+sub getAutoStart() {
+ my %p = getVarsFromSh($onstartupfile);
+ return to_bool($p{AUTOSTART} ne 'FALSE');
+}
+sub setAutoStart {
+ my $state = shift;
+ output_p $onstartupfile,
+ qq(AUTOSTART=$state
+);
+}
+
+sub get_unprocessed_attacks() {
+ my @packets = eval { $ifw->get_reports };
+ while (my @attack = splice(@packets, 0, 10)) {
+ handle_attack(@attack);
+ }
+}
+
+sub handle_attack {
+ my $attack = network::ifw::attack_to_hash(\@_);
+ push @attacks_queue, $attack;
+ @attacks_queue == 1 and notify_attack($attacks_queue[0]);
+}
+
+sub set_verdict {
+ my ($attack, $apply_verdict) = @_;
+ eval { $apply_verdict->($attack) };
+ $@ and err_dialog(N("Interactive Firewall"), N("Unable to contact daemon"));
+
+ shift @attacks_queue;
+ #- wait for some time so that the new bubble is noticeable
+ @attacks_queue and Glib::Timeout->add(500, sub { notify_attack($attacks_queue[0]); 0 });
+}
+
+sub apply_verdict_blacklist {
+ my ($attack) = @_;
+ $ifw->set_blacklist_verdict($attack->{seq}, 1);
+}
+
+sub apply_verdict_ignore {
+ my ($attack) = @_;
+ $ifw->set_blacklist_verdict($attack->{seq}, 0);
+}
+
+sub apply_verdict_whitelist {
+ my ($attack) = @_;
+ $ifw->whitelist($attack->{addr});
+ apply_verdict_ignore($attack);
+}
+
+sub notify_attack {
+ my ($attack) = @_;
+ unless ($attack->{msg}) {
+ print "unhandled attack type, skipping\n";
+ return;
+ }
+ unless ($ifw_alert) {
+ $ifw_alert = 1;
+ update_tray_icon();
+ Glib::Timeout->add(1000, sub {
+ update_tray_icon();
+ $ifw_alert;
+ });
+ }
+ $bubble->set(N("Interactive Firewall"), Gtk2::Image->new_from_pixbuf($pixbufs{firewall}), $attack->{msg});
+ $bubble->show(5000);
+}
+
+sub ask_attack_verdict {
+ my ($attack) = @_;
+
+ my $w = ugtk2->new(N("Interactive Firewall: intrusion detected"),
+ icon => "/usr/lib/libDrakX/icons/drakfirewall.png");
+ my ($blacklist, $whitelist, $ignore, $auto);
+
+ my $update_automatic_mode = sub { $auto->get_active and $interactive_cb->set_active(1) };
+
+ gtkadd($w->{window},
+ gtknew('VBox', spacing => 5, children_loose => [
+ gtknew('HBox', children => [
+ 0, Gtk2::Image->new_from_stock('gtk-dialog-warning', 'dialog'),
+ 0, gtknew('Label', text => " "),
+ 1, gtknew('VBox', children => [
+ 0, $attack->{msg},
+ 0, N("What do you want to do with this attacker?")
+ ])
+ ]),
+ gtksignal_connect(gtkadd(Gtk2::Expander->new(N("Attack details")),
+ gtknew('HBox', children => [
+ 0, gtknew('Label', text => " "),
+ 1, gtknew('VBox', children_loose => [
+ N("Attack time: %s", $attack->{date}),
+ N("Network interface: %s", $attack->{indev}),
+ N("Attack type: %s", $attack->{prefix}),
+ if_($attack->{protocol}, N("Protocol: %s", $attack->{protocol})),
+ N("Attacker IP address: %s", $attack->{ip_addr}),
+ if_($attack->{hostname} ne $attack->{ip_addr}, N("Attacker hostname: %s", $attack->{hostname})),
+ (
+ $attack->{service} ne $attack->{port} ?
+ N("Service attacked: %s", $attack->{service}) :
+ N("Port attacked: %s", $attack->{port}),
+ ),
+ if_($attack->{icmp_type}, N("Type of ICMP attack: %s", $attack->{icmp_type}))
+ ]),
+ ])),
+ activate => sub { $_[0]->get_expanded and $w->shrink_topwindow }
+ ),
+ $auto = gtknew('CheckButton', text => N("Always blacklist (do not ask again)"), toggled => sub {
+ $whitelist->set_sensitive(!$_[0]->get_active);
+ $ignore->set_sensitive(!$_[0]->get_active);
+ }),
+ gtknew('HButtonBox', layout => 'edge', children_loose => [
+ $blacklist = gtknew('Button', text => N("Blacklist"), clicked => sub {
+ $w->destroy;
+ $update_automatic_mode->();
+ set_verdict($attack, \&apply_verdict_blacklist);
+ }),
+ $whitelist = gtknew('Button', text => N("Whitelist"), clicked => sub {
+ $w->destroy;
+ $update_automatic_mode->();
+ set_verdict($attack, \&apply_verdict_whitelist);
+ }),
+ $ignore = gtknew('Button', text => N("Ignore"), clicked => sub {
+ $w->destroy;
+ set_verdict($attack, \&apply_verdict_ignore);
+ }),
+ ]),
+ ]));
+ eval { $auto->set_active(!$ifw->get_interactive) };
+ $blacklist->grab_focus;
+ gtksignal_connect($w->{window}, delete_event => sub {
+ set_verdict($attack, \&apply_verdict_ignore);
+ });
+ $w->{window}->show_all;
+}