#!/usr/bin/perl
#
# $Id: nf-shell.pl,v 1.4 2007/04/02 16:47:37 gomor Exp $
#
package Net::Frame::Shell;
use strict;
use warnings;

our $VERSION = '0.10';

my @subList = qw(
   F sr sd sd2 sd3 sniff dsniff read
);

my @layerList = qw(
   ETH RAW SLL NULL ARP IPv4 IPv6 TCP UDP VLAN ICMPv4 PPPoE PPP PPPLCP LLC CDP
   STP OSPF IGMPv4
);

use Net::Frame::Device;
use Net::Frame::Simple;
use Net::Frame::Dump::Online;
use Net::Frame::Dump::Offline;
use Net::Write::Layer2;
use Net::Write::Layer3;
use Data::Dumper;
use Term::ReadLine;

my $oDevice = Net::Frame::Device->new;
my $oDump;

{
   no strict 'refs';
   for my $l (@layerList) {
      *$l = sub {
         (my $module = "Net::Frame::Layer::$l") =~ s/::/\//g;
         require $module.'.pm';
         my $r = "Net::Frame::Layer::$l"->new(@_);
         $r->pack;
         $r;
      };
   }
}

sub F {
   my @layers = @_;
   Net::Frame::Simple->new(
      firstLayer => $layers[0]->layer,
      layers     => \@layers,
   );
}

sub sr {
   do { print "Nothing to send\n"; return } unless $_[0];

   my $oWrite = Net::Write::Layer2->new(dev => $oDevice->dev);
   $oWrite->open;
   $oWrite->send(shift());
   $oWrite->close;
}

sub sd {
   do { print "Nothing to send\n"; return } unless $_[0];

   return sd2(@_) if $_[0]->getLayer('ETH') || $_[0]->getLayer('RAW'); # XXX
   return sd3(@_) if $_[0]->l3;
}

sub sd2 {
   my ($f) = @_;

   do { print "Nothing to send\n"; return } unless $f;

   my $oWrite = Net::Write::Layer2->new(dev => $oDevice->dev);
   $oWrite->open;
   $oWrite->send($f->raw);
   $oWrite->close;
}

sub sd3 {
   my ($f) = @_;

   do { print "Nothing to send\n"; return } unless $f;

   do { print "We can only send IPv4 frames at layer 3\n"; return }
      if (! $f->getLayer('IPv4') || $f->getLayer('ETH')); # XXX, RAW, ...

   my $ip  = $f->getLayer('IPv4');
   my $dst = $ip->dst;

   my $oWrite = Net::Write::Layer3->new(dev => $oDevice->dev, dst => $dst);
   $oWrite->open;
   $oWrite->send($f->raw);
   $oWrite->close;
}

sub sniff {
   my ($filter) = @_;
   $oDump = Net::Frame::Dump::Online->new(dev => $oDevice->dev);
   $oDump->filter($filter) if $filter;
   $oDump->start;
   while (1) {
      if (my $h = $oDump->next) {
         my $f = Net::Frame::Simple->newFromDump($h);
         print $f->print."\n";
      }
   }
}

sub dsniff {
   my ($filter) = @_;
   $oDump = Net::Frame::Dump::Online->new(dev => $oDevice->dev);
   $oDump->filter($filter) if $filter;
   $oDump->start;
   while (1) {
      if (my $h = $oDump->next) {
         my $f = Net::Frame::Simple->newFromDump($h);
         my $ip = $f->getLayer('IPv4');
         next unless $ip;
         my $l;
         if (($l = $f->getLayer('UDP')) || ($l = $f->getLayer('TCP'))) {
            my $data = $l->payload;
            next unless $data =~ /^user\s+|^pass\s+/i;
            print $ip->src.':'.$ip->dst.'> '.$data."\n";
         }
      }
   }
}

sub read {
   my ($file) = @_;
   do { print "Please specify a pcap file to read\n"; return } unless $file;

   $oDump = Net::Packet::Dump::Offline->new(file => $file);
   $oDump->start;

   my $n = 0;
   while (my $h = $oDump->next) {
      ++$n;
      my $f = Net::Frame::Simple->newFromDump($h);
      my $len = length($h->{raw});
      print 'Frame number: '.$n." (length: $len)\n";
      print $f->print."\n";
   }

   $oDump->stop;
}

sub nfShell {
   my $prompt = 'nf-shell> ';
   my $name   = 'NF-Shell';
   my $term   = Term::ReadLine->new($name);
   $term->ornaments(0);

   $term->Attribs->{completion_function} = sub {
      ( @subList, @layerList )
   };

   {
      no strict;

      while (my $line = $term->readline($prompt)) {
         $line =~ s/s*read/Net::Frame::Shell::read/;
         eval($line);
         warn($@) if $@;
         print "\n";
      }
   }

   print "\n";
}

END {
   if ($oDump && $oDump->isRunning) {
      $oDump->stop;
   }
}

1;

package main;

Net::Frame::Shell::nfShell();

1;

__END__

=head1 NAME

nf-shell - Net::Frame Shell tool

=head1 SYNOPSIS

   None for now.

=head1 DESCRIPTION

This tool is in its very early stages. It tries to mimic the well-known Scapy tool.

=head1 AUTHOR

Patrice E<lt>GomoRE<gt> Auffret

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2006-2007, Patrice E<lt>GomoRE<gt> Auffret

You may distribute this module under the terms of the Artistic license.
See LICENSE.Artistic file in the source distribution archive.

=cut
