### Local Variables: ***
### mode:perl ***
### comment-column:0 ***
### comment-start: "### " ***
### comment-end: "***" ***
### End: ***
#
# ****************DO NOT MOVE OR CHANGE LINES ABOVE THIS*********************
#
# The first set of lines runs perl from any shell. The second set of lines 
# identifies the rest of the file as PERL for EMACS autoformatting. 
# See end of copyright for more information.
#
# 
# -------------------------------------------------------------------
#                                   X-BONE
#
#                          http://www.isi.edu/xbone
#                USC Information Sciences Institute (USC/ISI)
#                   Marina del Rey, California 90292, USA
#                          Copyright (c) 1998-2005
# 
# -------------------------------------------------------------------
#
# Copyright (c) 1998-2005 by the University of Southern California.
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and
# its documentation in source and binary forms for non-commercial
# purposes and without fee is hereby granted, provided that the above
# copyright notice appear in all copies and that both the copyright
# notice and this permission notice appear in supporting
# documentation, and that any documentation, advertising materials,
# and other materials related to such distribution and use acknowledge
# that the software was developed by the University of Southern
# California, Information Sciences Institute.  The name of the
# University may not be used to endorse or promote products derived
# from this software without specific prior written permission.
# 
# THE UNIVERSITY OF SOUTHERN CALIFORNIA MAKES NO REPRESENTATIONS ABOUT
# THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  THIS SOFTWARE IS
# PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# Other copyrights might apply to parts of this software and are so
# noted when applicable.
#
# -------------------------------------------------------------------
#
# Effort partly sponsored by the Defense Advanced Research Projects
# Agency (DARPA) and Air Force Research Laboratory, Air Force Materiel
# Command, USAF, under agreement numbers F30602-98-1-0200 (X-Bone) and
# F30602-01-2-0529 (DynaBone). The views and conclusions contained
# herein are those of the authors and should not be interpreted as
# necessarily representing the official policies or endorsements,
# either expressed or implied, of the Defense Advanced Research
# Projects Agency (DARPA), the Air Force Research Laboratory, or the
# U.S. Government.
#
# This work was partly supported by the NSF STI-XTEND (ANI-0230789)
# and NETFS (ANI-0129689) projects. Any opinions, findings, and
# conclusions or recommendations expressed in this material are those
# of the authors and do not necessarily reflect the views of the
# National Science Foundation.
#
# --------------------------------------------------------------------------
#  $RCSfile: XB_Cisco.pm,v $
#
# $Revision: 1.2 $
#   $Author: pingali $
#     $Date: 2005/03/31 07:03:53 $
#    $State: Exp $
# ----------------------------------------------------------------------------
#
# Primary Author: Mohit Pande

package Cisco;

require Exporter;
@ISA = qw(Exporter);
@Export = qw();
@EXPORT_OK = qw(new new_cmd);

use strict;
use sigtrap;
 
use XB_Log;

$|++;
use Net::SSH::Perl;
use Net::SSH::Perl::Constants qw( :msg );

############################################################################
# GENERIC EXPORTED API
############################################################################

# Description:
# 	Set up a connection to Cisco Router
# Argument:
#	a hash containing the Cisco Router access information
# Returns:
#	a object of the Cisco connection
# Exceptions:
#	None.
#
sub new {

    my $invocant = shift;
    my $class = ref($invocant) || $invocant;
    my $self = { 
	host  => '',
	username => '',
	password => '',
	enable_password => '',
	timeout => 10,
	@_,  #override previous attributes
    };
    
    bless($self, $class);
    return $self;
}


# Description:
#	Issue command to the connected Cisco Router
# Argument:
#	a object of the Cisco connection
# Returns:
#	the output of the cmd returned by the Cisco Router
# Exceptions:
#	None
#
sub new_cmd {
    
    my $self = shift;
    my $router_cmd = shift;
 
    my $host = $self->{host};
    my $username = $self->{username};
    my $password = $self->{password};
    my $enable_password = $self->{enable_password};
    my $timeout = $self->{timeout};
   
    XB_Log::log "debug1", "+++ enter new_cmd +++";
    XB_Log::log "debug1",  "+++ host=$host, username = $username,".
      " password= $password, enable_password = $enable_password,".
      " timeout = $timeout +++";
    my @cmd;
    
    # modify these in case of prompt (hostname) changes
    # assuming alphanumeric characters only:
    # [a-zA-Z0-9] is actually \w, but some hosts have '_' or '-' in their names
    my $enb_prompt  = qr/(?:[a-zA-Z0-9]+\#)\s*/;  # alphanumeric followed by '#'
    my $reg_prompt  = qr/(?:[a-zA-Z0-9]+>)\s*/;  # alphanumeric followed by '>'
    my $pass_prompt = qr/Password:\s*/;
    
    my ($prompt_cnt,$save)  = (0,0);
    my ($ssh, @config);

    # login on the device
    eval {
	local $SIG{'ALRM'} = sub { die 'TimedouT' };
	alarm $timeout;
	XB_Log::log "debug1", "+++ start login +++";
	$ssh = Net::SSH::Perl->new($host, protocol=>1, cipher=>'DES', port=>22);
	$ssh->login($username, $password);
	alarm 0;
    };
    
    XB_Log::log "debug1", "+++ login sucessfully +++";
    ($@)? ( die '[',scalar localtime,'] ', ($@ =~ /TimedouT/)?
	    "Takes too long to login on $host.\n" :
	    "Unexpected eval err: $@.\n"
	    ) : undef;
    
    
    # set up handler and intercept everything that goes to STDOUT
    
    $ssh->register_handler(SSH_SMSG_STDOUT_DATA, sub {
	my($ssh, $packet) = @_;  
	my $str = $packet->get_str;
	if ( $save )  {                       # reading config
	    if ( $str =~ /$enb_prompt$/ ) {   # last line of the config + prompt
		@cmd = "exit" . "\n";
		_send_cmd ($ssh, @cmd);
	    }
	    
	    $str =~ s/\cM//g;
	    chomp $str;
	    # skip echo of the command
	    push @config, $str  unless ($str =~ /^(\w|\s|\W)$/  || $str eq '');
	}
	else {                              # login part
	    if ($str =~ /$reg_prompt$/)  {  # go to enable mode
		@cmd = "enable" . "\n";
		_send_cmd ($ssh, @cmd);
	    }
	    elsif ( $str =~ /$pass_prompt$/ ) {
		# going into enable mode....
		@cmd = $enable_password . "\n"; 
		_send_cmd ($ssh, @cmd);
	    }
	    elsif ( $str =~ /$enb_prompt$/ && !$prompt_cnt ) {
		# exec first command in enable mode
		@cmd = "terminal length 0" .  "\n";
		_send_cmd ($ssh, @cmd);
		$prompt_cnt++;
	    }
	    elsif ( $str =~ /$enb_prompt$/ && $prompt_cnt ) {
		# exec second command in enable mode
		@cmd = $router_cmd . "\n"; 
		_send_cmd ($ssh, @cmd);
		$save++;
	    }
	    else {
		# Uncomment this for debug purposes
		#  print "Useless data: $str\n";
	    }
	}
    });
    
    eval { 
	local $SIG{'ALRM'} = sub { die 'TimedouT' };
	alarm $timeout;
	$ssh->cmd('');             
	alarm 0;
    };
    
    ($@)? ( die '[ ',scalar localtime,'] ', ($@ =~ /TimedouT/)?
	    "Timed out while pulling from $host.\n" :
	    "Unexpected eval err: $@.\n"
	    ) : undef;
    
    # at this point you have config in @config
    # do whatever you need to do with it
    my @output = split "\n", $config[0];
    pop @output;       # removing the router prompt from the output
    for (my $i= 0; $i<=$#output; $i++) {
	$output[$i] .= "\n";
    }
    XB_Log::log "debug1",  "+++ leave new_cmd +++";
    @config;
}

##########################################################################
# UTILITY FUNCTIONS 
##########################################################################

# Description: 
#	Dispatch the cisco command
# Arguments:
#	Object of  a cisco connection
# Returns: 
#	None.
#
sub _send_cmd {
  my $ssh = shift;
  my @cmd = @_;
  my $packet = $ssh->packet_start(SSH_CMSG_STDIN_DATA);
  $packet->put_str(@cmd);
  $packet->send;
}


1;

__END__
    
    


syntax highlighted by Code2HTML, v. 0.9.1