#!/usr/bin/env perl
#
# InspIRCd -- Internet Relay Chat Daemon
#
#   Copyright (C) 2020 Sadie Powell <sadie@witchery.services>
#
# This file is part of InspIRCd.  InspIRCd is free software: you can
# redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#


BEGIN {
	require 5.10.0;
}

use feature ':5.10';
use strict;
use warnings FATAL => qw(all);

use IO::Socket();
use IO::Socket::SSL();

use constant {
	CC_BOLD  => -t STDOUT ? "\e[1m"    : '',
	CC_RESET => -t STDOUT ? "\e[0m"    : '',
	CC_GREEN => -t STDOUT ? "\e[1;32m" : '',
	CC_RED   => -t STDOUT ? "\e[1;31m" : '',
};

if (scalar @ARGV < 2) {
	say STDERR "Usage: $0 <hostip> <port> [selfsigned]";
	exit 1;
}

# By default STDOUT is only flushed at the end of each line. This sucks for our
# needs so we disable it.
STDOUT->autoflush(1);

my $hostip = shift @ARGV;
if ($hostip =~ /[^A-Za-z0-9.:]/) {
	say STDERR "Error: invalid hostname or IP address: $hostip";
	exit 1;
}

my $port = shift @ARGV;
if ($port =~ /\D/ || $port < 1 || $port > 65535) {
	say STDERR "Error: invalid TCP port: $port";
	exit 1;
}

my $self_signed = shift // '' eq 'selfsigned';

print "Checking whether ${\CC_BOLD}$hostip/$port${\CC_RESET} is reachable ... ";
my $sock = IO::Socket::INET->new(
	PeerAddr => $hostip,
	PeerPort => $port,
);

unless ($sock) {
	say <<"EOM";
${\CC_RED}no${\CC_RESET}

It seems like the server endpoint you specified is not reachable! Make sure that:

  * You have specified a <bind> tag in your config for this endpoint.
  * You have rehashed or restarted the server since adding the <bind> tag.
  * If you are using a firewall incoming connections on TCP port $port are allowed.
  * The endpoint your server is listening on is not local or private.

See https://docs.inspircd.org/3/configuration/#ltbindgt for more information.
EOM
	exit 1;
}

say "${\CC_GREEN}yes${\CC_RESET}";
print "Checking whether ${\CC_BOLD}$hostip/$port${\CC_RESET} is using plaintext ... ";
my $error = $sock->recv(my $data, 1);

if ($error) {
	say <<"EOM";
${\CC_RED}error${\CC_RESET}

It seems like the server dropped the connection before sending anything! Make sure that:

  * The endpoint you specified is actually your IRC server.
  * If you are using a firewall incoming data on TCP port $port are allowed.

See https://docs.inspircd.org/3/configuration/#ltbindgt for more information.
EOM
	exit 1;
} elsif ($data =~ /[A-Z:@]/) {
	say <<"EOM";
${\CC_RED}yes${\CC_RESET}

It appears that the server endpoint is using plaintext! Make sure that:

  * You have one or more of the following modules loaded:
    - ssl_gnutls
    - ssl_openssl
    - ssl_mbedtls

  * If you have specified one or more <sslprofile> tags then the value of
    <bind:ssl> is the same as an <sslprofile:name> field. Otherwise, it should
    be set to "gnutls" for the ssl_gnutls module, "openssl" for the ssl_openssl
    module, or "mbedtls" for the ssl_mbedtls module.

  * If you have specified the name of an <sslprofile> in <bind:ssl> then the
    value of <sslprofile:provider> is set to "gnutls" if using the ssl_gnutls
    module, "openssl" if using the ssl_openssl module, or "mbedtls" if using
    the ssl_mbedtls module.

  * If you have your SSL configuration in a file other than inspircd.conf then
    that file is included by inspircd.conf.

See the following links for more information:

  https://docs.inspircd.org/3/modules/ssl_gnutls/#configuration
  https://docs.inspircd.org/3/modules/ssl_mbedtls/#configuration
  https://docs.inspircd.org/3/modules/ssl_openssl/#configuration
EOM
	exit 1;
}

$sock->close();
say "${\CC_GREEN}no${\CC_RESET}";
print "Checking whether ${\CC_BOLD}$hostip/$port${\CC_RESET} can have an SSL session negotiated ... ";
$sock = IO::Socket::SSL->new(
	PeerAddr => $hostip,
	PeerPort => $port,
	SSL_hostname => $hostip,
	SSL_verify_mode => $self_signed ? IO::Socket::SSL::SSL_VERIFY_NONE : IO::Socket::SSL::SSL_VERIFY_PEER,
);

unless ($sock) {
	say <<"EOM";
${\CC_RED}no${\CC_RESET}

It appears that something is wrong with your server. Make sure that:

  - You are not using an old version of GnuTLS, mbedTLS, or OpenSSL which only
    supports deprecated algorithms like SSLv3.

The error provided by the SSL library was:

  $IO::Socket::SSL::SSL_ERROR
EOM
	exit 1;
}


say <<"EOM";
${\CC_GREEN}yes${\CC_RESET}

It seems like SSL is working fine on your server. If you are having trouble
connecting try using a different client or connecting from a different host.

You may also find running some of the following commands to be helpful:

   gnutls-cli-debug --port $port $hostip
   openssl s_client -connect $hostip:$port -debug -security_debug

If you need any help working out what is wrong then visit our support channel
at irc.inspircd.org #inspircd.
EOM