Softpanorama
(slightly skeptical) Open Source Software Educational Society

May the source be with you, but remember the KISS principle ;-)

Softpanorama Search

Using Perl to simulate Telnet from a Program

News See also  Telnet Protocol Recommended Links Expect Perl Expect.pm

Telnet Windows Clients

      Humor Etc

If you are running a monitoring application it is often useful to simulate a telnet connection from your program to a remote machine using telnet, then issues one or a couple of commands and process output to generate alerts or status report. 

the simplest way to accomplish this is to use Expect (or Perl Expect.pm) but you can use the CPAN module Net::Telnet too:

use Net::Telnet;

$t = Net::Telnet->new( Timeout => 10,
                       Prompt  => '/%/',
                       Host    => $hostname );

$t->login($username, $password);
@files = $t->cmd("ls");
$t->print("top");
(undef, $process_string) = $t->waitfor('/\d+ processes/');
$t->close;

Net::Telnet provides an interface to the telnet protocol. If has fake and generally unnecessary object-oriented flavor: you first need to create a connection with Net::Telnet->new, and then interact with the remote machine using method calls on the resulting object.

Give the new method named parameters, passed in hash-like form. We'll only cover only a few of many possible parameters. The most important is Host, the machine you're telnetting to. The default host is localhost. If you want to telnet to a port other than one telnet normally uses, specify this in the Port option. Error handling is done through the function whose reference is specified in the Errmode parameter.

Another important option is Prompt. When you log in or run a command, Net::Telnet uses the Prompt pattern to determine when the login or command has completed. The default Prompt is:

/[\$%#>] $/

which matches the common shell prompts. If the prompt on the remote machine doesn't match the default pattern, you have to specify your own. Remember to include the slashes.

Timeout lets you control how long (in seconds) network operations wait before they give up. The default is 10 seconds.

If an error or timeout occurs in the Net::Telnet module, the default behavior is to raise an exception, which, if uncaught, prints a message to STDERR and exits. To change this, pass a subroutine reference to new in the Errmode argument. If instead of a code subroutine, you specify the string "return" as the Errmode, methods return undef (in scalar context) or an empty list (in list context) on error, with the error message available via the errmsg method:

$telnet = Net::Telnet->new( Errmode => sub { main::log(@_) }, ... );

The login method is used to send a username and password to the remote machine. It uses the Prompt to decide when the login is complete and times out if the machine doesn't reply with a prompt:

$telnet->login($username, $password)
    or die "Login failed: @{[ $telnet->errmsg() ]}\n";

To run a program and gather its output, use the cmd method. Pass it the string to send, and it returns the output of the command. In list context, it returns one line per list element. In scalar context, it returns one long line. It waits for the Prompt before returning.

You can separate the sending of the command from the reception of its output with the print and waitfor methods, as we do in the Solution. The waitfor method takes either a single string containing a Perl regular expression match operator:

$telnet->waitfor('/--more--/')

or named arguments. Timeout lets you specify a timeout to override the default, Match is a string containing a match operator as above, and String is a literal string to find:

$telnet->waitfor(String => 'greasy smoke', Timeout => 30)

In scalar context, waitfor returns true if the pattern or string was found. If it is not found, the Errmode action is performed. In list context, it returns two strings: all the text before the match, and the text that matched.

See also the documentation for the Net::Telnet module from CPAN; RFCs 854-856, as amended by later RFCs

Old News ;-)

expect spawns telnet -- screen height

Hi,

I'm trying to login to a router using telnet and EXPECT, to backup the
router config.

When I'm connecting to the device via telnet, everything is ok, but when I'm
using a perl/expect-Script to connect, the router asks "Press any key to
continue", even before the login prompt.

*** via telnet commandline:

# telnet 1.2.3.4
Trying 1.2.3.4...
Connected to 1.2.3.4.
Escape character is '^]'.


One200

Username:


*** via script

# ./save-cpe.pl
Trying 1.2.3.4...
Connected to 1.2.3.4.
Escape character is '^]'.


One200

Press any key to continue (Q to quit)



This is the perl code I spawn telnet with:

$telnet = Expect->spawn('telnet', $ipaddr)
or error_exit(11, "....");

Looks like the router thinks the screen height is just a few lines?

Any ideas?

Thanks, 

Re: "expect" spawns telnet -- screen height
> This is the perl code I spawn telnet with:
>
> $telnet = Expect->spawn('telnet', $ipaddr)
> or error_exit(11, "....");
>
> Looks like the router thinks the screen height is just a few lines?

OK, I've found out that the following code works ok when run from a bash
shell:

my $telnet = new Expect;
$telnet->raw_pty(1);
if (defined ($telnet->slave)) {
$telnet->slave->clone_winsize_from(\*STDIN);
}

$telnet->spawn('telnet', $ipaddr)
or error_exit(11, ".....");

Unfortunately, I'm running this script from a PHP Page, NOT from a bash
shell... perl teminates without any hint when reaching the line
"$telnet->slave->clone_winsize_from(\*STDIN);".

Any Idea how to set the winsize to e.g. 80x25 without cloning it or where to
clone it from when run from a PHP script?

 
Re: "expect" spawns telnet -- screen height
My solution, based on Jens script, is not very elegant, but it works
from crontab (for example) :

open TTY,"/dev/console" or die "not connected to a terminal\n";
$telnet->slave->clone_winsize_from(\*TTY);
close TTY;

Daniel


--
dpratlong
 

Using perl to connect to remote hosts via telnet.

>Suppose you needed to open a connection to a remote host from within your perl program. One thing you would probably think of doing at first is the following:

 

open TELNET "|telnet $hostname"; print TELNET "$username\n"; print TELNET "$password\n"; ...

 

Unfortunately, if you try this, you'll find it doesn't work. The telnet program connects to the remote host but it completely ignores any commands you pipe to it. That's because the telnet program reads its input only from the terminal and not from standard input.

 

The most cunning among you might then think a workaround for this: Open a socket to port 23 (the default telnet port) of the remote machine and write directly to it, thus, bypassing the telnet program altogether. Well, although that might sound like a neat idea, it's not. And that's because the telnet protocol requires that certain control data be exchanged between the two machines by sending them along through the same socket connection.

 

So, it turns out that the problem is much more complicated than it seemed at first. We don't just need to write code to perform socket I/O, but also we need to write code that speaks the TELNET protocol. This is the bad news. The good news is that this code has already been written, and that its author was kind enough to bundle it in a useful module, Net::Telnet, available at CPAN.

Net::Telnet

 

Using Net::Telnet is pretty straightforward and simple. Let's first see a no-thrills example:

 

use Net::Telnet; $telnet = new Net::Telnet ( Timeout=>10, Errmode=>'die'); $telnet->open('camel.perlfect.com'); $telnet->waitfor('/login: $/i'); $telnet->print('bilbo'); $telnet->waitfor('/password: $/i'); $telnet->print('baggins'); $telnet->waitfor('/\$ $/i'); $telnet->print('who'); $output = $telnet->waitfor('/\$ $/i'); print $output;

 

This simple program connects to camel.perlfect.com with username and password, 'bilbo' and 'baggins' respectively, and the issues the command 'who' to get a list of logged in users. It then retrieves and prints the output. Although the code is self explanatory, here are a few things worth of noting:

 

 

Shortcuts

You might notice that issuing commands involves repeating print() and waitfor() calls in a very much similar manner. We print a command and then we try to match a shell prompt. Net::Telnet provides a very nice mode of operation that rids you of some of the repetitive tt. All you need to do is to specify the regular expression that matches a prompt as a parameter in the object's constructor and then use the cmd() method to issue commands. Similarly to command issuing, Net::Telnet provides a handy method to simplify the login process, namely login(). The following example demonstrates these shortcuts.

 

use Net::Telnet; $telnet = new Net::Telnet ( Timeout=>10, Errmode=>'die' Prompt => '/\$ $/i'); $telnet->open('camel.perlfect.com'); $telnet->login('bilbo', 'baggins'); print $telnet->cmd('who');

This does the same as the previous example, but with much less typing. Note that the login() method matches the login and password prompts with the regular expressions /(login|username)[: ]*$/i and /password[: ]*$/i respectively.

So far we have covered the basics of the Net::Telnet module, enough to get you going with your first telnetting scripts. The module is rich with other features, so make sure you take the time to have a look at the documentation.

Happy TELNETing!

Recommended Links

Simple Telnet Automation Using Expect

Using perl to connect to remote hosts via telnet.

Perl Cookbook Solutions and ... - Google Book Search

O'Reilly - Safari Books Online - 1565922433 - Perl Cookbook



Copyright © 1996-2009 by Dr. Nikolai Bezroukov. www.softpanorama.org was created as a service to the UN Sustainable Development Networking Programme (SDNP) in the author free time. Submit comments This document is an industrial compilation designed and created exclusively for educational use and is placed under the copyright of the Open Content License(OPL). Site uses AdSense so you need to be aware of Google privacy policy. Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.

Disclaimer:

Last modified: October 10, 2009