Net::Telnet and SMTP

C

crr

I originally tried this out over in comp.lang.perl.modules, but didn't
really get anywhere, so I thought I'd take a crack at it over here.

Hi all,

I'm using Net::Telnet to test an SMTP proxy product and I've run into
an issue with it.

First, I'm trying to send a sequence of commands and then log both the
commands sent and the response from the server into a log file. The
responses need to match up EXACTLY to the commands to which they are in
response. Second, I've tried Net::SMTP and it doesn't quite do what I
need. Since the product I'm testing may have to deal with......er,
impolite SMTP clients (read: crackers) I want to make sure that if the
SMTP protocol being sent is broken that the proxy reponds in a correct
(read: secure) way. Unfortunately Net::SMTP doesn't give me the level
of control I need....mostly because it's too polite. ;)

I've included the script and a sample text file (that feeds the scripts
the commands) below. It's been since sometime in v4 since I last did
anything with PERL (unfortunately) so I apologize in advance for the
mess. :)

Thanks,

crr

************************************Script**************************************
#This script reads from a generated smtp test file and writes to a log
file in
#the logs directory, which will be created under the directory from
which the
#test is run. I recommend that you place the test file in the same dir
as the
#script, if for no other reason than that you won't have to maintain in
the
#script the location from which you're running the file.
#
#test file format note:
#The first line of the test file *must* contain the dns name (if you're
using
# DNS) or the IP address of the server under test, followed by a colon
:)) and
# the port to which you're connecting, typically 25
# You can place comments in the test file by prepending the line with a
#. Note
# that this can only be placed at the start of the line
# Each test should beging with "***Start test" (without quotes) and end
with
# "***Stop test"

use strict;
#no strict 'refs';
use Net::Telnet;

#set the test and log file names, and some globals
my $testfilename = "smtptests.txt";
my $t = localtime(time());
my $logfilename = "./logs/smtplog${t}.txt";
our $testcount = 0;
our $debug = 1;

open TESTFILE, $testfilename or die "Cannot open test file,
${testfilename}";

#print $logfilename;
if (-e $logfilename) {
die "Log file ${logfilename} already exists!";
}

mkdir 'logs', 0744;

open LOGFILE, ">${logfilename}" or die "Cannot open log file,
${logfilename}";
if ($debug) {print "*****Created log file\n";}

#grab the server and port out of the first line of the file
my ($server, $port) = split (/\:/, getline(*TESTFILE));

#create the telnet object and set some initial values
our $telnet = new Net::Telnet (Timeout => 660,
Telnetmode => 0,
Host => $server,
Port => $port,
Prompt => '//',
Errmode => 'die');
#need to set the prompt to null, since SMTP doesn't have a prompt,
large timeout is
#for testing our timeout

#timeout exception for debugging the script
$telnet->timeout(30) if ($debug);

if ($debug) {print "*****Telnet object created\n";}

$t = localtime(time());
print LOGFILE "Starting test of server ${server} at ${t}\n";

#begin processing the main part of the file
processfile(*TESTFILE, *LOGFILE);

close TESTFILE;
close LOGFILE;

#print "${server}";
#print $port;

#processfile is where the main work of the program is coordinated. it
reads
#commands from the test file and organizes the starting and stopping of
each
#test
sub processfile {
if ($debug) {print "*****Entering processfile\n";}
my $TF = shift;
my $LOG = shift;
my $line = getline($TF);

if ($line eq eof) {return 0;}

if ($debug) {print $line;}
while ($line ne eof) {
# checks the line and read if it's a command to start a
test
if ($line eq "***Start test\n") {
print "*****Starting test\n" if ($debug);
$testcount++;
print $LOG "Starting test ${testcount}\n";
if (defined(dotest($TF, $LOG))) {
print $LOG "Test ${testcount}
finished\n";
} else {
print $LOG "Test ${testcount}
failed\n";
}#end else
}#end if
#We should never encounter a stop test line in
processfile
if ($line eq "***Stop test\n") {die "Error in test
file!"}

$line = getline($TF);
if ($debug) {print $line;}
}#end while

return 0;

#once we've started a test, dotest actually runs the commands,
including opening
#the telnet session
sub dotest {
my $TF = shift;
my $LOG = shift;
my @response = ();

print "*****Entered dotest\n" if ($debug);

my $line = getline($TF);
$telnet->input_log($LOG);
print $LOG "Recd: ";
$telnet->open();
print "*****Telnet session open\n" if ($debug);

#Try a short delay
delay(.1);

$telnet->get(); #read output for input_log
#my $firstline = $telnet->getline();
#print $LOG "Recd: ${firstline}";

while($line ne "***Stop test\n"){
print $LOG "Sent: ${line}";
print $LOG "Recd: ";
print $LOG $telnet->cmd(String => $line);

#Try a short delay between sending command and reading output
delay(5);
$telnet->getline();

#if (substr($line, 0, 4) eq "ehlo") {
#@response = $telnet->getlines(All => "");
#} else {@response =
$telnet->getline();}

#print $LOG "Recd: @{response}";
$line = getline($TF);
}#end while
#$telnet->dump_log('') if ($debug);

$telnet->cmd(String => localtime(time())) if ($debug);
$telnet->close();

return 0;
}#end dotest

}#end processfile

#Small sub to strip out comments in the test file and exit gracefully
if we've
#reached the end of the input file
sub getline {
my $TF = shift;
my $gotline = 0;
my $line = '';

while (!($gotline)) {
$line = <$TF>;
if (eof($TF)) {
print "*****EOF reached\n" if ($debug);
print $line if ($debug);
exit;}
$gotline = 1;
my @a = split (//, $line);
if ($a[0] eq '#') {$gotline = 0;}
}#end while

#print "${line}";
return $line;

}#end getline

#small sub to insert a variable length time delay
sub delay {
my $delaytime = shift;

my $returntime = (time + $delaytime);
while(1) {
if (time >= $returntime) {return;}
}

}#end delay

************************************text
file*************************************
10.113.15.66:25
#Test comment
***Start test
ehlo test.com
mail from: (e-mail address removed)
rcpt to: (e-mail address removed)
data
To: bob
From: Bill
Subject: test
This is a test email
..
***Stop test
 
M

Mark Clements

I originally tried this out over in comp.lang.perl.modules, but didn't
really get anywhere, so I thought I'd take a crack at it over here.

Hi all,

I'm using Net::Telnet to test an SMTP proxy product and I've run into
an issue with it.

First, I'm trying to send a sequence of commands and then log both the
commands sent and the response from the server into a log file. The
responses need to match up EXACTLY to the commands to which they are in
response. Second, I've tried Net::SMTP and it doesn't quite do what I
need. Since the product I'm testing may have to deal with......er,
impolite SMTP clients (read: crackers) I want to make sure that if the
SMTP protocol being sent is broken that the proxy reponds in a correct
(read: secure) way. Unfortunately Net::SMTP doesn't give me the level
of control I need....mostly because it's too polite. ;)
<snip>
Hi

It isn't clear what your problem is. What issue are you having with your
first requirement and what is the smallest complete program that will
demonstrate this?

I am not particularly familiar with Net::Telnet or Net::SMTP, but I would
be tempted to use Expect.

Mark
 
M

Mike

Mark said:
<snip>
Hi

It isn't clear what your problem is. What issue are you having with your
first requirement and what is the smallest complete program that will
demonstrate this?

I am not particularly familiar with Net::Telnet or Net::SMTP, but I would
be tempted to use Expect.

Mark


Dump_log input_log and Output_log arguments will give you text files
with all the input.. all the output and everything in order both hex and
ascii .

You should be able to see the transactions back and forth no problem..
the text logs are primarily used for debug purposes but will work nicely
for what you need.

Its all in the readme

Mike
 
C

crr

Sorry, it seemed clear when I wrote the message, but then I've been
staring at this problem for two days! :)

My main issue is that the messages returned by the server are not
making it back to be read by the client side. It seems that if I do a
completely valid smtp session, I eventually get all the messages when I
do my final $obj->get(), but I can't get them to appear in-line with
the command that generated them. I also cannot get them to appear when
I do a shortened smtp session (such as when I generate an error) even
when I do a final get() (or getline() or getlines()).

I'm not sure if I'm hitting some sort of buffer limit or what, but the
initial messages don't have this problem. Frex, I get the smtp
greeting immediately, as well as the response to the EHLO command.

Thanks in advance for the help.

crr
 
C

crr

Hi, thanks for the response.

I'm actually using input_log, and I still don't see the messages
returned. Like I mentioned above, I can somtimes get them, but not
until I do my final get() after a complete smtp session, but if I do
something to generate an error (such as issuing smtp commands out of
order, etc.) I cannot get the error message returned by the server,
which is real interest here anyway.

Do you know if input_log has some sort of buffer limit before it writes
to the filehandle? Or is there some reason that it's not writing
immediately, except in the cases of the smtp greeting and the EHLO
response? Even in those cases, I have to do an $obj->get() before the
responses will be logged.

Thanks again for the help,

crr
 
A

A. Sinan Unur

Sorry, it seemed clear when I wrote the message,

Similarly, the context may seem clear to you now, when you are writing the
message, but *please* quote the appropriate amount of context when you
post a reply so that your readers can understand you without having to
hunt for messages (i) that may never have arrived on their newsserver,
(ii) may not have arrived on their newsserver yet, (iii) may have expired
on their newsserver, or people may simply not have threading on.

Google Groups is great as an archive but makes a lousy platform for
participating in UseNet discussions. You will have to make an extra effort
and follow established UseNet practice.

For help on how to make sure you can maximize the usefulness of this group
to you, please read the posting guidelines.

Sinan
 
M

Mike

crr said:
Hi, thanks for the response.

I'm actually using input_log, and I still don't see the messages
returned. Like I mentioned above, I can somtimes get them, but not
until I do my final get() after a complete smtp session, but if I do
something to generate an error (such as issuing smtp commands out of
order, etc.) I cannot get the error message returned by the server,
which is real interest here anyway.

Do you know if input_log has some sort of buffer limit before it writes
to the filehandle? Or is there some reason that it's not writing
immediately, except in the cases of the smtp greeting and the EHLO
response? Even in those cases, I have to do an $obj->get() before the
responses will be logged.

Thanks again for the help,

crr


Yeah .. you will see input in the input log.. any returned messages will
be in output log... use it and see.
 
C

crr

Jim said:
If you don't follow these guidelines, you will reduce the probability
of getting good answers. Many of the most knowledgable Perl experts
here have stopped reading posts from Google users because they too
often violate the guidelines for this group. You should consider using
another source for Usenet groups and using a real newsreader that does
not have these problems.

Bleh, sorry....I don't typically use Google Groups, so I wasn't aware
that it's...er, impolite....and wasn't aware how to make Google quote a
reply. Unfortunately, Google is my only choice here at work for
UseNet, so there we are. I'll take more care in the future when using
Google Groups, though hopefully I won't have to use it very often
(painful is not the word for it).

Thanks,

crr
 
C

crr

A. Sinan Unur said:
@g43g2000cwa.googlegroups.com:
Google Groups is great as an archive but makes a lousy platform for
participating in UseNet discussions. You will have to make an extra effort
and follow established UseNet practice.

Amen to *that*, and if I had any other choice, I'd be using my nice
sane newsreader at home, but alas, alack, welladay and so forth, I'm
stuck with Google here at work. I will make an effort to use Google
more politely, now that I know how to get it to do what I want.

Thanks,

crr
 
A

A. Sinan Unur

Amen to *that*, and if I had any other choice, I'd be using my nice
sane newsreader at home, but alas, alack, welladay and so forth, I'm
stuck with Google here at work. I will make an effort to use Google
more politely, now that I know how to get it to do what I want.

Thanks,

No, thank you very much for understanding our concern, and doing your
share.

Sinan
 
C

crr

Mike said:
Yeah .. you will see input in the input log.. any returned messages will
be in output log... use it and see.

I've tried both input_log and output_log, but still get bubkiss. I
also don't even see the responses with dump_log. Also, it seems like
it's reversed from what I would consider logical. Which is to say, the
input_log shows what's returned and the output_log shows the commands
I'm sending.

I just double checked it, and it does work reverse of what I would
expect. So if I do output_log instead of input_log, I get the commands
I'm sending, and if I do input_log instead of output_log I get (some)
of the responses to the commands I send, but only if I do a get() or
getline() after the command.

Any thoughts on why that is?

Thanks,

crr
 
C

crr

Mark said:
Hi

It isn't clear what your problem is. What issue are you having with your
first requirement and what is the smallest complete program that will
demonstrate this?

I am not particularly familiar with Net::Telnet or Net::SMTP, but I would
be tempted to use Expect.

I've started looking into Expect, and it seems like it might make the
task a bit easier; unfortunately my time window is too short (read:
this week) for me to get up to speed enough on Expect to be able to
port it over there, since I've not used Expect or tcl before.

Well, mostly my problem is that I'm not getting the responses back
immediately from the commands I'm sending to the server. In some cases
(the SMTP greeting and the response to the EHLO) I get the response
back immediately (either through input_log or by get()/getline()) and
in the case of the others (mail from:, rcpt to:, data, etc) I don't get
any responses until the the final get() or getline() whether I'm using
input_log or not.
From what I can tell, input_log should give me the responses of the
server as they happen, but instead it seems to be queuing them up and
spitting them all at me, but not until I do a get(). Frex, if I leave
of the last get(), I never get the response to the mail from: rcpt to:,
data or quit commands.

To give you an idea, here's what the resultant log file looks like,
below that I've included what it should look like:

Thanks again for all the help.

crr


Starting test of server 10.113.15.66 at Mon Jun 20 10:28:38 2005
Starting test 1
220 ACE66.roke.com ESMTP Service (Lotus Domino Release 6.5.3) ready at
Mon, 20 Jun 2005 10:33:54 -0400
ehlo test.com
250-ACE66.roke.com Hello test.com ([10.113.15.67]), pleased to meet you
250-HELP
250-SIZE
250 PIPELINING
mail from: (e-mail address removed)
rcpt to: (e-mail address removed)
data
To: bob
From: Bill
Subject: test
This is a test email
Mon Jun 20 10:28:58 2005
..
quit
250 (e-mail address removed)... Sender OK
250 (e-mail address removed)... Recipient OK
354 Enter message, end with "." on a line by itself
250 Message accepted for delivery
221 ACE66.roke.com SMTP Service closing transmission channel
Test 1 finished


What it *should* look like is this:

Starting test of server 10.113.15.66 at Mon Jun 20 10:28:38 2005
Starting test 1
220 ACE66.roke.com ESMTP Service (Lotus Domino Release 6.5.3) ready at
Mon, 20 Jun 2005 10:33:54 -0400
ehlo test.com
250-ACE66.roke.com Hello test.com ([10.113.15.67]), pleased to meet you
250-HELP
250-SIZE
250 PIPELINING
mail from: (e-mail address removed)
250 (e-mail address removed)... Sender OK
rcpt to: (e-mail address removed)
250 (e-mail address removed)... Recipient OK
data
354 Enter message, end with "." on a line by itself
To: bob
From: Bill
Subject: test
This is a test email
Mon Jun 20 10:28:58 2005
..
250 Message accepted for delivery
quit
221 ACE66.roke.com SMTP Service closing transmission channel
Test 1 finished
 
M

Mike

crr said:
I've tried both input_log and output_log, but still get bubkiss. I
also don't even see the responses with dump_log. Also, it seems like
it's reversed from what I would consider logical. Which is to say, the
input_log shows what's returned and the output_log shows the commands
I'm sending.

I just double checked it, and it does work reverse of what I would
expect. So if I do output_log instead of input_log, I get the commands
I'm sending, and if I do input_log instead of output_log I get (some)
of the responses to the commands I send, but only if I do a get() or
getline() after the command.

Any thoughts on why that is?

Thanks,

crr
Ahhhh so its not being flushed...

Sometimes when dealing with routers and other devices i have to send a
carriage return after a command in order to get the result of the
previous command. I believe there is a way to change this behavior.. but
i have been content with just sending carriage returns..

$connection->print("");

Mike
 
C

crr

Mike said:
Ahhhh so its not being flushed...

Sometimes when dealing with routers and other devices i have to send a
carriage return after a command in order to get the result of the
previous command. I believe there is a way to change this behavior.. but
i have been content with just sending carriage returns..

Hrm, well it seems to be a bit more complex than that. I wrote up a
quick test script to see if I could work out somethings without mucking
about with my full script. The first test I wrote up worked great,
with perfectly interspersed commands and responses, just like I need.
I then proceeded to implement these changes into my main script, but I
still see the same erroneous behaviour from before where I only see the
smtp greeting and the response to the ehlo command.

So I created a second script that includes all the calls to the telnet
object in a subroutine (much like my main script) and see the same
problems again, but this time I noted that the email isn't going
though. Long story short (too late, I know) I realized I was passing
the arrays incorrectly.

For whatever reason, it definitely seems to like my reading the files
separately into an array and then issuing the commands from that,
rather than reading them directly from the file. Could be I introduce
some sort of a race between the IO to and from the files and telnet
object.

For those interested, I'll include the shorter script that does the
trick, which I've backstitched into my main script.

Thanks again for the help.

crr

********************Script************************************
use strict;
use warnings;
use Net::Telnet;

my $server = '10.113.15.66';
my $port = '25';
my $t = localtime(time());
my $logfilename = "./logs/smtplog${t}.txt";
my @smtp = ("ehlo test.com", "mail from: bob\@test.com", "rcpt to:
nadmin\@ace66.roke.com", "data", "From: bob", "To: bill", "Subject:
This is a test", "This is a test email!", ".", "quit");
our $stopreading = 0;
my @smtp2 = ("ehlo test.com", "mail from: bob\@test.com", "data",
"quit");

if (-e $logfilename) {
die "Log file ${logfilename} already exists!";
}
mkdir 'logs', 0744;

open LOGFILE, ">${logfilename}" or die "Cannot open log file,
${logfilename}";

our $telnet = new Net::Telnet (Timeout => 30,
Telnetmode => 0,
Host => $server,
Port => $port,
Prompt => '//',
Errmode => 'die',
Binmode => 0,
Input_log => *LOGFILE,
Output_log => *LOGFILE);

dotest(\@smtp, \@smtp2);


sub dotest {

my ($smtpcommands, $smtpcommands2) = @_;

$telnet->open;
delay(2);
$telnet->get;

foreach my $element (@{$smtpcommands}) {
$telnet->print($element);
delay(2);
#don't want to wait for responses after we read the response to the
#data command, since there ain't none, but we want to start reading
#again once we hit the end of the data section
if ($element eq ".") {$stopreading = 0;}
unless($stopreading) {$telnet->get;}
if ($element eq "data") {$stopreading = 1;}
}#end foreach

$telnet->open;
delay(2);
$telnet->get;
foreach my $element (@{$smtpcommands2}) {
$telnet->print($element);
delay(2);
if ($element eq ".") {$stopreading = 0;}
unless($stopreading) {$telnet->get;}
if ($element eq "data") {$stopreading = 1;}
}#end foreach
}#end dotest

#small sub to introduce a delay in the code
sub delay {
my $delaytime = shift;
my $returntime = (time() + $delaytime);

while (1) {
if (time() >= $returntime) {return;}
}#end while
}#end delay
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Problems with "show tech" using the Net::Telnet Module 5
timeout problem with Net::Telnet 5
Net::SMTP errors 3
Remote SSH and Configuring code help 0
Telnet to cisco 3
net telnet mount 2
TF-IDF 2
net/smtp 6

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top