#!/usr/bin/perl

use strict;
use warnings;

use Getopt::Std;
use Data::Dumper;
use IO::Socket::INET;
use Net::SSLeay qw/XN_FLAG_RFC2253 ASN1_STRFLGS_ESC_MSB/;
use Time::Piece;

Net::SSLeay::randomize();
Net::SSLeay::load_error_strings();
Net::SSLeay::ERR_load_crypto_strings();
Net::SSLeay::SSLeay_add_ssl_algorithms();

# --- commandline options and global variables

our $opt_p;
getopts ("p:");

my $port = $opt_p || 443;

# check for less than 2 weeks remaining
my $check = time() + 2*7*24*3600;

my $summary;
my $fullmessage;

# --- subroutines


sub get_cert_details {
  my $host = shift;
  my $x509 = shift;

  my $cnmatch = 0;
  my $rv = {};
  my $flag_rfc22536_utf8 = (XN_FLAG_RFC2253) & (~ ASN1_STRFLGS_ESC_MSB);

  if(!$cnmatch)
  {
    my @arr = Net::SSLeay::X509_get_subjectAltNames($x509);
    foreach (@arr)
    {
      if($_ eq $host)
      {
        $cnmatch = 1;
        last;
      }
    }
  }


  my $notbeftmp = Net::SSLeay::P_ASN1_TIME_get_isotime(Net::SSLeay::X509_get_notBefore($x509));
  my $notbef = Time::Piece->strptime($notbeftmp, "%Y-%m-%dT%H:%M:%SZ");
  if($notbef->epoch > time())
  {
    $summary .= "$host(post dated) ";
    $fullmessage .= "$host has not before date $notbeftmp in future.\n";
  }
  my $notafttmp = Net::SSLeay::P_ASN1_TIME_get_isotime(Net::SSLeay::X509_get_notAfter($x509));
  my $notaft = Time::Piece->strptime($notafttmp, "%Y-%m-%dT%H:%M:%SZ");
  if($notaft->epoch < time())
  {
    $summary .= "$host(expired) ";
    $fullmessage .= "$host expired at $notafttmp.\n";
  }
  elsif($notaft->epoch < $check)
  {
    $summary .= "$host(expires soon) ";
    $fullmessage .= "$host expires at $notafttmp.\n";
  }

  my $ext_count = Net::SSLeay::X509_get_ext_count($x509);
  for my $i (0..$ext_count-1) {
    my $ext = Net::SSLeay::X509_get_ext($x509,$i);
    my $asn1_object = Net::SSLeay::X509_EXTENSION_get_object($ext);
    my $nid = Net::SSLeay::OBJ_obj2nid($asn1_object);
    if(Net::SSLeay::OBJ_nid2ln($nid) eq "commonName" && Net::SSLeay::X509V3_EXT_print($ext) eq $host)
    {
      $cnmatch = 1;
    }
  }

  return $rv;
}


foreach my $host (@ARGV)
{
  my $sock = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $port, Proto => 'tcp');
  if(!$sock)
  {
    $summary .= "$host(unavailable) ";
    $fullmessage .= "Can't connect to $host port $port via TCP.\n";
    next;
  }

  my $ctx = Net::SSLeay::CTX_new() or die "ERROR: CTX_new failed";
  my $ssl;
  if(not $ctx or not Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL) or not ($ssl = Net::SSLeay::new($ctx)) or not Net::SSLeay::set_fd($ssl, fileno($sock)))
  {
    $summary .= "$host(Can't setup SSL) ";
    $fullmessage .= "Can't setup SSL for $host, internal error?\n";
    next;
  }
  if(not Net::SSLeay::connect($ssl))
  {
    $summary .= "$host(Can't connect SSL) ";
    $fullmessage .= "Can't connect SSL for $host, internal error?\n";
    next;
  }
  my $x509 = Net::SSLeay::get_peer_certificate($ssl);
  if(not $x509)
  {
    $summary .= "$host(no SSL cert) ";
    $fullmessage .= "$host port $port doesn't give certificate, not SSL server?\n";
    next;
  }

  my $cert_details = get_cert_details($host, $x509);
}

if($summary)
{
  print "$summary\n";
  print $fullmessage;
  exit(1);
}
