Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

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 avoiding work at the Monastery: (8)
As of 2018-05-24 14:47 GMT
Find Nodes?
    Voting Booth?