Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Out of Band Access Testing

by reyjrar (Hermit)
on Feb 27, 2001 at 12:11 UTC ( [id://61054]=sourcecode: print w/replies, xml ) Need Help??
Category: Networking Code
Author/Contact Info Brad Lhotsky, brad@divisionbyzero.net
Description: given an file of "Site name : 9876543210\n"'s it attempts to dial the site and check for success. Success condition is getting to the router/server prompt AFTER successfully completing a login. I noticed some out of band equipment didn't prompt or username/password and figured that was a security risk. use with -v or -l for full effect.
#!/usr/bin/perl
#
# Purpose:      Dial the OOB on a cisco router
#               and determine the OOB if the
#               OOB line is up and functioning
#               properly
#
# Code by Brad Lhotsky <brad@divisionbyzero.net>
# Based off code by jcwren on http://perlmonks.org
#
# Note: The modem (US Robotics 56k v.90 Fax Sportster)
#       had no support for advanced error messages such
#       as "ring no answer" and various other "higher
#       level" error messages so I am using my own methods
#       to report what error messages get generated.
#
$|++;
use strict;                                     # always
use Device::SerialPort;                         # so we can communicat
+e with modem
use IPC::ShareLite;                             # for sharing memory
use POSIX "sys_wait_h";                         # for flags for waitpi
+d
use Time::HiRes qw(gettimeofday);               # for more error handl
+ing using times
use Time::localtime;                            # for logging date for
+mat crap.
use Getopt::Std;                                # to get command line 
+options

###################
# Defaults, for my
# sanity more than 
# anything else
my $DEF_DEV = '/dev/cua1';                      # set this for fun
my $DEF_USR = '';                        # should be a "read only" use
+r
my $DEF_PWD = '';                      # see above
my $DEF_RTR = 3;                                # for sanity
my $DEF_TMO = 90;                               # for sanity

###################
# configs.
my %OPTS = ();                                  # options hash
getopts("d:l:nr:t:u:p:hva", \%OPTS);            # get options
help() if $OPTS{'h'};
my $DEVICE = $OPTS{'i'} || $DEF_DEV;            # serial port ext. mod
+em is on
my $NOISEDIAL = $OPTS{'n'};                     # Turn on/off the mode
+m speaker
my $BAUD = 9600;                                # our initial baud rat
+e
my $MINBAUD = 9600;                             # if we find a baud le
+ss than this, bitch
my $USERNAME = ($OPTS{'u'} || $DEF_USR) . "\n"; # username for the rou
+ter
my $PASSWORD = ($OPTS{'p'} || $DEF_PWD)."\n";   # password for the rou
+ter
my $MAX_RETRY = $OPTS{'r'} || $DEF_RTR;         # number of times we r
+etry
my $LOG = $OPTS{'l'};                           # log file
my $TIMEOUT = $OPTS{'t'} || $DEF_TMO;           # how long before we d
+etermine failure
my $DIALPREFIX = 91;                            # to get an outside li
+ne

if($LOG) {
        open LOG, ">>$LOG" or die "couldn't open debug.out: $!\n";
        select(LOG); $|++; select('stdout');
}
###################
# Globals.
my %FAILED = ();
my @GOOD = ();

####################
# Modem commands 
# that might be fun.
my $MODEM_HANGUP = "ATH1\n";            # modem hangup, clear the line
+.
my $MODEM_QUIET = "ATM0\n";             # silence the speaker
my $MODEM_SOUND = "ATM1\n";             # unsilence the speaker

####################
# assemble our list
# of modems to dial
my $LIST = $ARGV[0];
die "usage: $0 sitefile ($ -h for more info)\n" unless -e $LIST;
my %SITES = ();         # Global sites hash.

open(CFG, "<$LIST") or die "could not open $LIST: $!\n";
my $TOTALNUM = 0;
while(local $_ = <CFG>) {
        s/^\s+//; s/\s+$//;
        next if !$_ || /^#/;
        ($_, undef) = split /\s*#\s*/;
        next if !$_;
        my ($site,$number) = split/\s*:\s*/;
        next unless $site && $number;
        next if exists $SITES{$site};   # no duplicates over writing c
+rap.
        $SITES{$site} = $number;
        $TOTALNUM++;
}
close CFG;
die "$LIST: empty configuration file!\n" unless $TOTALNUM;
logthis("SYSTEM","Loaded $TOTALNUM numbers to check!");

foreach my $site (sort keys %SITES) {
        print "Testing $site ... " unless $OPTS{'v'};
        my $share = new IPC::ShareLite( -key => 6969,
                                        -create => 'yes',
                                        -destroy => 'no',
                                        -size => 1024) or die "Couldn'
+t share memory: $!\n";
        my $SUCCESS = 0;
        my $REASON = "";
        # using this method of forking
        # is the only way to "next" in a loop using
        # alarm and trapping SIG_ALRM
        # many thanks to mikfire for this idea
        my $pid = fork();
        if($pid == 0) {
                # we're the child, do the grunt work
                exit OOBtest($site,$SITES{$site});
        } elsif(defined $pid) {
                # wait for the child to finish
                my $tmp = waitpid($pid,'');
                # Success/failure returned through OOBtest()
                # gets thrown in $? so lets check it.
                $SUCCESS = $?;
        } else {
                die "fork error: $!\n";
        }
        if(!$SUCCESS) {
                $REASON = $share->fetch() || "Unknown failure conditio
+n";
                $FAILED{$site} = $REASON;
        } else {
                push @GOOD,$site;
        }
        print "done.\n" unless $OPTS{'v'};
} # EOForeach


for(sort keys %FAILED) { print "FAILED: $_ : " . $FAILED{$_} . "\n"; }
for(sort @GOOD) { print "GOOD: $_\n"; }


#################################
# here's where the work is done.
sub OOBtest {
        my ($site,$number) = @_;
        # we'll use this hash to determine whether
        # or not we miss any steps along the way
        my %STAGES = (  "connect" => 0,
                        "login" => 0,
                        "password" => 0,
                        "prompt" => 0
                );
        my @STAGES = ("connect", "login", "password", "prompt");
        my $share = new IPC::ShareLite( -key => 6969,
                                        -create => 'no',
                                        -destroy => 'no',
                                        -size => 1024) or die "couldn'
+t share memory: $!\n";
        $share->store('NONE');
        my $ob = new Device::SerialPort ($DEVICE, 1);
                die "Couldn't access $DEVICE: $!\n" unless $ob;
        $SIG{"ALRM"} = sub {
                                select undef, undef, undef, 0.5;
                                if($share->fetch ne 'NONE') {
                                        my $newmsg = $share->fetch() .
+ ", Timeout after $TIMEOUT seconds";
                                        $share->store($newmsg);
                                } else {
                                        $share->store("Timeout after $
+TIMEOUT seconds");
                                }
                                logthis($site,"FATAL ERROR: Connection
+ Timedout after $TIMEOUT seconds");
                                $ob->close;
                                select undef, undef, undef, 0.5;
                                undef $ob;
                                select undef, undef, undef, 0.5;
                                exit 0; 
                        }; 
        alarm($TIMEOUT);
        ###################
        # setup device
        select undef, undef, undef, 0.5;        # sleep for half a sec
+ond
        $ob->baudrate($BAUD);
        $ob->parity('none');
        $ob->databits(8);
        $ob->stopbits(1);
        $ob->handshake('none');
        $ob->stty_icrnl(1);
        $ob->stty_ocrnl(1);
        $ob->stty_onlcr(1);
        $ob->stty_opost(1);
        $ob->write_settings;
        select undef, undef, undef, 0.5;        # sleep for half a sec
+ond

        $ob->write($MODEM_HANGUP);              # clear the line
        select undef, undef, undef, 1.5;        # sleep for one and ha
+lf a second
        my $buff = $ob->input;
        if($buff =~ /OK/) {
                logthis($site,"INIT: Modem hangup sent, line cleared."
+);
        } else { 
                logthis($site,"INIT ERROR: Modem did not respond to ha
+ngup");
        }
        if(!$NOISEDIAL && !$OPTS{'a'}) {
                $ob->write($MODEM_QUIET);               # silence the 
+modem
                select undef, undef, undef, 1.5;        # sleep for on
+e and half a second
                my $buff = $ob->input;
                if($buff =~ /OK/) {
                        logthis($site,"INIT: Modem sound off.");
                } else {
                        logthis($site,"INIT ERROR: Modem did not respo
+nd to sound off");
                }
        } else {
                $ob->write($MODEM_SOUND);               # make some no
+ise
                select undef, undef, undef, 1.5;        # sleep for on
+e and half a second
                my $buff = $ob->input;
                if($buff =~ /OK/) {
                        logthis($site,"INIT: Modem sound on.");
                } else {
                        logthis($site,"INIT ERROR: Modem did not respo
+nd to sound on");
                }
        }
        select undef, undef, undef, 0.5;        # sleep for half a sec
+ond

        my $dial = $OPTS{'a'} ? "ATDP $DIALPREFIX$number\n" : "ATDT $D
+IALPREFIX$number\n";
        logthis($site,"DIAL STR: $dial"); 
        ########################
        # Setup the environment
        my $RETRY = 0;
        my $DONE = 0;
        my $TOTALIN = 0;
        my $TOTALOUT = 0;
        my $SUCCESS = 0;
        my $ERROR = "";
        my $DIAL_TIME = gettimeofday;
        $ob->write($dial);
        select undef, undef, undef, 0.5;        # sleep for half a sec
+ond
        while(!$DONE) {
                # this next bit of code is purely
                # for my own amusement.
                my ($inbytes,$outbytes) = (0,0);
                (undef, $inbytes, $outbytes, undef) = $ob->status or
                        warn "could not get port status!\n";
                $TOTALIN += $inbytes;
                $TOTALOUT += $outbytes;
                # end amusement
                if(my $line = $ob->input) {
                        $line =~ s/[\r\n\cM]//mg;
                        if($line =~ /CONNECT\s+(\d+)/i) {
                                # We have initialized!
                                $STAGES{"connect"}++;
                                # Check other end's baudrate and 
                                # adjust if need be.
                                if($1 != $BAUD) {
                                        if($1 < $MINBAUD) {
                                                $share->store("Baudrat
+e of $1 is less than minimum ($MINBAUD)");
                                        }
                                        logthis($site,"CONN: baudrate 
+is now $1");
                                        $ob->baudrate($1);
                                        $ob->write_settings;
                                        # pause momentarily
                                        select undef, undef, undef, 1.
+2;
                                }
                                # now we have to "press enter a few ti
+mes"
                                $ob->write("\n\n");
                        } elsif($line =~ /(NO CARRIER)/i || $line =~ /
+(BUSY)/i) {
                                # we failed, keep trying!
                                my $err = $1;
                                if($RETRY > $MAX_RETRY) {
                                        if($line =~ /(BUSY)/i) {
                                                logthis($site,"DIAL ER
+ROR: Remote line busy");
                                                $share->store("Line wa
+s busy");
                                        } else {
                                                # using the time betwe
+en dialing to determine
                                                # local or remote lack
+ of a carrier
                                                my $diff = gettimeofda
+y - $DIAL_TIME;
                                                $diff *= 100;
                                                logthis($site,"DIAL DI
+FF: $diff");
                                                if($diff > 800) {
                                                        $share->store(
+"Local modem had no free line");
                                                } else {
                                                        $share->store(
+"No answer from remote modem");
                                                }
                                        }
                                        logthis($site,"FATAL ERROR: Re
+ached maximum retry for $site");
                                        $DONE++;
                                        last;
                                }
                                $share->store("line status $err (retry
+ $RETRY of $MAX_RETRY)");
                                logthis($site,"ERROR: line status $err
+ (retry $RETRY of $MAX_RETRY)");
                                $RETRY++;
                                my $diff = (gettimeofday - $DIAL_TIME)
+*100;
                                if($diff > 800) {
                                        if($line =~ /BUSY/i) {
                                                logthis($site,"DIAL ER
+ROR: Remote line was busy");
                                        } else {
                                                logthis($site,"DIAL ER
+ROR: Remote modem did not pickup");
                                        }
                                } else {
                                        if($line =~ /BUSY/i) {
                                                logthis($site,"DIAL ER
+ROR: Remote line was busy");
                                        } else {
                                                logthis($site,"DIAL ER
+ROR: Local modem had no free line");
                                        }
                                }
                                $ob->write($MODEM_HANGUP);
                                logthis($site,"REDIAL: clearing line")
+;
                                select undef, undef, undef, 1.5;
                                $DIAL_TIME = gettimeofday;
                                $ob->write($dial);
                                select undef, undef, undef, 0.5;
                                logthis($site,"REDIAL ($RETRY of $MAX_
+RETRY): dialing $number"); 
                        } elsif($line =~ /sername\:/i || $line =~ /ogi
+n\:/i) {
                                # got login prompt
                                $STAGES{"login"}++;
                                # attempt to login
                                $ob->write($USERNAME);
                                logthis($site,"CONN: sent username");
                        } elsif($line =~ /assword\:/i) {
                                # password prompt
                                $STAGES{"password"}++;
                                $ob->write($PASSWORD);
                                logthis($site,"CONN: sent password");
                        } elsif($line =~ /[\w().-]*[\$#>]/) {
                                # prompt
                                # which is all we were looking for!
                                $STAGES{"prompt"}++;
                                logthis($site,"CONN: Got to our prompt
+!");
                                $DONE++;
                                last;
                        }
                }
                select undef, undef, undef, 0.5;        # sleep for ha
+lf a second.
        }
        logthis($site,"Closing serial port.");
        sleep 1;
        $ob->close;
        sleep 1;
        undef $ob;
        logthis($site,"Total bytes transferred: $TOTALIN (in), $TOTALO
+UT (out)","Session closed.");
        my $stageError = 0;
        for(keys %STAGES) { $stageError++ if !$STAGES{$_}; }
        if($stageError) {
                my $logmsg = "FAILED CONNECTION STAGES: ";
                if($share->fetch eq 'NONE') {
                        $share->store("FAILED CONNECTION STAGES: ");
                } else {
                        my $new = $share->fetch() . ", FAILED CONNECTI
+ON STAGES:";
                        $share->store($new);
                }
                foreach my $stage (@STAGES) {
                        if(!$STAGES{$stage}) {
                                $logmsg .= " $stage";
                                my $new = $share->fetch() . " $stage";
                                $share->store($new);
                        }
                }
                logthis($site,$logmsg);
                return 0;
        }
        return 1;
}

sub logthis {
        my $site = shift;
        my @msgs = @_;
        my $date = ctime();
        foreach my $msg (@msgs) {
                $msg =~ s/[\r\n\cM]//g;
                $LOG && print LOG "$date - $site - $msg\n";
                print "$date - $site - $msg\n" if $OPTS{'v'};
        }
}

sub help {
        print <<"       EOHELP";
        OOB Test Dialer v0.1
        --------------------
        Code by:
                Brad Lhotsky <brad\@divisionbyzero.net>
        Distributed under the same license as perl itself,
        use at your own risk, and fight UCITA.

        usage: $0 [-anhv] [-l logfile] [-r maxretry] [-d device] \
                [-t timeout (in seconds)] [-u username] [-p password] 
+\
                filewithnumbers

        options:
                h - display this message
                d <device> - interface, typically /dev/cuaN of /dev/tt
+ySN
                             where N is the port number.
                                default: $DEF_DEV
                l <logfile> - file to append information to
                                default: off
                v - display all log info to STDOUT (can be used with -
+l)
                                default: off
                n - noisy dialing (turn modem speaker on)
                                default: off
                a - annoying (enable pulsa dialing)
                                default: off
                t <timeout> - timeout in seconds
                                default: $DEF_TMO
                r <number> - number of retries per number
                                default: $DEF_RTR
                u <username> - username to login
                p <password> - password to login

        EOHELP
        exit 0;
}

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://61054]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (5)
As of 2024-04-16 05:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found