Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask

Telnet over SSH2 from Windows

by Hessu (Friar)
on Mar 27, 2010 at 20:46 UTC ( #831401=perlquestion: print w/replies, xml ) Need Help??
Hessu has asked for the wisdom of the Perl Monks concerning the following question:


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 = '<>'; # SSH server addresss my $g_username = '<username>'; my $g_password = '<password>'; my $g_prompt = '<ssh server prompt>'; my $g_telnet_cmd = 'telnet <>'; # 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 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; }

Replies are listed 'Best First'.
Re: Telnet over SSH2 from Windows
by salva (Abbot) on Mar 27, 2010 at 21:59 UTC
    If tunnels are not forbidden in the intermediate host, you could use some SSH client (OpenSSH or plink from PuTTY) to create a tunnel from your local machine to the target device telnet port. Then, use Net::Telnet to talk to the remote machine through the tunnel.

    If tunnels are not enabled, you could run netcat or socat in the intermediate machine to obtain a similar effect. I believe that later versions of Perl for Windows already have an emulation of socketpair, so you would be able to do something like...

    Net::Telnet ===> socketpair ===> plink ===> netcat ===> destination h +ost/port \_______________ ____________/ \___ ___/ \/ \/ Local machine Intermediate machine

      Plink was nice suggestion because it worked. With command string below, I was able first to establish tunnel via SSH server to the device with telnet and command it with Net::Telnet. The plink command explanation can be found from plink documentation and it is also quite nicely explained in HOWTO ssh port-forwarding.

      I am still more than happy to hear explanations why reading during telnet session seems to break the write and if this is somehow doable from Windows.

      Thank You
      # From DOS prompt, give command below and leave the session open. # Version 0.60 from plink is needed because it contains the -nc # parameter. Version 0.58 did not have this parameter. The remote # host in here is the device IP address being access via telnet. # # plink.exe -L 1555:(remote host):(remote port) (ssh server) -l <user +name> -pw <password> -nc (remote host):(remote port) #!/usr/bin/perl -w use strict; use warnings; use Net::Telnet; use Data::Dumper; my $telnet = (); my @l_resp_a = (); $telnet = Net::Telnet->new( Timeout => 10, Host => 'localhost', # or Port => 1555 ); if( $telnet ){ print "connected\n"; $telnet->prompt('/<expected prompt>/'); $telnet->cmd( String => 'ls -al', Output => \@l_resp_a ); } else{ print "failed to connect"; }

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://831401]
Approved by ww
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (6)
As of 2018-06-23 20:08 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (125 votes). Check out past polls.