# $Id: ScannedHost.pm,v 1.18 2000/12/16 09:02:06 levine Exp $
#
# A record holding port scanning info on a per-host basis, and
# useful accessor and manipulation methods.
#
#
# Copyright (C) 2000  James D. Levine (jdl@vinecorp.com)
#
#
#   This program is free software; you can redistribute it and/or
#   modify it under the terms of the GNU General Public License
#   as published by the Free Software Foundation; either version 2
#   of the License, or (at your option) any later version.
# 
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
# 
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 
#   02111-1307, USA.
#
####################################################################

package PortScan::ScannedHost;
use strict;

use vars qw(@ISA);

@ISA = qw( Exporter );

use PortScan::SetOps;
use PortScan::PortSpec;


sub new
{
    my $type = shift;
    my $self = 
    {
	'scan_set' => undef,	# the ScanSet this belongs to, if any
	'port_specs' => {},	# all explicitly noted ports
	'addr' => '',
	'default_state' => 'closed',
    };

    bless $self, $type;
}


sub to_text
  {
    my $self = shift;
    return
      "addr: " . $self->{addr} . "\n"
      . "default_state: " . $self->{default_state} . "\n"
      . "ports: " . hash_to_text($self->{port_specs}) . "\n"
	;
  }

sub hash_to_text
  {
    my $h = shift;
    my $t;
    while (my ($k, $v) = each %$h)
      {
	$t .= "`${k}' -> `${v}'\n";
      }
    $t;
  }



sub set_or_get
{
    my $field = shift;
    my $self = shift;
    my $val = shift;

    $self->{$field} = $val if defined $val;

    return $self->{$field};
}

sub all_scanned_ports
{
    my $self = shift;

    return (defined $self->scan_set()) ? $self->scan_set()->all_scanned_ports() :{};
}

sub all_ports
{
    my ($self) = @_;

    my $ports = $self->all_scanned_ports();
    my $result_hash = {};

    @$result_hash{keys %$ports} = (values %$ports);

    foreach my $p (values %$result_hash)
    {
	$p->state($self->default_state());
    }
    # now overlay with noteworthy ports

    $ports = $self->port_specs();
    foreach my $p (values %$ports )
    {
	$result_hash->{$p->key_for()} = $p->clone();
#print "cloned $p with state " , $p->state(), "\n";
#print "overlaying ", $p->key_for(), " state ", $p->state(), "\n";
    }

    $result_hash;
}

sub default_state() {set_or_get('default_state', @_);} 
sub addr {set_or_get('addr', @_);}
sub scan_set{set_or_get('scan_set', @_);}

sub scanned_ports_hash
{
    my $self = shift;
    return (defined $self->scan_set()) ? $self->scan_set()->all_scanned_ports()
	: {};
}

sub add_port
{				# clobbers an existing port of same number
    my ($self, $pspec) = @_;

    $self->port_specs()->{$pspec->key_for()} = $pspec;
    if ($self->scan_set())
    {
	$self->scan_set()->add_scanned_port($pspec);

    }
}

sub remove_port
{
    my ($self, $pkey) = @_;

    delete $self->port_specs()->{$pkey};
}

sub port_specs
{
    set_or_get('port_specs', @_);
}

sub port_specs_sorted_list
{
    my $self= shift;
    return PortScan::PortSpec::sorted_list values %{$self->port_specs()};
}

sub port_spec
{
    my ($self, $number) = @_;

    return $self->port_specs()->{$number}; # port_spec or undef
}


sub out_pretty
{
    my $self = shift;

    printf "%s:\n", $self->addr();

    my $ports = $self->port_specs();

    foreach my $port (values %$ports)
    {
	printf $port->nmap_default_human_format(). "\n";
    }
}


sub sorted_list
{
    return sort compare @_;
}

sub sorted_ip_scalar_list
{
    return sort compare_ip_scalar @_; ;
}

sub compare
{
    return compare_ip_scalar( $a->addr(), $b->addr() )
}

sub compare_ip_scalar
{
    my ($a1, $a2, $a3, $a4) = split (/\./, $a);
    my ($b1, $b2, $b3, $b4) = split (/\./, $b);

#    print "a $a1 $a2 $a3 $a4 \n";
#    print "b $b1 $b2 $b3 $b4 \n";
    return -1 if ($a1 < $b1);
    return 1  if ($a1 > $b1);

    return -1 if ($a2 < $b2);
    return 1  if ($a2 > $b2);

    return -1 if ($a3 < $b3);
    return 1  if ($a3 > $b3);

    return -1 if ($a4 < $b4);
    return 1  if ($a4 > $b4);

    0;
}

sub addrs_sorted_list
{
    return sort compare_addrs @_;
}

sub compare_addrs
{
    my ($a1, $a2, $a3, $a4) = split (/\./, $a);
    my ($b1, $b2, $b3, $b4) = split (/\./, $b);

    return -1 if ($a1 < $b1);
    return 1  if ($a1 > $b1);

    return -1 if ($a2 < $b2);
    return 1  if ($a2 > $b2);

    return -1 if ($a3 < $b3);
    return 1  if ($a3 > $b3);

    return -1 if ($a4 < $b4);
    return 1  if ($a4 > $b4);

    0;
}



sub set_or_get
{
    my $field = shift;
    my $self = shift;
    my $val = shift;

    $self->{$field} = $val if defined $val;

    return $self->{$field};
}





1;






















