news.utdallas.edu!wupost!uunet!ftpbox!cssmp.corp.mot.com!mmuegel Tue Mar 16 10:16:13 CST 1993
Article: 1608 of comp.lang.perl
Xref: feenix.metronet.com comp.lang.perl:1608
Newsgroups: comp.lang.perl
Path: feenix.metronet.com!news.utdallas.edu!wupost!uunet!ftpbox!cssmp.corp.mot.com!mmuegel
From:
[email protected] (Michael S. Muegel)
#Subject: Re: MORE on getting perl to do a telnet
Message-ID: <
[email protected]>
Sender:
[email protected] (C News)
Organization: Corporate Information Office, Schaumburg, Illinois, Motorola, Inc.
References: <
[email protected]> <
[email protected]>
Date: Tue, 16 Mar 1993 04:16:54 GMT
Lines: 399
Previously,
[email protected] (Greg Rose) wrote:
> To correctly start up an actual telnet program on your local machine,
> you'll need to use pseudo-ttys.
>
> >Any ideas?
>
> Get Tcl and Expect. For a very small investment in learning, I think
> you'll be able to solve the problem much more easily.
You are correct about needing to emulate the telnet protocol; however,
[email protected] (David Noble) posted a couple of
packages to do just that in Perl.
Since I did not see it in the coombs.anu.edu.au archive I will repost
it here. Maybe it can be put in it Mark?
-Mike
=============================================================================
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# perl_sockets
# This archive created: Thu Feb 11 20:10:16 1993
export PATH; PATH=/bin:$PATH
if test ! -d 'perl_sockets'
then
mkdir 'perl_sockets'
fi
cd 'perl_sockets'
if test -f 'telnet.pl'
then
echo shar: will not over-write existing file "'telnet.pl'"
else
cat << \SHAR_EOF > 'telnet.pl'
#!/usr/local/bin/perl
package telnet;
;# USAGE:
;# ======
;#
;# $buffer = &telnet'read($handle, $timeout);
;#
;# INPUTS:
;#
;# $handle - regular file handle returned by opening the socket
;# $timeout - number of seconds to wait before returning empty-handed
;#
;# RETURN VALUE:
;#
;# Returns data from the socket after removing the garbage from telnet
;# handshaking. If there is no multiline pattern matching, ie: ($* == 0),
;# then only one line at a time is returned. The remaining lines are buffered
;# in the package, and will be used to satisfy further requests for data until
;# the buffer is empty again. A partial line may be returned if the timeout
;# was reached before a newline. On the other hand, when multiline pattern
;# matching is on ($* == 1), all the available data is returned.
;#
;# Returns the empty string on EOF or timeout.
;# To decide which it was, use these functions:
;#
;# if ( &telnet'eof ) { &outta_here; }
;# if ( &telnet'timeout ) { &whatever; }
;# if ( &telnet'ok ) { &data_received; }
;#
;# AUTHOR: David Noble (
[email protected])
;# DATE: 11 Feb 1993
;#
;# Modify and use as you see fit, but please leave my name on
;# it as long as it still resembles the original code.
;#
;#############################################################################
$status = 'ok';
sub read {
local ($handle) = shift (@_);
local ($endtime) = shift (@_);
local ($rmask, $nfound, $nread, $thisbuf);
local ($multilines) = $*;
local ($buf) == '';
$status = 'ok';
$* = 1; # this gets restored to its previous value before returning
if (!$TelnetBuffer{$handle}) {
$endtime += time;
get_data: while ($endtime > time) {
$rmask = "";
$thisbuf = "";
vec($rmask, fileno($handle), 1) = 1;
($nfound, $rmask) = select($rmask, undef, undef, $endtime - time);
if ($nfound) {
$nread = sysread($handle, $thisbuf, 1024);
if ($nread > 0) {
$TelnetBuffer{$handle} .= $thisbuf;
last get_data if &_preprocess($handle) && !$multilines;
}
else {
$status = 'eof';
return ''; # connection closed
}
}
else {
$status = 'timeout';
last get_data;
}
}
}
if ($TelnetBuffer{$handle}) {
if (!$multilines && ($TelnetBuffer{$handle} =~ m/\n/o)) {
$TelnetBuffer{$handle} =~ s/^(.*\n)//o;
$buf = $1;
}
else {
$buf = $TelnetBuffer{$handle};
$TelnetBuffer{$handle} = '';
}
}
$* = $multilines;
$buf;
}
sub ok { $status eq 'ok'; }
sub eof { $status eq 'eof'; }
sub timeout { $status eq 'timeout'; }
sub status { $status; }
sub _preprocess {
local ($handle) = shift(@_);
local ($_) = $TelnetBuffer{$handle};
s/\015\012/\012/go; # combine (CR NL) into NL
while (m/\377/o) {
# respond to "IAC DO x" or "IAC DON'T x" with "IAC WON'T x"
if (s/([^\377])?\377[\375\376](.|\n)/\1/o)
{ print $handle "\377\374$2"; }
# ignore "IAC WILL x" or "IAC WON'T x"
elsif (s/([^\377])?\377[\373\374](.|\n)/\1/o) {;}
# respond to "IAC AYT" (are you there)
elsif (s/([^\377])?\377\366/\1/o)
{ print $handle "nobody here but us pigeons\n"; }
else { last; }
}
s/\377\377/\377/go; # handle escaped IAC characters
$TelnetBuffer{$handle} = $_;
m/\n/o; # return value: whether there is a full line or not
}
;# For those who are curious, here are some of the special characters
;# interpretted by the telnet protocol:
;# Name Dec. Octal Description
;# ---- ---- ----- -----------
;# IAC 255 \377 /* interpret as command: */
;# DONT 254 \376 /* you are not to use option */
;# DO 253 \375 /* please, you use option */
;# WONT 252 \374 /* I won't use option */
;# WILL 251 \373 /* I will use option */
;# SB 250 \372 /* interpret as subnegotiation */
;# GA 249 \371 /* you may reverse the line */
;# EL 248 \370 /* erase the current line */
;# EC 247 \367 /* erase the current character */
;# AYT 246 \366 /* are you there */
;# AO 245 \365 /* abort output--but let prog finish */
;# IP 244 \364 /* interrupt process--permanently */
;# BREAK 243 \363 /* break */
;# DM 242 \362 /* data mark--for connect. cleaning */
;# NOP 241 \361 /* nop */
;# SE 240 \360 /* end sub negotiation */
;# EOR 239 \357 /* end of record (transparent mode) */
1;
SHAR_EOF
if test 4290 -ne "`wc -c < 'telnet.pl'`"
then
echo shar: error transmitting "'telnet.pl'" '(should have been 4290 characters)'
fi
fi # end of overwriting check
if test -f 'sock.pl'
then
echo shar: will not over-write existing file "'sock.pl'"
else
cat << \SHAR_EOF > 'sock.pl'
#!/usr/local/bin/perl
package sock;
;# USAGE:
;# ======
;#
;# To open a connection to a socket:
;#
;# $handle = &sock'open($hostname, $port) || die $!;
;# # hostname & port can each be either a name or a number
;#
;# Read and write the same as with any other file handle:
;#
;# print $handle "hello, socket\n";
;# $response = <$handle>;
;#
;# To close cleanly:
;#
;# &sock'close($handle);
;#
;# To close all open sockets, in case of an emergency exit:
;#
;# &sock'close_all;
;#
;# AUTHOR: David Noble (
[email protected])
;# DATE: 11 Feb 1993
;#
;# Modify and use as you see fit, but please leave my name on
;# it as long as it still resembles the original code.
;#
;#############################################################################
;# Get system-specific socket parameters, make assumptions if necessary.
$sockaddr_t = 'S n a4 x8';
eval "require 'sys/socket.ph'";
eval <<'END_SOCKET_DEFINITIONS' if $@;
sub AF_INET { 2; }
sub SOCK_STREAM { 1; }
sub SOL_SOCKET { 65535; }
sub SO_REUSEADDR { 4; }
END_SOCKET_DEFINITIONS
;# Seed the generation of names for file handles.
$latest_handle = 'sock0000000001';
sub open {
local ($remote_host, $remote_port) = @_;
if (!$remote_port) {
$! = "bad arguments to sock'open()";
return 0;
}
$sock = ++$latest_handle;
;# Look up the port if it was specified by name instead of by number.
if ($remote_port =~ /\D/o) {
($name,$aliases,$remote_port) = getservbyname($remote_port,'tcp');
}
;# Look up the address if it was specified by name instead of by number.
if ($remote_host =~ /\D/o) {
($name,$aliases,$type,$len,$remote_addr) = gethostbyname($remote_host);
} else {
$remote_addr = $remote_host;
}
;# Make the socket structures.
$this = pack($sockaddr_t, &AF_INET, 0, "\0\0\0\0");
$remote_sock = pack($sockaddr_t, &AF_INET, $remote_port, $remote_addr);
;# Make the socket filehandle.
($name,$aliases,$proto) = getprotobyname('tcp');
socket($sock, &AF_INET, &SOCK_STREAM, $proto) || return 0;
;# Set up the port so it's freed as soon as we're done.
setsockopt($sock, &SOL_SOCKET, &SO_REUSEADDR, 1);
;# Bind this socket to an address.
bind($sock, $this) || return 0;
;# Call up the remote socket.
connect($sock,$remote_sock) || return 0;
$handles{$sock} = 1;
$oldfh = select($sock); $| = 1; select($oldfh);
return "sock'" . $sock;
}
sub close {
local ($sock) = shift(@_) || return 0;
shutdown ($sock, 2);
delete $handles{$sock};
}
sub close_all {
for $sock (keys %handles) {
shutdown ($sock, 2);
delete $handles{$sock};
}
}
SHAR_EOF
if test 2588 -ne "`wc -c < 'sock.pl'`"
then
echo shar: error transmitting "'sock.pl'" '(should have been 2588 characters)'
fi
fi # end of overwriting check
if test -f 'test_telnet'
then
echo shar: will not over-write existing file "'test_telnet'"
else
cat << \SHAR_EOF > 'test_telnet'
#!/usr/local/bin/perl
#
# test_telnet - simple test of sock.pl and telnet.pl
#
# This opens a telnet connection, attempts to log in as "nobody" with a
# bad password, then leaves the telnet session by sending a CTRL-D.
# The prompt strings are those of a Sun, so you may have to change these.
#
#############################################################################
require 'sock.pl';
require 'telnet.pl';
# routine for clean shutdown on error
sub abort {
&sock'close_all;
die "ended unexpectedly, but shut down cleanly\n";
}
$hostname = "localhost";
$port = "telnet";
$timeout = 1;
$login_prompt = '^login:';
$password_prompt = '^Password:';
#############################################################################
#
# Open the connection
#
$session = &sock'open($hostname,$port) || die $!;
#############################################################################
#
# Get to the login prompt
#
while (1) {
$_ = &telnet'read($session, $timeout);
&abort if &telnet'eof;
print;
last if m/$login_prompt/o;
}
print $session "nobody\n"; # send a login name
#############################################################################
#
# Get the password prompt
#
while (1) {
$_ = &telnet'read($session, $timeout);
&abort if &telnet'eof;
print;
last if m/$password_prompt/o;
}
print $session "boguspw\n"; # send a password
#############################################################################
#
# Get the next login prompt, since the last one one should have failed
#
while (1) {
$_ = &telnet'read($session, $timeout);
&abort if &telnet'eof;
print;
last if m/$login_prompt/o;
}
print $session "\004"; # CTRL-D to abort the telnet session
#############################################################################
#
# Get any exit messages
#
until (&telnet'eof) {
print &telnet'read($session, $timeout);
}
print "\ntest completed\n";
&sock'close($session);
exit (0);
SHAR_EOF
if test 1930 -ne "`wc -c < 'test_telnet'`"
then
echo shar: error transmitting "'test_telnet'" '(should have been 1930 characters)'
fi
chmod +x 'test_telnet'
fi # end of overwriting check
cd ..
# End of shell archive
exit 0
--
+----------------------------------------------------------------------------+
| Michael S. Muegel | Internet E-Mail:
[email protected] |
| UNIX Applications Startup Group | Moto Dist E-Mail: X10090 |
| Corporate Information Office | Voice: (708) 576-0507 |
| Motorola | ... these are my opinions, honest ... |
+----------------------------------------------------------------------------+