
This file contains some basic examples on how to use the
internal libraries included with NDiff to do useful things
with nmap data in Perl.

This is just a starter - not all classes and methods are discussed
here.  Read the source.


-----------------------------------------------------------------
DataStore.pm:  handles external representations of nmap data
1. reading nmap data

TODO: There appears to be some redundancy of methods with respect to
prepare_tag() below. 


use PortScan::DataStore;
use Carp;

# define some tag associated with the ScanSet (results set)
my $input_tag = "10.0.1-net-30May";

# pass the tag through prepare_tag() to evaluate any embedded % substitutions
my $prepared_input_tag = PortScan::DataStore::prepare_tag( $input_tag );


# now generate a data store attached to the tag
my( $processed_input_tag, $input_data_store ) 
    = PortScan::DataStore::data_store_for( $prepared_input_tag );


# now retrieve the PortScan::ScanSet (results set) associated with the tag
my $input_scanset = $input_data_store->retrieve_scanset( $processed_input_tag );

# failed if $input_scanset is not defined
croak "can't get input scanset" if !defined $input_scanset;


-----------------------------------------------------------------
DataStore.pm:  
2. writing nmap data

use PortScan::DataStore;
use Carp;

# read in or generate a ScanSet...

my $scan_set = ...

# ... manipulations on the ScanSet...

# now prepare to write the ScanSet...


# define some tag associated with the ScanSet (results set)
my $output_tag = "10.0.1-net-30May-processed";

# pass the tag through prepare_tag() to evaluate any embedded % substitutions
my $prepared_output_tag = PortScan::DataStore::prepare_tag( $output_tag );

# associate the tag with the ScanSet
$scan_set->tag( $output_tag );

# now generate a data store attached to the tag
my( $processed_output_tag, $output_data_store ) 
    = PortScan::DataStore::data_store_for( $prepared_output_tag );


# now retrieve the PortScan::ScanSet (results set) associated with the tag
my $result = $output_data_store->put_scanset( $scan_set );

croak "couldn't write scanset $prepared_output_tag" if $result != 0;



-----------------------------------------------------------------

ScanSet.pm: container for a set of hosts all scanned at the same time


use ScanSet;
use PortSpec;

# normally one retrieves a ScanSet from a DataStore but it is 
# possible one would generate one programmatically as well
#

# see ScanSet.pm for description of the values passed in the
# constructor...in this example will set them after construction

my $set = new PortScan::ScanSet( "", {}, {}, {} );

# associate a tag with the set
$set->tag( "syn-scan-Thursday" );  


# add one or more scanned ports to the master set
# each host contained in a ScanSet is assumed to have had
# this master set of ports scanned
$set->add_scanned_port( new PortScan::PortSpec( 80, 'open', 'tcp', "", "", "", "" ) );
$set->add_scanned_port( new PortScan::PortSpec( 22, 'open, 'tcp', "", "", "", "" ) );
$set->add_scanned_port( new PortScan::PortSpec( 43, 'closed', 'udp', "", "", "", "" ) );


# add a few hosts to the set
# normally one might have added some ports to the hosts 
$set->add_host( new PortScan::ScannedHost( undef, {}, '192.168.2.3', 'closed' ) );
$set->add_host( new PortScan::ScannedHost( undef, {}, '192.168.2.4', 'closed' ) );
$set->add_host( new PortScan::ScannedHost( undef, {}, '192.168.2.123', 'closed' ) );
$set->add_host( new PortScan::ScannedHost( undef, {}, '192.168.2.200', 'closed' ) );


# oops, need to remove one
$set->remove_host( '192.168.2.200' );

# retrieve a host for some reason
my $check = $set->get_host( '192.168.2.200' );

# will return undef if not found
print "not found" if ! defined $check;


# print out all hosts

my @hosts = $set->hosts_sorted_list;

foreach my $h ( @hosts )
{
  # do something...
  # $h is an instance of PortScan::ScannedHost
}


-----------------------------------------------------------------

ScannedHost.pm: encapsulates the information about a host scanned with nmap

use PortScan::ScannedHost;
use PortScan::PortSpec;

# create a new ScannedHost instance
my $host = new PortScan::ScannedHost( undef, {}, "10.0.1.12", "closed" );

# "closed" is the "Ignored State" in nmap terms.  All ports not
# explicitly added to the host, but which exist in the hosts's
# owner-ScanSet master set, will have this Ignored State applied to it.
#
# Nmap models the information this way to save space during runtime and
# in results display and storage.  The "interesting ports" listed for a
# given host are the exceptions to the Ignored State.  The Ignored State
# applies only to the remaining ports which were scanned, which is
# the ScanSet's master set minus those interesting ports.


# add a PortScan::PortSpec instance
$host->add_port( new PortScan::PortSpec( 80, "open", "tcp", "", "", "", "" ) );

# add some ports using a slightly more convenient method

$host->set_port( 22, "open", "tcp", "", "", "", "" );
$host->set_port( 43, "open", "tcp", "", "", "", "" );
$host->set_port( 43, "filtered", "udp", "", "", "", "" );


# remove a port, which are referenced by opaque keys defined 
# by PortScan::PortSpec
$host->remove_port( PortScan::PortSpec::make_key( 80, "tcp" ) );


# retrieve a port, or undef
# note that ScannedHost methods use ( proto, port ) while PortSpec
# methods use ( prot, proto ).  Mea culpa.
my $port = $host->get_port( "tcp", "80" );
carp "no such port" if ! defined $port;


# get a hashref of all ports contained
my $specs = $host->port_specs();

# iterate over the hash of specs and do something
while my( $key, $pspec ) = each %$specs
{
  # do something with each $pspec, which is an instance of PortScan::PortSpec

  # $key is an opaque key created by PortScan::PortSpec, but can always be
  # split into ( port, proto ) by PortScan::PortSpec::decode_key

  my( $port, $proto ) = PortScan::PortSpec::decode_key( $key );
}

# get the sorted list of ports contained and iterate over them
my $sorted_specs = $host->port_specs_sorted_list
foreach my $pspec ( @sorted_specs )
{
  # do something...$pspec is a PortScan::PortSpec instance
}


# get the state associated with some port, but only if explicitly
# inserted into the host (the ScannedHosts's owner/ScanSet is not checked)
# will return the psuedo-state "unknown" if the port isn't found.
my $state = $host->get_state( "tcp", 22 );
print "tcp/22 -> '$state'\n";



-----------------------------------------------------------------

PortSpec.pm: Encapsulates the properties of a "port", in the general
             sense for TCP and UDP


use PortScan::PortSpec;
use PortScan::ScannedHost;

# generate a PortSpec
my $spec = new PortScan::PortSpec( 80, "open", "tcp", "", "", "", "" );

# the four trailing parameters are defined by nmap and require some
# further research by documentation.  The second of the four is 
# the well-known service name.


# insert the spec into a ScannedHost instance
$some_host->add_port( $spec );


# make a copy of the PortSpec
my $spec2 = $spec->clone();


# query the members and print

print "port number: " .  $spec->number();
print "port state: " .   $spec->state();
print "port proto: " .   $spec->proto();
print "port service: " . $spec->service();
print "port user1: " .   $spec->u1();
print "port user2: " .   $spec->u2();
print "port user3: " .   $spec->u3();


# for internal use of ndiff, map the state to
# a canonical uppercase single-character code

my $code = $spec->state_sm;

print "open" if $code eq "O";
print "closed" if $code eq "C";
print "filtered" if $code eq "F";
...


# print out a canonical key for the well-known service name

print "service for port http/tcp: " 
      . PortScan::PortSpec::spec->known_port_by_name( "http", "tcp" );


# print out a canonical key for the well-known port number

print "service for port 80/tcp: " 
      . PortScan::PortSpec::spec->known_port_by_number( 80, "tcp" );

-----------------------------------------------------------------

TODO:
 IPAddress.pm
 ScanComparison.pm
 NmapFile.pm
 ScanContext.pm
 SetOps.pm

