#============================================================================
#
# Program: libcli-sshnet.pl
# Programmer: Remo Rickli, dcr
#
# -> Net::Telnet/Net::SSH::Perl based Functions <-
#
# Needs quite some perl libs, but works well on OBSD4.  Rather limited at this stage:
#
# Ubuntu install hint
#$ wget http://search.cpan.org/CPAN/authors/id/D/DB/DBROBINS/Net-SSH-Perl-1.30.tar.gz
#$ tar zxvf Net-SSH-Perl-1.30.tar.gz
#$ cd Net-SSH-Perl-1.30
#$ perl Makefile.PL && make
#sudo checkinstall -D --pkgname="Marc-Net-SSH-Perl" --pkgversion="1.30" make install

# SSH doesn't support enable at this stage (only 1 command per session)
# Foundry only tested with simple telnet pw/en configs as Radius enable asks for username again...
# HP Procurve only works with "console terminal NONE", maybe the can add vt100 without ESC sequences and add an "anykey" override?
# Nortel doesn't work yet, because I can't get the script to login. Maybe they can provide a "normal" login screen alternative?
#============================================================================
package cli;
use Net::Telnet::Cisco;

use vars qw($sshnetok);

eval 'use Net::SSH::Perl';
if ($@){
	$sshnetok = 0;
	print "Net::SSH::Perl not available\n" if $main::opt{d};
}else{
	$sshnetok = 1;
	print "Net::SSH::Perl loaded\n" if $main::opt{d};
}

# original my $prompt = '/(?m:^[\w.-]+\s?(?:\(config[^\)]*\))?\s?[\$#>]\s?(?:\(enable\))?\s*$)/';
my $prompt = '/.+?[#>]\s?(?:\(enable\)\s*)?$/';

#============================================================================
# Find login, if device is compatible for mac-address-table or config retrieval
#============================================================================
sub PrepDev{

	my $us    = "";
	my $nok   = 2;										#  clibad=2 means very true ;-)
	my $pnmap = 1;										# port not mapped -> ssh will be tried...
	my @users = @misc::users;

	if($_[1] eq "fwd" and $main::dev{$_[0]}{os} !~ /^IOS/){					# Only IOS has support for mac-address stuff
		return $nok;
	}
	if(defined $main::dev{$_[0]}{cp} and $main::dev{$_[0]}{cp} != 0){			# Undef -> it's new, 0 -> set to be prepd
		if(!$main::dev{$_[0]}{us}){							# Do we have a  user?
			print "Pu";
			return $nok;								# No user but a real port -> failed before
		}elsif(exists $misc::login{$main::dev{$_[0]}{us}}){				# OK if in nedi.conf
			return 0;								# Lets use that then (clibad=false)
		}else{
			print "Pc";								# User not in nedi.conf -> Prep
		}
	}
	if(defined $misc::map{$main::dev{$_[0]}{ip}}{cp}){					# Disable SSH upon telnet port map
		$main::dev{$_[0]}{cp} = $misc::map{$main::dev{$_[0]}{ip}}{cp};
		print "M$main::dev{$_[0]}{cp}" if $main::opt{d};
		$pnmap = 0;
	}else{
		$main::dev{$_[0]}{cp} = 23;
	}
	if( $main::dev{$_[0]}{os} =~ /^(Cat1900|Nortel)/){
		do {
			$us = shift (@users);
			print " P:$us" if $main::opt{d};
			my $session = Net::Telnet::Cisco->new(	Host	=> $main::dev{$_[0]}{ip},
								Port	=> $main::dev{$_[0]}{cp},
								Prompt  => $prompt,
								Timeout => $misc::timeout,
								Input_log  => "input.log",
								Output_log  => "output.log",
								Errmode	=> 'return');
		
			if( defined($session) ){
				if($main::dev{$_[0]}{os} eq "Cat1900"){
					if( $session->waitfor('/Enter Selection:.*$/') ){
						$session->print("k");
						if ($session->enable( $misc::login{$us}{pw} ) ){
							$nok = 0;
						}else{
							print "Te";
						}
					}else{
						print "To";
					}
				}else{
					if( $session->waitfor('/Ctrl-Y.*$/') ){
						$session->put("\cY");
						if( $session->waitfor('/Username.*$/') ){
							$session->print($us);
						}else{
							print "Tp";
						}
						sleep 1;
						$session->print($misc::login{$us}{pw});
						if( $session->waitfor('/#\s*$/') ){		# Regular Enable fails due to Escs...
							$nok = 0;
						}else{
							print "Te";
						}
					}else{
						print "To";
					}
				}
				$session->close;
			}else{
				print "Tc";
				return $nok;
			}
		} while ($#users ne "-1" and $nok);						# Stop if ok or we ran out of logins
	}elsif( $main::dev{$_[0]}{os} =~ /^(IOS|CatOS|Ironware|ProCurve)/){
		do {
			$us = shift (@users);
			print " P:$us" if $main::opt{d};
			if($sshnetok and $pnmap){
				eval {
				my $ssh = Net::SSH::Perl->new($main::dev{$_[0]}{ip}, options => ["BatchMode yes",
										"RhostsAuthentication no",
										"protocol => 2" ]);
					$ssh->login($us, $misc::login{$us}{pw});
					my ($stdout, $stderr, $exit) = $ssh->cmd("exit");
					if ($stderr) {
						print "Hl";
					}else{
						$nok = 0;
						$main::dev{$_[0]}{cp} = 22;
					}
				};
			}else{
				$@ = "Hs";
			}
			print $@ if $main::opt{d};
			if ($@){
				my $session = Net::Telnet::Cisco->new(	Host	=> $main::dev{$_[0]}{ip},
									Port	=> $main::dev{$_[0]}{cp},
									Prompt  => $prompt,
									Timeout	=> $misc::timeout,
									#Input_log  => "input.log",
									#Output_log  => "output.log",
									Errmode	=> 'return');
				if(defined $session){
					if( $main::dev{$_[0]}{ty} =~ /^HP-J90/){		# Any key for some HPs
						$session->print("");
						print"Tk";
					}
					if( $session->login($us,$misc::login{$us}{pw}) ){
						if ($misc::login{$us}{en}){
							$session->enable($misc::login{$us}{en});
						}
						if ($session->is_enabled){			# Make sure we are enabled now
							$nok = 0;
						}else{
							print "Te";
						}
					}else{
						print "Tl";
					}
					$session->close;
				}else{
					print "Tc";
					return $nok;
				}
			}
		} while ($#users ne "-1" and $nok);						# Stop if ok or we ran out of logins
	}else{
		$main::dev{$_[0]}{cp} = 0;							# Port=0 for unsupported device
		return $nok;
	}
	if($nok){
		print "Tu";
	}else{
		print ":$main::dev{$_[0]}{cp} " if $main::opt{d};
		$main::dev{$_[0]}{us} = $us;
		sleep 1;									# Some (HP)devices need a pause
	}
	return $nok;
}

#============================================================================
# Get Ios mac address table.
#============================================================================
sub BridgeFwd{

	my $line = "";
	my $nspo = 0;
	my @cam  = ();
	my $cmd  = "sh mac-address-table dyn";
	my $cmd2  = "sh port-security addr";							# Thanks Duane Walker 7/2007
	my $cap  = 0;

	if($main::dev{$_[0]}{os} eq "IOS-wl"){							# Cisco WLan specific...
		$cap = 1;									# Needed to avoid using counters as vlans
		$cmd = 'sh bridge | exclude \*\*\*';						# Work around aged (***) forwarding entries
	}
	print " F:$main::dev{$_[0]}{us}:$main::dev{$_[0]}{cp} " if $main::opt{d};

	if( $main::dev{$_[0]}{cp} == 22 ){
		eval {
			my $ssh = Net::SSH::Perl->new($main::dev{$_[0]}{ip});
			$ssh->login($main::dev{$_[0]}{us}, $misc::login{$main::dev{$_[0]}{us}}{pw});
			my ($stdout, $stderr, $exit) = $ssh->cmd($cmd);
			@cam = split("\n", $stdout);
			
		};
		if ($@){
			print "Ho";
			return 2;
		}
	}else{
		my $session = Net::Telnet::Cisco->new(	Host	=> $main::dev{$_[0]}{ip},
							Port	=> $main::dev{$_[0]}{cp},
							Prompt  => $prompt,
							#Input_log  => "input.log",
							#Output_log  => "output.log",
							Timeout	=> $misc::timeout,
							Errmode	=> 'return');
		if( defined($session) ){
			if( $session->login( $main::dev{$_[0]}{us}, $misc::login{$main::dev{$_[0]}{us}}{pw} ) ){
				if ( $misc::login{$main::dev{$_[0]}{us}}{en} ){
					if (!$session->enable( $misc::login{$main::dev{$_[0]}{us}}{en} ) ){
						$session->close;
						print "Te";
						return 2;
					}
				}
				$session->cmd("terminal len 0");
				@cam = $session->cmd($cmd);
				push @cam, $session->cmd($cmd2) if ($misc::getfwd eq 's' and !$cap);	# Thanks Duane Walker 7/2007
				$session->close;
			}else{
				$session->close;
				print "Tl";
				return 2;
			}
			$session->close;
		}else{
			print "Tc";
			return 2;
		}
	}
	foreach my $l (@cam){
		if ($l =~ /\s+(dynamic|forward|secure(dynamic|sticky))\s+/i){			# (secure) Thanks to Duane Walker 7/2007
			my $mc = "";
			my $po = "";
			my $vl = "";
			my @mactab = split (/\s+/,$l);
			foreach my $col (@mactab){
				if ($col =~ /^(Gi|Fa|Do|Po|Vi)/){$po = &misc::Shif($col)}
				elsif ($col =~ /^[0-9|a-f]{4}\./){$mc = $col}			
				elsif (!$cap and $col =~ /^[0-9]{1,4}$/ and !$vl){$vl = $col}	# Only use this, if it's not a Cisco AP
			}
			if( exists($misc::portprop{$_[0]}{$po}) ){				# Make sure IFidx exists before using IF
				if($po =~ /\.[0-9]/){						# Does it look like a subinterface?
					my @subpo = split(/\./,$po);
					$vl = $subpo[1];
					if($misc::portprop{$_[0]}{$subpo[0]}{upl}){$misc::portprop{$_[0]}{$po}{upl} = 1}	# inherit uplink metric on subinterface
				}
				$mc =~ s/\.//g;

				if ($vl !~ /$misc::ignoredvlans/){
					if ($po =~ /^.EC-|^Po[0-9]|channel/){
						$misc::portprop{$_[0]}{$po}{chn} = 1;
					}
					$misc::portprop{$_[0]}{$po}{pop}++;
					$misc::portnew{$mc}{$_[0]}{po} = $po;
					$misc::portnew{$mc}{$_[0]}{vl} = $vl;
					print "\n FWC:$mc on $po vl$vl" if $main::opt{v};
					$nspo++;
				}
			}
		}
	}
	print " f$nspo ";
	return 0;
}

#============================================================================
# Fetch  Config and return it in an array.
#============================================================================
sub Config{

	my $go  = 0;
	my $cl	= 0;
	my @run = ();
	my @cfg = ();

	my $cmdrun = "show run";
	my $cmdpag = "terminal length 0";

	if($main::dev{$_[0]}{os} eq "Ironware"){
		$cmdpag = "skip-page-display";
	}elsif($main::dev{$_[0]}{os} eq "ProCurve"){
		$cmdpag = "no page";
	}elsif($main::dev{$_[0]}{os} eq "CatOS"){
		$cmdpag = "set length 0";
		$cmdrun = "show conf";
	}elsif($main::dev{$_[0]}{os} eq "IOS-fw"){
		$cmdpag = "pager 0";
	}
	print " B:$main::dev{$_[0]}{us}:$main::dev{$_[0]}{cp}-$cmdpag/$cmdrun " if $main::opt{d};

	if($main::dev{$_[0]}{os} eq "Cat1900"){
		my $session = Net::Telnet::Cisco->new(	Host	=> $main::dev{$_[0]}{ip},
							Port	=> $main::dev{$_[0]}{cp},
							Prompt  => $prompt,
							Timeout => $misc::timeout,
							Errmode	=> 'return');
		if( defined($session) ){
			if( $session->waitfor('/Enter Selection:.*$/') ){
				$session->print("k");
				if ($session->enable( $misc::login{$main::dev{$_[0]}{us}}{pw} ) ){
					$session->timeout($misc::timeout * 10);			# Increase for building config
					@run = $session->cmd($cmdrun);
				} else {
					$session->close;
					print "Te";
					return "Enable failed!\n";
				}
			}else{
				$session->close;
				print "To";
				return "Menu timeout!\n";
			}
		}else{
			print "Tc";
			return "Telnet failed!";
		}
	}elsif( $main::dev{$_[0]}{os} =~ /^(Nortel)/){
		my $session = Net::Telnet::Cisco->new(	Host	=> $main::dev{$_[0]}{ip},
							Port	=> $main::dev{$_[0]}{cp},
							Prompt  => $prompt,
							Timeout => $misc::timeout,
							Errmode	=> 'return');
	
		if( defined($session) ){
			if( $session->waitfor('/Ctrl-Y.*$/') ){
				$session->put("\cY");
				if( $session->waitfor('/Username.*$/') ){
					$session->print($main::dev{$_[0]}{us});
				}else{
					print "Tp";
				}
				sleep 1;
				$session->print($misc::login{$main::dev{$_[0]}{us}}{pw});
				if( $session->waitfor('/#\s*$/') ){				# Regular Enable fails due to Escs...
					$session->timeout($misc::timeout * 10);			# Increase for building config
					$session->print($cmdpag) if $cmdpag;
					@run = $session->cmd($cmdrun);
				}else{
					$session->close;
					print "Te";
					return "Enable failed!\n";
				}
			}else{
				$session->close;
				print "To";
				return "Menu timeout!\n";
			}
		}else{
			print "Tc";
			return "Telnet failed!";
		}
	}else{
		if( $main::dev{$_[0]}{cp} == 22 ){
			eval {
				my $ssh = Net::SSH::Perl->new($main::dev{$_[0]}{ip});
				$ssh->login($main::dev{$_[0]}{us}, $misc::login{$main::dev{$_[0]}{us}}{pw});
				my ($stdout, $stderr, $exit) = $ssh->cmd($cmdrun);
				@run = split("\n", $stdout);
			};
			if ($@){
				print "Ho";
				return "SSH failed!";
			}
		}else{
			my $session = Net::Telnet::Cisco->new(	Host	=> $main::dev{$_[0]}{ip},
								Port	=> $main::dev{$_[0]}{cp},
								Prompt  => $prompt,
								Timeout => ($misc::timeout),
								#Input_log  => "input.log",
								#output_log  => "output.log",
								Errmode	=> 'return');
			if( defined($session) ){
				if( $main::dev{$_[0]}{ty} =~ /^HP-J90/){			# Any key for some HPs
					$session->print("");
					print"Tk";
				}
				if( $session->login($main::dev{$_[0]}{us},$misc::login{$main::dev{$_[0]}{us}}{pw}) ){
					if ($misc::login{$main::dev{$_[0]}{us}}{en}){
						$session->enable($misc::login{$main::dev{$_[0]}{us}}{en});
					}
					if ($session->is_enabled){				# Make sure we are enabled now
						$session->cmd($cmdpag) if $cmdpag;
						$session->timeout($misc::timeout * 30);		# Increase for building config
						@run = $session->cmd($cmdrun);
						$session->close;
					}else{
						$session->close;
						print "Te";
						return "Enable failed!\n";
					}
				}else{
					$session->close;
					print "Tl";
					return "Login $main::dev{$_[0]}{us} failed!\n";
				}
			}else{
				print "Tc";
				return "Telnet failed!";
			}
		}
	}
	foreach my $line (@run){
		$line =~ s/[\r\n]//g;
		if ($line =~ /^(Running|Current|PIX|FWSM|ASA)\s|^begin$|\*\*\* CORE/){$go = 1}
		if ($go and $line !~ /--More--|^$/){
			print "\n CFG:$line" if $main::opt{v};
			push @cfg,$line;
			$cl++;
		}
	}
	if( defined($cfg[$#cfg]) ){ 
		if($cfg[$#cfg] eq "" ){pop @cfg}						# Remove empty line at the end.
	}else{											# Empty config can't be good...
		push @cfg,"Empty config!";
	}
	print "Bf";
	print "-$cl" if $main::opt{d};
	return @cfg;
}

1;
