#!/usr/bin/perl # nagios: -epn # $Id: check_whois,v 1.24 2018/05/01 17:31:04 wessels Exp $ # # check_whois # # nagios plugin to check the expiration date of a domain. # Copyright (c) 2008, The Measurement Factory, Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # Neither the name of The Measurement Factory nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # usage # # define command { # command_name check-whois # command_line /usr/local/libexec/nagios-local/check_whois $HOSTADDRESS$ # } # # define service { # name whois-service # check_command check-whois # ... # } # # define host { # use dns-zone # host_name zone.example.com # alias ZONE example.com # } # # define service { # use whois-service # host_name zone.example.com # } # AUTHORS and CONTRIBUTORS: # Duane Wessels # Matt Christian # John Lines use strict; use warnings; use Getopt::Std; use Date::Manip; use POSIX qw(strftime); use Env '@PATH'; my %opts; getopts('xds:', \%opts); my $string_found = 0; my $name = shift or die usage(); $name =~ s/^zone\.//; my $whoiscmd = findwhois(); print STDERR "$0 version ". '$Id: check_whois,v 1.24 2018/05/01 17:31:04 wessels Exp $'. "\n" if $opts{d}; print STDERR "Whois command = $whoiscmd\n" if $opts{d}; grok($whoiscmd, $name); exit 0; sub findwhois { # List of whois commands to find (in order) my @whoiscmds = qw( jwhois whois ); foreach my $wc (@whoiscmds) { if (grep { -x "$_/$wc" } @PATH) { $wc .= ' -n ' if ($wc eq 'jwhois'); return $wc; } } die "Could not find a whois command!\n"; } # # the ~ 2013-14-15 era New GTLDs have whois servers at whois.nic.$tld # and they are slow to be configured in either whois clients or the # whois-servers.net zone. # sub findnewgtldwhois($) { my $name = shift; my @x = split /\./, $name; my $tld = pop @x; my $whois = "whois.nic.$tld"; return gethostbyname($whois) ? $whois : undef; } sub not_in_whois($) { my $line = shift; return 1 if $line =~ /^no match for/; return 1 if $line =~ /^not found/; return 0; } sub grok { my $whoiscmd = shift || die; my $name = shift || die; my $arg = $name; if ($name =~ /\.name$/) { # In order to get useful information out of # the .NAME whois server, the query must # be reformatted $arg = "'domain = $name'"; } open (CMD, "$whoiscmd $arg|") || die; my $registrar = undef; my $whoisservice = undef; my $expires = undef; my $status = ''; my $state = 0; my $lineno = 0; print STDERR "checking $name\n" if $opts{d}; while () { $lineno++; tr/A-Z/a-z/; print STDERR "# $_" if $opts{d}; if ($lineno == 1 && not_in_whois($_)) { critical("$name not found in whois"); } $registrar = $1 if (/registrar:\s*(\S.*)/); if (!defined($registrar)) { $registrar = $1 if (/registrar id:\s*(\S.*)/); $registrar = $1 if (/registrar handle\.*:\s*(\S.*)/); $registrar = $1 if (/\s+(.*)\s*\[tag\s*=.*\]/); # # CIRA whois output if ($name =~ /\.ca$/ && /^registrar:/) { $_ = ; tr/A-Z/a-z/; $registrar = $1 if (/name:\s+(.*)/); } } if (!defined($whoisservice)) { $whoisservice = $1 if (/the\s+(\w+)\s+whois\s+database/); $whoisservice = $1 if (/^\s+(\w+)\s+whois\s+service/); $whoisservice = $1 if (/record\s+maintained\s+by:\s+(\S.*)/); } if (/expiration date:\s*(\d\d-\w\w\w-\d\d\d\d)/) { $expires = $1; } elsif (/(record )?(will )?expires? on[ :](\S.*\S)\.?$/) { $expires = $3; } elsif (/expires:\s+([-\w\s]+)/) { $expires = $1; } elsif (/(domain )?expiration date:\s+(\S.*\S)/) { $expires = $2; } elsif ($name =~ /\.gov\.uk$/ && /renewal date:/) { chomp($_ = ); tr/A-Z/a-z/; $expires = $_; } elsif (/(expiry|renewal) date:\s+(\S.*\S)/) { $expires = $2; } if (defined $opts{s}) { $string_found = 1 if (/$opts{s}/i); } } close(CMD); printf STDERR "Line %d: expires='%s'\n", __LINE__, $expires if $opts{d}; # Remove any leading/trailing whitespace # $expires =~ s/^\s+// if defined $expires; $expires =~ s/\s+$// if defined $expires; # Remove any trailing dot from the expire time # $expires =~ s/\.$// if defined $expires; printf STDERR "Line %d: expires='%s'\n", __LINE__, $expires if $opts{d}; if (defined $registrar) { $registrar = 'gandi' if ($registrar eq 'r42-lror'); $registrar = 'go daddy' if ($registrar =~ /go daddy/); $registrar = 'go daddy' if ($registrar eq 'r91-lror'); } elsif (defined $whoisservice) { $registrar = $whoisservice . ' whois service'; } elsif ((my $newgltdwhois = findnewgtldwhois($name)) && $whoiscmd !~ / -h /) { print STDERR "retrying with -h $newgltdwhois\n" if $opts{d}; $whoiscmd .= " -h $newgltdwhois"; grok($whoiscmd, $name); return; } elsif (defined $opts{x}) { $registrar = 'UNKNOWN'; } else { critical("Didn't find Registrar"); } # Date::Manip doesn't like the DD-MM-YYYY format, so we reformat it # as YYYY-MM-DD. Also month might not be numeric. # if ($expires =~ /-\w+-\d\d\d\d$/) { my @p = split(/-/, $expires); if ($p[1] =~ /^[a-zA-Z]+$/ || $p[0] > 12) { # DD-MM-YYYY $expires = join('-', $p[2], $p[1], $p[0]); } elsif ($p[0] =~ /^[a-zA-Z]+$/ || $p[1] > 12) { # MM-DD-YYYY $expires = join('-', $p[2], $p[0], $p[1]); } else { # Unknown, assume DD-MM-YYYY $expires = join('-', $p[2], $p[1], $p[0]); } } printf STDERR "Line %d: expires='%s'\n", __LINE__, $expires if $opts{d}; # Network Solutions has a bug in their whois output, returning dates like this: # Registrar Registration Expiration Date: Fri Jul 21 00:00:00 EDT 2023T00:00:00Z # if ($expires =~ /[a-z]{3} ([a-z]{3}) (\d\d) 00:00:00 \S+ (\d\d\d\d)T/i) { $expires = join('-', $2,$1,$3); } printf STDERR "Line %d: expires='%s'\n", __LINE__, $expires if $opts{d}; # Name.com returns dates like this: 2018-12-13T21:27:28-07:00Z # Just remove the timezone offset and call it good enough # if ($expires =~ /(\d{4}-\d\d-\d\dt\d\d:\d\d:\d\d)-\d\d:\d\dz/i) { $expires = $1; } printf STDERR "Line %d: expires='%s'\n", __LINE__, $expires if $opts{d}; my $t; if (defined $expires) { $expires =~ s/-t/ /i; $expires =~ s/z$//i; printf STDERR "Line %d: expires='%s'\n", __LINE__, $expires if $opts{d}; $t = UnixDate($expires, "%s"); critical("Invalid expiration time '$expires'") unless defined $t; critical("Invalid expiration time '$expires' (t=$t)") if ($t < 0); $expires = strftime("%Y-%m-%d", localtime($t)); } elsif (defined $opts{x}) { $t = time + (86400 * 90); $expires = 'UNKNOWN'; } else { critical("Didn't find expiration timestamp"); } if (defined $opts{s} && 0 == $string_found) { critical ("String '$opts{s}' not found in whois output"); } my $tense = $t < time ? 'd' : 's'; critical("Expire$tense $expires at $registrar") if ($t - time < (86400*7)); warning ("Expire$tense $expires at $registrar") if ($t - time < (86400*28)); success ("Expires $expires at $registrar"); } sub success { output('OK', shift); exit(0); } sub warning { output('WARNING', shift); exit(1); } sub critical { output('CRITICAL', shift); exit(2); } sub output { my $state = shift; my $msg = shift; printf "WHOIS %s: %s\n", $state, $msg; } sub usage { "usage: $0 [-xd] [-s string] domain\n". "\t-d\tDebugging\n". "\t-x\tDon't complain if Registrar cannot be determined.\n". "\t-s str\tRequire that the string 'str' appear in the whois output\n"; }