http://www.perlmonks.org?node_id=11148050

redapplesonly has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks, first-time poster, and Perl newbie here. (I'm a Java & C coder.) Thank you for reading my question.

So I am struggling to write a Perl script that uses telnet to see if a remote host is responsive. This script needn't be complex; basically, this is a "Are you alive?" test done on the remote machine. (I would normally use ICMP/ping, but my network traffic must pass through my company's firewall.) A bit of a spoiler; this question leans a lot on how to interpret the Net::Telnet module documentation. (https://metacpan.org/pod/Net::Telnet)

First, here's the behavior I'm hoping to emulate. If I manually telnet to a remote host from the command line, it looks like this:

me@ubuntu01$ telnet 10.0.0.1 1234 Trying 10.0.0.1... Connected to 10.0.0.1. Escape character is '^]'. SSH-2.0-OpenSSH_7.4

That "SSH-2.0-OpenSSH_7.4" message tells me that the telnet session is successful, and the remote server opened a valid TCP/IP connection for me. I will have to manually issue a ^C character to break out of the session.

Okay, here's an example of an trying to contact a server which is unavailable:

me@ubuntu01$ telnet 10.0.0.2 1235 Trying 10.0.0.2...

Here, I get nothing back, because the remote host is down. The telnet session will ultimately fail.

So now, I contimplate how to automate these actions in Perl. Ultimately, I'd like to have a subroutine that could take a remote host by IP address (expressed as a string) and then try my telnet test:

#!/usr/bin/perl use warnings; use strict; use Net::Telnet; sub attemptTelnetCheck { # host is a string, expressing an IPv4 address: my ($host) = @_; # Create a telnet object to use TCP port 1234, timeout 3 seconds: my $telnetObj = new Net::Telnet( Port => '1234', Timeout => 3 ); # Set errormode to "return error message upon failure", not die im +mediately: # (I'm not sure this works) my $telnetMode = $telnetObj->errmode("return"); # Try to open telnet session to host: $telnetObj->open($host); # How do I check to see if $telnetObj is valid??? if(exists($telnetObj)) # Line 22 { # Close telnet session: $telnetObj->print('exit'); # Return TRUE: return 1; } else { # Failure! Return FALSE: return 0; } } if(attemptTelnetCheck('10.0.0.1') { print "SUCCESS!"; } else { print "failure."; }

So right away, you can prob tell that I really don't know what I'm doing. I've made my best attempt. Right now, the code fails with the error message:

exists argument is not a HASH or ARRAY element or a subroutine at ./te +lnetTest.perl line 22.

Line 22 is:

if(exists($telnetObj))

I've been reading and rereading the Net::Telnet module documentation over and over, and I'm getting no-where. Does anyone have any practical advice? I'll take whatever I can get. Thank you.

Replies are listed 'Best First'.
Re: Perl Script to Test Telnet Connectivity
by sectokia (Pilgrim) on Nov 09, 2022 at 02:07 UTC

    This will do what you have requested: Connect via TCP and receive the first message sent form the server (which is the 'SSH-2.0-OpenSSH_7.4' you are after):

    use IO::Socket::INET; # Connect to TCP socket my $socket = new IO::Socket::INET( PeerHost => '10.0.0.1', PeerPort => '1234', Proto => 'tcp', Timeout => 3 ); if ($socket) { # We are connected... my $buffer = ""; my $length = 1024; $socket->recv($buffer, $length); $socket->close(); print "OK - got message '$buffer'"; } else { # Connection failed print "Failed"; }

      This looks great, thank you! I was tempted to use from-scratch socket programming, but didn't know how to start...

        I'd like to add that the TCP method is better anyway. SSH doesn't actually talk a telnet-compatible protocol, it just happens to show you something useful when you connect with a standard telnet client. You shouldn't rely on it being compatible with a module that expects an actual telnet server on the other end.
Re: Perl Script to Test Telnet Connectivity
by VinsWorldcom (Prior) on Nov 08, 2022 at 23:58 UTC

    You're very close - as LanX points out, I think defined is what you want.

    Something like this (which is not far off from what you already have):

    #!perl use strict; use warnings; use Net::Telnet; sub attemptTelnetCheck { my ($host) = @_; my $t = new Net::Telnet( Errmode => 'return', Timeout => 2, Port => 1234 ); my $r = $t->open($host); if (defined $r) { $t->close(); print("$host : SUCCESS!\n"); } else { print("$host : failure. " . $t->errmsg() . "\n"); } } my @hosts = ( "192.168.10.1", "192.168.10.2" ); for my $host (@hosts) { attemptTelnetCheck($host); }
    Cheers.

      Yes, this is fantastic! I did want defined, I just didn't know it when I was fumbling for an answer on Google. Thank you, I've fitted your code into my code, and its working perfectly. I owe you a beer!

Re: Perl Script to Test Telnet Connectivity
by sectokia (Pilgrim) on Nov 09, 2022 at 01:44 UTC

    If your intention is to simply determine if or not TCP opens on a port (i.e. you don't care about the protocol being transmitted on TCP), this can be done simply with TCP Ping (which just establishes TCP and then closes the connection).

    Example:

    use AnyEvent::Ping::TCP; my $t = tcp_ping '10.0.0.2', 1235; my $result = defined $t ? "OK" : "Failed"; print $result."\n";

      This is a great chunk of code! Unfortunately, I couldn't install the module thanks to our company firewll, but I'm saving this to my notes nonetheless. Much appreciated!

Re: Perl Script to Test Telnet Connectivity
by LanX (Saint) on Nov 08, 2022 at 22:12 UTC
    Welcome to the monastery! :)

    DISCLAIMER: I never used that module!

    But it's obvious that your use of exists on scalar values is weird.

    from this documentation of Net::Telnet ...

    > When mode is "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 errmsg().

    ... I suppose you want to used defined instead.

    FWIW: Another way is to keep the error-mode "die" and to catch the exception with a surrounding block eval (that's a try-catch in newest Perl).

    Like that you'd get better diagnostics what went wrong.

    Hope this helps! :)

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

      It did help, and thanks Rolf. So the defined keyword is a new one for me, but it made all the difference here. This is an awesome site...! Thank you!

Re: Perl Script to Test Telnet Connectivity
by hippo (Bishop) on Nov 09, 2022 at 10:06 UTC
    Hello Monks, first-time poster, and Perl newbie here.

    Hello redapplesonly and welcome to the Monastery.

    You have good, technical replies already. I just wanted to thank you as a first-time poster for writing such a well-formed post, both in terms of the content and the formatting. By including a clear problem statement, what you tried and how it failed you have given the monks every opportunity to help you. And this is borne out by the swift and helpful replies you have received.

    I hope that the responses encourage you to stick around here and make the most from what the Monastery has to offer.


    🦛

      Aw, shucks, thanks Hippo. I have to say, you Monks are so much nicer than the yowling that I get whenever I post on StackOverflow. And your solutions were dead-on; I had multiple examples and suggestions to play with. That's great, I'm well on my way to finishing my assignment.

      You.

      Guys.

      Rock!!!

Re: Perl Script to Test Telnet Connectivity
by ibm1620 (Hermit) on Nov 11, 2022 at 15:16 UTC
    I offer this as something to keep in your back pocket if you need to automate more elaborate dialogs with Perl: Expect.pm is a great tool to know about.

    Expect.pm is built to either spawn a process or take an existing filehandle and interact with it such that normally interactive tasks can be done without operator assistance.
    The CPAN doc even contains some telnet examples.