<?xml version="1.0" encoding="windows-1252"?>
<node id="831401" title="Telnet over SSH2 from Windows" created="2010-03-27 16:46:23" updated="2010-03-27 16:46:23">
<type id="115">
perlquestion</type>
<author id="733795">
Hessu</author>
<data>
<field name="doctext">
Hello,&lt;/p&gt;

&lt;p&gt;
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.&lt;/p&gt;

&lt;p&gt;
The problem is that after I issue 'telnet &amp;lt;IP Address&amp;gt;' 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.&lt;/p&gt;

&lt;p&gt;
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.&lt;/p&gt;

&lt;p&gt;
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-&gt;read() really is?
&lt;/p&gt;

Thank You

&lt;code&gt;
#!/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-&gt;debug(1);

use constant NEWLINE =&gt; qq{\n};

my $g_ssh_server  = '&lt;10.10.10.10&gt;';        # SSH server addresss
my $g_username    = '&lt;username&gt;';
my $g_password    = '&lt;password&gt;';
my $g_prompt      = '&lt;ssh server prompt&gt;';
my $g_telnet_cmd  = 'telnet &lt;10.10.10.10&gt;'; # 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, '&lt;expect username prom&gt;');
SSH2Send($g_ssh, $g_channel, 'username',    '&lt;expect password prom&gt;');
SSH2Send($g_ssh, $g_channel, 'passowrd',    '&lt;expect terminal prom&gt;');
SSH2Send($g_ssh, $g_channel, 'ls -al',      '&lt;expect terminal prom&gt;');

# 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-&gt;new();
  ${$ssh2}-&gt;connect($g_ssh_server) or die $!;
  ${$ssh2}-&gt;auth_password($g_username, $g_password) or die 'Auth. Failed';
  ${$channel} = ${$ssh2}-&gt;channel();
  ${$channel}-&gt;pty('vt80'); # To get the prompt also.
  ${$channel}-&gt;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-&gt;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( &lt;$channel&gt; ) {
      $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 =&gt; $channel, events =&gt; ['in', 'ext', 'err']});
    $string = q{};
    $channel-&gt;blocking(0);
    while(not $channel-&gt;eof and not $found) {
      $ssh-&gt;poll( 2000, [ @poll ] ); # 2 seconds
      print "poll" . Dumper \@poll;
      foreach my $poll ( @poll ) {
        foreach my $event ( qw( in ext ) ) {
          next unless $poll-&gt;{revents}{$event};
          if($n = $channel-&gt;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( &lt;$channel&gt; ) {
      $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 &lt; length $string ) {
    $found = 1;
  }

  return $found;
}
&lt;/code&gt;
</field>
</data>
</node>
