package Net::Telnet;
use strict;
require 5.002;
use vars qw(@EXPORT_OK);
@EXPORT_OK = qw(TELNET_IAC TELNET_DONT TELNET_DO TELNET_WONT TELNET_WILL
TELNET_SB TELNET_GA TELNET_EL TELNET_EC TELNET_AYT TELNET_AO
TELNET_IP TELNET_BREAK TELNET_DM TELNET_NOP TELNET_SE
TELNET_EOR TELNET_ABORT TELNET_SUSP TELNET_EOF TELNET_SYNCH
TELOPT_BINARY TELOPT_ECHO TELOPT_RCP TELOPT_SGA TELOPT_NAMS
TELOPT_STATUS TELOPT_TM TELOPT_RCTE TELOPT_NAOL TELOPT_NAOP
TELOPT_NAOCRD TELOPT_NAOHTS TELOPT_NAOHTD TELOPT_NAOFFD
TELOPT_NAOVTS TELOPT_NAOVTD TELOPT_NAOLFD TELOPT_XASCII
TELOPT_LOGOUT TELOPT_BM TELOPT_DET TELOPT_SUPDUP
TELOPT_SUPDUPOUTPUT TELOPT_SNDLOC TELOPT_TTYPE TELOPT_EOR
TELOPT_TUID TELOPT_OUTMRK TELOPT_TTYLOC TELOPT_3270REGIME
TELOPT_X3PAD TELOPT_NAWS TELOPT_TSPEED TELOPT_LFLOW
TELOPT_LINEMODE TELOPT_XDISPLOC TELOPT_OLD_ENVIRON
TELOPT_AUTHENTICATION TELOPT_ENCRYPT TELOPT_NEW_ENVIRON
TELOPT_EXOPL);
use Exporter ();
use Socket qw(AF_INET SOCK_STREAM inet_aton sockaddr_in);
use Symbol qw(qualify);
use vars qw(@ISA);
@ISA = qw(Exporter);
if (&_io_socket_include) { push @ISA, "IO::Socket::INET";
}
else { require FileHandle;
push @ISA, "FileHandle";
}
use vars qw($VERSION @Telopts);
$VERSION = "3.03";
@Telopts = ("BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME", "STATUS",
"TIMING MARK", "RCTE", "NAOL", "NAOP", "NAOCRD", "NAOHTS",
"NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD", "NAOLFD", "EXTEND ASCII",
"LOGOUT", "BYTE MACRO", "DATA ENTRY TERMINAL", "SUPDUP",
"SUPDUP OUTPUT", "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD",
"TACACS UID", "OUTPUT MARKING", "TTYLOC", "3270 REGIME", "X.3 PAD",
"NAWS", "TSPEED", "LFLOW", "LINEMODE", "XDISPLOC", "OLD-ENVIRON",
"AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON");
sub new {
my ($class) = @_;
my (
$errmode,
$fh_open,
$host,
$self,
%args,
);
local $_;
$self = $class->SUPER::new;
*$self->{net_telnet} = {
bin_mode => 0,
blksize => &_optimal_blksize(),
buf => "",
cmd_prompt => '/[\$%#>] $/',
cmd_rm_mode => "auto",
dumplog => '',
eofile => 1,
errormode => "die",
errormsg => "",
fdmask => '',
host => "localhost",
inputlog => '',
last_line => "",
last_prompt => "",
maxbufsize => 1_048_576,
num_wrote => 0,
ofs => "",
opened => '',
opt_cback => '',
opt_log => '',
opts => {},
ors => "\n",
outputlog => '',
pending_errormsg => "",
port => 23,
pushback_buf => "",
rs => "\n",
subopt_cback => '',
telnet_mode => 1,
time_out => 10,
timedout => '',
unsent_opts => "",
};
&_opt_accept($self,
{ option => &TELOPT_ECHO,
is_remote => 1,
is_enable => 1 },
{ option => &TELOPT_SGA,
is_remote => 1,
is_enable => 1 },
);
if (@_ == 2) { $host = $_[1];
}
elsif (@_ > 2) { (undef, %args) = @_;
foreach (keys %args) {
if (/^-?binmode$/i) {
$self->binmode($args{$_});
}
elsif (/^-?cmd_remove_mode$/i) {
$self->cmd_remove_mode($args{$_});
}
elsif (/^-?dump_log$/i) {
$self->dump_log($args{$_});
}
elsif (/^-?errmode$/i) {
$errmode = $args{$_};
}
elsif (/^-?fhopen$/i) {
$fh_open = $args{$_};
}
elsif (/^-?host$/i) {
$host = $args{$_};
}
elsif (/^-?input_log$/i) {
$self->input_log($args{$_});
}
elsif (/^-?input_record_separator$/i or /^-?rs$/i) {
$self->input_record_separator($args{$_});
}
elsif (/^-?option_log$/i) {
$self->option_log($args{$_});
}
elsif (/^-?output_log$/i) {
$self->output_log($args{$_});
}
elsif (/^-?output_record_separator$/i or /^-?ors$/i) {
$self->output_record_separator($args{$_});
}
elsif (/^-?port$/i) {
$self->port($args{$_});
}
elsif (/^-?prompt$/i) {
$self->prompt($args{$_});
}
elsif (/^-?telnetmode$/i) {
$self->telnetmode($args{$_});
}
elsif (/^-?timeout$/i) {
$self->timeout($args{$_});
}
else {
&_croak($self, "bad named parameter \"$_\" given " .
"to " . ref($self) . "::new()");
}
}
}
if (defined $errmode) { $self->errmode($errmode);
}
if (defined $fh_open) { $self->fhopen($fh_open)
or return;
}
elsif (defined $host) { $self->host($host);
$self->open
or return;
}
$self;
}
sub DESTROY {
}
sub binmode {
my ($self, $mode) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{bin_mode};
if (@_ >= 2) {
unless (defined $mode) {
$mode = 0;
}
$s->{bin_mode} = $mode;
}
$prev;
}
sub break {
my ($self) = @_;
my $s = *$self->{net_telnet};
my $break_cmd = "\xff\xf3";
$s->{timedout} = '';
&_put($self, \$break_cmd, "break");
}
sub buffer {
my ($self) = @_;
my $s = *$self->{net_telnet};
\$s->{buf};
}
sub buffer_empty {
my ($self) = @_;
my (
$buffer,
);
$buffer = $self->buffer;
$$buffer = "";
}
sub close {
my ($self) = @_;
my $s = *$self->{net_telnet};
$s->{eofile} = 1;
$s->{opened} = '';
close $self
if defined fileno($self);
1;
}
sub cmd {
my ($self, @args) = @_;
my (
$cmd_remove_mode,
$errmode,
$firstpos,
$last_prompt,
$lastpos,
$lines,
$ors,
$output,
$output_ref,
$prompt,
$remove_echo,
$rs,
$rs_len,
$s,
$telopt_echo,
$timeout,
%args,
);
my $cmd = "";
local $_;
$self->timed_out('');
$self->last_prompt("");
$s = *$self->{net_telnet};
$output = [];
$cmd_remove_mode = $self->cmd_remove_mode;
$errmode = $self->errmode;
$ors = $self->output_record_separator;
$prompt = $self->prompt;
$rs = $self->input_record_separator;
$timeout = $self->timeout;
if (@_ == 2) { $cmd = $_[1];
}
elsif (@_ > 2) { (undef, %args) = @_;
foreach (keys %args) {
if (/^-?cmd_remove/i) {
$cmd_remove_mode = &_parse_cmd_remove_mode($self, $args{$_});
}
elsif (/^-?errmode$/i) {
$errmode = &_parse_errmode($self, $args{$_});
}
elsif (/^-?input_record_separator$/i or /^-?rs$/i) {
$rs = &_parse_input_record_separator($self, $args{$_});
}
elsif (/^-?output$/i) {
$output_ref = $args{$_};
if (defined($output_ref) and ref($output_ref) eq "ARRAY") {
$output = $output_ref;
}
}
elsif (/^-?output_record_separator$/i or /^-?ors$/i) {
$ors = $self->output_record_separator($args{$_});
}
elsif (/^-?prompt$/i) {
$prompt = &_parse_prompt($self, $args{$_});
}
elsif (/^-?string$/i) {
$cmd = $args{$_};
}
elsif (/^-?timeout$/i) {
$timeout = &_parse_timeout($self, $args{$_});
}
else {
&_croak($self, "bad named parameter \"$_\" given " .
"to " . ref($self) . "::cmd()");
}
}
}
local $s->{errormode} = "return";
local $s->{time_out} = &_endtime($timeout);
$self->errmsg("");
$self->put($cmd . $ors)
and ($lines, $last_prompt) = $self->waitfor($prompt);
$s->{errormode} = $errmode;
return $self->error("command timed-out") if $self->timed_out;
return $self->error($self->errmsg) if $self->errmsg ne "";
$self->last_prompt($last_prompt);
$firstpos = 0;
$rs_len = length $rs;
while (($lastpos = index($lines, $rs, $firstpos)) > -1) {
push(@$output,
substr($lines, $firstpos, $lastpos - $firstpos + $rs_len));
$firstpos = $lastpos + $rs_len;
}
if ($firstpos < length $lines) {
push @$output, substr($lines, $firstpos);
}
if ($cmd_remove_mode eq "auto") {
$telopt_echo = $self->option_state(&TELOPT_ECHO);
$remove_echo = $telopt_echo->{remote_enabled};
}
else { $remove_echo = $cmd_remove_mode;
}
while ($remove_echo--) {
shift @$output;
}
unless (@$output) {
@$output = ("");
}
if (defined $output_ref) {
if (ref($output_ref) eq "SCALAR") {
$$output_ref = join "", @$output;
}
elsif (ref($output_ref) eq "HASH") {
%$output_ref = @$output;
}
}
wantarray ? @$output : 1;
}
sub cmd_remove_mode {
my ($self, $mode) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{cmd_rm_mode};
if (@_ >= 2) {
$s->{cmd_rm_mode} = &_parse_cmd_remove_mode($self, $mode);
}
$prev;
}
sub dump_log {
my ($self, $name) = @_;
my (
$fh,
$s,
);
$s = *$self->{net_telnet};
$fh = $s->{dumplog};
if (@_ >= 2) {
unless (defined $name) {
$name = "";
}
$fh = &_fname_to_handle($self, $name)
or return;
$s->{dumplog} = $fh;
}
$fh;
}
sub eof {
my ($self) = @_;
*$self->{net_telnet}{eofile};
}
sub errmode {
my ($self, $mode) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{errormode};
if (@_ >= 2) {
$s->{errormode} = &_parse_errmode($self, $mode);
}
$prev;
}
sub errmsg {
my ($self, @errmsgs) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{errormsg};
if (@_ >= 2) {
$s->{errormsg} = join "", @errmsgs;
}
$prev;
}
sub error {
my ($self, @errmsg) = @_;
my (
$errmsg,
$func,
$mode,
$s,
@args,
);
local $_;
$s = *$self->{net_telnet};
if (@_ >= 2) {
$errmsg = join "", @errmsg;
$s->{errormsg} = $errmsg;
$mode = $s->{errormode};
if (ref($mode) eq "CODE") {
&$mode($errmsg);
return;
}
elsif (ref($mode) eq "ARRAY") {
($func, @args) = @$mode;
&$func(@args);
return;
}
elsif ($mode =~ /^return$/i) {
return;
}
else { if ($errmsg =~ /\n$/) {
die $errmsg;
}
else {
&_croak($self, $errmsg);
}
}
}
else {
return $s->{errormsg} ne "";
}
}
sub fhopen {
my ($self, $fh) = @_;
my (
$globref,
$s,
);
$globref = &_qualify_fh($self, $fh);
return $self->error("fhopen filehandle isn't already open")
unless defined($globref) and defined(fileno $globref);
$self->close;
$s = *$self->{net_telnet};
*$self = *$globref;
*$self->{net_telnet} = $s;
select((select($self), $|=1)[$[]); $s = *$self->{net_telnet};
$s->{blksize} = &_optimal_blksize((stat $self)[11]);
$s->{buf} = "";
$s->{eofile} = '';
$s->{errormsg} = "";
vec($s->{fdmask}='', fileno($self), 1) = 1;
$s->{host} = "";
$s->{last_line} = "";
$s->{last_prompt} = "";
$s->{num_wrote} = 0;
$s->{opened} = 1;
$s->{pending_errormsg} = "";
$s->{port} = '';
$s->{pushback_buf} = "";
$s->{timedout} = '';
$s->{unsent_opts} = "";
&_reset_options($s->{opts});
1;
}
sub get {
my ($self, %args) = @_;
my (
$binmode,
$endtime,
$errmode,
$line,
$s,
$telnetmode,
$timeout,
);
local $_;
$s = *$self->{net_telnet};
$timeout = $s->{time_out};
$s->{timedout} = '';
return if $s->{eofile};
foreach (keys %args) {
if (/^-?binmode$/i) {
$binmode = $args{$_};
unless (defined $binmode) {
$binmode = 0;
}
}
elsif (/^-?errmode$/i) {
$errmode = &_parse_errmode($self, $args{$_});
}
elsif (/^-?telnetmode$/i) {
$telnetmode = $args{$_};
unless (defined $telnetmode) {
$telnetmode = 0;
}
}
elsif (/^-?timeout$/i) {
$timeout = &_parse_timeout($self, $args{$_});
}
else {
&_croak($self, "bad named parameter \"$_\" given " .
"to " . ref($self) . "::get()");
}
}
local $s->{errormode} = $errmode
if defined $errmode;
local $s->{bin_mode} = $binmode
if defined $binmode;
local $s->{telnet_mode} = $telnetmode
if defined $telnetmode;
$endtime = &_endtime($timeout);
if (length $s->{unsent_opts}) {
&_flush_opts($self);
}
{
local $s->{errormode} = "return";
$s->{errormsg} = "";
&_fillbuf($self, $s, 0);
}
return $self->error($s->{errormsg})
if ($s->{timedout} and defined($timeout) and $timeout == 0
and !length $s->{buf});
if ($s->{errormsg} and !$s->{timedout}) {
if (!length $s->{buf}) {
return $self->error($s->{errormsg});
}
else { $s->{pending_errormsg} = $s->{errormsg};
}
}
$s->{timedout} = '';
$s->{errormsg} = "";
if (!length $s->{buf}) {
&_fillbuf($self, $s, $endtime)
or do {
return if $s->{timedout};
$self->close;
return;
};
}
$line = $s->{buf};
$s->{buf} = "";
$line;
}
sub getline {
my ($self, %args) = @_;
my (
$binmode,
$endtime,
$errmode,
$len,
$line,
$offset,
$pos,
$rs,
$s,
$telnetmode,
$timeout,
);
local $_;
$s = *$self->{net_telnet};
$s->{timedout} = '';
return if $s->{eofile};
$rs = $s->{rs};
$timeout = $s->{time_out};
foreach (keys %args) {
if (/^-?binmode$/i) {
$binmode = $args{$_};
unless (defined $binmode) {
$binmode = 0;
}
}
elsif (/^-?errmode$/i) {
$errmode = &_parse_errmode($self, $args{$_});
}
elsif (/^-?input_record_separator$/i or /^-?rs$/i) {
$rs = &_parse_input_record_separator($self, $args{$_});
}
elsif (/^-?telnetmode$/i) {
$telnetmode = $args{$_};
unless (defined $telnetmode) {
$telnetmode = 0;
}
}
elsif (/^-?timeout$/i) {
$timeout = &_parse_timeout($self, $args{$_});
}
else {
&_croak($self, "bad named parameter \"$_\" given " .
"to " . ref($self) . "::getline()");
}
}
local $s->{bin_mode} = $binmode
if defined $binmode;
local $s->{errormode} = $errmode
if defined $errmode;
local $s->{telnet_mode} = $telnetmode
if defined $telnetmode;
$endtime = &_endtime($timeout);
if (length $s->{unsent_opts}) {
&_flush_opts($self);
}
$offset = 0;
while (($pos = index($s->{buf}, $rs, $offset)) == -1) {
$offset = length $s->{buf};
&_fillbuf($self, $s, $endtime)
or do {
return if $s->{timedout};
$self->close;
if (length $s->{buf}) {
return $s->{buf};
}
else {
return;
}
};
}
$len = $pos + length $rs;
$line = substr($s->{buf}, 0, $len);
substr($s->{buf}, 0, $len) = "";
$line;
}
sub getlines {
my ($self, %args) = @_;
my (
$binmode,
$errmode,
$line,
$rs,
$s,
$telnetmode,
$timeout,
);
my $all = 1;
my @lines = ();
local $_;
$s = *$self->{net_telnet};
$s->{timedout} = '';
return if $s->{eofile};
$timeout = $s->{time_out};
foreach (keys %args) {
if (/^-?all$/i) {
$all = $args{$_};
unless (defined $all) {
$all = '';
}
}
elsif (/^-?binmode$/i) {
$binmode = $args{$_};
unless (defined $binmode) {
$binmode = 0;
}
}
elsif (/^-?errmode$/i) {
$errmode = &_parse_errmode($self, $args{$_});
}
elsif (/^-?input_record_separator$/i or /^-?rs$/i) {
$rs = &_parse_input_record_separator($self, $args{$_});
}
elsif (/^-?telnetmode$/i) {
$telnetmode = $args{$_};
unless (defined $telnetmode) {
$telnetmode = 0;
}
}
elsif (/^-?timeout$/i) {
$timeout = &_parse_timeout($self, $args{$_});
}
else {
&_croak($self, "bad named parameter \"$_\" given " .
"to " . ref($self) . "::getlines()");
}
}
local $s->{bin_mode} = $binmode
if defined $binmode;
local $s->{errormode} = $errmode
if defined $errmode;
local $s->{rs} = $rs
if defined $rs;
local $s->{telnet_mode} = $telnetmode
if defined $telnetmode;
local $s->{time_out} = &_endtime($timeout);
if (! $all) {
return &_next_getlines($self, $s);
}
while (1) {
$line = $self->getline
or last;
push @lines, $line;
}
return if ! $self->eof;
@lines;
}
sub host {
my ($self, $host) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{host};
if (@_ >= 2) {
unless (defined $host) {
$host = "";
}
$s->{host} = $host;
}
$prev;
}
sub input_log {
my ($self, $name) = @_;
my (
$fh,
$s,
);
$s = *$self->{net_telnet};
$fh = $s->{inputlog};
if (@_ >= 2) {
unless (defined $name) {
$name = "";
}
$fh = &_fname_to_handle($self, $name)
or return;
$s->{inputlog} = $fh;
}
$fh;
}
sub input_record_separator {
my ($self, $rs) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{rs};
if (@_ >= 2) {
$s->{rs} = &_parse_input_record_separator($self, $rs);
}
$prev;
}
sub last_prompt {
my ($self, $string) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{last_prompt};
if (@_ >= 2) {
unless (defined $string) {
$string = "";
}
$s->{last_prompt} = $string;
}
$prev;
}
sub lastline {
my ($self, $line) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{last_line};
if (@_ >= 2) {
unless (defined $line) {
$line = "";
}
$s->{last_line} = $line;
}
$prev;
}
sub login {
my ($self) = @_;
my (
$errmode,
$error,
$is_passwd_arg,
$is_username_arg,
$lastline,
$match,
$ors,
$passwd,
$prematch,
$prompt,
$s,
$timeout,
$username,
%args,
);
local $_;
$self->timed_out('');
$self->last_prompt("");
$s = *$self->{net_telnet};
$timeout = $self->timeout;
$ors = $self->output_record_separator;
$prompt = $self->prompt;
if (@_ == 3) { $username = $_[1];
$passwd = $_[2];
$is_username_arg = 1;
$is_passwd_arg = 1;
}
else { (undef, %args) = @_;
foreach (keys %args) {
if (/^-?errmode$/i) {
$errmode = &_parse_errmode($self, $args{$_});
}
elsif (/^-?name$/i) {
$username = $args{$_};
unless (defined $username) {
$username = "";
}
$is_username_arg = 1;
}
elsif (/^-?pass/i) {
$passwd = $args{$_};
unless (defined $passwd) {
$passwd = "";
}
$is_passwd_arg = 1;
}
elsif (/^-?prompt$/i) {
$prompt = &_parse_prompt($self, $args{$_});
}
elsif (/^-?timeout$/i) {
$timeout = &_parse_timeout($self, $args{$_});
}
else {
&_croak($self, "bad named parameter \"$_\" given ",
"to " . ref($self) . "::login()");
}
}
}
&_croak($self,"Name argument not given to " . ref($self) . "::login()")
unless $is_username_arg;
&_croak($self,"Password argument not given to " . ref($self) . "::login()")
unless $is_passwd_arg;
local $s->{errormode} = $errmode
if defined $errmode;
local $s->{time_out} = &_endtime($timeout);
$error
= sub {
my ($errmsg) = @_;
if ($self->timed_out) {
return $self->error($errmsg);
}
elsif ($self->eof) {
($lastline = $self->lastline) =~ s/\n+//;
return $self->error($errmsg, ": ", $lastline);
}
else {
return $self->error($self->errmsg);
}
};
return $self->error("login failed: filehandle isn't open")
if $self->eof;
$self->waitfor(Match => '/login[: ]*$/i',
Match => '/username[: ]*$/i',
Errmode => "return")
or do {
return &$error("eof read waiting for login prompt")
if $self->eof;
return &$error("timed-out waiting for login prompt");
};
&_sleep(0.01);
$self->put(String => $username . $ors,
Errmode => "return")
or return &$error("login disconnected");
$self->waitfor(Match => '/password[: ]*$/i',
Errmode => "return")
or do {
return &$error("eof read waiting for password prompt")
if $self->eof;
return &$error("timed-out waiting for password prompt");
};
&_sleep(0.01);
$self->put(String => $passwd . $ors,
Errmode => "return")
or return &$error("login disconnected");
($prematch, $match) = $self->waitfor(Match => '/login[: ]*$/i',
Match => '/username[: ]*$/i',
Match => $prompt,
Errmode => "return")
or do {
return &$error("eof read waiting for command prompt")
if $self->eof;
return &$error("timed-out waiting for command prompt");
};
return $self->error("login failed: bad name or password")
if $match =~ /login[: ]*$/i or $match =~ /username[: ]*$/i;
$self->last_prompt($match);
1;
}
sub max_buffer_length {
my ($self, $maxbufsize) = @_;
my (
$prev,
$s,
);
my $minbufsize = 512;
$s = *$self->{net_telnet};
$prev = $s->{maxbufsize};
if (@_ >= 2) {
unless (defined $maxbufsize
and $maxbufsize =~ /^\d+$/
and $maxbufsize)
{
&_carp($self, "ignoring bad Max_buffer_length " .
"argument \"$maxbufsize\": it's not a positive integer");
$maxbufsize = $prev;
}
if ($maxbufsize < $minbufsize) {
$maxbufsize = $minbufsize;
}
$s->{maxbufsize} = $maxbufsize;
}
$prev;
}
*ofs = \&output_field_separator;
sub open {
my ($self) = @_;
my (
$errmode,
$errno,
$host,
$ip_addr,
$port,
$s,
$timeout,
%args,
);
local $_;
$s = *$self->{net_telnet};
$timeout = $s->{time_out};
$s->{timedout} = '';
if (@_ == 2) { $self->host($_[1]);
}
elsif (@_ > 2) { (undef, %args) = @_;
foreach (keys %args) {
if (/^-?errmode$/i) {
$errmode = &_parse_errmode($self, $args{$_});
}
elsif (/^-?host$/i) {
$self->host($args{$_});
}
elsif (/^-?port$/i) {
$self->port($args{$_})
or return;
}
elsif (/^-?timeout$/i) {
$timeout = &_parse_timeout($self, $args{$_});
}
else {
&_croak($self, "bad named parameter \"$_\" given ",
"to " . ref($self) . "::open()");
}
}
}
local $s->{errormode} = $errmode
if defined $errmode;
$host = $self->host;
$port = $self->port;
$self->close;
if (defined($timeout) and &_have_alarm) { if ($timeout >= $^T) { $timeout = $timeout - time;
}
if ($timeout < 1) {
$timeout = 1;
}
$timeout = int($timeout + 1.5);
eval {
local $SIG{"__DIE__"} = "DEFAULT";
local $SIG{ALRM} = sub { die "timed-out\n" };
alarm $timeout;
$ip_addr = inet_aton $host
or die "unknown remote host: $host\n";
socket $self, AF_INET, SOCK_STREAM, 0
or die "problem creating socket: $!\n";
connect $self, sockaddr_in($port, $ip_addr)
or die "problem connecting to \"$host\", port $port: $!\n";
};
alarm 0;
if ($@ =~ /^timed-out$/) { $s->{timedout} = 1;
$self->close;
if (!$ip_addr) {
return $self->error("unknown remote host: $host: ",
"name lookup timed-out");
}
else {
return $self->error("problem connecting to \"$host\", ",
"port $port: connect timed-out");
}
}
elsif ($@) { $self->close;
chomp $@;
return $self->error($@);
}
}
else { $timeout = undef;
$ip_addr = inet_aton $host
or return $self->error("unknown remote host: $host");
socket $self, AF_INET, SOCK_STREAM, 0
or return $self->error("problem creating socket: $!");
connect $self, sockaddr_in($port, $ip_addr)
or do {
$errno = "$!";
$self->close;
return $self->error("problem connecting to \"$host\", ",
"port $port: $errno");
};
}
select((select($self), $|=1)[$[]); $s->{blksize} = &_optimal_blksize((stat $self)[11]);
$s->{buf} = "";
$s->{eofile} = '';
$s->{errormsg} = "";
vec($s->{fdmask}='', fileno($self), 1) = 1;
$s->{last_line} = "";
$s->{num_wrote} = 0;
$s->{opened} = 1;
$s->{pending_errormsg} = "";
$s->{pushback_buf} = "";
$s->{timedout} = '';
$s->{unsent_opts} = "";
&_reset_options($s->{opts});
1;
}
sub option_accept {
my ($self, @args) = @_;
my (
$arg,
$option,
$s,
@opt_args,
);
local $_;
$s = *$self->{net_telnet};
while (($_, $arg) = splice @args, 0, 2) {
if (/^-?do$/i) {
return $self->error("usage: an option callback must already ",
"be defined when enabling with $_")
unless $s->{opt_cback};
$option = &_verify_telopt_arg($self, $arg, $_);
return unless defined $option;
push @opt_args, { option => $option,
is_remote => '',
is_enable => 1,
};
}
elsif (/^-?dont$/i) {
$option = &_verify_telopt_arg($self, $arg, $_);
return unless defined $option;
push @opt_args, { option => $option,
is_remote => '',
is_enable => '',
};
}
elsif (/^-?will$/i) {
return $self->error("usage: an option callback must already ",
"be defined when enabling with $_")
unless $s->{opt_cback};
$option = &_verify_telopt_arg($self, $arg, $_);
return unless defined $option;
push @opt_args, { option => $option,
is_remote => 1,
is_enable => 1,
};
}
elsif (/^-?wont$/i) {
$option = &_verify_telopt_arg($self, $arg, $_);
return unless defined $option;
push @opt_args, { option => $option,
is_remote => 1,
is_enable => '',
};
}
else {
return $self->error('usage: $obj->option_accept(' .
'[Do => $telopt,] ',
'[Dont => $telopt,] ',
'[Will => $telopt,] ',
'[Wont => $telopt,]');
}
}
&_opt_accept($self, @opt_args);
}
sub option_callback {
my ($self, $callback) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{opt_cback};
if (@_ >= 2) {
unless (defined $callback and ref($callback) eq "CODE") {
&_carp($self, "ignoring Option_callback argument because it's " .
"not a code ref");
$callback = $prev;
}
$s->{opt_cback} = $callback;
}
$prev;
}
sub option_log {
my ($self, $name) = @_;
my (
$fh,
$s,
);
$s = *$self->{net_telnet};
$fh = $s->{opt_log};
if (@_ >= 2) {
unless (defined $name) {
$name = "";
}
$fh = &_fname_to_handle($self, $name)
or return;
$s->{opt_log} = $fh;
}
$fh;
}
sub option_state {
my ($self, $option) = @_;
my (
$opt_state,
$s,
%opt_state,
);
$option = &_verify_telopt_arg($self, $option);
return unless defined $option;
$s = *$self->{net_telnet};
unless (defined $s->{opts}{$option}) {
&_set_default_option($s, $option);
}
$opt_state = $s->{opts}{$option};
%opt_state = %$opt_state;
\%opt_state;
}
*ors = \&output_record_separator;
sub output_field_separator {
my ($self, $ofs) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{ofs};
if (@_ >= 2) {
unless (defined $ofs) {
$ofs = "";
}
$s->{ofs} = $ofs;
}
$prev;
}
sub output_log {
my ($self, $name) = @_;
my (
$fh,
$s,
);
$s = *$self->{net_telnet};
$fh = $s->{outputlog};
if (@_ >= 2) {
unless (defined $name) {
$name = "";
}
$fh = &_fname_to_handle($self, $name)
or return;
$s->{outputlog} = $fh;
}
$fh;
}
sub output_record_separator {
my ($self, $ors) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{ors};
if (@_ >= 2) {
unless (defined $ors) {
$ors = "";
}
$s->{ors} = $ors;
}
$prev;
}
sub port {
my ($self, $port) = @_;
my (
$prev,
$s,
$service,
);
$s = *$self->{net_telnet};
$prev = $s->{port};
if (@_ >= 2) {
unless (defined $port) {
$port = "";
}
if (!$port) {
&_carp($self, "ignoring bad Port argument \"$port\"");
$port = $prev;
}
elsif ($port !~ /^\d+$/) { $service = $port;
$port = getservbyname($service, "tcp");
unless ($port) {
&_carp($self, "ignoring bad Port argument \"$service\": " .
"it's an unknown TCP service");
$port = $prev;
}
}
$s->{port} = $port;
}
$prev;
}
sub print {
my ($self) = shift;
my (
$buf,
$fh,
$s,
);
$s = *$self->{net_telnet};
$s->{timedout} = '';
return $self->error("write error: filehandle isn't open")
unless $s->{opened};
$buf = join($s->{ofs}, @_) . $s->{ors};
if ($s->{outputlog}) {
&_log_print($s->{outputlog}, $buf);
}
if (!$s->{bin_mode}) {
$buf =~ s(\n)(\015\012)g;
}
if ($s->{telnet_mode}) {
$buf =~ s(\377)(\377\377)g;
&_escape_cr(\$buf);
}
&_put($self, \$buf, "print");
}
sub print_length {
my ($self) = @_;
*$self->{net_telnet}{num_wrote};
}
sub prompt {
my ($self, $prompt) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{cmd_prompt};
if (@_ == 2) {
$s->{cmd_prompt} = &_parse_prompt($self, $prompt);
}
$prev;
}
sub put {
my ($self) = @_;
my (
$binmode,
$buf,
$errmode,
$is_timeout_arg,
$s,
$telnetmode,
$timeout,
%args,
);
local $_;
$s = *$self->{net_telnet};
$s->{timedout} = '';
if (@_ == 2) { $buf = $_[1];
}
elsif (@_ > 2) { (undef, %args) = @_;
foreach (keys %args) {
if (/^-?binmode$/i) {
$binmode = $args{$_};
unless (defined $binmode) {
$binmode = 0;
}
}
elsif (/^-?errmode$/i) {
$errmode = &_parse_errmode($self, $args{$_});
}
elsif (/^-?string$/i) {
$buf = $args{$_};
}
elsif (/^-?telnetmode$/i) {
$telnetmode = $args{$_};
unless (defined $telnetmode) {
$telnetmode = 0;
}
}
elsif (/^-?timeout$/i) {
$timeout = &_parse_timeout($self, $args{$_});
$is_timeout_arg = 1;
}
else {
&_croak($self, "bad named parameter \"$_\" given ",
"to " . ref($self) . "::put()");
}
}
}
local $s->{bin_mode} = $binmode
if defined $binmode;
local $s->{errormode} = $errmode
if defined $errmode;
local $s->{telnet_mode} = $telnetmode
if defined $telnetmode;
local $s->{time_out} = $timeout
if defined $is_timeout_arg;
return $self->error("write error: filehandle isn't open")
unless $s->{opened};
if ($s->{outputlog}) {
&_log_print($s->{outputlog}, $buf);
}
if (!$s->{bin_mode}) {
$buf =~ s(\n)(\015\012)g;
}
if ($s->{telnet_mode}) {
$buf =~ s(\377)(\377\377)g;
&_escape_cr(\$buf);
}
&_put($self, \$buf, "print");
}
*rs = \&input_record_separator;
sub suboption_callback {
my ($self, $callback) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{subopt_cback};
if (@_ >= 2) {
unless (defined $callback and ref($callback) eq "CODE") {
&_carp($self,"ignoring Suboption_callback argument because it's " .
"not a code ref");
$callback = $prev;
}
$s->{subopt_cback} = $callback;
}
$prev;
}
sub telnetmode {
my ($self, $mode) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{telnet_mode};
if (@_ >= 2) {
unless (defined $mode) {
$mode = 0;
}
$s->{telnet_mode} = $mode;
}
$prev;
}
sub timed_out {
my ($self, $value) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{timedout};
if (@_ >= 2) {
unless (defined $value) {
$value = "";
}
$s->{timedout} = $value;
}
$prev;
}
sub timeout {
my ($self, $timeout) = @_;
my (
$prev,
$s,
);
$s = *$self->{net_telnet};
$prev = $s->{time_out};
if (@_ >= 2) {
$s->{time_out} = &_parse_timeout($self, $timeout);
}
$prev;
}
sub waitfor {
my ($self, @args) = @_;
my (
$arg,
$binmode,
$endtime,
$errmode,
$len,
$match,
$match_op,
$pos,
$prematch,
$s,
$search,
$search_cond,
$telnetmode,
$timeout,
@match_cond,
@match_ops,
@search_cond,
@string_cond,
@warns,
);
local $_;
$s = *$self->{net_telnet};
$s->{timedout} = '';
return if $s->{eofile};
return unless @args;
$timeout = $s->{time_out};
@string_cond =
('if (($pos = index $s->{buf}, ', ') > -1) {
$len = ', ';
$prematch = substr $s->{buf}, 0, $pos;
$match = substr $s->{buf}, $pos, $len;
substr($s->{buf}, 0, $pos + $len) = "";
last;
}');
@match_cond =
('if ($s->{buf} =~ ', ') {
$prematch = $`;
$match = $&;
substr($s->{buf}, 0, length($`) + length($&)) = "";
last;
}');
if (@_ == 2) { $arg = $_[1];
push @match_ops, $arg;
push @search_cond, join("", $match_cond[0], $arg, $match_cond[1]);
}
elsif (@_ > 2) { while (($_, $arg) = splice @args, 0, 2) {
if (/^-?binmode$/i) {
$binmode = $arg;
unless (defined $binmode) {
$binmode = 0;
}
}
elsif (/^-?errmode$/i) {
$errmode = &_parse_errmode($self, $arg);
}
elsif (/^-?match$/i) {
push @match_ops, $arg;
push @search_cond, join("",
$match_cond[0], $arg, $match_cond[1]);
}
elsif (/^-?string$/i) {
$arg =~ s/'/\\'/g; push @search_cond, join("",
$string_cond[0], "'$arg'",
$string_cond[1], length($arg),
$string_cond[2]);
}
elsif (/^-?telnetmode$/i) {
$telnetmode = $arg;
unless (defined $telnetmode) {
$telnetmode = 0;
}
}
elsif (/^-?timeout$/i) {
$timeout = &_parse_timeout($self, $arg);
}
else {
&_croak($self, "bad named parameter \"$_\" given " .
"to " . ref($self) . "::waitfor()");
}
}
}
local $s->{errormode} = $errmode
if defined $errmode;
local $s->{bin_mode} = $binmode
if defined $binmode;
local $s->{telnet_mode} = $telnetmode
if defined $telnetmode;
foreach $match_op (@match_ops) {
return $self->error("missing opening delimiter of match operator ",
"in argument \"$match_op\" given to ",
ref($self) . "::waitfor()")
unless $match_op =~ m(^\s*/) or $match_op =~ m(^\s*m\s*\W);
}
$search_cond = join "\n\tels", @search_cond;
$search = join "", "
while (1) {\n\t",
$search_cond, '
&_fillbuf($self, $s, $endtime)
or do {
last if $s->{timedout};
$self->close;
last;
};
}';
$endtime = &_endtime($timeout);
{
local $^W = 1;
local $SIG{"__WARN__"} = sub { push @warns, @_ };
local $s->{errormode} = "return";
$s->{errormsg} = "";
eval $search;
}
return $self->error("pattern match timed-out") if $s->{timedout};
return $self->error($s->{errormsg}) if $s->{errormsg} ne "";
return $self->error("pattern match read eof") if $s->{eofile};
if ($@ or @warns) {
foreach $match_op (@match_ops) {
&_match_check($self, $match_op)
or return;
}
return $self->error($@) if $@;
return $self->error(@warns) if @warns;
}
wantarray ? ($prematch, $match) : 1;
}
sub _append_lineno {
my ($obj, @msgs) = @_;
my (
$file,
$line,
$pkg,
);
($pkg, $file , $line) = &_user_caller($obj);
join("", @msgs, " at ", $file, " line ", $line, "\n");
}
sub _carp {
warn &_append_lineno(@_);
}
sub _croak {
die &_append_lineno(@_);
}
sub _endtime {
my ($interval) = @_;
if (defined $interval) {
if ($interval >= $^T) { return $interval;
}
elsif ($interval > 0) { return int(time + 1.5 + $interval);
}
else { return 0;
}
}
else { return undef;
}
}
sub _escape_cr {
my ($string) = @_;
my (
$nextchar,
);
my $pos = 0;
while (($pos = index($$string, "\015", $pos)) > -1) {
$nextchar = substr $$string, $pos + 1, 1;
substr($$string, $pos, 1) = "\015\000"
unless $nextchar eq "\012";
$pos++;
}
1;
}
sub _fillbuf {
my ($self, $s, $endtime) = @_;
my (
$msg,
$nfound,
$nread,
$pushback_len,
$read_pos,
$ready,
$timed_out,
$timeout,
$unparsed_pos,
);
if ($s->{pending_errormsg}) {
$msg = $s->{pending_errormsg};
$s->{pending_errormsg} = "";
return $self->error($msg);
}
return unless $s->{opened};
while (1) {
return $self->error("maximum input buffer length exceeded: ",
$s->{maxbufsize}, " bytes")
unless length($s->{buf}) <= $s->{maxbufsize};
($timed_out, $timeout) = &_timeout_interval($endtime);
if ($timed_out) {
$s->{timedout} = 1;
return $self->error("read timed-out");
}
$nfound = select $ready=$s->{fdmask}, "", "", $timeout;
if (!defined $nfound or $nfound <= 0) { if (defined $nfound and $nfound == 0) { $s->{timedout} = 1;
return $self->error("read timed-out");
}
else { next if $! =~ /^interrupted/i;
$s->{opened} = '';
return $self->error("read error: $!");
}
}
$pushback_len = length $s->{pushback_buf};
if ($pushback_len) {
$s->{buf} .= $s->{pushback_buf};
$s->{pushback_buf} = "";
}
$read_pos = length $s->{buf};
$unparsed_pos = $read_pos - $pushback_len;
$nread = sysread $self, $s->{buf}, $s->{blksize}, $read_pos;
if (!defined $nread) { next if $! =~ /^interrupted/i;
$s->{opened} = '';
return $self->error("read error: $!");
}
if ($nread == 0) { $s->{opened} = '';
return;
}
if ($s->{dumplog}) {
&_log_dump('<', $s->{dumplog}, \$s->{buf}, $read_pos);
}
if ($s->{telnet_mode} and index($s->{buf},"\377",$unparsed_pos) > -1) {
&_interpret_tcmd($self, $s, $unparsed_pos);
}
&_interpret_cr($s, $unparsed_pos);
next if $unparsed_pos >= length $s->{buf};
if ($s->{inputlog}) {
&_log_print($s->{inputlog}, substr($s->{buf}, $unparsed_pos));
}
&_save_lastline($s);
last;
}
1;
}
sub _flush_opts {
my ($self) = @_;
my (
$option_chars,
);
my $s = *$self->{net_telnet};
$option_chars = $s->{unsent_opts};
$s->{unsent_opts} = "";
{
local $s->{errormode} = "return";
local $s->{time_out} = 0;
&_put($self, \$option_chars, "telnet option negotiation")
or do {
substr($option_chars, 0, $self->print_length) = "";
$s->{unsent_opts} .= $option_chars;
};
}
1;
}
sub _fname_to_handle {
my ($self, $fh) = @_;
my (
$filename,
);
return ""
unless defined $fh and (ref $fh or length $fh);
no strict "refs";
if (!ref($fh) and !defined(fileno $fh)) { $filename = $fh;
$fh = &_new_handle();
CORE::open $fh, "> $filename"
or return $self->error("problem creating $filename: $!");
}
select((select($fh), $|=1)[$[]); $fh;
}
sub _have_alarm {
eval {
local $SIG{"__DIE__"} = "DEFAULT";
local $SIG{ALRM} = sub { die };
alarm 0;
};
! $@;
}
sub _interpret_cr {
my ($s, $pos) = @_;
my (
$nextchar,
);
while (($pos = index($s->{buf}, "\015", $pos)) > -1) {
$nextchar = substr($s->{buf}, $pos + 1, 1);
if ($nextchar eq "\0") {
if ($s->{telnet_mode}) {
substr($s->{buf}, $pos + 1, 1) = "";
}
}
elsif ($nextchar eq "\012") {
if (!$s->{bin_mode}) {
substr($s->{buf}, $pos, 2) = "\n";
}
}
elsif (!length($nextchar) and ($s->{telnet_mode} or !$s->{bin_mode})) {
$s->{pushback_buf} .= "\015";
chop $s->{buf};
}
$pos++;
}
1;
}
sub _interpret_tcmd {
my ($self, $s, $offset) = @_;
my (
$callback,
$endpos,
$nextchar,
$option,
$parameters,
$pos,
$subcmd,
);
local $_;
$pos = $offset;
while (($pos = index $s->{buf}, "\377", $pos) > -1) { $nextchar = substr $s->{buf}, $pos + 1, 1;
if (!length $nextchar) {
$s->{pushback_buf} .= "\377";
chop $s->{buf};
last;
}
if ($nextchar eq "\377") { substr($s->{buf}, $pos, 1) = "";
$pos++;
}
elsif ($nextchar eq "\375" or $nextchar eq "\373" or
$nextchar eq "\374" or $nextchar eq "\376") { $option = substr $s->{buf}, $pos + 2, 1;
if (!length $option) {
$s->{pushback_buf} .= "\377" . $nextchar;
chop $s->{buf};
chop $s->{buf};
last;
}
substr($s->{buf}, $pos, 3) = "";
&_negotiate_recv($self, $s, $nextchar, ord($option), $pos);
}
elsif ($nextchar eq "\372") { $endpos = index $s->{buf}, "\360", $pos;
if ($endpos == -1) {
$s->{pushback_buf} .= substr $s->{buf}, $pos;
substr($s->{buf}, $pos) = "";
last;
}
$subcmd = substr($s->{buf}, $pos, $endpos - $pos + 1);
substr($s->{buf}, $pos, $endpos - $pos + 1) = "";
if ($s->{subopt_cback} and length($subcmd) >= 5) {
$option = unpack "C", substr($subcmd, 2, 1);
if (length($subcmd) >= 6) {
$parameters = substr $subcmd, 3, length($subcmd) - 5;
}
else {
$parameters = "";
}
$callback = $s->{subopt_cback};
&$callback($self, $option, $parameters);
}
}
else { substr($s->{buf}, $pos, 2) = "";
}
}
if (length $s->{unsent_opts}) {
&_flush_opts($self);
}
1;
}
sub _io_socket_include {
local $SIG{"__DIE__"} = "DEFAULT";
eval "require IO::Socket";
}
sub _log_dump {
my ($direction, $fh, $data, $offset, $len) = @_;
my (
$addr,
$hexvals,
$line,
);
$addr = 0;
$len = length($$data) - $offset
if !defined $len;
return 1 if $len <= 0;
while ($len > 0) {
if ($len >= 16) {
$line = substr $$data, $offset, 16;
}
else {
$line = substr $$data, $offset, $len;
}
$hexvals = unpack("H*", $line);
$hexvals .= ' ' x (32 - length $hexvals);
$hexvals = sprintf("%s %s %s %s " x 4,
unpack("a2" x 16, $hexvals));
$line =~ s/[\000-\037,\177-\237]/./g;
&_log_print($fh, sprintf("%s 0x%5.5lx: %s%s\n",
$direction, $addr, $hexvals, $line));
$addr += 16;
$offset += 16;
$len -= 16;
}
&_log_print($fh, "\n");
1;
}
sub _log_option {
my ($fh, $direction, $request, $option) = @_;
my (
$name,
);
if ($option >= 0 and $option <= $ $name = $Telopts[$option];
}
else {
$name = $option;
}
&_log_print($fh, "$direction $request $name\n");
}
sub _log_print {
my ($fh, $buf) = @_;
local $\ = '';
if (ref($fh) and ref($fh) ne "GLOB") { $fh->print($buf);
}
else { print $fh $buf;
}
}
sub _match_check {
my ($self, $code) = @_;
my $error;
my @warns = ();
{
local $SIG{"__DIE__"} = "DEFAULT";
local $SIG{"__WARN__"} = sub { push @warns, @_ };
local $^W = 1;
local $_ = '';
eval "\$_ =~ $code;";
}
if ($@) {
($error = $@) =~ s/ at \(eval \d+\) line \d+.?//;
chomp $error;
return $self->error("bad match operator: $error");
}
elsif (@warns) {
($error = shift @warns) =~ s/ at \(eval \d+\) line \d+.?//;
$error =~ s/ while "strict subs" in use//;
chomp $error;
return $self->error("bad match operator: $error");
}
1;
}
sub _negotiate_callback {
my ($self, $opt, $is_remote, $is_enabled, $was_enabled, $opt_bufpos) = @_;
my (
$callback,
$s,
);
local $_;
if ($is_remote and $opt == &TELOPT_ECHO) { $s = *$self->{net_telnet};
if ($is_enabled and !$was_enabled) { $s->{remote_echo} = 1;
}
elsif (!$is_enabled and $was_enabled) { $s->{remote_echo} = '';
}
}
$callback = $self->option_callback;
if ($callback) {
&$callback($self, $opt, $is_remote,
$is_enabled, $was_enabled, $opt_bufpos);
}
1;
}
sub _negotiate_recv {
my ($self, $s, $opt_request, $opt, $opt_bufpos) = @_;
unless (defined $s->{opts}{$opt}) {
&_set_default_option($s, $opt);
}
if ($opt_request eq "\376") { &_negotiate_recv_disable($self, $s, $opt, "dont", $opt_bufpos,
$s->{opts}{$opt}{local_enable_ok},
\$s->{opts}{$opt}{local_enabled},
\$s->{opts}{$opt}{local_state});
}
elsif ($opt_request eq "\375") { &_negotiate_recv_enable($self, $s, $opt, "do", $opt_bufpos,
$s->{opts}{$opt}{local_enable_ok},
\$s->{opts}{$opt}{local_enabled},
\$s->{opts}{$opt}{local_state});
}
elsif ($opt_request eq "\374") { &_negotiate_recv_disable($self, $s, $opt, "wont", $opt_bufpos,
$s->{opts}{$opt}{remote_enable_ok},
\$s->{opts}{$opt}{remote_enabled},
\$s->{opts}{$opt}{remote_state});
}
elsif ($opt_request eq "\373") { &_negotiate_recv_enable($self, $s, $opt, "will", $opt_bufpos,
$s->{opts}{$opt}{remote_enable_ok},
\$s->{opts}{$opt}{remote_enabled},
\$s->{opts}{$opt}{remote_state});
}
else { die;
}
1;
}
sub _negotiate_recv_disable {
my ($self, $s, $opt, $opt_request,
$opt_bufpos, $enable_ok, $is_enabled, $state) = @_;
my (
$ack,
$disable_cmd,
$enable_cmd,
$is_remote,
$nak,
$was_enabled,
);
if ($opt_request eq "wont") {
$enable_cmd = "\377\375" . pack("C", $opt); $disable_cmd = "\377\376" . pack("C", $opt); $is_remote = 1;
$ack = "DO";
$nak = "DONT";
&_log_option($s->{opt_log}, "RCVD", "WONT", $opt)
if $s->{opt_log};
}
elsif ($opt_request eq "dont") {
$enable_cmd = "\377\373" . pack("C", $opt); $disable_cmd = "\377\374" . pack("C", $opt); $is_remote = '';
$ack = "WILL";
$nak = "WONT";
&_log_option($s->{opt_log}, "RCVD", "DONT", $opt)
if $s->{opt_log};
}
else { die;
}
if ($$state eq "no") { }
elsif ($$state eq "yes") { $$is_enabled = '';
$$state = "no";
$s->{unsent_opts} .= $disable_cmd;
&_log_option($s->{opt_log}, "SENT", $nak, $opt)
if $s->{opt_log};
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
elsif ($$state eq "wantno") { $$is_enabled = '';
$$state = "no";
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
elsif ($$state eq "wantno opposite") { $$is_enabled = '';
$$state = "wantyes";
$s->{unsent_opts} .= $enable_cmd;
&_log_option($s->{opt_log}, "SENT", $ack, $opt)
if $s->{opt_log};
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
elsif ($$state eq "wantyes") { $$is_enabled = '';
$$state = "no";
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
elsif ($$state eq "wantyes opposite") { $$is_enabled = '';
$$state = "no";
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
}
sub _negotiate_recv_enable {
my ($self, $s, $opt, $opt_request,
$opt_bufpos, $enable_ok, $is_enabled, $state) = @_;
my (
$ack,
$disable_cmd,
$enable_cmd,
$is_remote,
$nak,
$was_enabled,
);
if ($opt_request eq "will") {
$enable_cmd = "\377\375" . pack("C", $opt); $disable_cmd = "\377\376" . pack("C", $opt); $is_remote = 1;
$ack = "DO";
$nak = "DONT";
&_log_option($s->{opt_log}, "RCVD", "WILL", $opt)
if $s->{opt_log};
}
elsif ($opt_request eq "do") {
$enable_cmd = "\377\373" . pack("C", $opt); $disable_cmd = "\377\374" . pack("C", $opt); $is_remote = '';
$ack = "WILL";
$nak = "WONT";
&_log_option($s->{opt_log}, "RCVD", "DO", $opt)
if $s->{opt_log};
}
else { die;
}
$was_enabled = $$is_enabled;
if ($$state eq "no") { if ($enable_ok) { $$is_enabled = 1;
$$state = "yes";
$s->{unsent_opts} .= $enable_cmd;
&_log_option($s->{opt_log}, "SENT", $ack, $opt)
if $s->{opt_log};
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
else { $s->{unsent_opts} .= $disable_cmd;
&_log_option($s->{opt_log}, "SENT", $nak, $opt)
if $s->{opt_log};
}
}
elsif ($$state eq "yes") { }
elsif ($$state eq "wantno") { $$is_enabled = '';
$$state = "no";
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
elsif ($$state eq "wantno opposite") { $$is_enabled = 1;
$$state = "yes";
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
elsif ($$state eq "wantyes") { $$is_enabled = 1;
$$state = "yes";
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
elsif ($$state eq "wantyes opposite") { $$is_enabled = 1;
$$state = "wantno";
$s->{unsent_opts} .= $disable_cmd;
&_log_option($s->{opt_log}, "SENT", $nak, $opt)
if $s->{opt_log};
&_negotiate_callback($self, $opt, $is_remote,
$$is_enabled, $was_enabled, $opt_bufpos);
}
1;
}
sub _new_handle {
if ($INC{"IO/Handle.pm"}) {
return IO::Handle->new;
}
else {
require FileHandle;
return FileHandle->new;
}
}
sub _next_getlines {
my ($self, $s) = @_;
my (
$len,
$line,
$pos,
@lines,
);
$line = $self->getline
or return;
push @lines, $line;
while (($pos = index($s->{buf}, $s->{rs})) != -1) {
$len = $pos + length $s->{rs};
push @lines, substr($s->{buf}, 0, $len);
substr($s->{buf}, 0, $len) = "";
}
@lines;
}
sub _opt_accept {
my ($self, @args) = @_;
my (
$arg,
$option,
$s,
);
$s = *$self->{net_telnet};
foreach $arg (@args) {
$option = $arg->{option};
if (!defined $s->{opts}{$option}) {
&_set_default_option($s, $option);
}
if ($arg->{is_remote}) {
$s->{opts}{$option}{remote_enable_ok} = $arg->{is_enable};
}
else {
$s->{opts}{$option}{local_enable_ok} = $arg->{is_enable};
}
}
1;
}
sub _optimal_blksize {
my ($blksize) = @_;
local $^W = '';
return 8192
unless defined $blksize and $blksize >= 1 and $blksize <= 1_048_576;
$blksize;
}
sub _parse_cmd_remove_mode {
my ($self, $mode) = @_;
if (!defined $mode) {
$mode = 0;
}
elsif ($mode =~ /^\s*auto\s*$/i) {
$mode = "auto";
}
elsif ($mode !~ /^\d+$/) {
&_carp($self, "ignoring bad Cmd_remove_mode " .
"argument \"$mode\": it's not \"auto\" or a " .
"non-negative integer");
$mode = *$self->{net_telnet}{cmd_rm_mode};
}
$mode;
}
sub _parse_errmode {
my ($self, $errmode) = @_;
if (!defined $errmode) {
&_carp($self, "ignoring undefined Errmode argument");
$errmode = *$self->{net_telnet}{errormode};
}
elsif ($errmode =~ /^\s*return\s*$/i) {
$errmode = "return";
}
elsif ($errmode =~ /^\s*die\s*$/i) {
$errmode = "die";
}
elsif (ref($errmode) eq "CODE") {
}
elsif (ref($errmode) eq "ARRAY") {
unless (ref($errmode->[0]) eq "CODE") {
&_carp($self, "ignoring bad Errmode argument: " .
"first list item isn't a code ref");
$errmode = *$self->{net_telnet}{errormode};
}
}
else {
&_carp($self, "ignoring bad Errmode argument \"$errmode\"");
$errmode = *$self->{net_telnet}{errormode};
}
$errmode;
}
sub _parse_input_record_separator {
my ($self, $rs) = @_;
unless (defined $rs and length $rs) {
&_carp($self, "ignoring null Input_record_separator argument");
$rs = *$self->{net_telnet}{rs};
}
$rs;
}
sub _parse_prompt {
my ($self, $prompt) = @_;
unless (defined $prompt) {
$prompt = "";
}
unless ($prompt =~ m(^\s*/) or $prompt =~ m(^\s*m\s*\W)) {
&_carp($self, "ignoring bad Prompt argument \"$prompt\": " .
"missing opening delimiter of match operator");
$prompt = *$self->{net_telnet}{cmd_prompt};
}
$prompt;
}
sub _parse_timeout {
my ($self, $timeout) = @_;
if (defined $timeout) {
eval {
local $SIG{"__DIE__"} = "DEFAULT";
local $SIG{"__WARN__"} = sub { die "non-numeric\n" };
local $^W = 1;
$timeout *= 1;
};
if ($@) { &_carp($self,
"ignoring non-numeric Timeout argument \"$timeout\"");
$timeout = *$self->{net_telnet}{time_out};
}
elsif ($timeout < 0) { &_carp($self, "ignoring negative Timeout argument \"$timeout\"");
$timeout = *$self->{net_telnet}{time_out};
}
}
$timeout;
}
sub _put {
my ($self, $buf, $subname) = @_;
my (
$endtime,
$len,
$nfound,
$nwrote,
$offset,
$ready,
$s,
$timed_out,
$timeout,
$zero_wrote_count,
);
$s = *$self->{net_telnet};
$s->{num_wrote} = 0;
$zero_wrote_count = 0;
$offset = 0;
$len = length $$buf;
$endtime = &_endtime($s->{time_out});
return $self->error("write error: filehandle isn't open")
unless $s->{opened};
if (length $s->{unsent_opts}) {
&_flush_opts($self);
}
while ($len) {
($timed_out, $timeout) = &_timeout_interval($endtime);
if ($timed_out) {
$s->{timedout} = 1;
return $self->error("$subname timed-out");
}
$nfound = select "", $ready=$s->{fdmask}, "", $timeout;
if (!defined $nfound or $nfound <= 0) { if (defined $nfound and $nfound == 0) { $s->{timedout} = 1;
return $self->error("$subname timed-out");
}
else { next if $! =~ /^interrupted/i;
$s->{opened} = '';
return $self->error("write error: $!");
}
}
$nwrote = syswrite $self, $$buf, $len, $offset;
if (!defined $nwrote) { next if $! =~ /^interrupted/i;
$s->{opened} = '';
return $self->error("write error: $!");
}
elsif ($nwrote == 0) { if ($zero_wrote_count++ <= 10) {
&_sleep(0.01);
next;
}
$s->{opened} = '';
return $self->error("write error: zero length write: $!");
}
if ($s->{dumplog}) {
&_log_dump('>', $s->{dumplog}, $buf, $offset, $nwrote);
}
$s->{num_wrote} += $nwrote;
$offset += $nwrote;
$len -= $nwrote;
}
1;
}
sub _qualify_fh {
my ($obj, $name) = @_;
my (
$user_class,
);
local $_;
($user_class) = &_user_caller($obj);
$name = qualify($name, $user_class);
if (!ref $name) {
no strict;
local $^W = 0;
$name =~ s/^\*+//;
$name = eval "\\*$name";
return unless ref $name;
}
$name;
}
sub _reset_options {
my ($opts) = @_;
my (
$opt,
);
foreach $opt (keys %$opts) {
$opts->{$opt}{remote_enabled} = '';
$opts->{$opt}{remote_state} = "no";
$opts->{$opt}{local_enabled} = '';
$opts->{$opt}{local_state} = "no";
}
1;
}
sub _save_lastline {
my ($s) = @_;
my (
$firstpos,
$lastpos,
$len_w_sep,
$len_wo_sep,
$offset,
);
my $rs = "\n";
if (($lastpos = rindex $s->{buf}, $rs) > -1) { while (1) {
$firstpos = rindex $s->{buf}, $rs, $lastpos - 1;
if ($firstpos == -1) {
$offset = 0;
}
else {
$offset = $firstpos + length $rs;
}
$len_wo_sep = $lastpos - $offset;
$len_w_sep = $len_wo_sep + length $rs;
if (substr($s->{buf}, $offset, $len_wo_sep)
!~ /^\s*$/)
{
$s->{last_line} = substr($s->{buf},
$offset,
$len_w_sep);
last;
}
last if $firstpos == -1;
$lastpos = $firstpos;
}
}
1;
}
sub _set_default_option {
my ($s, $option) = @_;
$s->{opts}{$option} = {
remote_enabled => '',
remote_state => "no",
remote_enable_ok => '',
local_enabled => '',
local_state => "no",
local_enable_ok => '',
};
}
sub _sleep {
my ($secs) = @_;
my $bitmask = "";
local *SOCK;
socket SOCK, AF_INET, SOCK_STREAM, 0;
vec($bitmask, fileno(SOCK), 1) = 1;
select $bitmask, "", "", $secs;
CORE::close SOCK;
1;
}
sub _timeout_interval {
my ($endtime) = @_;
my (
$timeout,
);
if (defined $endtime) {
return ('', 0) if $endtime == 0;
$timeout = $endtime - time;
return (1, 0) unless $timeout > 0;
return ('', $timeout);
}
else { return ('', undef);
}
}
sub _user_caller {
my ($obj) = @_;
my (
$class,
$curr_pkg,
$file,
$i,
$line,
$pkg,
%isa,
@isa,
);
local $_;
$class = ref $obj;
@isa = eval "\@${class}::ISA";
push @isa, $class;
($curr_pkg) = caller 1;
push @isa, $curr_pkg;
%isa = map { $_ => 1 } @isa;
$i = 1;
while (($pkg, $file, $line) = caller ++$i) {
next if $isa{$pkg};
return ($pkg, $file, $line);
}
($pkg, $file, $line) = caller --$i;
return ($pkg, $file, $line);
}
sub _verify_telopt_arg {
my ($self, $option, $argname) = @_;
if (defined $argname) {
$argname = "for arg $argname";
}
else {
$argname = "";
}
eval {
local $SIG{"__DIE__"} = "DEFAULT";
local $SIG{"__WARN__"} = sub { die "non-numeric\n" };
local $^W = 1;
$option = abs(int $option);
};
return $self->error("bad telnet option $argname: non-numeric")
if $@;
return $self->error("bad telnet option $argname: option > 255")
unless $option <= 255;
$option;
}
sub TELNET_IAC () {255}; sub TELNET_DONT () {254}; sub TELNET_DO () {253}; sub TELNET_WONT () {252}; sub TELNET_WILL () {251}; sub TELNET_SB () {250}; sub TELNET_GA () {249}; sub TELNET_EL () {248}; sub TELNET_EC () {247}; sub TELNET_AYT () {246}; sub TELNET_AO () {245}; sub TELNET_IP () {244}; sub TELNET_BREAK () {243}; sub TELNET_DM () {242}; sub TELNET_NOP () {241}; sub TELNET_SE () {240}; sub TELNET_EOR () {239}; sub TELNET_ABORT () {238}; sub TELNET_SUSP () {237}; sub TELNET_EOF () {236}; sub TELNET_SYNCH () {242};
sub TELOPT_BINARY () {0}; sub TELOPT_ECHO () {1}; sub TELOPT_RCP () {2}; sub TELOPT_SGA () {3}; sub TELOPT_NAMS () {4}; sub TELOPT_STATUS () {5}; sub TELOPT_TM () {6}; sub TELOPT_RCTE () {7}; sub TELOPT_NAOL () {8}; sub TELOPT_NAOP () {9}; sub TELOPT_NAOCRD () {10}; sub TELOPT_NAOHTS () {11}; sub TELOPT_NAOHTD () {12}; sub TELOPT_NAOFFD () {13}; sub TELOPT_NAOVTS () {14}; sub TELOPT_NAOVTD () {15}; sub TELOPT_NAOLFD () {16}; sub TELOPT_XASCII () {17}; sub TELOPT_LOGOUT () {18}; sub TELOPT_BM () {19}; sub TELOPT_DET () {20}; sub TELOPT_SUPDUP () {21}; sub TELOPT_SUPDUPOUTPUT () {22}; sub TELOPT_SNDLOC () {23}; sub TELOPT_TTYPE () {24}; sub TELOPT_EOR () {25}; sub TELOPT_TUID () {26}; sub TELOPT_OUTMRK () {27}; sub TELOPT_TTYLOC () {28}; sub TELOPT_3270REGIME () {29}; sub TELOPT_X3PAD () {30}; sub TELOPT_NAWS () {31}; sub TELOPT_TSPEED () {32}; sub TELOPT_LFLOW () {33}; sub TELOPT_LINEMODE () {34}; sub TELOPT_XDISPLOC () {35}; sub TELOPT_OLD_ENVIRON () {36}; sub TELOPT_AUTHENTICATION () {37}; sub TELOPT_ENCRYPT () {38}; sub TELOPT_NEW_ENVIRON () {39}; sub TELOPT_EXOPL () {255};
1;
__END__;
=head1 NAME
Net::Telnet - interact with TELNET port or other TCP ports
=head1 SYNOPSIS
C<use Net::Telnet ();>
see METHODS section below
=head1 DESCRIPTION
Net::Telnet allows you to make client connections to a TCP port and do
network I/O, especially to a port using the TELNET protocol. Simple
I/O methods such as print, get, and getline are provided. More
sophisticated interactive features are provided because connecting to
a TELNET port ultimately means communicating with a program designed
for human interaction. These interactive features include the ability
to specify a time-out and to wait for patterns to appear in the input
stream, such as the prompt from a shell.
Other reasons to use this module than strictly with a TELNET port are:
=over 2
=item *
You're not familiar with sockets and you want a simple way to make
client connections to TCP services.
=item *
You want to be able to specify your own time-out while connecting,
reading, or writing.
=item *
You're communicating with an interactive program at the other end of
some socket or pipe and you want to wait for certain patterns to
appear.
=back
Here's an example that prints who's logged-on to the remote host
sparky. In addition to a username and password, you must also know
the user's shell prompt, which for this example is C<bash$>
use Net::Telnet ();
$t = new Net::Telnet (Timeout => 10,
Prompt => '/bash\$ $/');
$t->open("sparky");
$t->login($username, $passwd);
@lines = $t->cmd("who");
print @lines;
More examples are in the B<EXAMPLES> section below.
Usage questions should be directed to the Usenet newsgroup
comp.lang.perl.modules.
Contact me, Jay Rogers <jay@rgrs.com>, if you find any bugs or have
suggestions for improvement.
=head2 What To Know Before Using
=over 2
=item *
All output is flushed while all input is buffered. Each object
contains its own input buffer.
=item *
The output record separator for C<print()> and C<cmd()> is set to
C<"\n"> by default, so that you don't have to append all your commands
with a newline. To avoid printing a trailing C<"\n"> use C<put()> or
set the I<output_record_separator> to C<"">.
=item *
The methods C<login()> and C<cmd()> use the I<prompt> setting in the
object to determine when a login or remote command is complete. Those
methods will fail with a time-out if you don't set the prompt
correctly.
=item *
Use a combination of C<print()> and C<waitfor()> as an alternative to
C<login()> or C<cmd()> when they don't do what you want.
=item *
Errors such as timing-out are handled according to the error mode
action. The default action is to print an error message to standard
error and have the program die. See the C<errmode()> method for more
information.
=item *
When constructing the match operator argument for C<prompt()> or
C<waitfor()>, always use single quotes instead of double quotes to
avoid unexpected backslash interpretation (e.g. C<'/bash\$ $/'>). If
you're constructing a DOS like file path, you'll need to use four
backslashes to represent one (e.g. C<'/c:\\\\users\\\\billE<gt>$/i'>).
Of course don't forget about regexp metacharacters like C<.>, C<[>, or
C<$>. You'll only need a single backslash to quote them. The anchor
metacharacters C<^> and C<$> refer to positions in the input buffer.
To avoid matching characters read that look like a prompt, it's a good
idea to end your prompt pattern with the C<$> anchor. That way the
prompt will only match if it's the last thing read.
=item *
In the input stream, each sequence of I<carriage return> and I<line
feed> (i.e. C<"\015\012"> or CR LF) is converted to C<"\n">. In the
output stream, each occurrence of C<"\n"> is converted to a sequence
of CR LF. See C<binmode()> to change the behavior. TCP protocols
typically use the ASCII sequence, carriage return and line feed to
designate a newline.
=item *
Timing-out while making a connection is disabled for machines that
don't support the C<alarm()> function. Most notably these include
MS-Windows machines.
=item *
You'll need to be running at least Perl version 5.002 to use this
module. This module does not require any libraries that don't already
come with a standard Perl distribution.
If you have the IO:: libraries installed (they come standard with
perl5.004 and later) then IO::Socket::INET is used as a base class,
otherwise FileHandle is used.
=item *
Contact me, Jay Rogers <jay@rgrs.com>, if you find any bugs or have
suggestions for improvement.
=back
=head2 Debugging
The typical usage bug causes a time-out error because you've made
incorrect assumptions about what the remote side actually sends. The
easiest way to reconcile what the remote side sends with your
expectations is to use C<input_log()> or C<dump_log()>.
C<dump_log()> allows you to see the data being sent from the remote
side before any translation is done, while C<input_log()> shows you
the results after translation. The translation includes converting
end of line characters, removing and responding to TELNET protocol
commands in the data stream.
=head2 Style of Named Parameters
Two different styles of named parameters are supported. This document
only shows the IO:: style:
Net::Telnet->new(Timeout => 20);
however the dash-option style is also allowed:
Net::Telnet->new(-timeout => 20);
=head2 Connecting to a Remote MS-Windows Machine
By default MS-Windows doesn't come with a TELNET server. However
third party TELNET servers are available. Unfortunately many of these
servers falsely claim to be a TELNET server. This is especially true
of the so-called "Microsoft Telnet Server" that comes installed with
some newer versions MS-Windows.
When a TELNET server first accepts a connection, it must use the ASCII
control characters carriage-return and line-feed to start a new line
(see RFC854). A server like the "Microsoft Telnet Server" that
doesn't do this, isn't a TELNET server. These servers send ANSI
terminal escape sequences to position to a column on a subsequent line
and to even position while writing characters that are adjacent to
each other. Worse, when sending output these servers resend
previously sent command output in a misguided attempt to display an
entire terminal screen.
Connecting Net::Telnet to one of these false TELNET servers makes your
job of parsing command output very difficult. It's better to replace
a false TELNET server with a real TELNET server. The better TELNET
servers for MS-Windows allow you to avoid the ANSI escapes by turning
off something some of them call I<console mode>.
=head1 METHODS
In the calling sequences below, square brackets B<[]> represent
optional parameters.
=over 4
=item B<new> - create a new Net::Telnet object
$obj = new Net::Telnet ([$host]);
$obj = new Net::Telnet ([Binmode => $mode,]
[Cmd_remove_mode => $mode,]
[Dump_Log => $filename,]
[Errmode => $errmode,]
[Fhopen => $filehandle,]
[Host => $host,]
[Input_log => $file,]
[Input_record_separator => $chars,]
[Option_log => $file,]
[Ors => $chars,]
[Output_log => $file,]
[Output_record_separator => $chars,]
[Port => $port,]
[Prompt => $matchop,]
[Rs => $chars,]
[Telnetmode => $mode,]
[Timeout => $secs,]);
This is the constructor for Net::Telnet objects. A new object is
returned on success, the error mode action is performed on failure -
see C<errmode()>. The optional arguments are short-cuts to methods of
the same name.
If the I<$host> argument is given then the object is opened by
connecting to TCP I<$port> on I<$host>. Also see C<open()>. The new
object returned is given the following defaults in the absence of
corresponding named parameters:
=over 4
=item
The default I<Host> is C<"localhost">
=item
The default I<Port> is C<23>
=item
The default I<Prompt> is C<'/[\$%#E<gt>] $/'>
=item
The default I<Timeout> is C<10>
=item
The default I<Errmode> is C<"die">
=item
The default I<Output_record_separator> is C<"\n">. Note that I<Ors>
is synonymous with I<Output_record_separator>.
=item
The default I<Input_record_separator> is C<"\n">. Note that I<Rs> is
synonymous with I<Input_record_separator>.
=item
The default I<Binmode> is C<0>, which means do newline translation.
=item
The default I<Telnetmode> is C<1>, which means respond to TELNET
commands in the data stream.
=item
The default I<Cmd_remove_mode> is C<"auto">
=item
The defaults for I<Dump_log>, I<Input_log>, I<Option_log>, and
I<Output_log> are C<"">, which means that logging is turned-off.
=back
=back
=over 4
=item B<binmode> - toggle newline translation
$mode = $obj->binmode;
$prev = $obj->binmode($mode);
This method controls whether or not sequences of carriage returns and
line feeds (CR LF or more specifically C<"\015\012">) are translated.
By default they are translated (i.e. binmode is C<0>).
If no argument is given, the current mode is returned.
If I<$mode> is C<1> then binmode is I<on> and newline translation is
not done.
If I<$mode> is C<0> then binmode is I<off> and newline translation is
done. In the input stream, each sequence of CR LF is converted to
C<"\n"> and in the output stream, each occurrence of C<"\n"> is
converted to a sequence of CR LF.
Note that input is always buffered. Changing binmode doesn't effect
what's already been read into the buffer. Output is not buffered and
changing binmode will have an immediate effect.
=back
=over 4
=item B<break> - send TELNET break character
$ok = $obj->break;
This method sends the TELNET break character. This character is
provided because it's a signal outside the ASCII character set which
is currently given local meaning within many systems. It's intended
to indicate that the Break Key or the Attention Key was hit.
This method returns C<1> on success, or performs the error mode action
on failure.
=back
=over 4
=item B<buffer> - scalar reference to object's input buffer
$ref = $obj->buffer;
This method returns a scalar reference to the input buffer for
I<$obj>. Data in the input buffer is data that has been read from the
remote side but has yet to be read by the user. Modifications to the
input buffer are returned by a subsequent read.
=back
=over 4
=item B<buffer_empty> - discard all data in object's input buffer
$obj->buffer_empty;
This method removes all data in the input buffer for I<$obj>.
=back
=over 4
=item B<close> - close object
$ok = $obj->close;
This method closes the socket, file, or pipe associated with the
object. It always returns a value of C<1>.
=back
=over 4
=item B<cmd> - issue command and retrieve output
$ok = $obj->cmd($string);
$ok = $obj->cmd(String => $string,
[Output => $ref,]
[Cmd_remove_mode => $mode,]
[Errmode => $mode,]
[Input_record_separator => $chars,]
[Ors => $chars,]
[Output_record_separator => $chars,]
[Prompt => $match,]
[Rs => $chars,]
[Timeout => $secs,]);
@output = $obj->cmd($string);
@output = $obj->cmd(String => $string,
[Output => $ref,]
[Cmd_remove_mode => $mode,]
[Errmode => $mode,]
[Input_record_separator => $chars,]
[Ors => $chars,]
[Output_record_separator => $chars,]
[Prompt => $match,]
[Rs => $chars,]
[Timeout => $secs,]);
This method sends the command I<$string>, and reads the characters
sent back by the command up until and including the matching prompt.
It's assumed that the program to which you're sending is some kind of
command prompting interpreter such as a shell.
The command I<$string> is automatically appended with the
output_record_separator, By default that's C<"\n">. This is similar
to someone typing a command and hitting the return key. Set the
output_record_separator to change this behavior.
In a scalar context, the characters read from the remote side are
discarded and C<1> is returned on success. On time-out, eof, or other
failures, the error mode action is performed. See C<errmode()>.
In a list context, just the output generated by the command is
returned, one line per element. In other words, all the characters in
between the echoed back command string and the prompt are returned.
If the command happens to return no output, a list containing one
element, the empty string is returned. This is so the list will
indicate true in a boolean context. On time-out, eof, or other
failures, the error mode action is performed. See C<errmode()>.
The characters that matched the prompt may be retrieved using
C<last_prompt()>.
Many command interpreters echo back the command sent. In most
situations, this method removes the first line returned from the
remote side (i.e. the echoed back command). See C<cmd_remove_mode()>
for more control over this feature.
Use C<dump_log()> to debug when this method keeps timing-out and you
don't think it should.
Consider using a combination of C<print()> and C<waitfor()> as an
alternative to this method when it doesn't do what you want, e.g. the
command you send prompts for input.
The I<Output> named parameter provides an alternative method of
receiving command output. If you pass a scalar reference, all the
output (even if it contains multiple lines) is returned in the
referenced scalar. If you pass an array or hash reference, the lines
of output are returned in the referenced array or hash. You can use
C<input_record_separator()> to change the notion of what separates a
line.
Optional named parameters are provided to override the current
settings of cmd_remove_mode, errmode, input_record_separator, ors,
output_record_separator, prompt, rs, and timeout. Rs is synonymous
with input_record_separator and ors is synonymous with
output_record_separator.
=back
=over 4
=item B<cmd_remove_mode> - toggle removal of echoed commands
$mode = $obj->cmd_remove_mode;
$prev = $obj->cmd_remove_mode($mode);
This method controls how to deal with echoed back commands in the
output returned by cmd(). Typically, when you send a command to the
remote side, the first line of output returned is the command echoed
back. Use this mode to remove the first line of output normally
returned by cmd().
If no argument is given, the current mode is returned.
If I<$mode> is C<0> then the command output returned from cmd() has no
lines removed. If I<$mode> is a positive integer, then the first
I<$mode> lines of command output are stripped.
By default, I<$mode> is set to C<"auto">. Auto means that whether or
not the first line of command output is stripped, depends on whether
or not the remote side offered to echo. By default, Net::Telnet
always accepts an offer to echo by the remote side. You can change
the default to reject such an offer using C<option_accept()>.
A warning is printed to STDERR when attempting to set this attribute
to something that's not C<"auto"> or a non-negative integer.
=back
=over 4
=item B<dump_log> - log all I/O in dump format
$fh = $obj->dump_log;
$fh = $obj->dump_log($fh);
$fh = $obj->dump_log($filename);
This method starts or stops dump format logging of all the object's
input and output. The dump format shows the blocks read and written
in a hexadecimal and printable character format. This method is
useful when debugging, however you might want to first try
C<input_log()> as it's more readable.
If no argument is given, the current log filehandle is returned. An
empty string indicates logging is off.
To stop logging, use an empty string as an argument.
If an open filehandle is given, it is used for logging and returned.
Otherwise, the argument is assumed to be the name of a file, the file
is opened and a filehandle to it is returned. If the file can't be
opened for writing, the error mode action is performed.
=back
=over 4
=item B<eof> - end of file indicator
$eof = $obj->eof;
This method returns C<1> if end of file has been read, otherwise it
returns an empty string. Because the input is buffered this isn't the
same thing as I<$obj> has closed. In other words I<$obj> can be
closed but there still can be stuff in the buffer to be read. Under
this condition you can still read but you won't be able to write.
=back
=over 4
=item B<errmode> - define action to be performed on error
$mode = $obj->errmode;
$prev = $obj->errmode($mode);
This method gets or sets the action used when errors are encountered
using the object. The first calling sequence returns the current
error mode. The second calling sequence sets it to I<$mode> and
returns the previous mode. Valid values for I<$mode> are C<"die">
(the default), C<"return">, a I<coderef>, or an I<arrayref>.
When mode is C<"die"> and an error is encountered using the object,
then an error message is printed to standard error and the program
dies.
When mode is C<"return"> then the method generating the error places
an error message in the object and returns an undefined value in a
scalar context and an empty list in list context. The error message
may be obtained using C<errmsg()>.
When mode is a I<coderef>, then when an error is encountered
I<coderef> is called with the error message as its first argument.
Using this mode you may have your own subroutine handle errors. If
I<coderef> itself returns then the method generating the error returns
undefined or an empty list depending on context.
When mode is an I<arrayref>, the first element of the array must be a
I<coderef>. Any elements that follow are the arguments to I<coderef>.
When an error is encountered, the I<coderef> is called with its
arguments. Using this mode you may have your own subroutine handle
errors. If the I<coderef> itself returns then the method generating
the error returns undefined or an empty list depending on context.
A warning is printed to STDERR when attempting to set this attribute
to something that's not C<"die">, C<"return">, a I<coderef>, or an
I<arrayref> whose first element isn't a I<coderef>.
=back
=over 4
=item B<errmsg> - most recent error message
$msg = $obj->errmsg;
$prev = $obj->errmsg(@msgs);
The first calling sequence returns the error message associated with
the object. The empty string is returned if no error has been
encountered yet. The second calling sequence sets the error message
for the object to the concatenation of I<@msgs> and returns the
previous error message. Normally, error messages are set internally
by a method when an error is encountered.
=back
=over 4
=item B<error> - perform the error mode action
$obj->error(@msgs);
This method concatenates I<@msgs> into a string and places it in the
object as the error message. Also see C<errmsg()>. It then performs
the error mode action. Also see C<errmode()>.
If the error mode doesn't cause the program to die, then an undefined
value or an empty list is returned depending on the context.
This method is primarily used by this class or a sub-class to perform
the user requested action when an error is encountered.
=back
=over 4
=item B<fhopen> - use already open filehandle for I/O
$ok = $obj->fhopen($fh);
This method associates the open filehandle I<$fh> with I<$obj> for
further I/O. Filehandle I<$fh> must already be opened.
Suppose you want to use the features of this module to do I/O to
something other than a TCP port, for example STDIN or a filehandle
opened to read from a process. Instead of opening the object for I/O
to a TCP port by using C<open()> or C<new()>, call this method
instead.
The value C<1> is returned success, the error mode action is performed
on failure.
=back
=over 4
=item B<get> - read block of data
$data = $obj->get([Binmode => $mode,]
[Errmode => $errmode,]
[Telnetmode => $mode,]
[Timeout => $secs,]);
This method reads a block of data from the object and returns it along
with any buffered data. If no buffered data is available to return,
it will wait for data to read using the timeout specified in the
object. You can override that timeout using I<$secs>. Also see
C<timeout()>. If buffered data is available to return, it also checks
for a block of data that can be immediately read.
On eof an undefined value is returned. On time-out or other failures,
the error mode action is performed. To distinguish between eof or an
error occurring when the error mode is not set to C<"die">, use
C<eof()>.
Optional named parameters are provided to override the current
settings of binmode, errmode, telnetmode, and timeout.
=back
=over 4
=item B<getline> - read next line
$line = $obj->getline([Binmode => $mode,]
[Errmode => $errmode,]
[Input_record_separator => $chars,]
[Rs => $chars,]
[Telnetmode => $mode,]
[Timeout => $secs,]);
This method reads and returns the next line of data from the object.
You can use C<input_record_separator()> to change the notion of what
separates a line. The default is C<"\n">. If a line isn't
immediately available, this method blocks waiting for a line or a
time-out.
On eof an undefined value is returned. On time-out or other failures,
the error mode action is performed. To distinguish between eof or an
error occurring when the error mode is not set to C<"die">, use
C<eof()>.
Optional named parameters are provided to override the current
settings of binmode, errmode, input_record_separator, rs, telnetmode,
and timeout. Rs is synonymous with input_record_separator.
=back
=over 4
=item B<getlines> - read next lines
@lines = $obj->getlines([Binmode => $mode,]
[Errmode => $errmode,]
[Input_record_separator => $chars,]
[Rs => $chars,]
[Telnetmode => $mode,]
[Timeout => $secs,]
[All => $boolean,]);
This method reads and returns all the lines of data from the object
until end of file is read. You can use C<input_record_separator()> to
change the notion of what separates a line. The default is C<"\n">.
A time-out error occurs if all the lines can't be read within the
time-out interval. See C<timeout()>.
The behavior of this method was changed in version 3.03. Prior to
version 3.03 this method returned just the lines available from the
next read. To get that old behavior, use the optional named parameter
I<All> and set I<$boolean> to C<""> or C<0>.
If only eof is read then an empty list is returned. On time-out or
other failures, the error mode action is performed. Use C<eof()> to
distinguish between reading only eof or an error occurring when the
error mode is not set to C<"die">.
Optional named parameters are provided to override the current
settings of binmode, errmode, input_record_separator, rs, telnetmode,
and timeout. Rs is synonymous with input_record_separator.
=back
=over 4
=item B<host> - name of remote host
$host = $obj->host;
$prev = $obj->host($host);
This method designates the remote host for C<open()>. With no
argument it returns the current host name set in the object. With an
argument it sets the current host name to I<$host> and returns the
previous host name. You may indicate the remote host using either a
hostname or an IP address.
The default value is C<"localhost">. It may also be set by C<open()>
or C<new()>.
=back
=over 4
=item B<input_log> - log all input
$fh = $obj->input_log;
$fh = $obj->input_log($fh);
$fh = $obj->input_log($filename);
This method starts or stops logging of input. This is useful when
debugging. Also see C<dump_log()>. Because most command interpreters
echo back commands received, it's likely all your output will also be
in this log. Note that input logging occurs after newline
translation. See C<binmode()> for details on newline translation.
If no argument is given, the log filehandle is returned. An empty
string indicates logging is off.
To stop logging, use an empty string as an argument.
If an open filehandle is given, it is used for logging and returned.
Otherwise, the argument is assumed to be the name of a file, the file
is opened for logging and a filehandle to it is returned. If the file
can't be opened for writing, the error mode action is performed.
=back
=over 4
=item B<input_record_separator> - input line delimiter
$chars = $obj->input_record_separator;
$prev = $obj->input_record_separator($chars);
This method designates the line delimiter for input. It's used with
C<getline()>, C<getlines()>, and C<cmd()> to determine lines in the
input.
With no argument this method returns the current input record
separator set in the object. With an argument it sets the input
record separator to I<$chars> and returns the previous value. Note
that I<$chars> must have length.
A warning is printed to STDERR when attempting to set this attribute
to a string with no length.
=back
=over 4
=item B<last_prompt> - last prompt read
$string = $obj->last_prompt;
$prev = $obj->last_prompt($string);
With no argument this method returns the last prompt read by cmd() or
login(). See C<prompt()>. With an argument it sets the last prompt
read to I<$string> and returns the previous value. Normally, only
internal methods set the last prompt.
=back
=over 4
=item B<lastline> - last line read
$line = $obj->lastline;
$prev = $obj->lastline($line);
This method retrieves the last line read from the object. This may be
a useful error message when the remote side abnormally closes the
connection. Typically the remote side will print an error message
before closing.
With no argument this method returns the last line read from the
object. With an argument it sets the last line read to I<$line> and
returns the previous value. Normally, only internal methods set the
last line.
=back
=over 4
=item B<login> - perform standard login
$ok = $obj->login($username, $password);
$ok = $obj->login(Name => $username,
Password => $password,
[Errmode => $mode,]
[Prompt => $match,]
[Timeout => $secs,]);
This method performs a standard login by waiting for a login prompt
and responding with I<$username>, then waiting for the password prompt
and responding with I<$password>, and then waiting for the command
interpreter prompt. If any of those prompts sent by the remote side
don't match what's expected, this method will time-out, unless timeout
is turned off.
Login prompt must match either of these case insensitive patterns:
/login[: ]*$/i
/username[: ]*$/i
Password prompt must match this case insensitive pattern:
/password[: ]*$/i
The command interpreter prompt must match the current setting of
prompt. See C<prompt()>.
Use C<dump_log()> to debug when this method keeps timing-out and you
don't think it should.
Consider using a combination of C<print()> and C<waitfor()> as an
alternative to this method when it doesn't do what you want, e.g. the
remote host doesn't prompt for a username.
On success, C<1> is returned. On time out, eof, or other failures,
the error mode action is performed. See C<errmode()>.
Optional named parameters are provided to override the current
settings of errmode, prompt, and timeout.
=back
=over 4
=item B<max_buffer_length> - maximum size of input buffer
$len = $obj->max_buffer_length;
$prev = $obj->max_buffer_length($len);
This method designates the maximum size of the input buffer. An error
is generated when a read causes the buffer to exceed this limit. The
default value is 1,048,576 bytes (1MB). The input buffer can grow
much larger than the block size when you continuously read using
C<getline()> or C<waitfor()> and the data stream contains no newlines
or matching waitfor patterns.
With no argument, this method returns the current maximum buffer
length set in the object. With an argument it sets the maximum buffer
length to I<$len> and returns the previous value. Values of I<$len>
smaller than 512 will be adjusted to 512.
A warning is printed to STDERR when attempting to set this attribute
to something that isn't a positive integer.
=back
=over 4
=item B<ofs> - field separator for print
$chars = $obj->ofs
$prev = $obj->ofs($chars);
This method is synonymous with C<output_field_separator()>.
=back
=over 4
=item B<open> - connect to port on remote host
$ok = $obj->open($host);
$ok = $obj->open([Host => $host,]
[Port => $port,]
[Errmode => $mode,]
[Timeout => $secs,]);
This method opens a TCP connection to I<$port> on I<$host>. If either
argument is missing then the current value of C<host()> or C<port()>
is used. Optional named parameters are provided to override the
current setting of errmode and timeout.
On success C<1> is returned. On time-out or other connection
failures, the error mode action is performed. See C<errmode()>.
Time-outs don't work for this method on machines that don't implement
SIGALRM - most notably MS-Windows machines. For those machines, an
error is returned when the system reaches its own time-out while
trying to connect.
A side effect of this method is to reset the alarm interval associated
with SIGALRM.
=back
=over 4
=item B<option_accept> - indicate willingness to accept a TELNET option
$fh = $obj->option_accept([Do => $telopt,]
[Dont => $telopt,]
[Will => $telopt,]
[Wont => $telopt,]);
This method is used to indicate whether to accept or reject an offer
to enable a TELNET option made by the remote side. If you're using
I<Do> or I<Will> to indicate a willingness to enable, then a
notification callback must have already been defined by a prior call
to C<option_callback()>. See C<option_callback()> for details on
receiving enable/disable notification of a TELNET option.
You can give multiple I<Do>, I<Dont>, I<Will>, or I<Wont> arguments
for different TELNET options in the same call to this method.
The following example describes the meaning of the named parameters.
A TELNET option, such as C<TELOPT_ECHO> used below, is an integer
constant that you can import from Net::Telnet. See the source in file
Telnet.pm for the complete list.
=over 4
=item
I<Do> => C<TELOPT_ECHO>
=over 4
=item
we'll accept an offer to enable the echo option on the local side
=back
=item
I<Dont> => C<TELOPT_ECHO>
=over 4
=item
we'll reject an offer to enable the echo option on the local side
=back
=item
I<Will> => C<TELOPT_ECHO>
=over 4
=item
we'll accept an offer to enable the echo option on the remote side
=back
=item
I<Wont> => C<TELOPT_ECHO>
=over 4
=item
we'll reject an offer to enable the echo option on the remote side
=back
=back
=item
Use C<option_send()> to send a request to the remote side to enable or
disable a particular TELNET option.
=back
=over 4
=item B<option_callback> - define the option negotiation callback
$coderef = $obj->option_callback;
$prev = $obj->option_callback($coderef);
This method defines the callback subroutine that's called when a
TELNET option is enabled or disabled. Once defined, the
I<option_callback> may not be undefined. However, calling this method
with a different I<$coderef> changes it.
A warning is printed to STDERR when attempting to set this attribute
to something that isn't a coderef.
Here are the circumstances that invoke I<$coderef>:
=over 4
=item
An option becomes enabled because the remote side requested an enable
and C<option_accept()> had been used to arrange that it be accepted.
=item
The remote side arbitrarily decides to disable an option that is
currently enabled. Note that Net::Telnet always accepts a request to
disable from the remote side.
=item
C<option_send()> was used to send a request to enable or disable an
option and the response from the remote side has just been received.
Note, that if a request to enable is rejected then I<$coderef> is
still invoked even though the option didn't change.
=back
=item
Here are the arguments passed to I<&$coderef>:
&$coderef($obj, $option, $is_remote,
$is_enabled, $was_enabled, $buf_position);
=over 4
=item
1. I<$obj> is the Net::Telnet object
=item
2. I<$option> is the TELNET option. Net::Telnet exports constants
for the various TELNET options which just equate to an integer.
=item
3. I<$is_remote> is a boolean indicating for which side the option
applies.
=item
4. I<$is_enabled> is a boolean indicating the option is enabled or
disabled
=item
5. I<$was_enabled> is a boolean indicating the option was previously
enabled or disabled
=item
6. I<$buf_position> is an integer indicating the position in the
object's input buffer where the option takes effect. See C<buffer()>
to access the object's input buffer.
=back
=back
=over 4
=item B<option_log> - log all TELNET options sent or received
$fh = $obj->option_log;
$fh = $obj->option_log($fh);
$fh = $obj->option_log($filename);
This method starts or stops logging of all TELNET options being sent
or received. This is useful for debugging when you send options via
C<option_send()> or you arrange to accept option requests from the
remote side via C<option_accept()>. Also see C<dump_log()>.
If no argument is given, the log filehandle is returned. An empty
string indicates logging is off.
To stop logging, use an empty string as an argument.
If an open filehandle is given, it is used for logging and returned.
Otherwise, the argument is assumed to be the name of a file, the file
is opened for logging and a filehandle to it is returned. If the file
can't be opened for writing, the error mode action is performed.
=back
=over 4
=item B<option_send> - send TELNET option negotiation request
$ok = $obj->option_send([Do => $telopt,]
[Dont => $telopt,]
[Will => $telopt,]
[Wont => $telopt,]
[Async => $boolean,]);
This method is not yet implemented. Look for it in a future version.
=back
=over 4
=item B<option_state> - get current state of a TELNET option
$hashref = $obj->option_state($telopt);
This method returns a hashref containing a copy of the current state
of TELNET option I<$telopt>.
Here are the values returned in the hash:
=over 4
=item
I<$hashref>->{remote_enabled}
=over 4
=item
boolean that indicates if the option is enabled on the remote side.
=back
=item
I<$hashref>->{remote_enable_ok}
=over 4
=item
boolean that indicates if it's ok to accept an offer to enable this
option on the remote side.
=back
=item
I<$hashref>->{remote_state}
=over 4
=item
string used to hold the internal state of option negotiation for this
option on the remote side.
=back
=item
I<$hashref>->{local_enabled}
=over 4
=item
boolean that indicates if the option is enabled on the local side.
=back
=item
I<$hashref>->{local_enable_ok}
=over 4
=item
boolean that indicates if it's ok to accept an offer to enable this
option on the local side.
=back
=item
I<$hashref>->{local_state}
=over 4
=item
string used to hold the internal state of option negotiation for this
option on the local side.
=back
=back
=back
=over 4
=item B<ors> - output line delimiter
$chars = $obj->ors;
$prev = $obj->ors($chars);
This method is synonymous with C<output_record_separator()>.
=back
=over 4
=item B<output_field_separator> - field separator for print
$chars = $obj->output_field_separator;
$prev = $obj->output_field_separator($chars);
This method designates the output field separator for C<print()>.
Ordinarily the print method simply prints out the comma separated
fields you specify. Set this to specify what's printed between
fields.
With no argument this method returns the current output field
separator set in the object. With an argument it sets the output
field separator to I<$chars> and returns the previous value.
By default it's set to an empty string.
=back
=over 4
=item B<output_log> - log all output
$fh = $obj->output_log;
$fh = $obj->output_log($fh);
$fh = $obj->output_log($filename);
This method starts or stops logging of output. This is useful when
debugging. Also see C<dump_log()>. Because most command interpreters
echo back commands received, it's likely all your output would also be
in an input log. See C<input_log()>. Note that output logging occurs
before newline translation. See C<binmode()> for details on newline
translation.
If no argument is given, the log filehandle is returned. An empty
string indicates logging is off.
To stop logging, use an empty string as an argument.
If an open filehandle is given, it is used for logging and returned.
Otherwise, the argument is assumed to be the name of a file, the file
is opened for logging and a filehandle to it is returned. If the file
can't be opened for writing, the error mode action is performed.
=back
=over 4
=item B<output_record_separator> - output line delimiter
$chars = $obj->output_record_separator;
$prev = $obj->output_record_separator($chars);
This method designates the output line delimiter for C<print()> and
C<cmd()>. Set this to specify what's printed at the end of C<print()>
and C<cmd()>.
The output record separator is set to C<"\n"> by default, so there's
no need to append all your commands with a newline. To avoid printing
the output_record_separator use C<put()> or set the
output_record_separator to an empty string.
With no argument this method returns the current output record
separator set in the object. With an argument it sets the output
record separator to I<$chars> and returns the previous value.
=back
=over 4
=item B<port> - remote port
$port = $obj->port;
$prev = $obj->port($port);
This method designates the remote TCP port. With no argument this
method returns the current port number. With an argument it sets the
current port number to I<$port> and returns the previous port. If
I<$port> is a TCP service name, then it's first converted to a port
number using the perl function C<getservbyname()>.
The default value is C<23>. It may also be set by C<open()> or
C<new()>.
A warning is printed to STDERR when attempting to set this attribute
to something that's not a positive integer or a valid TCP service
name.
=back
=over 4
=item B<print> - write to object
$ok = $obj->print(@list);
This method writes I<@list> followed by the I<output_record_separator>
to the open object and returns C<1> if all data was successfully
written. On time-out or other failures, the error mode action is
performed. See C<errmode()>.
By default, the C<output_record_separator()> is set to C<"\n"> so all
your commands automatically end with a newline. In most cases your
output is being read by a command interpreter which won't accept a
command until newline is read. This is similar to someone typing a
command and hitting the return key. To avoid printing a trailing
C<"\n"> use C<put()> instead or set the output_record_separator to an
empty string.
On failure, it's possible that some data was written. If you choose
to try and recover from a print timing-out, use C<print_length()> to
determine how much was written before the error occurred.
You may also use the output field separator to print a string between
the list elements. See C<output_field_separator()>.
=back
=over 4
=item B<print_length> - number of bytes written by print
$num = $obj->print_length;
This returns the number of bytes successfully written by the most
recent C<print()> or C<put()>.
=back
=over 4
=item B<prompt> - pattern to match a prompt
$matchop = $obj->prompt;
$prev = $obj->prompt($matchop);
This method sets the pattern used to find a prompt in the input
stream. It must be a string representing a valid perl pattern match
operator. The methods C<login()> and C<cmd()> try to read until
matching the prompt. They will fail with a time-out error if the
pattern you've chosen doesn't match what the remote side sends.
With no argument this method returns the prompt set in the object.
With an argument it sets the prompt to I<$matchop> and returns the
previous value.
The default prompt is C<'/[\$%#E<gt>] $/'>
Always use single quotes, instead of double quotes, to construct
I<$matchop> (e.g. C<'/bash\$ $/'>). If you're constructing a DOS like
file path, you'll need to use four backslashes to represent one
(e.g. C<'/c:\\\\users\\\\billE<gt>$/i'>).
Of course don't forget about regexp metacharacters like C<.>, C<[>, or
C<$>. You'll only need a single backslash to quote them. The anchor
metacharacters C<^> and C<$> refer to positions in the input buffer.
A warning is printed to STDERR when attempting to set this attribute
with a match operator missing its opening delimiter.
=back
=over 4
=item B<put> - write to object
$ok = $obj->put($string);
$ok = $obj->put(String => $string,
[Binmode => $mode,]
[Errmode => $errmode,]
[Telnetmode => $mode,]
[Timeout => $secs,]);
This method writes I<$string> to the opened object and returns C<1> if
all data was successfully written. This method is like C<print()>
except that it doesn't write the trailing output_record_separator
("\n" by default). On time-out or other failures, the error mode
action is performed. See C<errmode()>.
On failure, it's possible that some data was written. If you choose
to try and recover from a put timing-out, use C<print_length()> to
determine how much was written before the error occurred.
Optional named parameters are provided to override the current
settings of binmode, errmode, telnetmode, and timeout.
=back
=over 4
=item B<rs> - input line delimiter
$chars = $obj->rs;
$prev = $obj->rs($chars);
This method is synonymous with C<input_record_separator()>.
=back
=over 4
=item B<telnetmode> - turn off/on telnet command interpretation
$mode = $obj->telnetmode;
$prev = $obj->telnetmode($mode);
This method controls whether or not TELNET commands in the data stream
are recognized and handled. The TELNET protocol uses certain
character sequences sent in the data stream to control the session.
If the port you're connecting to isn't using the TELNET protocol, then
you should turn this mode off. The default is I<on>.
If no argument is given, the current mode is returned.
If I<$mode> is C<0> then telnet mode is off. If I<$mode> is C<1> then
telnet mode is on.
=back
=over 4
=item B<timed_out> - time-out indicator
$boolean = $obj->timed_out;
$prev = $obj->timed_out($boolean);
This method indicates if a previous read, write, or open method
timed-out. Remember that timing-out is itself an error. To be able
to invoke C<timed_out()> after a time-out error, you'd have to change
the default error mode to something other than C<"die">. See
C<errmode()>.
With no argument this method returns C<1> if the previous method
timed-out. With an argument it sets the indicator. Normally, only
internal methods set this indicator.
=back
=over 4
=item B<timeout> - I/O time-out interval
$secs = $obj->timeout;
$prev = $obj->timeout($secs);
This method sets the timeout interval that's used when performing I/O
or connecting to a port. When a method doesn't complete within the
timeout interval then it's an error and the error mode action is
performed.
A timeout may be expressed as a relative or absolute value. If
I<$secs> is greater than or equal to the time the program started, as
determined by $^T, then it's an absolute time value for when time-out
occurs. The perl function C<time()> may be used to obtain an absolute
time value. For a relative time-out value less than $^T, time-out
happens I<$secs> from when the method begins.
If I<$secs> is C<0> then time-out occurs if the data cannot be
immediately read or written. Use the undefined value to turn off
timing-out completely.
With no argument this method returns the timeout set in the object.
With an argument it sets the timeout to I<$secs> and returns the
previous value. The default timeout value is C<10> seconds.
A warning is printed to STDERR when attempting to set this attribute
to something that's not an C<undef> or a non-negative integer.
=back
=over 4
=item B<waitfor> - wait for pattern in the input
$ok = $obj->waitfor($matchop);
$ok = $obj->waitfor([Match => $matchop,]
[String => $string,]
[Binmode => $mode,]
[Errmode => $errmode,]
[Telnetmode => $mode,]
[Timeout => $secs,]);
($prematch, $match) = $obj->waitfor($matchop);
($prematch, $match) = $obj->waitfor([Match => $matchop,]
[String => $string,]
[Binmode => $mode,]
[Errmode => $errmode,]
[Telnetmode => $mode,]
[Timeout => $secs,]);
This method reads until a pattern match or string is found in the
input stream. All the characters before and including the match are
removed from the input stream.
In a list context the characters before the match and the matched
characters are returned in I<$prematch> and I<$match>. In a scalar
context, the matched characters and all characters before it are
discarded and C<1> is returned on success. On time-out, eof, or other
failures, for both list and scalar context, the error mode action is
performed. See C<errmode()>.
You can specify more than one pattern or string by simply providing
multiple I<Match> and/or I<String> named parameters. A I<$matchop>
must be a string representing a valid Perl pattern match operator.
The I<$string> is just a substring to find in the input stream.
Use C<dump_log()> to debug when this method keeps timing-out and you
don't think it should.
An optional named parameter is provided to override the current
setting of timeout.
To avoid unexpected backslash interpretation, always use single quotes
instead of double quotes to construct a match operator argument for
C<prompt()> and C<waitfor()> (e.g. C<'/bash\$ $/'>). If you're
constructing a DOS like file path, you'll need to use four backslashes
to represent one (e.g. C<'/c:\\\\users\\\\billE<gt>$/i'>).
Of course don't forget about regexp metacharacters like C<.>, C<[>, or
C<$>. You'll only need a single backslash to quote them. The anchor
metacharacters C<^> and C<$> refer to positions in the input buffer.
Optional named parameters are provided to override the current
settings of binmode, errmode, telnetmode, and timeout.
=back
=head1 SEE ALSO
=over 2
=item RFC 854
S<TELNET Protocol Specification>
S<ftp://ftp.isi.edu/in-notes/rfc854.txt>
=item RFC 1143
S<Q Method of Implementing TELNET Option Negotiation>
S<ftp://ftp.isi.edu/in-notes/rfc1143.txt>
=item TELNET Option Assignments
S<http://www.iana.org/assignments/telnet-options>
=back
=head1 EXAMPLES
This example gets the current weather forecast for Brainerd, Minnesota.
my ($forecast, $t);
use Net::Telnet ();
$t = new Net::Telnet;
$t->open("rainmaker.wunderground.com");
## Wait for first prompt and "hit return".
$t->waitfor('/continue:.*$/');
$t->print("");
## Wait for second prompt and respond with city code.
$t->waitfor('/city code.*$/');
$t->print("BRD");
## Read and print the first page of forecast.
($forecast) = $t->waitfor('/[ \t]+press return to continue/i');
print $forecast;
exit;
This example checks a POP server to see if you have mail.
my ($hostname, $line, $passwd, $pop, $username);
$hostname = "your_destination_host_here";
$username = "your_username_here";
$passwd = "your_password_here";
use Net::Telnet ();
$pop = new Net::Telnet (Telnetmode => 0);
$pop->open(Host => $hostname,
Port => 110);
## Read connection message.
$line = $pop->getline;
die $line unless $line =~ /^\+OK/;
## Send user name.
$pop->print("user $username");
$line = $pop->getline;
die $line unless $line =~ /^\+OK/;
## Send password.
$pop->print("pass $passwd");
$line = $pop->getline;
die $line unless $line =~ /^\+OK/;
## Request status of messages.
$pop->print("list");
$line = $pop->getline;
print $line;
exit;
Here's an example that uses the ssh program to connect to a remote
host. Because the ssh program reads and writes to its controlling
terminal, the IO::Pty module is used to create a new pseudo terminal
for use by ssh. A new Net::Telnet object is then created to read and
write to that pseudo terminal. To use the code below, substitute
"changeme" with the actual host, user, password, and command prompt.
## Main program.
{
my ($pty, $ssh, @lines);
my $host = "changeme";
my $user = "changeme";
my $password = "changeme";
my $prompt = '/changeme:~> $/';
## Start ssh program.
$pty = &spawn("ssh", "-l", $user, $host); # spawn() defined below
## Create a Net::Telnet object to perform I/O on ssh's tty.
use Net::Telnet;
$ssh = new Net::Telnet (-fhopen => $pty,
-prompt => $prompt,
-telnetmode => 0,
-cmd_remove_mode => 1,
-output_record_separator => "\r");
## Login to remote host.
$ssh->waitfor(-match => '/password: ?$/i',
-errmode => "return")
or die "problem connecting to host: ", $ssh->lastline;
$ssh->print($password);
$ssh->waitfor(-match => $ssh->prompt,
-errmode => "return")
or die "login failed: ", $ssh->lastline;
## Send command, get and print its output.
@lines = $ssh->cmd("who");
print @lines;
exit;
} # end main program
sub spawn {
my(@cmd) = @_;
my($pid, $pty, $tty, $tty_fd);
## Create a new pseudo terminal.
use IO::Pty ();
$pty = new IO::Pty
or die $!;
## Execute the program in another process.
unless ($pid = fork) { # child process
die "problem spawning program: $!\n" unless defined $pid;
## Disassociate process from existing controlling terminal.
use POSIX ();
POSIX::setsid
or die "setsid failed: $!";
## Associate process with a new controlling terminal.
$tty = $pty->slave;
$tty_fd = $tty->fileno;
close $pty;
## Make stdio use the new controlling terminal.
open STDIN, "<&$tty_fd" or die $!;
open STDOUT, ">&$tty_fd" or die $!;
open STDERR, ">&STDOUT" or die $!;
close $tty;
## Execute requested program.
exec @cmd
or die "problem executing $cmd[0]\n";
} # end child process
$pty;
} # end sub spawn
Here's an example that changes a user's login password. Because the
passwd program always prompts for passwords on its controlling
terminal, the IO::Pty module is used to create a new pseudo terminal
for use by passwd. A new Net::Telnet object is then created to read
and write to that pseudo terminal. To use the code below, substitute
"changeme" with the actual old and new passwords.
my ($pty, $passwd);
my $oldpw = "changeme";
my $newpw = "changeme";
## Start passwd program.
$pty = &spawn("passwd"); # spawn() defined above
## Create a Net::Telnet object to perform I/O on passwd's tty.
use Net::Telnet;
$passwd = new Net::Telnet (-fhopen => $pty,
-timeout => 2,
-output_record_separator => "\r",
-telnetmode => 0,
-cmd_remove_mode => 1);
$passwd->errmode("return");
## Send existing password.
$passwd->waitfor('/password: ?$/i')
or die "no old password prompt: ", $passwd->lastline;
$passwd->print($oldpw);
## Send new password.
$passwd->waitfor('/new password: ?$/i')
or die "bad old password: ", $passwd->lastline;
$passwd->print($newpw);
## Send new password verification.
$passwd->waitfor('/new password: ?$/i')
or die "bad new password: ", $passwd->lastline;
$passwd->print($newpw);
## Display success or failure.
$passwd->waitfor('/changed/')
or die "bad new password: ", $passwd->lastline;
print $passwd->lastline;
$passwd->close;
exit;
Here's an example you can use to down load a file of any type. The
file is read from the remote host's standard output using cat. To
prevent any output processing, the remote host's standard output is
put in raw mode using the Bourne shell. The Bourne shell is used
because some shells, notably tcsh, prevent changing tty modes. Upon
completion, FTP style statistics are printed to stderr.
my ($block, $filename, $host, $hostname, $k_per_sec, $line,
$num_read, $passwd, $prevblock, $prompt, $size, $size_bsd,
$size_sysv, $start_time, $total_time, $username);
$hostname = "your_destination_host_here";
$username = "your_username_here";
$passwd = "your_password_here";
$filename = "your_download_file_here";
## Connect and login.
use Net::Telnet ();
$host = new Net::Telnet (Timeout => 30,
Prompt => '/[%#>] $/');
$host->open($hostname);
$host->login($username, $passwd);
## Make sure prompt won't match anything in send data.
$prompt = "_funkyPrompt_";
$host->prompt("/$prompt\$/");
$host->cmd("set prompt = '$prompt'");
## Get size of file.
($line) = $host->cmd("/bin/ls -l $filename");
($size_bsd, $size_sysv) = (split ' ', $line)[3,4];
if ($size_sysv =~ /^\d+$/) {
$size = $size_sysv;
}
elsif ($size_bsd =~ /^\d+$/) {
$size = $size_bsd;
}
else {
die "$filename: no such file on $hostname";
}
## Start sending the file.
binmode STDOUT;
$host->binmode(1);
$host->print("/bin/sh -c 'stty raw; cat $filename'");
$host->getline; # discard echoed back line
## Read file a block at a time.
$num_read = 0;
$prevblock = "";
$start_time = time;
while (($block = $host->get) and ($block !~ /$prompt$/o)) {
if (length $block >= length $prompt) {
print $prevblock;
$num_read += length $prevblock;
$prevblock = $block;
}
else {
$prevblock .= $block;
}
}
$host->close;
## Print last block without trailing prompt.
$prevblock .= $block;
$prevblock =~ s/$prompt$//;
print $prevblock;
$num_read += length $prevblock;
die "error: expected size $size, received size $num_read\n"
unless $num_read == $size;
## Print totals.
$total_time = (time - $start_time) || 1;
$k_per_sec = ($size / 1024) / $total_time;
$k_per_sec = sprintf "%3.1f", $k_per_sec;
warn("$num_read bytes received in $total_time seconds ",
"($k_per_sec Kbytes/s)\n");
exit;
=head1 AUTHOR
Jay Rogers <jay@rgrs.com>
=head1 COPYRIGHT
Copyright 1997, 2000, 2002 by Jay Rogers. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.