http://www.perlmonks.org?node_id=831401

Hessu has asked for the wisdom of the Perl Monks concerning the following question:

Hello,

I would like to connect from Windows XP to SSH2 server and from there establish a telnet connection to device. I would like to send a command to this device, wait expected prompt from the telnet session over SSH connection and read the result. So far best results have been with Net::SSH2. The problem is the telnet connection.

The problem is that after I issue 'telnet <IP Address>' command, I cannot read the data without breaking the command sending/reading. If I read the result once after telnet has been established, I get after that only the command that was sent. For example after sending the telnet command, I read the result to look for username prompt and send the username. After this I get only the username that was sent as a result, not the actual output. Seems like output and input are mixed after first read.

I can issue the commands over telnet session quite nicely if I do not read the results at all. But unfortunately, I need to parse the output and decide following actions based on collected data. This might be possible with several sequential sessions, but I am looking send command, wait expected data and read response type of solution on Windows machine. I am trying to compile this as executable with ActiveState tools and for this reason I don't think that Cygwin is an option.

Is there a way to do this on Windows machine? Is this somehow related to terminal settings that I should modify before establishing the telnet session? Newbie question, but I was not able to find the module where implementation of $channel->read() really is?

Thank You
#!/usr/bin/perl # Tested with ActiveState perl version 5.8.8 on Windows XP SP3. use strict; use warnings; use Net::SSH2; # Using version 0.28. use Data::Dumper; #Net::SSH2->debug(1); use constant NEWLINE => qq{\n}; my $g_ssh_server = '<10.10.10.10>'; # SSH server addresss my $g_username = '<username>'; my $g_password = '<password>'; my $g_prompt = '<ssh server prompt>'; my $g_telnet_cmd = 'telnet <10.10.10.10>'; # Command to telnet my $g_ssh = undef; my $g_channel = undef; # Connection opened with shell. SSH2Open(\$g_ssh, \$g_channel); # Separate commands are executed and read nicely. SSH2Send($g_ssh, $g_channel, "\n", $g_prompt); SSH2Send($g_ssh, $g_channel, 'ls -al', $g_prompt); SSH2Send($g_ssh, $g_channel, 'uname -a', $g_prompt); SSH2Send($g_ssh, $g_channel, 'ls -l', $g_prompt); # Starting telnet causes problems if command output is read SSH2Send($g_ssh, $g_channel, $g_telnet_cmd, '<expect username prom>'); SSH2Send($g_ssh, $g_channel, 'username', '<expect password prom>'); SSH2Send($g_ssh, $g_channel, 'passowrd', '<expect terminal prom>'); SSH2Send($g_ssh, $g_channel, 'ls -al', '<expect terminal prom>'); # Read to test commands without reading after each command. SSH2Read($g_ssh, $g_channel); sub SSH2Open{ my $ssh2 = shift; # OUT my $channel = shift; # OUT ${$ssh2} = Net::SSH2->new(); ${$ssh2}->connect($g_ssh_server) or die $!; ${$ssh2}->auth_password($g_username, $g_password) or die 'Auth. Fail +ed'; ${$channel} = ${$ssh2}->channel(); ${$channel}->pty('vt80'); # To get the prompt also. ${$channel}->shell(); return; } sub SSH2Send{ my $ssh = shift; # IN my $channel = shift; # IN - Created channel my $command = shift; # IN - Command to be sent my $waitfor = shift; # IN - String to be waited for. my $found = 0; my $string = q{}; my @result = (); print "===============================\n"; print "send command ($command)\n"; $channel->write(($command . NEWLINE)); if(0) { # Read response with method 1. This works randomly depending on # latency I quess. This may not read all of the data or last # command reads the data for every command. $string = q{}; while( <$channel> ) { $string = $string . $_; } } if(0) { # Read response with method 2. same as method 1. $string = q{}; my @result = readline($channel); $string = $string . join("\n", @result); } # Read response with method 3. Try polling until requested prompt # found. Based on http://search.cpan.org/~remi/Net-SSH2-Simple/. if(1) { my( $n, $buffer ); my @poll = ({handle => $channel, events => ['in', 'ext', 'err']}); $string = q{}; $channel->blocking(0); while(not $channel->eof and not $found) { $ssh->poll( 2000, [ @poll ] ); # 2 seconds print "poll" . Dumper \@poll; foreach my $poll ( @poll ) { foreach my $event ( qw( in ext ) ) { next unless $poll->{revents}{$event}; if($n = $channel->read( $buffer, 10_240, $event eq 'ext' )){ $string = $string . $buffer; #print "read string ($n) - $string\n"; } # Check if the requested data was read. $found = __test_waitfor($waitfor, $string); } } } } print "response: $string\n"; print "===============================\n"; return; } sub SSH2Read{ my $ssh = shift; # IN my $channel = shift; # IN my $string = q{}; if(0) { $string = q{}; while( <$channel> ) { $string = $string . $_; } print "string $string\n"; } return; } sub __test_waitfor{ my $waitfor = shift; # IN my $string = shift; # IN my $last_line = q{}; my $found = 0; # If user defined waitfor, it is checked from last line of the # string. Otherwise the string is just tested for couple of # characters. if( $waitfor ) { ($last_line) = $string =~ m/\n?(.*)\z/x; if( $last_line ) { print "last line ($last_line) wait ($waitfor)\n"; if( $last_line =~ m/$waitfor/ ) { $found = 1; } } } elsif( 5 < length $string ) { $found = 1; } return $found; }