#!/usr/bin/perl -CADSio

no warnings 'redefine';

our $SID_main = "@(#) o-saft.pl 3.164 24/09/09 01:00:02";
my $VERSION = _VERSION();    ## no critic qw(ValuesAndExpressions::RequireConstantVersion)

our $time0 = time();
$time0 = 0 if ( defined $ENV{'OSAFT_MAKE'} );
our @perl_inc;
our @perl_noinc;
my @perl_incorig;

sub _VERSION { return "24.09.24"; }

sub _set_binmode {
    my $layer = shift;
    binmode( STDOUT, $layer );
    binmode( STDERR, $layer );
    return;
}
_set_binmode(":unix:utf8");

sub _is_ARGV {
    my $rex = shift;
    return ( grep { /$rex/ } @ARGV );
}

sub _is_argv {
    my $rex = shift;
    return ( grep { /$rex/i } @ARGV );
}

sub _is_trace {
    my $rex = shift;
    return ( grep { /--(?:trace(?:=\d*)?$)/ } @ARGV );
}

sub _is_v_trace {
    my $rex = shift;
    return ( grep { /--(?:v|trace(?:=\d*)?$)/ } @ARGV );
}

our $make_text = "(OSAFT_MAKE exists)";
our $time_text = $make_text;

sub _vprint {
    my @txt = @_;
    return if ( 0 >= _is_ARGV('(?:--v$)') );
    my %STR;
    printf( "%s%s\n", $OText::STR{'INFO'} || '**INFO: ', join( " ", @txt ) );
    return;
}

sub _trace_time {
    my @txt = @_;
    my $me  = $0;
    $me =~ s{.*?([^/\\]+)$}{$1};
    return if ( _is_argv('(?:--trace.?(?:time|cmd))') <= 0 );
    my $now = 0;
    if ( defined $time0 ) {
        $now = time();
        $now -= $time0 if not _is_argv('(?:--time.*absolut)');
        $now = 0       if ( 0 > $now );
    }
    $now -= 3600;
    $now = sprintf( "%02s:%02s:%02s", ( localtime($now) )[ 2, 1, 0 ] );
    if ( defined $ENV{'OSAFT_MAKE'} ) {

        $now = "HH:MM:SS";
        printf("#$me timstamp printed as $now $time_text\n") if $time_text;
        $time_text = "";
    }
    printf("#$me $now @txt\n");
    return;
}

sub _trace_exit {
    my $txt = shift;
    $txt =~ s#^\s*##;
    my $arg = $txt;
    $arg =~ s#^\s*##;
    $arg =~ s# .*##;
    if ( 0 < _is_ARGV(qr/(([+,]|--)exit=\Q$arg\E).*/) ) {
        my $me = $0;
        $me =~ s{.*?([^/\\]+)$}{$1};
        printf STDERR ("#${me}::_trace_exit --exit=$txt\n");
        exit 0;
    }
    return;
}

sub _trace_next {
    my $txt = shift;
    if ( exists &_vprint ) { _vprint($txt); }
    $txt =~ s#^\s*##;
    _trace_time("$txt");
    my $arg = $txt;
    $arg =~ s#^\s*##;
    $arg =~ s# .*##;
    if ( 0 < _is_ARGV(qr/(([+,]|--)exit=\Q$arg\E).*/) ) {
        my $me = $0;
        $me =~ s{.*?([^/\\]+)$}{$1};
        printf STDERR ("#${me}::_trace_next --exit=$txt\n");
        return 1;
    }
    return 0;
}

sub _trace_info {
    my $txt = shift;
    $txt =~ s#^\s*##;
    if ( exists &_vprint ) { _vprint($txt); }
    _trace_time("$txt");
    _trace_exit("$txt");
    return;
}

sub _version_exit { print _VERSION() . "\n"; exit 0; }

BEGIN {
    my $rex = qr/(?:^--trace)/i;
    if ( _is_argv($rex) or ( defined $ENV{'OSAFT_OPTIONS'} and grep { /$rex/ } $ENV{'OSAFT_OPTIONS'} ) ) {
        printf( "#$0 %s\n", join( " ", @ARGV ) ) if not _is_argv('--help=');
    }

    _trace_info("BEGIN0  - start");
    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##;
    my $_pwd = $ENV{PWD} || ".";

    @perl_incorig = @INC;
    if ( "." ne $_path and not( grep { /^$_path$/ } @INC ) ) {
        unshift( @INC, "$_path/lib" );
        unshift( @INC, $_path );
    }
    unshift( @INC, $_pwd ) if ( 1 > ( grep { /^$_pwd$/ } @INC ) );
    unshift( @INC, "lib" ) if ( 1 > ( grep { /^lib$/ } @INC ) );
    unshift( @INC, "." );
    _version_exit() if _is_ARGV('(?:([+,]|--)VERSION)');
    print STDERR "**WARNING: 019: on $^O additional option  --v  required, sometimes ...\n" if ( $^O =~ m/MSWin32/ );
    foreach my $arg (@ARGV) {
        push( @perl_inc,   $1 ) if ( $arg =~ m/^--inc=(.*)/ );
        push( @perl_noinc, $1 ) if ( $arg =~ m/^--no[,._-]?inc=(.*)/ );
        if ( $arg =~ m/^--inc=(.*)/ ) {
            unshift( @INC, $1 ) if ( 1 > ( grep { /^$1$/ } @INC ) );
        }
    }
    foreach my $arg (@perl_noinc) {
        @INC = grep { $_ !~ m#$arg# } @INC;
    }
    _trace_info("BEGIN9  - end");
}

_trace_info("INIT0   - initialisation start");

$::osaft_standalone = 0;

our $osaft_standalone = 1;

use warnings;
use utf8;
use Carp;
use Encode;
use Socket;
use IO::Socket::INET;
use IO::Socket::SSL;
use Net::SSLeay;
use Net::DNS;
use Time::Local;
use autouse 'Data::Dumper' => qw(Dumper);

package OText;

my $SID_otext = "@(#) OText.pm 3.18 24/09/04 09:00:34";
our $VERSION = "24.09.24";

our %STR = (
    'ERROR'   => "**ERROR: ",
    'WARN'    => "**WARNING: ",
    'HINT'    => "!!Hint: ",
    'INFO'    => "**INFO: ",
    'USAGE'   => "**USAGE: ",
    'DBX'     => "#dbx# ",
    'UNDEF'   => "<<undef>>",
    'NOTXT'   => "<<>>",
    'MAKEVAL' => "<<value not printed (OSAFT_MAKE exists)>>",
);

BEGIN {
    our @EXPORT_OK = qw( %STR );
}


sub print_pod {
    my $file = shift;
    my $pack = shift;
    my $vers = shift;
    printf( "# %s %s\n", $pack, $vers );
    if ( eval { require Pod::Perldoc; } ) {
        exit( Pod::Perldoc->run( args => [$file] ) );
    }
    if (qx(perldoc -V)) {

        printf("# no Pod::Perldoc installed, please try:\n  perldoc $file\n");
    }
    exit 0;
}

sub usage_show {
    my $prefix = shift || "";
    my $data   = shift;
    my $name   = ( caller(0) )[1];

    printf("# general commands:\n");
    my %usage = (
        '--help'   => 'show own help',
        '--usage'  => 'show this text',
        'version'  => 'show own SID',
        '+VERSION' => 'show official version SID',
    );
    foreach my $opt ( sort keys %usage ) {
        printf( "%s\t%s %s\t# %s\n", $prefix, $name, $opt, $usage{$opt} );
    }
    foreach my $key ( sort keys %{$data} ) {
        printf( "%s%s:\n", $prefix, $key );
        foreach my $opt ( sort keys %{ $data->{$key} } ) {
            printf( "%s\t%s %s\t# %s\n", $prefix, $name, $opt, $data->{$key}{$opt} );
        }
    }
    return;
}

sub test_show {
    my $arg = shift;
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal text constants ===
=
= variable      value
=--------------+-------------------
EoT

    printf( " STR{'%s'}\t%s\n", $_, $OText::STR{$_} ) foreach ( sort keys(%STR) );
    printf("=--------------+-------------------\n");
    return;
}

sub _main {
    my @argv = @_;
    push( @argv, "--help" ) if ( 0 > $#argv );
    my %usage = ( '# commands to show data' => { '+test-text' => 'show %STR' } );
    while ( my $arg = shift @argv ) {
        if ( $arg =~ m/^--?h(?:elp)?$/msx ) { print_pod( $0, __PACKAGE__, $SID_otext ); exit 0; }
        if ( $arg =~ /^--usage$/x )         { usage_show( "", \%usage );                exit 0; }
        if ( $arg =~ /^version$/x )         { print "$SID_otext\n";                     next; }
        if ( $arg =~ /^[-+]?V(ERSION)?$/x ) { print "$VERSION\n";                       next; }
        if ( $arg =~ m/^\+(?:test[_.-]?)text/mx ) { test_show($arg); }
    }
    exit 0;
}

sub done { }

package OCfg;

my $SID_ocfg = "@(#) OCfg.pm 3.70 24/09/06 21:30:43";
our $VERSION = "24.09.24";

my $cfg__me = $0;
$cfg__me =~ s#^.*[/\\]##;

BEGIN {
    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##x;
    if ( exists $ENV{'PWD'} and not( grep { /^$ENV{'PWD'}$/ } @INC ) ) {
        unshift( @INC, $ENV{'PWD'} );
    }
    unshift( @INC, $_path ) if not( grep { /^$_path$/ } @INC );
    unshift( @INC, "lib" )  if not( grep { /^lib$/ } @INC );

    our @EXPORT_OK = qw( %cfg %dbx %data %data_oid %prot _dbx );
}


our %prot = (

    'SSLv2'             => { 'txt' => "SSL 2.0 ",  'hex' => 0x0002, 'opt' => "-ssl2" },
    'SSLv3'             => { 'txt' => "SSL 3.0 ",  'hex' => 0x0300, 'opt' => "-ssl3" },
    'TLSv1'             => { 'txt' => "TLS 1.0 ",  'hex' => 0x0301, 'opt' => "-tls1" },
    'TLSv11'            => { 'txt' => "TLS 1.1 ",  'hex' => 0x0302, 'opt' => "-tls1_1" },
    'TLSv12'            => { 'txt' => "TLS 1.2 ",  'hex' => 0x0303, 'opt' => "-tls1_2" },
    'TLSv13'            => { 'txt' => "TLS 1.3 ",  'hex' => 0x0304, 'opt' => "-tls1_3" },
    'DTLSv09'           => { 'txt' => "DTLS 0.9",  'hex' => 0x0100, 'opt' => "-dtls" },
    'DTLSv1'            => { 'txt' => "DTLS 1.0",  'hex' => 0xFEFF, 'opt' => "-dtls1" },
    'DTLSv11'           => { 'txt' => "DTLS 1.1",  'hex' => 0xFEFE, 'opt' => "-dtls1_1" },
    'DTLSv12'           => { 'txt' => "DTLS 1.2",  'hex' => 0xFEFD, 'opt' => "-dtls1_2" },
    'DTLSv13'           => { 'txt' => "DTLS 1.3",  'hex' => 0xFEFC, 'opt' => "-dtls1_3" },
    'TLS1FF'            => { 'txt' => "--dummy--", 'hex' => 0x03FF, 'opt' => undef },
    'DTLSfamily'        => { 'txt' => "--dummy--", 'hex' => 0xFE00, 'opt' => undef },
    'fallback'          => { 'txt' => "cipher",    'hex' => 0x0000, 'opt' => undef },
    'TLS_FALLBACK_SCSV' => { 'txt' => "SCSV",      'hex' => 0x5600, 'opt' => undef },
);

our %prot_txt = (
    'cnt'         => "Supported total ciphers for ",
    '-?-'         => "Supported ciphers with security unknown",
    'WEAK'        => "Supported ciphers with security WEAK",
    'LOW'         => "Supported ciphers with security LOW",
    'MEDIUM'      => "Supported ciphers with security MEDIUM",
    'HIGH'        => "Supported ciphers with security HIGH",
    'ciphers_pfs' => "PFS (all  ciphers)",
    'cipher_pfs'  => "PFS (selected cipher)",
    'default'     => "Selected  cipher  by server",
    'protocol'    => "Selected protocol by server",
);

our %tls_handshake_type = (
    0 => 'hello_request',
    1 => 'client_hello',
    2 => 'server_hello',
    3 => 'hello_verify_request',
    4 => 'new_session_ticket',
    6   => 'hello_retry_request',
    8   => 'encrypted_extensions',
    11  => 'certificate',
    12  => 'server_key_exchange',
    13  => 'certificate_request',
    14  => 'server_hello_done',
    15  => 'certificate_verify',
    16  => 'client_key_exchange',
    20  => 'finished',
    21  => 'certificate_url',
    22  => 'certificate_status',
    23  => 'supplemental_data',
    24  => 'key_update',
    254 => 'message_hash',
    255 => '255',
    -1  => '<<undefined>>',
    -99 => '<<fragmented_message>>',

);

our %tls_key_exchange_type = (
    20  => 'change_cipher_spec',
    21  => 'alert',
    22  => 'handshake',
    23  => 'application_data',
    24  => 'heartbeat',
    255 => '255',
    -1  => '<<undefined>>',

);

our %tls_record_type = (
    20  => 'change_cipher_spec',
    21  => 'alert',
    22  => 'handshake',
    23  => 'application_data',
    24  => 'heartbeat',
    255 => '255',
    -1  => '<<undefined>>',

);

our %tls_compression_method = (
    0  => 'NONE',
    1  => 'zlib compression',
    64 => 'LZS compression',
    -1 => '<<undefined>>',

);

our %tls_error_alerts = (

    0 => [qw( close_notify                      6066  Y  -)],
    10  => [qw( unexpected_message                6066  Y  -)],
    20  => [qw( bad_record_mac                    6066  Y  -)],
    21  => [qw( decryption_failed                 6066  Y  -)],
    22  => [qw( record_overflow                   6066  Y  -)],
    30  => [qw( decompression_failure             6066  Y  -)],
    40  => [qw( handshake_failure                 6066  Y  -)],
    41  => [qw( no_certificate_RESERVED           5246  Y  -)],
    42  => [qw( bad_certificate                   6066  Y  -)],
    43  => [qw( unsupported_certificate           6066  Y  -)],
    44  => [qw( certificate_revoked               6066  Y  -)],
    45  => [qw( certificate_expired               6066  Y  -)],
    46  => [qw( certificate_unknown               6066  Y  -)],
    47  => [qw( illegal_parameter                 6066  Y  -)],
    48  => [qw( unknown_ca                        6066  Y  -)],
    49  => [qw( access_denied                     6066  Y  -)],
    50  => [qw( decode_error                      6066  Y  -)],
    51  => [qw( decrypt_error                     6066  Y  -)],
    60  => [qw( export_restriction_RESERVED       6066  Y  -)],
    70  => [qw( protocol_version                  6066  Y  -)],
    71  => [qw( insufficient_security             6066  Y  -)],
    80  => [qw( internal_error                    6066  Y  -)],
    86  => [qw( inappropriate_fallback            7507  Y  -)],
    90  => [qw( user_canceled                     6066  Y  -)],
    100 => [qw( no_renegotiation                  6066  Y  -)],
    109 => [qw( missing_extension                 8446  Y  -)],
    110 => [qw( unsupported_extension             6066  Y  -)],
    111 => [qw( certificate_unobtainable          6066  Y  -)],
    112 => [qw( unrecognized_name                 6066  Y  -)],
    113 => [qw( bad_certificate_status_response   6066  Y  -)],
    114 => [qw( bad_certificate_hash_value        6066  Y  -)],
    115 => [qw( unknown_psk_identity              4279  Y  -)],
    116 => [qw( certificate_required              8446  Y  -)],
    120 => [qw( no_application_protocol           7301  Y  -)],
);

our %TLS_EC_POINT_FORMATS = (
    TEXT   => "ec point format(s)",
    FORMAT => [qw( "%s"                                          )],

    0 => [qw( uncompressed                    Y    Y   4492 )],
    1 => [qw( ansiX962_compressed_prime       Y?   N?  4492 )],
    2 => [qw( ansiX962_compressed_char2       Y?   N?  4492 )],
);

our %TLS_NAME_TYPE = (
    TEXT   => "server name type",
    FORMAT => [qw( %s                                           )],

    0x00 => [qw( host_name                       Y    6066    )],
);

our %TLS_MAX_FRAGMENT_LENGTH = (
    TEXT   => "max fragment length negotiation",
    FORMAT => [ "%s", "(%s bytes)" ],

    0x01 => [qw( 2^9        512                  -    6066    )],
    0x02 => [qw( 2^10      1024                  -    6066    )],
    0x03 => [qw( 2^11      2048                  -    6066    )],
    0x04 => [qw( 2^12      4096                  -    6066    )],
);

our %TLS_PROTOCOL_VERSION = (
    TEXT   => "supported protocol version(s)",
    FORMAT => [qw( %s    )],

    0x0304 => [qq( TLS 1.3 )],
    0x0303 => [qq( TLS 1.2 )],
    0x0302 => [qq( TLS 1.1 )],
    0x0301 => [qq( TLS 1.0 )],
    0x0300 => [qq( SSL 3   )],
);

our %TLS_PSK_KEY_EXCHANGE_MODE = (
    TEXT   => "PSK key exchange mode(s)",
    FORMAT => [qw( "%s"                                         )],

    0x00 => [qw( psk_ke                          Y    8446    )],
    0x01 => [qw( psk_dhe_ke                      Y    8446    )],
);

our %TLS_SIGNATURE_SCHEME = (
    TEXT   => "signature scheme(s)",
    FORMAT => [qw( %s                                           )],

    0x0201 => [qw( rsa_pkcs1_sha1                   Y   8446    )],
    0x0202 => [qw( dsa_sha1                         ?   8446    )],
    0x0203 => [qw( ecdsa_sha1                       Y   8446    )],

    0x0301 => [qw( rsa_sha224                       ?   ?       )],
    0x0302 => [qw( dsa_sha224                       ?   ?       )],
    0x0303 => [qw( ecdsa_sha224                     ?   ?       )],

    0x0401 => [qw( rsa_pkcs1_sha256                 Y   8446    )],
    0x0402 => [qw( dsa_sha256                       ?   8446    )],
    0x0403 => [qw( ecdsa_secp256r1_sha256           Y   8446    )],
    0x0420 => [qw( rsa_pkcs1_sha256_legacy          N   draft-davidben-tls13-pkcs1-00 )],

    0x0501 => [qw( rsa_pkcs1_sha384                 Y   8446    )],
    0x0502 => [qw( dsa_sha384                       ?   8446]   )],
    0x0503 => [qw( ecdsa_secp384r1_sha384           Y   8446    )],

    0x0520 => [qw( rsa_pkcs1_sha384_legacy          N   draft-davidben-tls13-pkcs1-00 )],

    0x0601 => [qw( rsa_pkcs1_sha512                 Y   8446    )],
    0x0602 => [qw( dsa_pkcs1_sha512                 Y   8446    )],
    0x0603 => [qw( ecdsa_secp521r1_sha512           Y   8446    )],

    0x0620 => [qw( rsa_pkcs1_sha512_legacy          N   draft-davidben-tls13-pkcs1-00 )],

    0x0704 => [qw( eccsi_sha256                     N   draft-wang-tls-raw-public-key-with-ibc )],
    0x0705 => [qw( iso_ibs1                         N   draft-wang-tls-raw-public-key-with-ibc])],
    0x0706 => [qw( iso_ibs2                         N   draft-wang-tls-raw-public-key-with-ibc])],
    0x0707 => [qw( iso_chinese_ibs                  N   draft-wang-tls-raw-public-key-with-ibc])],
    0x0708 => [qw( sm2sig_sm3                       N   draft-yang-tls-tls13-sm-suites )],
    0x0709 => [qw( gostr34102012_256a               N   draft-smyshlyaev-tls13-gost-suites )],
    0x070A => [qw( gostr34102012_256b               N   draft-smyshlyaev-tls13-gost-suites )],
    0x070B => [qw( gostr34102012_256c               N   draft-smyshlyaev-tls13-gost-suites )],
    0x070C => [qw( gostr34102012_256d               N   draft-smyshlyaev-tls13-gost-suites )],
    0x070D => [qw( gostr34102012_512a               N   draft-smyshlyaev-tls13-gost-suites )],
    0x070E => [qw( gostr34102012_512b               N   draft-smyshlyaev-tls13-gost-suites )],
    0x070F => [qw( gostr34102012_512c               N   draft-smyshlyaev-tls13-gost-suites )],

    0x0804 => [qw( rsa_pss_rsae_sha256              Y   8446    )],
    0x0805 => [qw( rsa_pss_rsae_sha384              Y   8446    )],
    0x0806 => [qw( rsa_pss_rsae_sha512              Y   8446    )],
    0x0807 => [qw( ed25519                          Y   8446    )],
    0x0808 => [qw( ed448                            Y   8446    )],
    0x0809 => [qw( rsa_pss_pss_sha256               Y   8446    )],
    0x080A => [qw( rsa_pss_pss_sha384               Y   8446    )],
    0x080B => [qw( rsa_pss_pss_sha512               Y   8446    )],

    0x081A => [qw( ecdsa_brainpoolP256r1tls13_sha256 N  8734    )],
    0x081B => [qw( ecdsa_brainpoolP384r1tls13_sha384 N  8734    )],
    0x081C => [qw( ecdsa_brainpoolP512r1tls13_sha512 N  8734    )],

);

our %TLS_SUPPORTED_GROUPS = (
    TEXT   => "supported group(s)",
    FORMAT => [ "%s", "(%s bits)" ],

    0  => [qw( Reverved_0                 0    N    N   8447    )],
    1  => [qw( sect163k1                163    Y    N   4492    )],
    2  => [qw( sect163r1                163    Y    N   4492    )],
    3  => [qw( sect163r2                163    Y    N   4492    )],
    4  => [qw( sect193r1                193    Y    N   4492    )],
    5  => [qw( sect193r2                193    Y    N   4492    )],
    6  => [qw( sect233k1                233    Y    N   4492    )],
    7  => [qw( sect233r1                233    Y    N   4492    )],
    8  => [qw( sect239k1                239    Y    N   4492    )],
    9  => [qw( sect283k1                283    Y    N   4492    )],
    10 => [qw( sect283r1                283    Y    N   4492    )],
    11 => [qw( sect409k1                409    Y    N   4492    )],
    12 => [qw( sect409r1                409    Y    N   4492    )],
    13 => [qw( sect571k1                571    Y    N   4492    )],
    14 => [qw( sect571r1                571    Y    N   4492    )],
    15 => [qw( secp160k1                160    Y    N   4492    )],
    16 => [qw( secp160r1                160    Y    N   4492    )],
    17 => [qw( secp160r2                160    Y    N   4492    )],
    18 => [qw( secp192k1                192    Y    N   4492    )],
    19 => [qw( secp192r1                192    Y    N   4492    )],
    20 => [qw( secp224k1                224    Y    N   4492    )],
    21 => [qw( secp224r1                224    Y    N   4492    )],
    22 => [qw( secp256k1                256    Y    N   4492    )],
    23 => [qw( secp256r1                256    Y    Y   4492    )],
    24 => [qw( secp384r1                384    Y    Y   4492    )],
    25 => [qw( secp521r1                521    Y    N   4492    )],
    26 => [qw( brainpoolP256r1          256    Y    Y   7027    )],
    27 => [qw( brainpoolP384r1          384    Y    Y   7027    )],
    28 => [qw( brainpoolP512r1          512    Y    Y   7027    )],
    29 => [qw( x25519                   255    Y    Y   8446:8422 )],
    30 => [qw( x448                     448    Y    Y   8446:8422 )],
    31 => [qw( brainpoolP256r1tls13     256    Y    N   8734    )],
    32 => [qw( brainpoolP384r1tls13     384    Y    N   8734    )],
    33 => [qw( brainpoolP512r1tls13     512    Y    N   8734    )],
    34 => [qw( GC256A                   256    Y    N   draft-smyshlyaev-tls12-gost-suites )],
    35 => [qw( GC256B                   256    Y    N   draft-smyshlyaev-tls12-gost-suites )],
    36 => [qw( GC256C                   256    Y    N   draft-smyshlyaev-tls12-gost-suites )],
    37 => [qw( GC256D                   256    Y    N   draft-smyshlyaev-tls12-gost-suites )],
    38 => [qw( GC512A                   512    Y    N   draft-smyshlyaev-tls12-gost-suites )],
    39 => [qw( GC512B                   512    Y    N   draft-smyshlyaev-tls12-gost-suites )],
    40 => [qw( GC512C                   512    Y    N   draft-smyshlyaev-tls12-gost-suites )],
    41 => [qw( curveSM2                 256    N    N   draft-yang-tls-tls13-sm-suites )],
    256 => [qw( ffdhe2048               2048    Y    N   7919    )],
    257 => [qw( ffdhe3072               3072    Y    N   7919    )],
    258 => [qw( ffdhe4096               4096    Y    N   7919    )],
    259 => [qw( ffdhe6144               6144    Y    N   7919    )],
    260 => [qw( ffdhe8192               8192    Y    N   7919    )],
    508 => [qw( ffdhe_private_use_508     NN    Y    N   7919    )],
    509 => [qw( ffdhe_private_use_509     NN    Y    N   7919    )],
    510 => [qw( ffdhe_private_use_510     NN    Y    N   7919    )],
    511 => [qw( ffdhe_private_use_511     NN    Y    N   7919    )],
    2570 => [qw( Reserved_2570             NN    Y    N   8701    )],
    6682 => [qw( Reserved_6682             NN    Y    N   8701    )],
    10794 => [qw( Reserved_10794            NN    Y    N   8701    )],
    14906 => [qw( Reserved_14906            NN    Y    N   8701    )],
    19018 => [qw( Reserved_19018            NN    Y    N   8701    )],
    23130 => [qw( Reserved_23130            NN    Y    N   8701    )],
    27242 => [qw( Reserved_27242            NN    Y    N   8701    )],
    31354 => [qw( Reserved_31354            NN    Y    N   8701    )],
    35466 => [qw( Reserved_35466            NN    Y    N   8701    )],
    39578 => [qw( Reserved_39578            NN    Y    N   8701    )],
    43690 => [qw( Reserved_43690            NN    Y    N   8701    )],
    47802 => [qw( Reserved_47802            NN    Y    N   8701    )],
    51914 => [qw( Reserved_51914            NN    Y    N   8701    )],
    56026 => [qw( Reserved_56026            NN    Y    N   8701    )],
    60138 => [qw( Reserved_60138            NN    Y    N   8701    )],
    64250 => [qw( Reserved_64250            NN    Y    N   8701    )],
    0xFE00 => [qw( ecdhe_private_use_65024   NN    Y    N   NN      )],
    0xFE01 => [qw( ecdhe_private_use_65025   NN    Y    N   NN      )],
    0xFE02 => [qw( ecdhe_private_use_65026   NN    Y    N   NN      )],
    0xFE03 => [qw( ecdhe_private_use_65027   NN    Y    N   NN      )],
    0xFE04 => [qw( ecdhe_private_use_65028   NN    Y    N   NN      )],
    0xFE05 => [qw( ecdhe_private_use_65029   NN    Y    N   NN      )],
    0xFE06 => [qw( ecdhe_private_use_65030   NN    Y    N   NN      )],
    0xFE07 => [qw( ecdhe_private_use_65031   NN    Y    N   NN      )],
    65281 => [qw( arbitrary_explicit_prime_curves  -variable- N   8422    )],
    65282 => [qw( arbitrary_explicit_char2_curves  -variable- Y   8422    )],
);

our %TLS_EXTENSIONS = (
    server_name => {
        ID          => 0,
        CH          => [qw(len2 len2 sequence val1 len2 raw)],
        CH_TEXT     => [ "length", "server name list length", "server name element", \%TLS_NAME_TYPE, "server name length", "server name" ],
        RX          => [qw(len2 raw)],
        RX_TEXT     => [ "length", "server name list length" ],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH EE)],
        RFC         => [qw(6066)],
        DEFAULT     => [ [ 0x00, "localhost", ], ],
        CHECK       => q(VALUE),
        COMMENT     => q(),
    },

    max_fragment_length => {
        ID          => 1,
        CH          => [qw(len2 len2 val1List)],
        CH_TEXT     => [ "length", "length of max fragment lenght", \%TLS_MAX_FRAGMENT_LENGTH ],
        RX          => [qw(len2 raw)],
        RX_TEXT     => [ "length", \%TLS_MAX_FRAGMENT_LENGTH ],
        RECOMMENDED => q(-),
        TLS13       => [qw(CH EE)],
        RFC         => [qw(6066 8449)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(replaced by extension 'record_size_limit'; Default max length is 2^14 if this extension is not negotiated),
    },

    client_certificate_url => {
        ID          => 2,
        CH          => [qw(len2 len2 val1 sequence len2 val1 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(6066)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(val20 oder len2_val?),
    },

    trusted_ca_keys => {
        ID          => 3,
        CH          => [qw(len2 len2 val1 len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(N)],
        RFC         => [qw(6066)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(?),
    },
    truncated_hmac => {
        ID          => 4,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q[N],
        TLS13       => [qw(N)],
        RFC         => [qw(6066 IESG_Action_2018-08-16)],
        DEFAULT     => [],
        CHECK       => q[VALUE],
        COMMENT     => q[Shall be empty],
    },
    status_request => {
        ID          => 5,
        CH          => [qw(len2 val1 len2 raw len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH CR CT)],
        RFC         => [qw(6066)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[SH ext_form_val1_len2_val?],
    },
    user_mapping => {
        ID          => 6,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(4681)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(),
    },
    client_authz => {
        ID          => 7,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(-)],
        RFC         => [qw(5878)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(),
    },
    server_authz => {
        ID          => 8,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(-)],
        RFC         => [qw(5878)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(),
    },
    cert_type => {
        ID          => 9,
        CH          => [qw(len2 len1 val1List)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(-)],
        RFC         => [qw(6091)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(Server: val1),
    },
    supported_groups => {
        ID          => 10,
        CH          => [qw(len2 len2 val2List)],
        CH_TEXT     => [ "length", "supported groups list length", \%TLS_SUPPORTED_GROUPS ],
        RX          => [qw(len2 val2)],
        RX_TEXT     => [ "length", \%TLS_SUPPORTED_GROUPS ],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH EE)],
        RFC         => [qw(8422 7919)],
        DEFAULT     => [
            [
                0x0001,
                0x0002,
                0x0003,
                0x0004,
                0x0005,
                0x0006,
                0x0007,
                0x0008,
                0x0009,
                0x000a,
                0x000b,
                0x000c,
                0x000d,
                0x000e,
                0x000f,
                0x0010,
                0x0011,
                0x0012,
                0x0013,
                0x0014,
                0x0015,
                0x0016,
                0x0017,
                0x0018,
                0x0019,
                0x001a,
                0x001b,
                0x001c,
                0x001d,
                0x001e,
                0x001f,
                0x0020,
                0x0021,
                0x0022,
                0x0023,
                0x0024,
                0x0025,
                0x0026,
                0x0027,
                0x0028,
                0x0029,

                0x0100,
                0x0101,
                0x0102,
                0x0103,
                0x0104,
            ],
        ],
        CHECK   => q(VALUE),
        COMMENT => q(renamed from "elliptic_curves"),
    },
    ec_point_formats => {
        ID          => 11,
        CH          => [qw(len2 len1 val1List)],
        CH_TEXT     => [ "length", "ec point formats list length", \%TLS_EC_POINT_FORMATS ],
        RX          => [qw(len2 len1 val1List)],
        RX_TEXT     => [ "length", "ec point formats list length", \%TLS_EC_POINT_FORMATS ],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(8422)],
        DEFAULT     => [
            [ 0x00, 0x01, 0x02, ],
        ],
        CHECK   => q(VALUE),
        COMMENT => q(),
    },
    srp => {
        ID          => 12,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        ,
        TLS13   => [qw(-)],
        RFC     => [qw(5054)],
        DEFAULT => [],
        CHECK   => q(VALUE),
        COMMENT => q(),
    },
    signature_algorithms => {
        ID          => 13,
        CH          => [qw(len2 len2 val2List)],
        CH_TEXT     => [ "length", "signature hash algorithms list length", \%TLS_SIGNATURE_SCHEME ],
        RX          => [qw(len2 val2)],
        RX_TEXT     => [ "length", \%TLS_SIGNATURE_SCHEME ],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH CR)],
        RFC         => [qw(8446)],
        DEFAULT     => [
            [
                0x0201,
                0x0202,
                0x0203,

                0x0301,
                0x0302,
                0x0303,

                0x0401,
                0x0402,
                0x0403,
                0x0420,

                0x0501,
                0x0502,
                0x0503,

                0x0520,

                0x0601,
                0x0602,
                0x0603,

                0x0620,

                0x0704,
                0x0705,
                0x0706,
                0x0707,
                0x0708,
                0x0709,
                0x070A,
                0x070B,
                0x070C,
                0x070D,
                0x070E,
                0x070F,

                0x0804,
                0x0805,
                0x0806,
                0x0807,
                0x0808,
                0x0809,
                0x080A,
                0x080B,

                0x081A,
                0x081B,
                0x081C,
            ],
        ],
        CHECK   => q(VALUE),
        COMMENT => q[],
    },
    use_srtp => {
        ID          => 14,
        CH          => [qw(len2 size2 val2List len1 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH EE)],
        RFC         => [qw(5764)],
        DEFAULT     => [ [ 0x0001, 0x0002, 0x0005, 0x0006, ] ],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    heartbeat => {
        ID          => 15,
        CH          => [qw(len2 val1)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH EE)],
        RFC         => [qw(6520)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(Syntax prüfen!),
    },
    application_layer_protocol_negotiation => {
        ID          => 16,
        CH          => [qw(len2 len2 size1 raw size1 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH EE)],
        RFC         => [qw(7301)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    status_request_v2 => {
        ID          => 17,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(6961)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    signed_certificate_timestamp => {
        ID          => 18,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(CH CR CT)],
        RFC         => [qw(6962)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    client_certificate_type => {
        ID          => 19,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH EE)],
        RFC         => [qw(7250)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    server_certificate_type => {
        ID          => 20,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH EE)],
        RFC         => [qw(7250)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    padding => {
        ID          => 21,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH)],
        RFC         => [qw(7685)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(val= 0x00-Bytes),
    },
    encrypt_then_mac => {
        ID          => 22,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(7366)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    extended_master_secret => {
        ID          => 23,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(7627)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    token_binding => {
        ID          => 24,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(8472)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    cached_info => {
        ID          => 25,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(7924)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    tls_lts => {
        ID          => 26,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(-)],
        RFC         => [qw(draft-gutmann-tls-lts)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    compress_certificate => {
        ID          => 27,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH CR)],
        RFC         => [qw(draft-ietf-tls-certificate-compression)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(TEMPORARY registered 2018-05-23 extension registered 2019-04-22 expires 2020-05-23),
    },
    record_size_limit => {
        ID          => 28,
        CH          => [qw(len2 val2)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH EE)],
        RFC         => [qw(8449)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    pwd_protect => {
        ID          => 29,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(CH)],
        RFC         => [qw(8492)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    pwd_clear => {
        ID          => 30,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(CH)],
        RFC         => [qw(8492)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    password_salt => {
        ID          => 31,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(CH SH HRR)],
        RFC         => [qw(8492)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    session_ticket => {
        ID => 35,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(5077 8447)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(renamed from "SessionTicket TLS"),
    },

    extended_random => {
        ID          => 40,
        CH          => [qw(len2 len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N!),
        TLS13       => [qw(?)],
        RFC         => [qw(draft-rescorla-tls-extended-random-02)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(NSA; March 02, 2009; DO NOT USE!! https://gist.github.com/bonsaiviking/9921180: 0x0028, RSA BSAFE library),
    },
    pre_shared_key => {
        ID          => 41,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH SH)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    early_data => {
        ID          => 42,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH EE NST)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    supported_versions => {
        ID          => 43,
        CH          => [qw(len2 len1 val2List)],
        CH_TEXT     => [ "length", "supported versions list length", \%TLS_PROTOCOL_VERSION ],
        RX          => [qw(len2 val2)],
        RX_TEXT     => [ "length", \%TLS_PROTOCOL_VERSION ],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH SH HRR)],
        RFC         => [qw(8446)],
        DEFAULT     => [
            [
                0x0304,

            ],
        ],
        CHECK   => q(VALUE),
        COMMENT => q[],
    },
    cookie => {
        ID          => 44,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH HRR)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    psk_key_exchange_modes => {
        ID          => 45,
        CH          => [qw(len2 len1 val1List)],
        CH_TEXT     => [ "length", "PSK key exchange modes list length", %TLS_PSK_KEY_EXCHANGE_MODE ],
        RX          => [qw(len2 val1)],
        RX_TEXT     => [ "length", %TLS_PSK_KEY_EXCHANGE_MODE ],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH)],
        RFC         => [qw(8446)],
        DEFAULT     => [
            [ 0x00, 0x01, ],
        ],
        CHECK   => q(VALUE),
        COMMENT => q[],
    },
    certificate_authorities => {
        ID          => 47,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH CR)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    oid_filters => {
        ID          => 48,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CR)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    post_handshake_auth => {
        ID          => 49,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    signature_algorithms_cert => {
        ID          => 50,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH CR)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    key_share => {
        ID          => 51,
        CH          => [qw(len2 len2 sequence val2 size2 raw)],
        CH_TEXT     => [ "length", "client key share list length", "key share element", \%TLS_SUPPORTED_GROUPS, "key exchange length", "key exchange" ],
        RX          => [qw(len2 val2 size2 raw)],
        RX_TEXT     => [ "length", \%TLS_SUPPORTED_GROUPS, "key exchange length", "key exchange" ],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH SH HRR)],
        RFC         => [qw(8446)],
        DEFAULT     => [
            [ 0x001d, "\x01\x02\x03\x04\x05\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20", ],
            [
                0x0017,
                "\x21\x22\x23\x24\x25\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40"
                  . "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60\x61",
            ],
        ],
        CHECK   => q(VALUE),
        COMMENT => q[],
    },
    transparency_info => {
        ID          => 52,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(Y),
        TLS13       => [qw(CH CR CT)],
        RFC         => [qw(draft-ietf-trans-6962-bis)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    supports_npn => {
        ID => 13172,
        CH          => [qw(len2 len1 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(?),
        TLS13       => [qw(?)],
        RFC         => [qw(draft-agl-tls-nextprotoneg-04)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q[],
    },
    channel_id_old => {
        ID          => 33031,
        CH          => [qw(len2 val4 val4 val4 val4)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(?)],
        RFC         => [qw(draft-balfanz-tls-channelid-00)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(channel_id_old=0x754F),
    },
    channel_id => {
        ID          => 33032,
        CH          => [qw(len2 val4 val4 val4 val4)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N),
        TLS13       => [qw(?)],
        RFC         => [qw(draft-balfanz-tls-channelid-01)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(channel_id=0x7550),
    },
    opaque_prf_input => {
        ID          => 38183,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(N!),
        TLS13       => [qw(?)],
        RFC         => [qw(draft-rescorla-tls-opaque-prf-input-00)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(NSA; December 13, 2006; DO NOT USE!! https://www.openssl.org/news/changelog.html#x44 [29 Mar 2010]: opaque_prf_input=0x9527),
    },
    tack => {
        ID          => 62208,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(?),
        TLS13       => [qw(?)],
        RFC         => [qw(draft-perrin-tls-tack-02)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(January 07, 2013, expired July 11, 2013),
    },

    private_65280 => {
        ID          => 65280,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(?),
        TLS13       => [qw(?)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(for private use),
    },
    renegotiation_info => {
        ID          => 65281,
        CH          => [qw(len2 len1 raw)],
        CH_TEXT     => [ "length", "renegotiated connection data length", "client verify data" ],
        RX          => [qw(len2 len1 raw)],
        RX_TEXT     => [ "length", "renegotiated connection data length", "server verify data" ],
        RECOMMENDED => q(Y),
        TLS13       => [qw(-)],
        RFC         => [qw(5746)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(Default value is empty => len1=0x00 => len2=0x0001),
    },

    private_65282 => {
        ID          => 65282,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(?),
        TLS13       => [qw(?)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(for private use),
    },
    private_65283 => {
        ID          => 65283,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(?),
        TLS13       => [qw(?)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(for private use),
    },
    private_65284 => {
        ID          => 65284,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(?),
        TLS13       => [qw(?)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(for private use),
    },
    private_65285 => {
        ID          => 65285,
        CH          => [qw(len2 raw)],
        RX          => [qw(len2 raw)],
        RECOMMENDED => q(?),
        TLS13       => [qw(?)],
        RFC         => [qw(8446)],
        DEFAULT     => [],
        CHECK       => q(VALUE),
        COMMENT     => q(for private use),
    },
);

our %TLS_ID_TO_EXTENSIONS = (

    FORMAT => [ "Extension '%s':", ],
);

foreach my $key ( keys %TLS_EXTENSIONS ) {
    $TLS_ID_TO_EXTENSIONS{ $TLS_EXTENSIONS{$key}{ID} }[0] = $key;
}

my %tls_extensions__text = (
    'extension' => {
        '00000' => "renegotiation info length",
        '00001' => "renegotiation length",
        '00009' => "cert type",
        '00010' => "elliptic curves",
        '00011' => "EC point formats",
        '00012' => "SRP",
        '00015' => "heartbeat",
        '00035' => "session ticket",
        '13172' => "next protocol",
        '62208' => "TACK",
        '65281' => "renegotiation info",
    },
);

our %tls_signature_algorithms = (
    0x0201 => "rsa_pkcs1_sha1",
    0x0203 => "ecdsa_sha1",
    0x0401 => "rsa_pkcs1_sha256",
    0x0501 => "rsa_pkcs1_sha384",
    0x0601 => "rsa_pkcs1_sha512",
    0x0403 => "ecdsa_secp256r1_sha256",
    0x0503 => "ecdsa_secp384r1_sha384",
    0x0603 => "ecdsa_secp521r1_sha512",
    0x0804 => "rsa_pss_rsae_sha256",
    0x0805 => "rsa_pss_rsae_sha384",
    0x0806 => "rsa_pss_rsae_sha512",
    0x0807 => "ed25519",
    0x0808 => "ed448",
    0x0809 => "rsa_pss_pss_sha256",
    0x080a => "rsa_pss_pss_sha384",
    0x080b => "rsa_pss_pss_sha512",
    0x0202 => "dsa_sha1_RESERVED",
    0x0402 => "dsa_sha256_RESERVED",
    0x0502 => "dsa_sha384_RESERVED",
    0x0602 => "dsa_sha512_RESERVED",
    0xFFFF => "private_use",
);

our %tls_supported_groups = (

    0x0001 => "obsolete_RESERVED",
    0x0017 => "secp256r1",
    0x0018 => "secp384r1",
    0x0019 => "secp521r1",
    0x001A => "obsolete_RESERVED",
    0x001D => "x25519",
    0x001E => "x448",
    0x0100 => "ffdhe2048",
    0x0101 => "ffdhe3072",
    0x0102 => "ffdhe4096",
    0x0103 => "ffdhe6144",
    0x0104 => "ffdhe8192",

    0x01FC => "ffdhe_private_use",
    0xFE00 => "ecdhe_private_use",
    0xFF01 => "obsolete_RESERVED_ff01",
    0xFF02 => "obsolete_RESERVED_ff02",
    0xFFFF => "FFFF",
);

our %ec_point_formats = (

    0   => [qw( uncompressed              4492  Y   )],
    1   => [qw( ansiX962_compressed_prime 4492  Y   )],
    2   => [qw( ansiX962_compressed_char2 4492  Y   )],
    248 => [qw( reserved_248              4492  N   )],
    249 => [qw( reserved_249              4492  N   )],
    250 => [qw( reserved_250              4492  N   )],
    251 => [qw( reserved_251              4492  N   )],
    252 => [qw( reserved_252              4492  N   )],
    253 => [qw( reserved_253              4492  N   )],
    254 => [qw( reserved_254              4492  N   )],
    255 => [qw( reserved_255              4492  N   )],
);

our %ec_curve_types = (

    0   => [qw( unassigned                4492  N   )],
    1   => [qw( explicit_prime            4492  Y   )],
    2   => [qw( explicit_char2            4492  Y   )],
    3   => [qw( named_curve               4492  Y   )],
    248 => [qw( reserved_248              4492  N   )],
    249 => [qw( reserved_249              4492  N   )],
    250 => [qw( reserved_250              4492  N   )],
    251 => [qw( reserved_251              4492  N   )],
    252 => [qw( reserved_252              4492  N   )],
    253 => [qw( reserved_253              4492  N   )],
    254 => [qw( reserved_254              4492  N   )],
    255 => [qw( reserved_255              4492  N   )],
);

our %tls_curves = (
    0  => [qw( unassigned                        IANA  -      -    0                      )],
    1  => [qw( sect163k1                         4492  Y  K-163  163 1.3.132.0.1          )],
    2  => [qw( sect163r1                         4492  Y      -  163 1.3.132.0.2          )],
    3  => [qw( sect163r2                         4492  Y  B-163  163 1.3.132.0.15         )],
    4  => [qw( sect193r1                         4492  Y      -  193 1.3.132.0.24         )],
    5  => [qw( sect193r2                         4492  Y      -  193 1.3.132.0.25         )],
    6  => [qw( sect233k1                         4492  Y  K-233  233 1.3.132.0.26         )],
    7  => [qw( sect233r1                         4492  Y  B-233  233 1.3.132.0.27         )],
    8  => [qw( sect239k1                         4492  Y      -  239 1.3.132.0.3          )],
    9  => [qw( sect283k1                         4492  Y  K-283  283 1.3.132.0.16         )],
    10 => [qw( sect283r1                         4492  Y  B-283  283 1.3.132.0.17         )],
    11 => [qw( sect409k1                         4492  Y  K-409  409 1.3.132.0.36         )],
    12 => [qw( sect409r1                         4492  Y  B-409  409 1.3.132.0.37         )],
    13 => [qw( sect571k1                         4492  Y  K-571  571 1.3.132.0.38         )],
    14 => [qw( sect571r1                         4492  Y  B-571  571 1.3.132.0.39         )],
    15 => [qw( secp160k1                         4492  Y      -  160 1.3.132.0.9          )],
    16 => [qw( secp160r1                         4492  Y      -  160 1.3.132.0.8          )],
    17 => [qw( secp160r2                         4492  Y      -  160 1.3.132.0.30         )],
    18 => [qw( secp192k1                         4492  Y      -  192 1.3.132.0.31         )],
    19 => [qw( secp192r1                         4492  Y  P-192  192 1.2.840.10045.3.1.1  )],
    20 => [qw( secp224k1                         4492  Y       - 224 1.3.132.0.32         )],
    21 => [qw( secp224r1                         4492  Y  P-224  224 1.3.132.0.33         )],
    22 => [qw( secp256k1                         4492  Y  P-256  256 1.3.132.0.10         )],
    23 => [qw( secp256r1                         4492  Y  P-256  256 1.2.840.10045.3.1.7  )],
    24 => [qw( secp384r1                         4492  Y  P-384  384 1.3.132.0.34         )],
    25 => [qw( secp521r1                         4492  Y  P-521  521 1.3.132.0.35         )],
    26 => [qw( brainpoolP256r1                   7027  Y      -  256 1.3.36.3.3.2.8.1.1.7 )],
    27 => [qw( brainpoolP384r1                   7027  Y      -  384 1.3.36.3.3.2.8.1.1.11)],
    28 => [qw( brainpoolP512r1                   7027  Y      -  512 1.3.36.3.3.2.8.1.1.13)],
    29 => [qw( ecdh_x25519                       4492bis Y    -  225                      )],
    30 => [qw( ecdh_x448                         4492bis Y    -  448                      )],

    256   => [qw( ffdhe2048                         ietf-tls-negotiated-ff-dhe-10 Y - 2048   )],
    257   => [qw( ffdhe3072                         ietf-tls-negotiated-ff-dhe-10 Y - 3072   )],
    258   => [qw( ffdhe4096                         ietf-tls-negotiated-ff-dhe-10 Y - 4096   )],
    259   => [qw( ffdhe6144                         ietf-tls-negotiated-ff-dhe-10 Y - 6144   )],
    260   => [qw( ffdhe8192                         ietf-tls-negotiated-ff-dhe-10 Y - 8192   )],
    65281 => [qw( arbitrary_explicit_prime_curves   4492  Y      -    ?                      )],
    65282 => [qw( arbitrary_explicit_char2_curves   4492  Y      -    ?                      )],

    42001 => [qw( Curve3617                         ????  N      -   -1                      )],
    42002 => [qw( secp112r1                         ????  N      -   -1 1.3.132.0.6          )],
    42003 => [qw( secp112r2                         ????  N      -   -1 1.3.132.0.7          )],
    42004 => [qw( secp113r1                         ????  N      -   -1 1.3.132.0.4          )],
    42005 => [qw( secp113r2                         ????  N      -   -1 1.3.132.0.5          )],
    42006 => [qw( secp131r1                         ????  N      -   -1 1.3.132.0.22         )],
    42007 => [qw( secp131r2                         ????  N      -   -1 1.3.132.0.23         )],
    42008 => [qw( secp128r1                         ????  N      -   -1 1.3.132.0.28         )],
    42009 => [qw( secp128r2                         ????  N      -   -1 1.3.132.0.29         )],
    42011 => [qw( ed25519                           ????  N Ed25519  -1 1.3.6.1.4.1.11591.15.1)],
    42012 => [qw( brainpoolp160r1                   ????  N      -   -1 1.3.36.3.3.2.8.1.1.1 )],
    42013 => [qw( brainpoolp192r1                   ????  N      -   -1 1.3.36.3.3.2.8.1.1.3 )],
    42014 => [qw( brainpoolp224r1                   ????  N      -   -1 1.3.36.3.3.2.8.1.1.5 )],
    42015 => [qw( brainpoolp320r1                   ????  N      -   -1 1.3.36.3.3.2.8.1.1.9 )],
    42016 => [qw( brainpoolp512r1                   ????  N      -   -1 1.3.36.3.3.2.8.1.1.13)],
    42020 => [qw( GOST2001-test                     ????  N      -   -1 1.2.643.2.2.35.0     )],
    42021 => [qw( GOST2001-CryptoPro-A              ????  N      -   -1 1.2.643.2.2.35.1     )],
    42022 => [qw( GOST2001-CryptoPro-B              ????  N      -   -1 1.2.643.2.2.35.2     )],
    42023 => [qw( GOST2001-CryptoPro-C              ????  N      -   -1 1.2.643.2.2.35.3     )],
    42024 => [qw( GOST2001-CryptoPro-A              ????  N      -   -1                      )],
    42025 => [qw( GOST2001-CryptoPro-C              ????  N      -   -1                      )],
    42026 => [qw( GOST2001-CryptoPro-A              ????  N      -   -1 1.2.643.2.2.36.0     )],
    42027 => [qw( GOST2001-CryptoPro-C              ????  N      -   -1 1.2.643.2.2.36.1     )],
    42031 => [qw( X9.62 prime192v2                  ????  N      -   -1 1.2.840.10045.3.1.2  )],
    42032 => [qw( X9.62 prime192v3                  ????  N      -   -1 1.2.840.10045.3.1.3  )],
    42033 => [qw( X9.62 prime239v1                  ????  N      -   -1 1.2.840.10045.3.1.4  )],
    42034 => [qw( X9.62 prime239v2                  ????  N      -   -1 1.2.840.10045.3.1.5  )],
    42035 => [qw( X9.62 prime239v3                  ????  N      -   -1 1.2.840.10045.3.1.6  )],
    42041 => [qw( X9.62 c2tnb191v1                  ????  N      -   -1 1.2.840.10045.3.0.5  )],
    42042 => [qw( X9.62 c2tnb191v2                  ????  N      -   -1 1.2.840.10045.3.0.6  )],
    42043 => [qw( X9.62 c2tnb191v3                  ????  N      -   -1 1.2.840.10045.3.0.7  )],
    42044 => [qw( X9.62 c2tnb239v1                  ????  N      -   -1 1.2.840.10045.3.0.11 )],
    42045 => [qw( X9.62 c2tnb239v2                  ????  N      -   -1 1.2.840.10045.3.0.12 )],
    42046 => [qw( X9.62 c2tnb239v3                  ????  N      -   -1 1.2.840.10045.3.0.13 )],
    42047 => [qw( X9.62 c2tnb359v1                  ????  N      -   -1 1.2.840.10045.3.0.18 )],
    42048 => [qw( X9.62 c2tnb431r1                  ????  N      -   -1 1.2.840.10045.3.0.20 )],
    42061 => [qw( X9.62 c2pnb163v1                  ????  N      -   -1 1.2.840.10045.3.0.1  )],
    42062 => [qw( X9.62 c2pnb163v2                  ????  N      -   -1 1.2.840.10045.3.0.2  )],
    42063 => [qw( X9.62 c2pnb163v3                  ????  N      -   -1 1.2.840.10045.3.0.3  )],
    42064 => [qw( X9.62 c2pnb176w1                  ????  N      -   -1 1.2.840.10045.3.0.4  )],
    42065 => [qw( X9.62 c2pnb208w1                  ????  N      -   -1 1.2.840.10045.3.0.10 )],
    42066 => [qw( X9.62 c2pnb272w1                  ????  N      -   -1 1.2.840.10045.3.0.16 )],
    42067 => [qw( X9.62 c2pnb304w1                  ????  N      -   -1 1.2.840.10045.3.0.18 )],
    42068 => [qw( X9.62 c2pnb368w1                  ????  N      -   -1 1.2.840.10045.3.0.19 )],
    42101 => [qw( prime192v1                        ????  N      -   92 )],
    42101 => [qw( prime192v2                        ????  N      -   92 )],
    42101 => [qw( prime192v3                        ????  N      -   92 )],
    42101 => [qw( prime239v1                        ????  N      -   39 )],
    42101 => [qw( prime239v2                        ????  N      -   39 )],
    42101 => [qw( prime239v3                        ????  N      -   39 )],
    42101 => [qw( prime256v1                        ????  N      -   56 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls1            ????  N      -  113 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls3            ????  N      -  163 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls4            ????  N      -  112 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls5            ????  N      -  163 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls6            ????  N      -  112 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls7            ????  N      -  160 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls8            ????  N      -  112 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls9            ????  N      -  160 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls10           ????  N      -  233 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls11           ????  N      -  233 )],
    42101 => [qw( wap-wsg-idm-ecid-wtls12           ????  N      -  224 )],
    42101 => [qw( Oakley-EC2N-3                     ????  N      -   55 )],
    42101 => [qw( Oakley-EC2N-4                     ????  N      -   85 )],

    41147 => [qw( Curve1147                         ????  N      -   -1 )],
    41187 => [qw( Curve511157                       ????  N      -   -1 )],
    41417 => [qw( Curve41417                        ????  N      -   -1 )],
    42213 => [qw( Curve2213                         ????  N      -   -1 )],
    42448 => [qw( Curve448                          ????  N      -   -1 )],
    42519 => [qw( X25519                            ????  N      -   -1 )],
    42222 => [qw( E222                              ????  N      -   -1 )],
    42382 => [qw( E382                              ????  N      -   -1 )],
    42383 => [qw( E383                              ????  N      -   -1 )],
    42521 => [qw( E521                              ????  N      -   -1 )],
    42147 => [qw( GOST28147-89                      ????  N      -   -1 )],
    42147 => [qw( GOST-R34.11-94                    ????  N      -   -1 )],

    65165 => [qw( CurveCECPQ1                       ????  N      -   -1 )],
);

our %data_oid = (

    '1.3.6.1' => { 'txt' => "Internet OID" },
    '1.3.6.1.5.5.7.1.1'        => { 'txt' => "Authority Information Access" },
    '1.3.6.1.5.5.7.1.12'       => { 'txt' => $OText::STR{UNDEF} },
    '1.3.6.1.5.5.7.1.14'       => { 'txt' => "Proxy Certification Information" },
    '1.3.6.1.5.5.7.1.24'       => { 'txt' => "id-pe-tlsfeature" },
    '1.3.6.1.5.5.7.3.1'        => { 'txt' => "Server Authentication" },
    '1.3.6.1.5.5.7.3.2'        => { 'txt' => "Client Authentication" },
    '1.3.6.1.5.5.7.3.3'        => { 'txt' => "Code Signing" },
    '1.3.6.1.5.5.7.3.4'        => { 'txt' => "Email Protection" },
    '1.3.6.1.5.5.7.3.5'        => { 'txt' => "IPSec end system" },
    '1.3.6.1.5.5.7.3.6'        => { 'txt' => "IPSec tunnel" },
    '1.3.6.1.5.5.7.3.7'        => { 'txt' => "IPSec user" },
    '1.3.6.1.5.5.7.3.8'        => { 'txt' => "Timestamping" },
    '1.3.6.1.5.5.7.48.1'       => { 'txt' => "ocsp" },
    '1.3.6.1.5.5.7.48.2'       => { 'txt' => "caIssuer" },
    '1.3.6.1.4.1.11129.2.5.1'  => { 'txt' => $OText::STR{UNDEF} },
    '1.3.6.1.4.1.14370.1.6'    => { 'txt' => $OText::STR{UNDEF} },
    '1.3.6.1.4.1.311.10.3.3'   => { 'txt' => "Microsoft Server Gated Crypto" },
    '1.3.6.1.4.1.311.10.11'    => { 'txt' => "Microsoft Server: EV additional Attributes" },
    '1.3.6.1.4.1.311.10.11.11' => { 'txt' => "Microsoft Server: EV ??friendly name??" },
    '1.3.6.1.4.1.311.10.11.83' => { 'txt' => "Microsoft Server: EV ??root program??" },
    '1.3.6.1.4.1.4146.1.10'    => { 'txt' => $OText::STR{UNDEF} },
    '1.3.6.1.5.5.7.8.7'        => { 'txt' => "otherName" },
    '2.16.840.1.113730.4.1'    => { 'txt' => "Netscape SGC" },
    '1.2.840.113549.1.1.1'     => { 'txt' => "SubjectPublicKeyInfo" },
    '1.2.840.113549.1.1.5'     => { 'txt' => "SignatureAlgorithm" },
    '2.5.4.10' => { 'txt' => "EV Certificate: subject:organizationName" },
    '2.5.4.11' => { 'txt' => "EV Certificate: subject:organizationalUnitName" },
    '2.5.4.15' => { 'txt' => "EV Certificate: subject:businessCategory" },
    '2.5.4.3'  => { 'txt' => "EV Certificate: subject:commonName" },

    '1.3.6.1.4.1.311.60.2.1.1' => { 'txt' => "EV Certificate: subject:jurisdictionOfIncorporationLocalityName" },
    '1.3.6.1.4.1.311.60.2.1.2' => { 'txt' => "EV Certificate: subject:jurisdictionOfIncorporationStateOrProvinceName" },
    '1.3.6.1.4.1.311.60.2.1.3' => { 'txt' => "EV Certificate: subject:jurisdictionOfIncorporationCountryName" },
    '2.5.4.5'                  => { 'txt' => "EV Certificate: subject:serialNumber" },
    '2.5.4.6'  => { 'txt' => "EV Certificate: subject:countryName" },
    '2.5.4.7'  => { 'txt' => "EV Certificate: subject:localityName" },
    '2.5.4.8'  => { 'txt' => "EV Certificate: subject:stateOrProvinceName" },
    '2.5.4.9'  => { 'txt' => "EV Certificate: subject:streetAddress" },
    '2.5.4.17' => { 'txt' => "EV Certificate: subject:postalCode" },
    '1.3.6.1.4.1.311.60.2.1' => { 'txt' => "EV Certificate: qcStatements:qcStatement:statementId" },
    '1.3.6.1.4.1.311.60.1.1'     => { 'txt' => "EV Certificate: ??fake root??" },
    '2.5.29.32.0'                => { 'txt' => "EV Certificate: subject:anyPolicy" },
    '2.5.29.35'                  => { 'txt' => "EV Certificate: subject:authorityKeyIdentifier" },
    '2.5.29.37'                  => { 'txt' => "EV Certificate: subject:extendedKeyUsage" },
    '0.9.2342.19200300.100.1.25' => { 'txt' => "EV Certificate: subject:domainComponent" },
    '2.5.4.4'                    => { 'txt' => "subject:surname" },
    '2.5.4.12'                   => { 'txt' => "subject:title" },
    '2.5.4.41'                   => { 'txt' => "subject:name" },
    '2.5.4.42'                   => { 'txt' => "subject:givenName" },
    '2.5.4.43'                   => { 'txt' => "subject:intials" },
    '2.5.4.44'                   => { 'txt' => "subject:generationQualifier" },
    '2.5.4.46'                   => { 'txt' => "subject:dnQualifier" },
    '2.5.29.14'                  => { 'txt' => "subject:subjectKeyIdentifier" },
    '2.5.29.15'                  => { 'txt' => "subject:keyUsage" },
    '2.5.29.17'                  => { 'txt' => "subject:subjectAlternateName" },
    '2.5.29.19'                  => { 'txt' => "subject:basicConstraints" },
    '2.5.29.31'                  => { 'txt' => "subject:crlDistributionPoints" },
    '2.5.29.32'                  => { 'txt' => "subject:certificatePolicies" },
    '2.5.29.37'                  => { 'txt' => "subject:extendedKeyUsage" },
    '2.16.840.1.113733.1.7.23.6' => { 'txt' => $OText::STR{UNDEF} },
    '2.16.840.1.113733.1.7.48.1' => { 'txt' => $OText::STR{UNDEF} },
    '2.16.840.1.113733.1.7.54'   => { 'txt' => $OText::STR{UNDEF} },
    '0.9.2342.19200300.100.1.3'  => { 'txt' => "subject:mail" },
);

our %cfg = (
    'mename'         => "O-Saft ",
    'need_netdns'    => 0,
    'need_timelocal' => 0,
    'need_netinfo'   => 1,

    'me'             => "",
    'ARG0'           => "",
    'ARGV'           => [],
    'RC-ARGV'        => [],
    'RC-FILE'        => "",
    'prefix_trace'   => "",
    'prefix_verbose' => "",

    'dirs' => {
        'lib'  => "lib",
        'doc'  => "doc",
        'usr'  => "usr",
        'test' => "t",
    },

    'try'     => 0,
    'exec'    => 0,
    'trace'   => 0,
    'traceME' => 0,

    'time0'       => 0,
    'linux_debug' => 0,
    'verbose'     => 0,
    'v_cipher'    => 0,
    'proxyhost'   => "",
    'proxyport'   => 0,
    'proxyauth'   => "",
    'proxyuser'   => "",
    'proxypass'   => "",
    'starttls'    => "",

    'starttls_delay'    => 0,
    'starttls_phase'    => [],
    'starttls_error'    => [],
    'slow_server_delay' => 0,
    'connect_delay'     => 0,
    'socket_reuse'      => 1,

    'ignore_no_conn' => 0,
    'protos_next'    => 'http/1.1,h2c,h2c-14,spdy/1,npn-spdy/2,spdy/2,spdy/3,spdy/3.1,spdy/4a2,spdy/4a4,grpc-exp,h2-14,h2-15,h2-16,http/2.0,h2',
    'protos_alpn' => [],
    'protos_npn'  => [],
    'slowly'      => 0,
    'usesni'      => 1,
    'sni_name'    => undef,

    'use_sni_name' => 0,

    'sclient_opt' => "",
    'no_cert_txt' => "",
    'ca_depth'    => undef,
    'ca_crl'      => undef,
    'ca_file'     => undef,
    'ca_path'     => undef,

    'ca_files' => [qw(ca-certificates.crt certificates.crt certs.pem cert.pem)],
    'ca_paths' => [qw(/etc/ssl/certs       /usr/lib/certs           /System/Library/OpenSSL /etc/tls/certs)],
    'openssl_cnfs' => [qw(/etc/ssl/openssl.cnf /usr/lib/ssl/openssl.cnf /System//Library/OpenSSL/openssl.cnf /usr/ssl/openssl.cnf)],
    'openssl_cnf'   => undef,
    'openssl_env'   => undef,
    'openssl_fips'  => undef,
    'openssl_msg'   => "",
    'ignorecase'    => 1,
    'ignorenoreply' => 1,
    'label'         => 'long',
    'labels'        => [qw(full long short key)],
    'version'       => [],
    'versions'      =>

      [qw(SSLv2 SSLv3 TLSv1 TLSv11 TLSv12 TLSv13 DTLSv09 DTLSv1 DTLSv11 DTLSv12 DTLSv13)],
    'DTLS_versions' => [qw(DTLSv09 DTLSv1 DTLSv11 DTLSv12 DTLSv13)],
    'SSLv2'      => 1,
    'SSLv3'      => 1,
    'TLSv1'      => 1,
    'TLSv11'     => 1,
    'TLSv12'     => 1,
    'TLSv13'     => 1,
    'DTLSv09'    => 0,
    'DTLSv1'     => 1,
    'DTLSv11'    => 0,
    'DTLSv12'    => 1,
    'DTLSv13'    => 0,
    'TLS1FF'     => 0,
    'DTLSfamily' => 0,
    'ciphers'    => [],

    'cipher' => [],

    'cipherpattern' => "ALL:NULL:eNULL:aNULL:LOW:EXP",

    'cipherpatterns' => {

        'all'     => [ "All ciphers known by openssl", 'ALL:NULL:eNULL:aNULL:LOW:EXP' ],
        'openssl' => [ "All ciphers known by openssl", 'ALL:NULL:eNULL:aNULL:LOW:EXP' ],
        'null'   => [ "Null Ciphers",                           'NULL:eNULL' ],
        'anull'  => [ "Anonymous NULL Ciphers",                 'aNULL' ],
        'anon'   => [ "Anonymous DH Ciphers",                   'ADH' ],
        'adh'    => [ "Anonymous DH Ciphers",                   'ADH' ],
        'aes'    => [ "AES Ciphers",                            'AES' ],
        'aes128' => [ "AES128 Ciphers",                         'AES128' ],
        'aes256' => [ "AES256 Ciphers",                         'AES256' ],
        'aesGCM' => [ "AESGCM Ciphers",                         'AESGCM' ],
        'chacha' => [ "CHACHA20 Ciphers",                       'CHACHA' ],
        'dhe'    => [ "Ephermeral DH Ciphers",                  'EDH' ],
        'edh'    => [ "Ephermeral DH Ciphers",                  'EDH' ],
        'ecdh'   => [ "Ecliptical curve DH Ciphers",            'ECDH' ],
        'ecdsa'  => [ "Ecliptical curve DSA Ciphers",           'ECDSA' ],
        'ecdhe'  => [ "Ephermeral ecliptical curve DH Ciphers", 'EECDH' ],
        'eecdh'  => [ "Ephermeral ecliptical curve DH Ciphers", 'EECDH' ],
        'aecdh'  => [ "Anonymous ecliptical curve DH Ciphers",  'AECDH' ],
        'exp40'  => [ "40 Bit encryption",                      'EXPORT40' ],
        'exp56'  => [ "56 Bit export ciphers",                  'EXPORT56' ],
        'export' => [ "all Export Ciphers",                     'EXPORT' ],
        'exp'    => [ "all Export Ciphers",                     'EXPORT' ],
        'des'    => [ "DES Ciphers",                            'DES:!ADH:!EXPORT:!aNULL' ],
        '3des'   => [ "Triple DES Ciphers",                     '3DES' ],
        'fips'   => [ "FIPS compliant Ciphers",                 'FIPS' ],
        'gost'   => [ "all GOST Ciphers",                       'GOST' ],
        'gost89' => [ "all GOST89 Ciphers",                     'GOST89' ],
        'gost94' => [ "all GOST94 Ciphers",                     'GOST94' ],
        'idea'   => [ "IDEA Ciphers",                           'IDEA' ],
        'krb'    => [ "KRB5 Ciphers",                           'KRB5' ],
        'krb5'   => [ "KRB5 Ciphers",                           'KRB5' ],
        'md5'    => [ "Ciphers with MD5 Mac",                   'MD5' ],
        'psk'    => [ "PSK Ciphers",                            'PSK' ],
        'rc2'    => [ "RC2 Ciphers",                            'RC2' ],
        'rc4'    => [ "RC4 Ciphers",                            'RC4' ],
        'rsa'    => [ "RSA Ciphers",                            'RSA' ],
        'seed'   => [ "Seed Ciphers",                           'SEED' ],
        'sslv2'  => [ "all SSLv2 Ciphers",                      'SSLv2' ],
        'sslv3'  => [ "all SSLv3 Ciphers",                      'SSLv3' ],
        'tlsv1'  => [ "all TLSv1 Ciphers",                      'TLSv1' ],
        'tlsv11' => [ "all TLSv11 Ciphers",                     'TLSv1' ],
        'tlsv12' => [ "all TLSv12 Ciphers",                     'TLSv1.2' ],
        'tls13'  => [ "some TLS13 Ciphers",      'TLS13' ],
        'srp'    => [ "SRP Ciphers",             'SRP' ],
        'sha'    => [ "Ciphers with SHA1 Mac",   'SHA' ],
        'sha'    => [ "Ciphers with SHA1 Mac",   'SHA' ],
        'sha1'   => [ "Ciphers with SHA1 Mac",   'SHA1' ],
        'sha2'   => [ "Ciphers with SHA256 Mac", 'SHA256' ],
        'sha256' => [ "Ciphers with SHA256 Mac", 'SHA256' ],
        'sha384' => [ "Ciphers with SHA384 Mac", 'SHA384' ],
        'sha512' => [ "Ciphers with SHA512 Mac", 'SHA512' ],
        'weak'   => [ "Weak grade encryption",   'LOW:3DES:DES:RC4:ADH:EXPORT' ],
        'low'    => [ "Low grade encryption",    'LOW:3DES:RC4:!ADH' ],
        'medium' => [ "Medium grade encryption", 'MEDIUM:!NULL:!aNULL:!SSLv2:!3DES:!RC4' ],
        'high'   => [ "High grade encryption",   'HIGH:!NULL:!aNULL:!DES:!3DES' ],
    },
    'ciphermode'  => 'intern',
    'ciphermodes' => [qw(dump intern openssl socket)],
    'cipherrange'  => 'intern',
    'cipherranges' => {

        'openssl' => 'openssl',
        'rfc'     =>

          "0x03000000 .. 0x030000FF, 0x03001300 .. 0x030013FF,
                        0x0300C000 .. 0x0300C1FF, 0x0300CC00 .. 0x0300CCFF,
                        0x0300D000 .. 0x0300D0FF,
                        0x0300FE00 .. 0x0300FFFF,
                       ",
        'iana' =>

          "0x0300009E, 0x0300009F, 0x030000AA, 0x030000AB,
                        0x03001301, 0x03001302, 0x03001303, 0x03001304,
                        0x0300C02B, 0x0300C02C, 0x0300C02F, 0x0300C030,
                        0x0300C09E, 0x0300C09F,
                        0x0300C0A6, 0x0300C0A7, 0x0300C0A8, 0x0300C0A9,
                        0x0300CCAA, 0x0300CCAC, 0x0300CCAD,
                        0x0300D001, 0x0300D002, 0x0300D005,
                       ",
        'shifted' => "0x03000100 .. 0x0300013F, 0x0300FE00 .. 0x0300FFFF,",
        'long' => "0x03000000 .. 0x030013FF, 0x0300C000 .. 0x0300FFFF,",
        'huge' => "0x03000000 .. 0x0300FFFF",
        'safe' =>

          "0x03000000 .. 0x032FFFFF",
        'full' => "0x03000000 .. 0x03FFFFFF",
        'SSLv2_base' => "0x02000000,   0x02010080, 0x02020080, 0x02030080, 0x02040080,
                        0x02050080,   0x02060040, 0x02060140, 0x020700C0, 0x020701C0,
                        0x02FF0800,   0x02FF0810, 0x02FFFFFF,
                       ",
        'SSLv2_rfc'  => "0x03000000 .. 0x03000002, 0x03000007 .. 0x0300002C, 0x030000FF,",
        'SSLv2_rfc+' => "0x03000000 .. 0x0300002F, 0x030000FF,",
        'SSLv2_FIPS' => "0x0300FEE0,   0x0300FEE1, 0x0300FEFE, 0x0300FEFF,",
        'SSLv2'      => "",

        'SSLv2_long' => "",

        'SSLv2_full' => "0x02000000 .. 0x02FFFFFF",
        'SSLv3'      => "0x03000000 .. 0x0300003A, 0x03000041 .. 0x03000046,
                        0x03000060 .. 0x03000066, 0x03000080 .. 0x0300009B,
                        0x0300C000 .. 0x0300C022, 0x0300FEE0 .. 0x0300FEFF,
                        0x0300FF00 .. 0x0300FF03, 0x0300FF80 .. 0x0300FF83, 0x0300FFFF,
                       ",
        'SSLv3_SSLv2' => "",

        'TLSv10' => "",
        'TLSv11' => "",
        'TLSv12' => "0x0300003B .. 0x03000040, 0x03000067 .. 0x0300006D,
                        0x0300009C .. 0x030000A7, 0x030000BA .. 0x030000C5,
                        0x0300C023 .. 0x0300C032, 0x0300C072 .. 0x0300C079,
                        0x0300CC13 .. 0x0300CC15, 0x0300D000 .. 0x0300D005,
                        0x0300C100 .. 0x0300C102, 0x030000FE, 0x0300FFFF,
                       ",
        'TLSv13' => "0x03001301 .. 0x03001305, 0x0300FF85, 0x0300FF87,
                        0x030000C6,   0x030000C7, 0x0300C0B4, 0x0300C0B5,
                        0x0300C100 .. 0x0300C107, 0x030000FE,
                       ",
        'GREASE' => "0x03000A0A, 0x03001A1A, 0x03002A2A, 0x03003A3A, 0x03004A4A,
                        0x03005A5A, 0x03006A6A, 0x03007A7A, 0x03008A8A, 0x03009A9A,
                        0x0300AAAA, 0x0300BABA, 0x0300CACA, 0x0300DADA, 0x0300EAEA, 0x0300FAFA,
                       ",
        'c0xx'   => "0x0300C000 .. 0x0300C0FF",
        'ccxx'   => "0x0300CC00 .. 0x0300CCFF",
        'ecc'    => "0x0300C000 .. 0x0300C0FF, 0x0300CC00 .. 0x0300CCFF,",
        'intern' => "",

    },
    'cipher_dh'  => 0,
    'cipher_md5' => 1,

    'cipher_ecdh'  => 1,
    'cipher_alpns' => [],
    'cipher_npns'  => [],
    'ciphercurves' => [
        qw(prime192v1 prime256v1),
        qw(sect163k1 sect163r1 sect193r1           sect233k1 sect233r1),
        qw(sect283k1 sect283r1 sect409k1 sect409r1 sect571k1 sect571r1),
        qw(secp160k1 secp160r1 secp160r2 secp192k1 secp224k1 secp224r1),
        qw(secp256k1 secp384r1 secp521r1),
        qw(brainpoolP256r1 brainpoolP384r1 brainpoolP512r1),
    ],

    'extensions_by_prot' => {
        'SSLv3'  => [],
        'TLSv1'  => [qw(renegotiation_info supported_groups ec_point_formats session_ticket)],
        'TLSv11' => [qw(renegotiation_info supported_groups ec_point_formats session_ticket)],
        'TLSv12' => [qw(renegotiation_info supported_groups ec_point_formats signature_algorithms )],
        'TLSv13' => [
            qw(supported_versions supported_groups ec_point_formats signature_algorithms
              session_ticket renegotiation_info encrypt_then_mac
              extended_master_secret psk_key_exchange_modes key_share
            )
        ],
    },

    'do'       => [],
    'commands' => [],

    'commands_cmd' => [],
    'commands_usr' => [],

    'commands_exp'    => [ ],
    'commands_notyet' => [
        qw(zlib lzo open_pgp fallback closure sgc scsv time
          cps_valid cipher_order cipher_weak
        ),
    ],
    'commands_int' => [

        qw(
          cipher cipher_intern cipher_openssl cipher_socket
          cipher_dump   cipher_dh cipher_default
          bsi check check_sni dump ev exec help info info--v http
          quick list libversion sigkey sizes s_client version quit
        ),
        qw(cn_nosni valid_years valid_months valid_days valid_host)
    ],
    'commands_hint' => [

        qw(rfc_7525 tr_02102+ tr_02102- tr_03116+ tr_03116-)
    ],
    'cmd-beast'   => [qw(beast)],
    'cmd-crime'   => [qw(crime)],
    'cmd-drown'   => [qw(drown)],
    'cmd-freak'   => [qw(freak)],
    'cmd-lucky13' => [qw(lucky13)],
    'cmd-robot'   => [qw(robot)],
    'cmd-sweet32' => [qw(sweet32)],
    'cmd-http'    => [],
    'cmd-hsts'    => [],
    'cmd-info'    => [],
    'cmd-info--v' => [],
    'cmd-check'   => [],
    'cmd-sizes'   => [],
    'cmd-quick'   => [

        qw(
          sslversion hassslv2 hassslv3 hastls12 hastls13 hasdtls12
          cipher_selected cipher_strong cipher_null cipher_adh
          cipher_exp cipher_cbc cipher_des cipher_rc4 cipher_edh
          cipher_pfs beast crime drown freak heartbleed logjam
          lucky13 poodle rc4 robot sloth sweet32
          fingerprint_hash fp_not_md5 sha2signature pub_encryption
          pub_enc_known email serial subject dates verify heartbeat
          expansion compression hostname hsts_sts crl master_secret
          renegotiation resumption tr_02102+ tr_02102- rfc_7525
        )
    ],
    'cmd-ev'  => [qw(cn subject altname dv ev ev- ev+ ev_chars)],
    'cmd-bsi' => [

        qw(after dates crl cipher_rc4 renegotiation
          tr_02102+ tr_02102- tr_03116+ tr_03116-
        )
    ],
    'cmd-pfs'    => [qw(cipher_pfs cipher_pfsall session_random)],
    'cmd-sni'    => [qw(sni hostname certfqdn)],
    'cmd-sni--v' => [qw(sni cn altname verify_altname verify_hostname hostname wildhost wildcard)],
    'cmd-vulns'  => [
        qw(
          beast breach ccs crime drown freak heartbleed logjam
          lucky13 poodle rc4 robot sloth sweet32 time
          hassslv2 hassslv3 compression cipher_pfs session_random
          renegotiation resumption
        )
    ],
    'cmd-prots' => [
        qw(hassslv2 hassslv3 hastls10 hastls11 hastls12 hastls13 hasdtls1 hasdtls12 hasdtls13 hasalpn hasnpn session_protocol fallback_protocol alpn alpns npn npns next_protocols https_protocols http_protocols https_svc http_svc)
    ],
    'need-NL' => [

        qw(certificate extensions pem pubkey sigdump text
          chain chain_verify ocsp_response_data)
    ],

    'need-sslv3' => [
        qw(check cipher cipher_dh cipher_strong cipher_selected
          cipher_weak protocols hassslv3 beast freak poodle
          tr_02102+ tr_02102- tr_03116+ tr_03116- rfc_7525
        )
    ],
    'need-cipher' => [
        qw(check cipher cipher_dh  cipher_strong cipher_weak
          cipher_dump cipher_intern cipher_socket cipher_openssl
          cipher_null cipher_adh cipher_cbc cipher_des cipher_edh
          cipher_exp  cipher_rc4 cipher_pfs cipher_pfsall
          beast crime time drown freak logjam
          lucky13 poodle rc4 robot sloth sweet32
          tr_02102+ tr_02102- tr_03116+ tr_03116- rfc_7525
          hassslv2 hassslv3 hastls10 hastls11 hastls12 hastls13
        )
    ],
    'need-default' => [
        qw(check cipher cipher_default
          cipher_dump cipher_intern cipher_socket cipher_openssl
          cipher_pfs  cipher_order  cipher_strong cipher_selected),
        qw(sslv3  tlsv1   tlsv10  tlsv11 tlsv12),
        qw(sslv2  tlsv13  dtlsv09 dtlvs1 dtlsv11 dtlsv12 dtlsv13)
    ],
    'need-checkssl' => [
        qw(check beast crime time breach freak
          cipher_adh cipher_cbc cipher_des cipher_null
          cipher_edh cipher_exp cipher_rc4 cipher_selected
          cipher_pfs cipher_pfsall
          ev+ ev- tr_02102+ tr_02102- tr_03116+ tr_03116-
          ocsp_response ocsp_response_status ocsp_stapling
          ocsp_uri ocsp_valid
          rfc_7525 rfc_6125_names rfc_2818_names
        )
    ],
    'need-checkalnp'  => [ qw(alpns alpn hasalpn npns npn hasnpn), ],
    'need-checkbleed' => [qw(heartbleed)],
    'need-check_dh'   => [ qw(logjam dh_512 dh_2048 ecdh_256 ecdh_512)],
    'need-checkdest'  => [
        qw(reversehost ip resumption renegotiation
          session_protocol session_ticket session_random session_lifetime
          krb5 psk_hint psk_identity srp heartbeat ocsp_stapling
          cipher_selected cipher_pfs ccs compression crime
        )
    ],
    'need-checkhttp' => [ qw(breach https_pins)],
    'need-checkprot' => [
        qw(
          sslversion
          hassslv2 hassslv3 hastls10 hastls11 hastls12 hastls13
          alpns alpn hasalpn npns npn hasnpn
          crime drown poodle
        )
    ],
    'need-checksni' => [ qw(hostname certfqdn cn cn_nosni sni)],
    'need-checkchr' => [ qw(cn subject issuer altname ext_crl ocsp_uri), ],
    'data_hex'      => [

        qw(
          fingerprint fingerprint_hash fingerprint_md5
          fingerprint_sha1 fingerprint_sha2
          sigkey_value pubkey_value modulus
          subject_hash issuer_hash
          ocsp_public_hash ocsp_subject_hash
          master_key resumption_psk session_id session_ticket
          ext_authorityid ext_subjectkeyid
        )
    ],

    'ignore-out' => [qw(https_body)],

    'out' => {
        'disabled'     => 1,
        'enabled'      => 1,
        'header'       => 0,
        'hostname'     => 0,
        'hint_cipher'  => 1,
        'hint_check'   => 1,
        'hint_info'    => 1,
        'hint'         => 1,
        'http_body'    => 0,
        'traceARG'     => 0,
        'traceCMD'     => 0,
        'traceKEY'     => 0,
        'traceTIME'    => 0,
        'time_absolut' => 0,
        'warning'      => 1,
        'score'        => 0,
        'ignore'       => [qw(https_body)],
        'warnings_no_dups' => [qw(303 304 412)],
        'warnings_printed' => [],

        'exitcode'        => 0,
        'exitcode_checks' => 1,
        'exitcode_cipher' => 1,
        'exitcode_medium' => 1,
        'exitcode_weak'   => 1,
        'exitcode_low'    => 1,
        'exitcode_pfs'    => 1,
        'exitcode_prot'   => 1,
        'exitcode_sizes'  => 1,
        'exitcode_quiet'  => 0,
    },

    'use' => {
        'mx'   => 0,
        'dns'  => 1,
        'http' => 1,

        'https' => 1,

        'forcesni' => 0,
        'sni'      => 1,

        'lwp'          => 0,
        'user_agent'   => undef,
        'alpn'         => 1,
        'npn'          => 1,
        'reconnect'    => 1,
        'extdebug'     => 1,
        'cert'         => 1,
        'no_comp'      => 0,
        'ssl_lazy'     => 0,
        'nullssl2'     => 0,
        'ssl_error'    => 1,
        'experimental' => 0,
        'exitcode'     => 0,

    },

    'tty' => {
        'width' => undef,

        'ident' => 2,
        'arrow' => "↲",

    },

    'opt-v'   => 0,
    'opt-V'   => 0,
    'format'  => "",
    'formats' => [qw(csv html json ssv tab xml fullxml raw hex 0x esc)],
    'tmplib'       => "/tmp/yeast-openssl/",
    'pass_options' => "",
    'mx_domains'   => [],
    'hosts'        => [],

    'targets' => [],

    'port'    => undef,
    'host'    => "",
    'ip'      => "",
    'IP'      => "",
    'rhost'   => "",
    'DNS'     => "",
    'timeout' => 2,

    'openssl' => {

        '-CAfile'                   => [ 1, "using -CAfile disabled" ],
        '-CApath'                   => [ 1, "using -CApath disabled" ],
        '-alpn'                     => [ 1, "checks with ALPN disabled" ],
        '-npn'                      => [ 1, "checks with NPN  disabled" ],
        '-nextprotoneg'             => [ 1, "checks with NPN  disabled" ],
        '-reconnect'                => [ 1, "checks with openssl reconnect disabled" ],
        '-fallback_scsv'            => [ 1, "checks for TLS_FALLBACK_SCSV wrong" ],
        '-comp'                     => [ 1, "<<NOT YET USED>>" ],
        '-no_comp'                  => [ 1, "<<NOT YET USED>>" ],
        '-no_tlsext'                => [ 1, "<<NOT YET USED>>" ],
        '-no_ticket'                => [ 1, "<<NOT YET USED>>" ],
        '-serverinfo'               => [ 1, "checks without TLS extension disabled" ],
        '-servername'               => [ 1, "checks with TLS extension SNI disabled" ],
        '-serverpref'               => [ 1, "<<NOT YET USED>>" ],
        '-showcerts'                => [ 1, "<<NOT YET USED>>" ],
        '-curves'                   => [ 1, "using -curves disabled" ],
        '-debug'                    => [ 1, "<<NOT YET USED>>" ],
        '-bugs'                     => [ 1, "<<NOT YET USED>>" ],
        '-key'                      => [ 1, "<<NOT YET USED>>" ],
        '-msg'                      => [ 1, "using -msg disabled, DH paramaters missing or wrong" ],
        '-nbio'                     => [ 1, "<<NOT YET USED>>" ],
        '-psk'                      => [ 1, "PSK  missing or wrong" ],
        '-psk_identity'             => [ 1, "PSK identity missing or wrong" ],
        '-pause'                    => [ 1, "<<NOT YET USED>>" ],
        '-prexit'                   => [ 1, "<<NOT YET USED>>" ],
        '-proxy'                    => [ 1, "<<NOT YET USED>>" ],
        '-quiet'                    => [ 1, "<<NOT YET USED>>" ],
        '-sigalgs'                  => [ 1, "<<NOT YET USED>>" ],
        '-state'                    => [ 1, "<<NOT YET USED>>" ],
        '-status'                   => [ 1, "<<NOT YET USED>>" ],
        '-strict'                   => [ 1, "<<NOT YET USED>>" ],
        '-nbio_test'                => [ 1, "<<NOT YET USED>>" ],
        '-tlsextdebug'              => [ 1, "TLS extension missing or wrong" ],
        '-client_sigalgs'           => [ 1, "<<NOT YET USED>>" ],
        '-record_padding'           => [ 1, "<<NOT YET USED>>" ],
        '-no_renegotiation'         => [ 1, "<<NOT YET USED>>" ],
        '-legacyrenegotiation'      => [ 1, "<<NOT YET USED>>" ],
        '-legacy_renegotiation'     => [ 1, "<<NOT YET USED>>" ],
        '-legacy_server_connect'    => [ 1, "<<NOT YET USED>>" ],
        '-no_legacy_server_connect' => [ 1, "<<NOT YET USED>>" ],
        '-ssl2'      => [ 1, "SSLv2 for +cipher disabled" ],
        '-ssl3'      => [ 1, "SSLv3 for +cipher disabled" ],
        '-tls1'      => [ 1, "TLSv1 for +cipher disabled" ],
        '-tls1_1'    => [ 1, "TLSv1.1 for +cipher disabled" ],
        '-tls1_2'    => [ 1, "TLSv1.2 for +cipher disabled" ],
        '-tls1_3'    => [ 1, "TLSv1.3 for +cipher disabled" ],
        '-dtls'      => [ 1, "DTLSv1 for +cipher disabled" ],
        '-dtls1'     => [ 1, "DTLSv1 for +cipher disabled" ],
        '-dtls1_1'   => [ 1, "DTLSv1.1 for +cipher disabled" ],
        '-dtls1_2'   => [ 1, "DTLSv1.2 for +cipher disabled" ],
        '-dtls1_3'   => [ 1, "DTLSv1.3 for +cipher disabled" ],
        '-no_ssl2'   => [ 1, "option ignored" ],
        '-no_ssl3'   => [ 1, "option ignored" ],
        '-no_tls1'   => [ 1, "option ignored" ],
        '-no_tls1_1' => [ 1, "option ignored" ],
        '-no_tls1_2' => [ 1, "option ignored" ],
        '-no_tls1_3' => [ 1, "option ignored" ],
    },
    'openssl_option_map' => {

    },
    'openssl_version_map' => {

    },

    'ssleay' => {

        'openssl'  => 1,
        'get_alpn' => 1,
        'get_npn'  => 1,
        'set_alpn' => 1,
        'set_npn'  => 1,
        'can_npn'  => 1,
        'can_ecdh' => 1,
        'can_sni'  => 1,
        'can_ocsp' => 1,
        'iosocket' => 1,
    },
    'sslerror' => {
        'timeout' => 1,
        'max'     => 5,
        'total'   => 10,

        'delay'          => 0,
        'per_prot'       => 1,
        'ignore_no_conn' => 0,

        'ignore_handshake' => 1,
    },
    'sslhello' => {
        'timeout'         => 2,
        'retry'           => 2,
        'maxciphers'      => 32,
        'usesignaturealg' => 1,
        'useecc'          => 1,
        'useecpoint'      => 1,
        'usereneg'        => 0,
        'double_reneg'    => 0,

        'nodatanocipher' => 1,
    },
    'legacy'  => "simple",
    'legacys' => [
        qw(cnark sslaudit sslcipher ssldiagnos sslscan dump
          ssltest ssltest-g sslyze testsslserver thcsslcheck
          simple full compact quick owasp osaft o-saft
          openssl openssl-v openssl-V)
    ],
    'usr_args' => [],

    'data' => {
        'file_sclient' => "",
        'file_ciphers' => "",
        'file_pem'     => "",
        'file_pcap'    => "",

    },

    'regex' => {
        'tools-ext' => '(?:ldd|openssl|timeout|Net::SSLeay|IO::Socket(?:::SSL|::INET)?)',
        'tools-own' => '(?:\$0|o-saft(?:\.(?:cgi|pl|tcl))?|o-saft-docker|lib/[^./]*\.pm|checkAllCiphers.pl|perl|perl2exe|perlapp)',
        'cmd-http'     => '^h?(?:ttps?|sts)_',
        'cmd-hsts'     => '^h?sts',
        'cmd-sizes'    => '^(?:cnt|len)_',
        'cmd-cfg'      => '(?:cmd|checks?|data|info|hint|text|scores?)',
        'commands_int' => '^(?:cn_nosni|valid_(?:year|month|day|host)s?)',
        'opt_empty'    => '(?:[+]|--)(?:cmd|help|host|port|format|legacy|timeout|trace|openssl|(?:cipher|proxy|sep|starttls|exe|lib|ca-|cfg-|ssl-|usr-).*)',
        'std_format' => '^(?:unix|raw|crlf|utf8|win32|perlio)$',

        'anon_output' => '',

        'SSLprot' => '^(SSL|D?TLS)v[0-9]',

        '_or-' => '[\+_-]',
        'ADHorDHA' => '(?:A(?:NON[_-])?DH|DH(?:A|[_-]ANON))[_-]',
        'RC4orARC4' => '(?:ARC(?:4|FOUR)|RC4)',
        '3DESorCBC3' => '(?:3DES(?:[_-]EDE)[_-]CBC|DES[_-]CBC3)',
        'DESor3DES' => '(?:[_-]3DES|DES[_-]_192)',
        'DHEorEDH' => '(?:DHE|EDH)[_-]',
        'EC-DSA' => 'EC(?:DHE|EDH)[_-]ECDSA',
        'EC-RSA' => 'EC(?:DHE|EDH)[_-]RSA',
        'EC'     => 'EC(?:DHE|EDH)[_-]',
        'EXPORT' => 'EXP(?:ORT)?(?:40|56|1024)?[_-]',
        'FRZorFZA' => '(?:FORTEZZA|FRZ|FZA)[_-]',
        'SHA2' => 'sha(?:2|224|256|384|512)',
        'AES-CCM' => 'AES(?:128|256)[_-]CCM[_-]?8?$',
        'AES-GCM' => 'AES(?:128|256)[_-]GCM[_-]SHA(?:256|384|512)',
        'SSLorTLS' => '^(?:SSL[23]?|TLS[12]?|PCT1?)[_-]',
        'aliases' => '(?:(?:DHE|DH[_-]ANON|DSS|RAS|STANDARD)[_-]|EXPORT_NONE?[_-]?XPORT|STRONG|UNENCRYPTED)',

        'compression'   => '(?:DEFLATE|LZO)',
        'nocompression' => '(?:NONE|NULL|^\s*$)',
        'encryption'    => '(?:encryption|ecPublicKey)',
        'encryption_ok' => '(?:(?:(?:(?:md[245]|ripemd160|sha(?:1|224|256|384|512))with)?[rd]saencryption)|id-ecPublicKey)',
        'encryption_no' => '(?:rsa(?:ssapss)?|sha1withrsa|dsawithsha1?|dsa_with_sha256)',
        'security' => '(?:HIGH|MEDIUM|LOW|WEAK|NONE)',
        'isIP'        => '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)',
        'isDNS'       => '(?:[a-z0-9.-]+)',
        'isIDN'       => '(?:xn--)',
        'leftwild'    => '^\*(?:[a-z0-9.-]+)',
        'doublewild'  => '(?:[a-z0-9.-]+\*[a-z0-9-]+\*)',
        'invalidwild' => '(?:\.\*\.)',
        'invalidIDN'  => '(?:xn--[a-z0-9-]*\*)',
        'isSPDY3'     => '(?:spdy\/3)',

        'OWASP_AA' => '^(TLS(?:v?13)?[_-](?:AES...|CHACHA20)[_-])',
        'OWASP_A'  => '^(?:TLSv?1[123]?)?(?:(EC)?(?:DHE|EDH).*?(?:AES...[_-]GCM|CHACHA20-POLY1305[_-]SHA))|TLS13[_-]AES-?...[_-]',
        'OWASP_B' => '^(?:TLSv1[123]?)?(?:(EC)?(?:DHE|EDH).*?(?:AES|CHACHA).*?(?!GCM|POLY1305)[_-]SHA)',
        'OWASP_C' => '^((?:TLSv1[123]?)?.*?(?:AES...|RSA)[_-]|(?:(?:EC)?DHE-)?PSK[_-]CHACHA)',
        'OWASP_D' =>
          '(?:^SSLv[23]|(?:NULL|EXP(?:ORT)?(?:40|56|1024)|A(?:EC|NON[_-])?DH|DH(?:A|[_-]ANON)|ECDSA|DSS|CBC|DES|MD[456]|RC[24]|PSK[_-]SHA|UNFFINED))',
        'OWASP_NA' => '(?:^PCT_|AEGIS|ARIA|CAMELLIA|ECDS[AS]|GOST|IDEA|SEED|CECPQ|SM4|FZA[_-]FZA)',
        'notOWASP_A    ' => '^(?:TLSv11?)',
        'notOWASP_B'     => '',
        'notOWASP_C'     => '',
        'notOWASP_D'     => '',
        'notCipher'      => '^GREASE|SCSV',

        'BREACH' => '(?:gzip|defalte)',
        'BEAST'  => '^(?:SSL[23]?|TLS[12]|PCT1?[_-])?.*?[_-]CBC',
        'FREAK' => '^(?:SSL[23]?)?(?:EXP(?:ORT)?(?:40|56|1024)?[_-])',
        'notCRIME' => '(?:NONE|NULL|^\s*$)',
        'Lucky13' => '^(?:SSL[23]?|TLS[12]|PCT1?[_-])?.*?[_-]CBC',
        'Logjam'  => 'EXP(?:ORT)?(?:40|56|1024)?[_-]',

        'POODLE'   => '^(?:SSL[23]?|TLS1)?[A-Z].*?[_-]CBC',
        'ROBOT'    => '^(?:(?:SSLv?3|TLSv?1(?:[12]))[_-])?(?:A?DH[_-])?(RC2|RC4|RSA)[_-]',
        'notROBOT' => '(?:(?:EC)?DHE[_-])',

        'SLOTH'      => '(?:(EXP(?:ORT)?|NULL).*MD5$|EC(?:DHE|EDH)[_-]ECDSA[_-].*(?:MD5|SHA)$)',
        'Sweet32'    => '(?:[_-](?:CBC3|3DES|DES|192)[_-])',
        'notSweet32' => '(?:[_-]AES[_-])',

        'PFS'    => '^(?:(?:SSLv?3|TLSv?1(?:[12])?|PCT1?)[_-])?((?:EC)?DHE|EDH)[_-]',
        'notPFS' => '^(?:LEGACY-GOST2012|GOST2012-NULL|(?:WDM|SHA).*?[_-]SHA(?:256|384)$)',
        'TR-02102' => '(?:DHE|EDH)[_-](?:PSK[_-])?(?:(?:EC)?DSS|RSA)[_-]',
        'notTR-02102' => '[_-]SHA$',
        'TR-02102-noPFS' => '(?:EC)?DH)[_-](?:EC)?(?:DSS|RSA)[_-]',
        'TR-03116+' => 'EC(?:DHE|EDH)[_-](?:PSK|(?:EC)?(?:DSS|RSA))[_-]AES128[_-](?:GCM[_-])?SHA256',
        'TR-03116-' => 'EC(?:DHE|EDH)[_-](?:PSK|(?:EC)?(?:DSS|RSA))[_-]AES(?:128|256)[_-](?:GCM[_-])?SHA(?:256|384)',
        'notTR-03116' => '(?:PSK[_-]AES256|[_-]SHA$)',
        'RFC7525'           => 'EC(?:DHE|EDH)[_-](?:PSK|(?:EC)?(?:DSS|RSA))[_-]AES128[_-](?:GCM[_-])?SHA256',
        '1.3.6.1.5.5.7.1.1' => '(?:1\.3\.6\.1\.5\.5\.7\.1\.1|authorityInfoAccess)',
        'NSA-B'             => '(?:ECD(?:H|SA).*?AES.*?GCM.*?SHA(?:256|384|512))',

        'notISM'      => '(?:NULL|A(?:NON[_-])?DH|DH(?:A|[_-]ANON)[_-]|(?:^DES|[_-]DES)[_-]CBC[_-]|MD5|RC)',
        'notPCI'      => '(?:NULL|(?:A(?:NON[_-])?DH|DH(?:A|[_-]ANON)|(?:^DES|[_-]DES)[_-]CBC|EXP(?:ORT)?(?:40|56|1024)?)[_-])',
        'notFIPS-140' => '(?:(?:ARC(?:4|FOUR)|RC4)|MD5|IDEA)',
        'FIPS-140'    => '(?:(?:3DES(?:[_-]EDE)[_-]CBC|DES[_-]CBC3)|AES)',

        'nonprint' => '/[\x00-\x1f\x7f-\xff]+/',
        'crnlnull' => '/[\r\n\t\v\0]+/',

        '2.5.4.10' => '(?:2\.5\.4\.10|organizationName|O)',
        '2.5.4.11' => '(?:2\.5\.4\.1?|organizationalUnitName|OU)',
        '2.5.4.15' => '(?:2\.5\.4\.15|businessCategory)',
        '2.5.4.3'  => '(?:2\.5\.4\.3|commonName|CN)',
        '2.5.4.5'  => '(?:2\.5\.4\.5|serialNumber)',
        '2.5.4.6'  => '(?:2\.5\.4\.6|countryName|C)',
        '2.5.4.7'  => '(?:2\.5\.4\.7|localityName|L)',
        '2.5.4.8'  => '(?:2\.5\.4\.8|stateOrProvinceName|SP|ST)',
        '2.5.4.9'  => '(?:2\.5\.4\.9|street(?:Address)?)',
        '2.5.4.17' => '(?:2\.5\.4\.17|postalCode)',
        '1.3.6.1.4.1.311.60.2.1.1' => '(?:1\.3\.6\.1\.4\.1\.311\.60\.2\.1\.1|jurisdictionOfIncorporationLocalityName)',
        '1.3.6.1.4.1.311.60.2.1.2' => '(?:1\.3\.6\.1\.4\.1\.311\.60\.2\.1\.2|jurisdictionOfIncorporationStateOrProvinceName)',
        '1.3.6.1.4.1.311.60.2.1.3' => '(?:1\.3\.6\.1\.4\.1\.311\.60\.2\.1\.3|jurisdictionOfIncorporationCountryName)',

        'EV-chars'    => '[a-zA-Z0-9,./:= @?+\'()-]',
        'notEV-chars' => '[^a-zA-Z0-9,./:= @?+\'()-]',
        'EV-empty'    => '^(?:n\/a|(?:in|not )valid)\s*$',

    },

    'hints' => {

        'help=warnings' => "consider building the file using: 'make warnings-info'",
        'renegotiation' => "checks only if renegotiation is implemented server-side according RFC 5746 ",
        'drown'         => "checks only if the target server itself is vulnerable to DROWN ",
        'lucky13'       => "checks only if the target offers ciphers vulnerable to Lucky13 ",
        'robot'         => "checks only if the target offers ciphers vulnerable to ROBOT ",
        'cipherall'     => "+cipherall : functionality changed, please see '$cfg__me --help=TECHNIC'",
        'cipherraw'     => "+cipherraw : functionality changed, please see '$cfg__me --help=TECHNIC'",
        'openssl3'      => "OpenSSL 3.x changed some functionality, please see '$cfg__me --help=TECHNIC'",
        'openssl3c'     => "+cipher for OpenSSL 3.x may result in many warnings, consider using '--no-warning'",
        '150'           => "when using OpenSSL 3.x consider using '--no-npn', please see '$cfg__me --help=TECHNIC'",
    },

    'ourstr' => {
        'error'    => qr(^\*\*ERR),
        'warning'  => qr(^\*\*WARN),
        'hint'     => qr(^\!\!Hint),
        'info'     => qr(^\*\*INFO),
        'dbx'      => qr(^#dbx#),
        'headline' => qr(^={1,3} ),
        'keyline'  => qr(^#\[),
        'verbose'  => qr(^#[^[]),

        'undef' => qr(\<\<undef),
        'yeast' => qr(\<\<.*?\>\>),
        'na'    => qr(N\/A),
        'yes'   => qr(:\s*yes),
        'no'    => qr(:\s*no ),
    },

    'compliance' => {
        'TR-02102' => "no RC4, only eclipic curve, only SHA256 or SHA384, need CRL and AIA, no wildcards, and verifications ...",
        'TR-03116' =>
"TLSv1.2, only ECDSA, RSA or PSK ciphers, only eclipic curve, only SHA224 or SHA256, need OCSP-Stapling CRL and AIA, no wildcards, and verifications ...",
        'ISM'        => "no NULL cipher, no Anonymous Auth, no single DES, no MD5, no RC ciphers",
        'PCI'        => "no NULL cipher, no Anonymous Auth, no single DES, no Export encryption, DH > 1023",
        'FIPS-140'   => "must be TLSv1 or 3DES or AES, no IDEA, no RC4, no MD5",
        'FIPS-140-2' => "-- NOT YET IMPLEMENTED --",
        'RFC7525'    => "TLS 1.2; AES with GCM; ECDHE and SHA256 or SHA384; HSTS",
        'NSA-B' => "must be AES with CTR or GCM; ECDSA or ECDH and SHA256 or SHA512",
    },
    'sig_algorithms' => [
        qw(
          dsaEncryption dsaEncryption-old dsaWithSHA dsaWithSHA1 dsa_With_SHA256
          ecdsa-with-SHA256
          md2WithRSAEncryption    md4WithRSAEncryption  md5WithRSAEncryption
          None   ripemd160WithRSA rsa  rsaEncryption    rsassapss
          shaWithRSAEncryption    sha1WithRSAEncryption sha1WithRSA
          sha224WithRSAEncryption sha256WithRSAEncryption
          sha384WithRSAEncryption sha512WithRSAEncryption
        ),
        "rsassapss (invalid pss parameters)"
    ],
    'sig_algorithm_common' => [
        qw(None ecdsa-with-SHA256
          sha1WithRSAEncryption   sha256WithRSAEncryption
          sha384WithRSAEncryption sha512WithRSAEncryption
        )
    ],
    'files' => {
        'RC-FILE'  => "",
        'SELF'     => "o-saft.pl",
        'coding'   => "coding.txt",
        'glossary' => "glossary.txt",
        'help'     => "help.txt",
        'links'    => "links.txt",
        'rfc'      => "rfc.txt",
        'tools'    => "tools.txt",
        'pattern-help' => [
            qw( --help --help=rfc --help=alias --help=checks
              --help=commands   --help=data  --help=opts
              --help=warnings --help=glossar --help=regex
              --help=ciphers-list   --help=ciphers-text
              --help=ciphers-html
            )
        ],
    },

    'done' => {},
);

our %target_desc = (

    'Nr',
    'Protocol',
    'Host',
    'Port',
    'Auth',
    'Proxy',

    'Path',
    'orig. Argument',

    'Time started',
    'Time opened',
    'Time stopped',
    'Errors',

);

our @target_defaults = [ 0, "https", "", "443", "", 0, "", "<<defaults>>", 0, 0, 0, 0, ];

our %dbx = (

    'argv'      => undef,
    'cfg'       => undef,
    'exe'       => undef,
    'files'     => undef,
    'cmd-check' => undef,
    'cmd-http'  => undef,
    'cmd-info'  => undef,
    'cmd-quick' => undef,
);

*_trace = sub {
    local $\ = undef;
    my $func = shift;
    print( join( " ", "#$cfg{'me'}::$func", @_ ), "\n" ) if ( 0 < $cfg{'trace'} );
    return;
  }
  if not defined &_trace;
*_trace1 = sub { _trace(@_) if ( 1 < $cfg{'trace'} ); return; }
  if not defined &_trace1;
*_trace2 = sub { _trace(@_) if ( 2 < $cfg{'trace'} ); return; }
  if not defined &_trace2;
*_trace3 = sub { _trace(@_) if ( 3 < $cfg{'trace'} ); return; }
  if not defined &_trace3;


sub _dbx { printf( STDERR "%s%s\n", $OText::STR{DBX}, join( " ", @_ ) ); return; }

sub hint {
    my @txt = @_;
    my $_no = "@txt";
    $_no =~ s/^\s*([0-9(]{3}):?.*/$1/smx;
    $_no =~ s/([+*(\\])/\\$1/g;

    return if grep( { /(?:--no.?hint)/i } @ARGV );
    return if not $cfg{'out'}->{'hint'};
    if ( 0 < ( grep { /^$_no$/ } @{ $cfg{out}->{'warnings_no_dups'} } ) ) {
        return if ( 0 < ( grep { /^$_no$/ } @{ $cfg{out}->{'warnings_printed'} } ) );
    }
    printf( $OText::STR{HINT} . "%s\n", join( " ", @txt ) );
    return;
}

sub warn {

    my @txt = @_;
    my $_no = "@txt";
    $_no =~ s/^\s*([0-9(]{3}):?.*/$1/smx;
    return if grep( { /(?:--no.?warn(?:ings?)$)/i } @ARGV );
    return if grep( { /(?:--(?:quiet|silent?)$)/i } @ARGV );
    return if not $cfg{'out'}->{'warning'};
    if ( 0 < ( grep { /^$_no$/ } @{ $cfg{out}->{'warnings_no_dups'} } ) ) {
        return if ( 0 < ( grep { /^$_no$/ } @{ $cfg{out}->{'warnings_printed'} } ) );
        push( @{ $cfg{out}->{'warnings_printed'} }, $_no );
    }
    printf( $OText::STR{WARN} . "%s\n", join( " ", @txt ) );
    return;
}


sub tls_valid_key {
    my $key = shift;
    $key = "0x$key" if $key !~ m/^0x/;
    return ( $key =~ m/^0x[0-9a-fA-F]{8}$/ ) ? $key : "-";
}

sub tls_text2key {
    my $txt = shift;
    $txt =~ s/(,|0x)//g;
    if ( 4 < length($txt) ) {
        $txt = "0x02$txt";
    }
    else {
        $txt = "0x0300$txt";
    }
    return $txt;
}

sub tls_key2text {
    my $key = shift;
    if ( $key =~ m/^0x0300/ ) {
        $key =~ s/0x0300//;
    }
    else {
        $key =~ s/^0x02//;
    }
    $key =~ s/(..)/,0x$1/g;
    $key =~ s/^,//;
    $key = "     $key" if ( 10 > length($key) );
    return "$key";
}

sub tls_const2text { my $c = shift; $c =~ s/_/ /g; return $c; }


sub get_ciphers_range {
    my $ssl   = shift;
    my $range = shift;
    $range = 'SSLv2' if ( $ssl eq 'SSLv2' );
    my @all;
    _trace2("get_ciphers_range($ssl, $range)");
    goto FIN if not exists $cfg{'cipherranges'}->{$range};
    goto FIN if ( $cfg{'cipherranges'}->{$range} !~ m/^[x0-9A-Fa-f,.\s]+$/ );
    foreach my $c ( eval( $cfg{'cipherranges'}->{$range} ) ) {
        push( @all, sprintf( "0x%08X", $c ) );
    }
  FIN:
    _trace2("get_ciphers_range()\t= @all");
    return @all;
}

sub get_cipher_owasp {
    my $cipher = shift;
    my $sec    = "miss";
    _trace2("get_cipher_owasp($cipher, $sec)");
    return $sec if not defined $cipher;
    return $sec if ( $cipher =~ m/^\s*$/ );

    $sec = "-?-" if ( $cipher =~ /$cfg{'regex'}->{'OWASP_NA'}/ );
    $sec = "C"   if ( $cipher =~ /$cfg{'regex'}->{'OWASP_C'}/ );
    $sec = "B"   if ( $cipher =~ /$cfg{'regex'}->{'OWASP_B'}/ );
    $sec = "A"   if ( $cipher =~ /$cfg{'regex'}->{'OWASP_A'}/ );
    $sec = "D"   if ( $cipher =~ /$cfg{'regex'}->{'OWASP_D'}/ );
    if ( " D" ne $sec ) {
        $sec = "-?-" if ( $cipher =~ /$cfg{'regex'}->{'OWASP_NA'}/ );
    }
    $sec = "A" if ( $cipher =~ /$cfg{'regex'}->{'OWASP_AA'}/ );
    $sec = "-" if ( $cipher =~ /$cfg{'regex'}->{'notCipher'}/ );

    _trace2("get_cipher_owasp()\t= $sec");
    return $sec;
}

sub get_openssl_version {
    my $cmd = shift;
    return $OText::STR{UNDEF} if not $cmd;
    my $data = qx("$cmd" version);

    chomp $data;
    _trace("get_openssl_version: $data");
    $data =~ s#^.*?(\d+(?:\.\d+)*).*$#$1#;
    _trace("get_openssl_version()\t= $data");
    return $data;
}


sub get_dh_paramter {
    my ( $cipher, $data ) = @_;
    if ( $data =~ m#Server Temp Key:# ) {
        $data =~ s/.*?Server Temp Key:\s*([^\n]*)\n.*/$1/si;
        _trace("get_dh_paramter(){ Server Temp Key\t= $data }");
        return $data;
    }
    my $dh = "";
    return "" if ( $data !~ m#ServerKeyExchange# );

    $data =~ s{
            .*?Handshake
            \s*?\[length\s*([0-9a-fA-F]{2,4})\]\,?
            \s*?ServerKeyExchange
            \s*[\n\r]+(.*?)
            [\n\r][<>]+.*
        }
        {$1_$2}xsi;
    _trace("get_dh_paramter: #{ DHE RAW data:\n$data\n#}\n");
    $data =~ s/\s+/ /gi;
    $data =~ s/[^0-9a-f_]//gi;
    my ( $lenStr, $len ) = 0;
    ( $lenStr, $data ) = split( /_/, $data );
    _trace3("get_dh_paramter: #{ DHE RAW data): len: $lenStr\n$data\n#}\n");
    $len = hex($lenStr);
    my $message = pack( "H*", $data );

    my $msgData = "";
    my ( $msgType, $msgFirstByte, $msgLen ) = 0;
    ( $msgType, $msgFirstByte, $msgLen, $msgData ) = unpack( "C C n a*", $message );

    if ( 0x0C == $msgType ) {

        my $keyExchange = $cipher;
        _trace1("get_dh_paramter: cipher: $keyExchange");
        $keyExchange =~ s/^((?:EC)?DHE?)_anon.*/A$1/;
        $keyExchange =~ s/^((?:EC)?DH)E.*/E$1/;
        $keyExchange =~ s/^(?:E|A|EA)((?:EC)?DH).*/$1/;
        _trace1(" get_dh_paramter: keyExchange (DH or ECDH) = $keyExchange");
        $dh = Net::SSLhello::parseServerKeyExchange( $keyExchange, $msgLen, $msgData );
    }

    chomp $dh;
    _trace("get_dh_paramter(){ ServerKeyExchange\t= $dh }");
    return $dh;
}


sub get_target_nr    { my $i = shift; return $cfg{'targets'}[$i][0]; }
sub get_target_prot  { my $i = shift; return $cfg{'targets'}[$i][1]; }
sub get_target_host  { my $i = shift; return $cfg{'targets'}[$i][2]; }
sub get_target_port  { my $i = shift; return $cfg{'targets'}[$i][3]; }
sub get_target_auth  { my $i = shift; return $cfg{'targets'}[$i][4]; }
sub get_target_proxy { my $i = shift; return $cfg{'targets'}[$i][5]; }
sub get_target_path  { my $i = shift; return $cfg{'targets'}[$i][6]; }
sub get_target_orig  { my $i = shift; return $cfg{'targets'}[$i][7]; }
sub get_target_start { my $i = shift; return $cfg{'targets'}[$i][8]; }
sub get_target_open  { my $i = shift; return $cfg{'targets'}[$i][9]; }
sub get_target_stop  { my $i = shift; return $cfg{'targets'}[$i][10]; }
sub get_target_error { my $i = shift; return $cfg{'targets'}[$i][11]; }
sub set_target_nr    { my $i = shift; $cfg{'targets'}[$i][0]      = shift; return; }
sub set_target_prot  { my $i = shift; $cfg{'targets'}[$i][1]      = shift; return; }
sub set_target_host  { my $i = shift; $cfg{'targets'}[$i][2]      = shift; return; }
sub set_target_port  { my $i = shift; $cfg{'targets'}[$i][3]      = shift; return; }
sub set_target_auth  { my $i = shift; $cfg{'targets'}[$i][4]      = shift; return; }
sub set_target_proxy { my $i = shift; $cfg{'targets'}[$i][5]      = shift; return; }
sub set_target_path  { my $i = shift; $cfg{'targets'}[$i][6]      = shift; return; }
sub set_target_orig  { my $i = shift; $cfg{'targets'}[$i][7]      = shift; return; }
sub set_target_start { my $i = shift; $cfg{'targets'}[$i][8]      = shift; return; }
sub set_target_open  { my $i = shift; $cfg{'targets'}[$i][9]      = shift; return; }
sub set_target_stop  { my $i = shift; $cfg{'targets'}[$i][10]     = shift; return; }
sub set_target_error { my $i = shift; $cfg{'targets'}[$i][11]     = shift; return; }
sub set_user_agent   { my $t = shift; $cfg{'use'}->{'user_agent'} = $t;    return; }


sub ocfg_sleep {
    my $wait = shift;
    select( undef, undef, undef, $wait );
    return;
}

sub printhint {
    my @args = @_;
    my $cmd  = shift @args;
    print $OText::STR{HINT}, $cfg{'hints'}->{$cmd}, join( " ", @args ) if ( defined $cfg{'hints'}->{$cmd} );
    return;
}


sub _regex_head { return sprintf( "= %s\t%s\t%s\t%s", "PFS", "OWASP", "owasp", "cipher" ); }
sub _regex_line { return "=------+-------+-------+---------------------------------------"; }

sub test_cipher_regex {
    local $\ = "\n";
    printf( "#%s:\n", ( caller(0) )[3] );
    print "
=== internal data structure: various RegEx to check cipher properties ===
=
= Check RegEx to detect ciphers, which support PFS using the internal function
= ::_is_compliant(.., 'PFS') .
=   \$cfg{'regex'}->{'PFS'}:      # match ciphers supporting PFS
=     $cfg{'regex'}->{'PFS'}
=   \$cfg{'regex'}->{'notPFS'}:   # match ciphers not supporting PFS
=     $cfg{'regex'}->{'notPFS'}
=
= Check to which RegEx for OWASP scoring a given cipher matches.
=
=   \$cfg{'regex'}->{'OWASP_NA'}: # unrated in OWASP TLS Cipher Cheat Sheet (2018)
=     $cfg{'regex'}->{'OWASP_NA'}
=   \$cfg{'regex'}->{'OWASP_C'}:  # 1st legacy
=     $cfg{'regex'}->{'OWASP_C'}
=   \$cfg{'regex'}->{'OWASP_B'}:  # 2nd broad compatibility
=     $cfg{'regex'}->{'OWASP_B'}
=   \$cfg{'regex'}->{'OWASP_A'}:  # 3rd best practice
=     $cfg{'regex'}->{'OWASP_A'}
=   \$cfg{'regex'}->{'OWASP_D'}:  # finally brocken ciphers, overwrite previous
=     $cfg{'regex'}->{'OWASP_D'}
=   \$cfg{'regex'}->{'OWASP_AA'}: # last secure TLSv1.3
=     $cfg{'regex'}->{'OWASP_AA'}
=
";
    print _regex_head();
    print _regex_line();
    my $cnt = 0;
    foreach my $key ( sort ( Ciphers::get_keys_list() ) ) {
        my $ssl    = Ciphers::get_ssl($key);
        my $cipher = Ciphers::get_name($key);
        my $is_pfs = ( ::_is_compliant( $ssl, $cipher, 'PFS' ) eq "" ) ? "no" : "yes";
        my @o      = ( '', '', '', '', '' );
        $o[4] = "-?-" if ( $cipher =~ /$cfg{'regex'}->{'OWASP_NA'}/ );
        $o[2] = "C"   if ( $cipher =~ /$cfg{'regex'}->{'OWASP_C'}/ );
        $o[1] = "B"   if ( $cipher =~ /$cfg{'regex'}->{'OWASP_B'}/ );
        $o[0] = "A"   if ( $cipher =~ /$cfg{'regex'}->{'OWASP_A'}/ );
        $o[3] = "D"   if ( $cipher =~ /$cfg{'regex'}->{'OWASP_D'}/ );
        $o[0] = "A"   if ( $cipher =~ /$cfg{'regex'}->{'OWASP_AA'}/ );

        if ( $cipher =~ /$cfg{'regex'}->{'notCipher'}/ ) {
            $is_pfs = '-';
            $o[0] = "-";
        }
        printf( "  %s\t%s\t%s\t%s\n", $is_pfs, get_cipher_owasp($cipher), join( "", @o ), $cipher );
        $cnt++;
    }
    print "= Please use  o-saft.pl +test-regex  for data." if ( 1 > $cnt );
    print _regex_line();
    print _regex_head();
    print <<'EoT';
= PFS values:
=   yes   cipher supports PFS
=   no    cipher does not supports PFS
=   -     pseudo cipher
= OWASP values:
=   x     value A or B or C or D as returned by get_cipher_owasp()
=   -?-   cypher not rated by OWASP
=   miss  cipher not matched by any RegEx, programming error
=   -     pseudo cipher
= owasp values:
=   xx    list of all matching OWASP_x RegEx (OWASP column picks best one)
EoT
    return;
}

sub test_cipher_sort {
    return;
}

sub _prot_init_value {
    foreach my $ssl ( keys %prot ) {
        $prot{$ssl}->{'cnt'}           = 0;
        $prot{$ssl}->{'-?-'}           = 0;
        $prot{$ssl}->{'WEAK'}          = 0;
        $prot{$ssl}->{'LOW'}           = 0;
        $prot{$ssl}->{'MEDIUM'}        = 0;
        $prot{$ssl}->{'HIGH'}          = 0;
        $prot{$ssl}->{'OWASP_AA'}      = 0;
        $prot{$ssl}->{'OWASP_A'}       = 0;
        $prot{$ssl}->{'OWASP_B'}       = 0;
        $prot{$ssl}->{'OWASP_C'}       = 0;
        $prot{$ssl}->{'OWASP_D'}       = 0;
        $prot{$ssl}->{'OWASP_NA'}      = 0;
        $prot{$ssl}->{'OWASP_miss'}    = 0;
        $prot{$ssl}->{'protocol'}      = 0;
        $prot{$ssl}->{'ciphers_pfs'}   = [];
        $prot{$ssl}->{'cipher_pfs'}    = $OText::STR{UNDEF};
        $prot{$ssl}->{'default'}       = $OText::STR{UNDEF};
        $prot{$ssl}->{'cipher_strong'} = $OText::STR{UNDEF};
        $prot{$ssl}->{'cipher_weak'}   = $OText::STR{UNDEF};
    }
    return;
}

sub _cfg_init {
    push( @{ $cfg{'targets'} }, @target_defaults );
    $cfg{'openssl_option_map'}->{$_}  = $prot{$_}->{'opt'} foreach ( keys %prot );
    $cfg{'openssl_version_map'}->{$_} = $prot{$_}->{'hex'} foreach ( keys %prot );
    $cfg{'protos_alpn'}               = [ split( /,/, $cfg{'protos_next'} ) ];
    $cfg{'protos_npn'}                = [ split( /,/, $cfg{'protos_next'} ) ];
    $cfg{'cipher_alpns'} = [ split( /,/, $cfg{'protos_next'} ) ];
    $cfg{'cipher_npns'}  = [ split( /,/, $cfg{'protos_next'} ) ];
    $cfg{'openssl_env'}  = $ENV{'OPENSSL'}      if ( defined $ENV{'OPENSSL'} );
    $cfg{'openssl_cnf'}  = $ENV{'OPENSSL_CONF'} if ( defined $ENV{'OPENSSL_CONF'} );
    $cfg{'openssl_fips'} = $ENV{'OPENSSL_FIPS'} if ( defined $ENV{'OPENSSL_FIPS'} );
    $cfg{'cipherranges'}->{'SSLv2'}       = $cfg{'cipherranges'}->{'SSLv2_base'} . $cfg{'cipherranges'}->{'SSLv2_rfc'} . $cfg{'cipherranges'}->{'SSLv2_FIPS'};
    $cfg{'cipherranges'}->{'SSLv2_long'}  = $cfg{'cipherranges'}->{'SSLv2_base'} . $cfg{'cipherranges'}->{'SSLv2_rfc+'} . $cfg{'cipherranges'}->{'SSLv2_FIPS'};
    $cfg{'cipherranges'}->{'SSLv3_SSLv2'} = $cfg{'cipherranges'}->{'SSLv2_base'} . $cfg{'cipherranges'}->{'SSLv2_rfc+'} . $cfg{'cipherranges'}->{'SSLv3'};
    $cfg{'cipherranges'}->{'TLSv10'}      = $cfg{'cipherranges'}->{'SSLV3'};
    $cfg{'cipherranges'}->{'TLSv11'}      = $cfg{'cipherranges'}->{'SSLV3'};
    $cfg{'cipherranges'}->{'rfc'}     .= $cfg{'cipherranges'}->{'GREASE'};
    $cfg{'cipherranges'}->{'shifted'} .= $cfg{'cipherranges'}->{'rfc'};
    $cfg{'cipherranges'}->{'TLSv13'}  .= $cfg{'cipherranges'}->{'GREASE'};
    $cfg{'cipherranges'}->{'intern'} = $cfg{'cipherranges'}->{'shifted'};
    return;
}

sub _cmd_init {
    foreach my $key ( sort( keys %cfg ) ) {
        push( @{ $cfg{'commands_cmd'} }, $key ) if ( $key =~ m/^cmd-/ );
    }
    @{ $cfg{'commands_cmd'} } = sort( @{ $cfg{'commands_cmd'} } );
    @{ $cfg{'cmd-info--v'} }  = sort( @{ $cfg{'cmd-info--v'} } );
    return;
}

sub _doc_init {
    foreach my $k ( @{ $cfg{'files'}->{'pattern-help'} } ) {
        my $_path = $0;
        $_path =~ s#[/\\][^/\\]*$##;
        $cfg{'files'}->{$k} = join( "/", $_path, $cfg{'dirs'}->{'doc'}, "o-saft.pl.$k" );
    }
    return;
}

sub _dbx_init {
    $dbx{'cmd-check'} = $cfg{'cmd-check'};
    $dbx{'cmd-http'}  = $cfg{'cmd-http'};
    $dbx{'cmd-info'}  = $cfg{'cmd-info'};
    $dbx{'cmd-quick'} = $cfg{'cmd-quick'};
    push( @{ $dbx{'files'} }, "lib/OCfg.pm" );
    return;
}

sub _init {
    my $me = $0;
    $me =~ s#.*[/\\]##;
    $cfg{'me'}             = $me;
    $cfg{'RC-FILE'}        = "./.$me";
    $cfg{'ARG0'}           = $0;
    $cfg{'ARGV'}           = [@ARGV];
    $cfg{'prefix_trace'}   = "#${me}::";
    $cfg{'prefix_verbose'} = "#${me}: ";
    _prot_init_value();
    _cfg_init();
    _cmd_init();
    _dbx_init();
    _doc_init();

    foreach my $k ( keys %data_oid ) {
        $data_oid{$k}->{val} = "<<check error>>";
    }
    $me = $cfg{'mename'};
    $me =~ s/\s*$//;
    set_user_agent("$me/3.70");
    return;
}

sub _main {
    my @argv = @_;
    push( @argv, "--help" ) if ( 0 > $#argv );
    my %usage = (
        '# commands to show internal configuration data' => {
            'regex' => 'show %cfg{regex}',
        },
    );
    while ( my $arg = shift @argv ) {
        if ( $arg =~ m/^--?h(?:elp)?$/ ) { OText::print_pod( $0, __FILE__, $SID_ocfg ); exit 0; }
        if ( $arg eq '--usage' )         { OText::usage_show( "", \%usage );            exit 0; }
        if ( $arg =~ /^version$/ )         { print "$SID_ocfg\n"; next; }
        if ( $arg =~ /^[-+]?V(ERSION)?$/ ) { print "$VERSION\n";  next; }
        $arg =~ s/^\+test[_.-]?//;
        if ( $arg eq 'regex' ) { test_cipher_regex(); }
    }
    exit 0;
}

sub done { }

_init();

package OData;

my $SID_odata = "@(#) OData.pm 3.31 24/09/06 16:20:11";
our $VERSION = "24.09.24";

BEGIN {
    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##x;
    if ( exists $ENV{'PWD'} and not( grep { /^$ENV{'PWD'}$/ } @INC ) ) {
        unshift( @INC, $ENV{'PWD'} );
    }
    unshift( @INC, $_path ) if not( grep { /^$_path$/ } @INC );
    unshift( @INC, "lib" )  if not( grep { /^lib$/ } @INC );
    our @EXPORT_OK = qw(
      %checks
      %data
      %data0
      %info
      %shorttexts
      %check_cert
      %check_conn
      %check_dest
      %check_http
      %check_size
    );
}


our %info = (
    'alpn'  => "",
    'npn'   => "",
    'alpns' => "",
    'npns'  => "",
);

our %data0 = ();

our %data = (

    'cn_nosni'         => { 'val' => "",                                                            'txt' => "Certificate CN without SNI" },
    'pem'              => { 'val' => sub { SSLinfo::pem( $_[0], $_[1] ) },                          'txt' => "Certificate PEM" },
    'text'             => { 'val' => sub { SSLinfo::text( $_[0], $_[1] ) },                         'txt' => "Certificate PEM decoded" },
    'cn'               => { 'val' => sub { SSLinfo::cn( $_[0], $_[1] ) },                           'txt' => "Certificate Common Name" },
    'subject'          => { 'val' => sub { SSLinfo::subject( $_[0], $_[1] ) },                      'txt' => "Certificate Subject" },
    'issuer'           => { 'val' => sub { SSLinfo::issuer( $_[0], $_[1] ) },                       'txt' => "Certificate Issuer" },
    'altname'          => { 'val' => sub { SSLinfo::altname( $_[0], $_[1] ) },                      'txt' => "Certificate Subject's Alternate Names" },
    'cipher_selected'  => { 'val' => sub { SSLinfo::selected( $_[0], $_[1] ) },                     'txt' => "Selected Cipher" },
    'ciphers_local'    => { 'val' => sub { SSLinfo::cipher_openssl() },                             'txt' => "Local SSLlib Ciphers" },
    'ciphers'          => { 'val' => sub { return join( " ", SSLinfo::ciphers( $_[0], $_[1] ) ) },  'txt' => "Client Ciphers" },
    'dates'            => { 'val' => sub { return join( " .. ", SSLinfo::dates( $_[0], $_[1] ) ) }, 'txt' => "Certificate Validity (date)" },
    'before'           => { 'val' => sub { SSLinfo::before( $_[0], $_[1] ) },                       'txt' => "Certificate valid since" },
    'after'            => { 'val' => sub { SSLinfo::after( $_[0], $_[1] ) },                        'txt' => "Certificate valid until" },
    'aux'              => { 'val' => sub { SSLinfo::aux( $_[0], $_[1] ) },                          'txt' => "Certificate Trust Information" },
    'email'            => { 'val' => sub { SSLinfo::email( $_[0], $_[1] ) },                        'txt' => "Certificate Email Addresses" },
    'pubkey'           => { 'val' => sub { SSLinfo::pubkey( $_[0], $_[1] ) },                       'txt' => "Certificate Public Key" },
    'pubkey_algorithm' => { 'val' => sub { SSLinfo::pubkey_algorithm( $_[0], $_[1] ) },             'txt' => "Certificate Public Key Algorithm" },
    'pubkey_value'     => { 'val' => sub { __SSLinfo( 'pubkey_value', $_[0], $_[1] ) },             'txt' => "Certificate Public Key Value" },
    'modulus_len'      => { 'val' => sub { SSLinfo::modulus_len( $_[0], $_[1] ) },                  'txt' => "Certificate Public Key Length" },
    'modulus'          => { 'val' => sub { SSLinfo::modulus( $_[0], $_[1] ) },                      'txt' => "Certificate Public Key Modulus" },
    'modulus_exponent' => { 'val' => sub { SSLinfo::modulus_exponent( $_[0], $_[1] ) },             'txt' => "Certificate Public Key Exponent" },
    'serial'           => { 'val' => sub { SSLinfo::serial( $_[0], $_[1] ) },                       'txt' => "Certificate Serial Number" },
    'serial_hex'       => { 'val' => sub { SSLinfo::serial_hex( $_[0], $_[1] ) },                   'txt' => "Certificate Serial Number (hex)" },
    'serial_int'       => { 'val' => sub { SSLinfo::serial_int( $_[0], $_[1] ) },                   'txt' => "Certificate Serial Number (int)" },
    'certversion'      => { 'val' => sub { SSLinfo::version( $_[0], $_[1] ) },                      'txt' => "Certificate Version" },
    'sigdump'          => { 'val' => sub { SSLinfo::sigdump( $_[0], $_[1] ) },                      'txt' => "Certificate Signature (hexdump)" },
    'sigkey_len'       => { 'val' => sub { SSLinfo::sigkey_len( $_[0], $_[1] ) },                   'txt' => "Certificate Signature Key Length" },
    'signame'          => { 'val' => sub { SSLinfo::signame( $_[0], $_[1] ) },                      'txt' => "Certificate Signature Algorithm" },
    'sigkey_value'     => { 'val' => sub { __SSLinfo( 'sigkey_value', $_[0], $_[1] ) },             'txt' => "Certificate Signature Key Value" },
    'trustout'         => { 'val' => sub { SSLinfo::trustout( $_[0], $_[1] ) },                     'txt' => "Certificate trusted" },
    'extensions'      => { 'val' => sub { __SSLinfo( 'extensions',      $_[0], $_[1] ) }, 'txt' => "Certificate extensions" },
    'tlsextdebug'     => { 'val' => sub { __SSLinfo( 'tlsextdebug',     $_[0], $_[1] ) }, 'txt' => "TLS extensions (debug)" },
    'tlsextensions'   => { 'val' => sub { __SSLinfo( 'tlsextensions',   $_[0], $_[1] ) }, 'txt' => "TLS extensions" },
    'ext_authority'   => { 'val' => sub { __SSLinfo( 'ext_authority',   $_[0], $_[1] ) }, 'txt' => "Certificate extensions Authority Information Access" },
    'ext_authorityid' => { 'val' => sub { __SSLinfo( 'ext_authorityid', $_[0], $_[1] ) }, 'txt' => "Certificate extensions Authority key Identifier" },
    'ext_constraints' => { 'val' => sub { __SSLinfo( 'ext_constraints', $_[0], $_[1] ) }, 'txt' => "Certificate extensions Basic Constraints" },
    'ext_cps'         => { 'val' => sub { __SSLinfo( 'ext_cps',         $_[0], $_[1] ) }, 'txt' => "Certificate extensions Certificate Policies" },
    'ext_cps_cps'     => { 'val' => sub { __SSLinfo( 'ext_cps_cps',     $_[0], $_[1] ) }, 'txt' => "Certificate extensions Certificate Policies: CPS" },
    'ext_cps_policy'  => { 'val' => sub { __SSLinfo( 'ext_cps_policy',  $_[0], $_[1] ) }, 'txt' => "Certificate extensions Certificate Policies: Policy" },
    'ext_cps_notice'  => { 'val' => sub { __SSLinfo( 'ext_cps_notice',  $_[0], $_[1] ) }, 'txt' => "Certificate extensions Certificate Policies: User Notice" },
    'ext_crl'         => { 'val' => sub { __SSLinfo( 'ext_crl',         $_[0], $_[1] ) }, 'txt' => "Certificate extensions CRL Distribution Points" },
    'ext_subjectkeyid'     => { 'val' => sub { __SSLinfo( 'ext_subjectkeyid', $_[0], $_[1] ) }, 'txt' => "Certificate extensions Subject Key Identifier" },
    'ext_keyusage'         => { 'val' => sub { __SSLinfo( 'ext_keyusage',     $_[0], $_[1] ) }, 'txt' => "Certificate extensions Key Usage" },
    'ext_extkeyusage'      => { 'val' => sub { __SSLinfo( 'ext_extkeyusage',  $_[0], $_[1] ) }, 'txt' => "Certificate extensions Extended Key Usage" },
    'ext_certtype'         => { 'val' => sub { __SSLinfo( 'ext_certtype',     $_[0], $_[1] ) }, 'txt' => "Certificate extensions Netscape Cert Type" },
    'ext_issuer'           => { 'val' => sub { __SSLinfo( 'ext_issuer',       $_[0], $_[1] ) }, 'txt' => "Certificate extensions Issuer Alternative Name" },
    'ocsp_uri'             => { 'val' => sub { SSLinfo::ocsp_uri( $_[0], $_[1] ) },              'txt' => "Certificate OCSP Responder URL" },
    'ocspid'               => { 'val' => sub { __SSLinfo( 'ocspid', $_[0], $_[1] ) },            'txt' => "Certificate OCSP Hashes" },
    'ocsp_subject_hash'    => { 'val' => sub { __SSLinfo( 'ocsp_subject_hash', $_[0], $_[1] ) }, 'txt' => "Certificate OCSP Subject Hash" },
    'ocsp_public_hash'     => { 'val' => sub { __SSLinfo( 'ocsp_public_hash', $_[0], $_[1] ) },  'txt' => "Certificate OCSP Public Key Hash" },
    'ocsp_response'        => { 'val' => sub { SSLinfo::ocsp_response( $_[0], $_[1] ) },         'txt' => "Target's OCSP Response" },
    'ocsp_response_data'   => { 'val' => sub { SSLinfo::ocsp_response_data( $_[0], $_[1] ) },    'txt' => "Target's OCSP Response Data" },
    'ocsp_response_status' => { 'val' => sub { SSLinfo::ocsp_response_status( $_[0], $_[1] ) },  'txt' => "Target's OCSP Response Status" },
    'ocsp_cert_status'     => { 'val' => sub { SSLinfo::ocsp_cert_status( $_[0], $_[1] ) },      'txt' => "Target's OCSP Response Cert Status" },
    'ocsp_next_update'     => { 'val' => sub { SSLinfo::ocsp_next_update( $_[0], $_[1] ) },      'txt' => "Target's OCSP Response Next Update" },
    'ocsp_this_update'     => { 'val' => sub { SSLinfo::ocsp_this_update( $_[0], $_[1] ) },      'txt' => "Target's OCSP Response This Update" },
    'subject_hash'         => { 'val' => sub { SSLinfo::subject_hash( $_[0], $_[1] ) },          'txt' => "Certificate Subject Name Hash" },
    'issuer_hash'          => { 'val' => sub { SSLinfo::issuer_hash( $_[0], $_[1] ) },           'txt' => "Certificate Issuer Name Hash" },
    'selfsigned'           => { 'val' => sub { SSLinfo::selfsigned( $_[0], $_[1] ) },            'txt' => "Certificate Validity (signature)" },
    'fingerprint_type'     => { 'val' => sub { SSLinfo::fingerprint_type( $_[0], $_[1] ) },      'txt' => "Certificate Fingerprint Algorithm" },
    'fingerprint_hash'     => { 'val' => sub { __SSLinfo( 'fingerprint_hash', $_[0], $_[1] ) },  'txt' => "Certificate Fingerprint Hash Value" },
    'fingerprint_sha2'     => { 'val' => sub { __SSLinfo( 'fingerprint_sha2', $_[0], $_[1] ) },  'txt' => "Certificate Fingerprint SHA2" },
    'fingerprint_sha1'     => { 'val' => sub { __SSLinfo( 'fingerprint_sha1', $_[0], $_[1] ) },  'txt' => "Certificate Fingerprint SHA1" },
    'fingerprint_md5'      => { 'val' => sub { __SSLinfo( 'fingerprint_md5', $_[0], $_[1] ) },   'txt' => "Certificate Fingerprint  MD5" },
    'fingerprint'          => { 'val' => sub { __SSLinfo( 'fingerprint', $_[0], $_[1] ) },       'txt' => "Certificate Fingerprint" },
    'cert_type'            => { 'val' => sub { SSLinfo::cert_type( $_[0], $_[1] ) },             'txt' => "Certificate Type (bitmask)" },
    'sslversion'           => { 'val' => sub { SSLinfo::SSLversion( $_[0], $_[1] ) },            'txt' => "Selected SSL Protocol" },
    'resumption'           => { 'val' => sub { SSLinfo::resumption( $_[0], $_[1] ) },            'txt' => "Target supports Resumption" },
    'renegotiation'        => { 'val' => sub { SSLinfo::renegotiation( $_[0], $_[1] ) },         'txt' => "Target supports Renegotiation" },
    'compression'          => { 'val' => sub { SSLinfo::compression( $_[0], $_[1] ) },           'txt' => "Target supports Compression" },
    'expansion'            => { 'val' => sub { SSLinfo::expansion( $_[0], $_[1] ) },             'txt' => "Target supports Expansion" },
    'krb5'                 => { 'val' => sub { SSLinfo::krb5( $_[0], $_[1] ) },                  'txt' => "Target supports Krb5" },
    'psk_hint'             => { 'val' => sub { SSLinfo::psk_hint( $_[0], $_[1] ) },              'txt' => "Target supports PSK Identity Hint" },
    'psk_identity'         => { 'val' => sub { SSLinfo::psk_identity( $_[0], $_[1] ) },          'txt' => "Target supports PSK" },
    'srp'                  => { 'val' => sub { SSLinfo::srp( $_[0], $_[1] ) },                   'txt' => "Target supports SRP" },
    'heartbeat'            => { 'val' => sub { __SSLinfo( 'heartbeat', $_[0], $_[1] ) },         'txt' => "Target supports Heartbeat" },
    'master_secret'        => { 'val' => sub { SSLinfo::master_secret( $_[0], $_[1] ) },         'txt' => "Target supports Extended Master Secret" },
    'next_protocols' => { 'val' => sub { SSLinfo::next_protocols( $_[0], $_[1] ) }, 'txt' => "Target's advertised protocols" },
    'alpn'               => { 'val' => sub { return $info{'alpn'}; },                       'txt' => "Target's selected protocol (ALPN)" },
    'npn'                => { 'val' => sub { return $info{'npn'}; },                        'txt' => "Target's selected protocol  (NPN)" },
    'alpns'              => { 'val' => sub { return $info{'alpns'}; },                      'txt' => "Target's supported ALPNs" },
    'npns'               => { 'val' => sub { return $info{'npns'}; },                       'txt' => "Target's supported  NPNs" },
    'master_key'         => { 'val' => sub { SSLinfo::master_key( $_[0], $_[1] ) },         'txt' => "Target's Master-Key" },
    'public_key_len'     => { 'val' => sub { SSLinfo::public_key_len( $_[0], $_[1] ) },     'txt' => "Target's Server public key length" },
    'resumption_psk'     => { 'val' => sub { SSLinfo::resumption_psk( $_[0], $_[1] ) },     'txt' => "Target's Resumption PSK" },
    'session_id'         => { 'val' => sub { SSLinfo::session_id( $_[0], $_[1] ) },         'txt' => "Target's Session-ID" },
    'session_id_ctx'     => { 'val' => sub { SSLinfo::session_id_ctx( $_[0], $_[1] ) },     'txt' => "Target's Session-ID-ctx" },
    'session_protocol'   => { 'val' => sub { SSLinfo::session_protocol( $_[0], $_[1] ) },   'txt' => "Target's selected SSL Protocol" },
    'session_ticket'     => { 'val' => sub { SSLinfo::session_ticket( $_[0], $_[1] ) },     'txt' => "Target's TLS Session Ticket" },
    'session_lifetime'   => { 'val' => sub { SSLinfo::session_lifetime( $_[0], $_[1] ) },   'txt' => "Target's TLS Session Ticket Lifetime" },
    'session_timeout'    => { 'val' => sub { SSLinfo::session_timeout( $_[0], $_[1] ) },    'txt' => "Target's TLS Session Timeout" },
    'session_starttime'  => { 'val' => sub { SSLinfo::session_starttime( $_[0], $_[1] ) },  'txt' => "Target's TLS Session Start Time EPOCH" },
    'session_startdate'  => { 'val' => sub { SSLinfo::session_startdate( $_[0], $_[1] ) },  'txt' => "Target's TLS Session Start Time locale" },
    'dh_parameter'       => { 'val' => sub { SSLinfo::dh_parameter( $_[0], $_[1] ) },       'txt' => "Target's DH Parameter" },
    'chain'              => { 'val' => sub { SSLinfo::chain( $_[0], $_[1] ) },              'txt' => "Certificate Chain" },
    'chain_verify'       => { 'val' => sub { SSLinfo::chain_verify( $_[0], $_[1] ) },       'txt' => "CA Chain Verification (trace)" },
    'verify'             => { 'val' => sub { SSLinfo::verify( $_[0], $_[1] ) },             'txt' => "Validity Certificate Chain" },
    'error_verify'       => { 'val' => sub { SSLinfo::error_verify( $_[0], $_[1] ) },       'txt' => "CA Chain Verification error" },
    'error_depth'        => { 'val' => sub { SSLinfo::error_depth( $_[0], $_[1] ) },        'txt' => "CA Chain Verification error in level" },
    'verify_altname'     => { 'val' => sub { SSLinfo::verify_altname( $_[0], $_[1] ) },     'txt' => "Validity Alternate Names" },
    'verify_hostname'    => { 'val' => sub { SSLinfo::verify_hostname( $_[0], $_[1] ) },    'txt' => "Validity Hostname" },
    'https_protocols'    => { 'val' => sub { SSLinfo::https_protocols( $_[0], $_[1] ) },    'txt' => "HTTPS Alternate-Protocol" },
    'https_content_enc'  => { 'val' => sub { SSLinfo::https_content_enc( $_[0], $_[1] ) },  'txt' => "HTTPS Content-Encoding header" },
    'https_transfer_enc' => { 'val' => sub { SSLinfo::https_transfer_enc( $_[0], $_[1] ) }, 'txt' => "HTTPS Transfer-Encoding header" },
    'https_svc'          => { 'val' => sub { SSLinfo::https_svc( $_[0], $_[1] ) },          'txt' => "HTTPS Alt-Svc header" },
    'https_status'       => { 'val' => sub { SSLinfo::https_status( $_[0], $_[1] ) },       'txt' => "HTTPS Status line" },
    'https_server'       => { 'val' => sub { SSLinfo::https_server( $_[0], $_[1] ) },       'txt' => "HTTPS Server banner" },
    'https_location'     => { 'val' => sub { SSLinfo::https_location( $_[0], $_[1] ) },     'txt' => "HTTPS Location header" },
    'https_refresh'      => { 'val' => sub { SSLinfo::https_refresh( $_[0], $_[1] ) },      'txt' => "HTTPS Refresh header" },
    'https_alerts'       => { 'val' => sub { SSLinfo::https_alerts( $_[0], $_[1] ) },       'txt' => "HTTPS Error alerts" },
    'https_pins'         => { 'val' => sub { SSLinfo::https_pins( $_[0], $_[1] ) },         'txt' => "HTTPS Public-Key-Pins header" },
    'https_body'         => { 'val' => sub { SSLinfo::https_body( $_[0], $_[1] ) },         'txt' => "HTTPS Body" },
    'https_sts'          => { 'val' => sub { SSLinfo::https_sts( $_[0], $_[1] ) },          'txt' => "HTTPS STS header" },
    'hsts_httpequiv'     => { 'val' => sub { SSLinfo::hsts_httpequiv( $_[0], $_[1] ) },     'txt' => "HTTPS STS in http-equiv" },
    'hsts_maxage'        => { 'val' => sub { SSLinfo::hsts_maxage( $_[0], $_[1] ) },        'txt' => "HTTPS STS MaxAge" },
    'hsts_subdom'        => { 'val' => sub { SSLinfo::hsts_subdom( $_[0], $_[1] ) },        'txt' => "HTTPS STS include sub-domains" },
    'hsts_preload'       => { 'val' => sub { SSLinfo::hsts_preload( $_[0], $_[1] ) },       'txt' => "HTTPS STS preload" },
    'http_protocols'     => { 'val' => sub { SSLinfo::http_protocols( $_[0], $_[1] ) },     'txt' => "HTTP Alternate-Protocol" },
    'http_svc'           => { 'val' => sub { SSLinfo::http_svc( $_[0], $_[1] ) },           'txt' => "HTTP Alt-Svc header" },
    'http_status'        => { 'val' => sub { SSLinfo::http_status( $_[0], $_[1] ) },        'txt' => "HTTP Status line" },
    'http_location'      => { 'val' => sub { SSLinfo::http_location( $_[0], $_[1] ) },      'txt' => "HTTP Location header" },
    'http_refresh'       => { 'val' => sub { SSLinfo::http_refresh( $_[0], $_[1] ) },       'txt' => "HTTP Refresh header" },
    'http_sts'           => { 'val' => sub { SSLinfo::http_sts( $_[0], $_[1] ) },           'txt' => "HTTP STS header" },
    'options'           => { 'val' => sub { SSLinfo::options( $_[0], $_[1] ) },                   'txt' => "internal used SSL options bitmask" },
    'fallback_protocol' => { 'val' => sub { print('$prot{fallback_protocol}->{val} in _init'); }, 'txt' => "Target's fallback SSL Protocol" },
    'valid_years'  => { 'val' => 0, 'txt' => "certificate validity in years" },
    'valid_months' => { 'val' => 0, 'txt' => "certificate validity in months" },
    'valid_days'   => { 'val' => 0, 'txt' => "certificate validity in days" },
    'valid_host'   => { 'val' => 0, 'txt' => "dummy used for printing DNS stuff" },
);

our %checks = (

);

our %check_cert = (

    'verify'              => { 'txt' => "Certificate chain validated" },
    'fp_not_md5'          => { 'txt' => "Certificate Fingerprint is not MD5" },
    'dates'               => { 'txt' => "Certificate is valid" },
    'expired'             => { 'txt' => "Certificate is not expired" },
    'certfqdn'            => { 'txt' => "Certificate is valid according given hostname" },
    'wildhost'            => { 'txt' => "Certificate's wildcard does not match hostname" },
    'wildcard'            => { 'txt' => "Certificate does not contain wildcards" },
    'rootcert'            => { 'txt' => "Certificate is not root CA" },
    'selfsigned'          => { 'txt' => "Certificate is not self-signed" },
    'dv'                  => { 'txt' => "Certificate Domain Validation (DV)" },
    'ev+'                 => { 'txt' => "Certificate strict Extended Validation (EV)" },
    'ev-'                 => { 'txt' => "Certificate lazy Extended Validation (EV)" },
    'ocsp_uri'            => { 'txt' => "Certificate has OCSP Responder URL" },
    'cps'                 => { 'txt' => "Certificate has Certification Practice Statement" },
    'crl'                 => { 'txt' => "Certificate has CRL Distribution Points" },
    'zlib'                => { 'txt' => "Certificate has (TLS extension) compression" },
    'lzo'                 => { 'txt' => "Certificate has (GnuTLS extension) compression" },
    'open_pgp'            => { 'txt' => "Certificate has (TLS extension) authentication" },
    'ocsp_valid'          => { 'txt' => "Certificate has valid OCSP URL" },
    'cps_valid'           => { 'txt' => "Certificate has valid CPS URL" },
    'crl_valid'           => { 'txt' => "Certificate has valid CRL URL" },
    'sernumber'           => { 'txt' => "Certificate Serial Number size RFC 5280" },
    'constraints'         => { 'txt' => "Certificate Basic Constraints is false" },
    'sha2signature'       => { 'txt' => "Certificate Private Key Signature SHA2" },
    'modulus_exp_1'       => { 'txt' => "Certificate Public Key Modulus Exponent <>1" },
    'modulus_size_oldssl' => { 'txt' => "Certificate Public Key Modulus >16385 bits" },
    'modulus_exp_65537'   => { 'txt' => "Certificate Public Key Modulus Exponent =65537" },
    'modulus_exp_oldssl'  => { 'txt' => "Certificate Public Key Modulus Exponent >65537" },
    'pub_encryption'      => { 'txt' => "Certificate Public Key with Encryption" },
    'pub_enc_known'       => { 'txt' => "Certificate Public Key Encryption known" },
    'sig_encryption'      => { 'txt' => "Certificate Private Key with Encryption" },
    'sig_enc_known'       => { 'txt' => "Certificate Private Key Encryption known" },
    'rfc_6125_names'      => { 'txt' => "Certificate Names compliant to RFC 6125" },
    'rfc_2818_names'      => { 'txt' => "Certificate subjectAltNames compliant to RFC 2818" },
    'nonprint' => { 'txt' => "Certificate does not contain non-printable characters" },
    'crnlnull' => { 'txt' => "Certificate does not contain CR, NL, NULL characters" },
    'ev_chars' => { 'txt' => "Certificate has no invalid characters in extensions" },
);

our %check_conn = (

    'reversehost' => { 'txt' => "Given hostname is same as reverse resolved hostname" },
    'hostname'    => { 'txt' => "Connected hostname equals certificate's Subject" },
    'beast'       => { 'txt' => "Connection is safe against BEAST attack (any cipher)" },
    'breach'      => { 'txt' => "Connection is safe against BREACH attack" },
    'ccs'         => { 'txt' => "Connection is safe against CCS Injection attack" },
    'crime'       => { 'txt' => "Connection is safe against CRIME attack" },
    'drown'       => { 'txt' => "Connection is safe against DROWN attack" },
    'time'        => { 'txt' => "Connection is safe against TIME attack" },
    'freak'       => { 'txt' => "Connection is safe against FREAK attack" },
    'heartbleed'  => { 'txt' => "Connection is safe against Heartbleed attack" },
    'logjam'      => { 'txt' => "Connection is safe against Logjam attack" },
    'lucky13'     => { 'txt' => "Connection is safe against Lucky 13 attack" },
    'poodle'      => { 'txt' => "Connection is safe against POODLE attack" },
    'rc4'         => { 'txt' => "Connection is safe against RC4 attack" },
    'robot'       => { 'txt' => "Connection is safe against ROBOT attack" },
    'sloth'       => { 'txt' => "Connection is safe against SLOTH attack" },
    'sweet32'     => { 'txt' => "Connection is safe against Sweet32 attack" },
    'sni'         => { 'txt' => "Connection is not based on SNI" },
);

our %check_dest = (

    'sgc'           => { 'txt' => "Target supports Server Gated Cryptography (SGC)" },
    'hassslv2'      => { 'txt' => "Target does not support SSLv2" },
    'hassslv3'      => { 'txt' => "Target does not support SSLv3" },
    'hastls10'      => { 'txt' => "Target does not supports TLSv1" },
    'hastls11'      => { 'txt' => "Target does not supports TLSv1.1" },
    'hastls10_old'  => { 'txt' => "Target supports TLSv1" },
    'hastls11_old'  => { 'txt' => "Target supports TLSv1.1" },
    'hastls12'      => { 'txt' => "Target supports TLSv1.2" },
    'hastls13'      => { 'txt' => "Target supports TLSv1.3" },
    'hasdtls1'      => { 'txt' => "Target supports DTLSv1" },
    'hasdtls12'     => { 'txt' => "Target supports DTLSv1.2" },
    'hasdtls13'     => { 'txt' => "Target supports DTLSv1.3" },
    'hasalpn'       => { 'txt' => "Target supports ALPN" },
    'hasnpn'        => { 'txt' => "Target supports  NPN" },
    'cipher_strong' => { 'txt' => "Target selects strongest cipher" },
    'cipher_order'  => { 'txt' => "Target does not honors client's cipher order" },
    'cipher_weak'   => { 'txt' => "Target does not accept weak cipher" },
    'cipher_null'   => { 'txt' => "Target does not accept NULL ciphers" },
    'cipher_adh'    => { 'txt' => "Target does not accept ADH ciphers" },
    'cipher_exp'    => { 'txt' => "Target does not accept EXPORT ciphers" },
    'cipher_cbc'    => { 'txt' => "Target does not accept CBC ciphers" },
    'cipher_des'    => { 'txt' => "Target does not accept DES ciphers" },
    'cipher_rc4'    => { 'txt' => "Target does not accept RC4 ciphers" },
    'cipher_edh'    => { 'txt' => "Target supports EDH ciphers" },
    'cipher_pfs'    => { 'txt' => "Target supports PFS (selected cipher)" },
    'cipher_pfsall' => { 'txt' => "Target supports PFS (all ciphers)" },
    'closure'       => { 'txt' => "Target understands TLS closure alerts" },
    'compression'   => { 'txt' => "Target does not support Compression" },
    'fallback'      => { 'txt' => "Target supports fallback from TLSv1.1" },
    'ism'           => { 'txt' => "Target is ISM compliant (ciphers only)" },
    'pci'           => { 'txt' => "Target is PCI compliant (ciphers only)" },
    'fips'          => { 'txt' => "Target is FIPS-140 compliant" },
    'tr_02102+'         => { 'txt' => "Target is strict TR-02102-2 compliant" },
    'tr_02102-'         => { 'txt' => "Target is  lazy  TR-02102-2 compliant" },
    'tr_03116+'         => { 'txt' => "Target is strict TR-03116-4 compliant" },
    'tr_03116-'         => { 'txt' => "Target is  lazy  TR-03116-4 compliant" },
    'rfc_7525'          => { 'txt' => "Target is RFC 7525 compliant" },
    'sstp'              => { 'txt' => "Target does not support method SSTP" },
    'resumption'        => { 'txt' => "Target supports Resumption" },
    'renegotiation'     => { 'txt' => "Target supports Secure Renegotiation" },
    'krb5'              => { 'txt' => "Target supports Krb5" },
    'psk_hint'          => { 'txt' => "Target supports PSK Identity Hint" },
    'psk_identity'      => { 'txt' => "Target supports PSK" },
    'srp'               => { 'txt' => "Target supports SRP" },
    'ocsp_stapling'     => { 'txt' => "Target supports OCSP Stapling" },
    'master_secret'     => { 'txt' => "Target supports Extended Master Secret" },
    'session_ticket'    => { 'txt' => "Target supports TLS Session Ticket" },
    'session_lifetime'  => { 'txt' => "Target TLS Session Ticket Lifetime" },
    'session_starttime' => { 'txt' => "Target TLS Session Start Time match" },
    'session_random'    => { 'txt' => "Target TLS Session Ticket is random" },
    'heartbeat'         => { 'txt' => "Target does not support heartbeat extension" },
    'scsv'              => { 'txt' => "Target does not support SCSV" },
    'dh_512'   => { 'txt' => "Target DH Parameter >= 512 bits" },
    'dh_2048'  => { 'txt' => "Target DH Parameter >= 2048 bits" },
    'ecdh_256' => { 'txt' => "Target DH Parameter >= 256 bits (ECDH)" },
    'ecdh_512' => { 'txt' => "Target DH Parameter >= 512 bits (ECDH)" },
);

our %check_size = (

    'len_pembase64' => { 'txt' => "Certificate PEM (base64) size" },
    'len_pembinary' => { 'txt' => "Certificate PEM (binary) size" },
    'len_subject'   => { 'txt' => "Certificate Subject size" },
    'len_issuer'    => { 'txt' => "Certificate Issuer size" },
    'len_cps'       => { 'txt' => "Certificate CPS size" },
    'len_crl'       => { 'txt' => "Certificate CRL size" },
    'len_crl_data'  => { 'txt' => "Certificate CRL data size" },
    'len_ocsp'      => { 'txt' => "Certificate OCSP size" },
    'len_oids'      => { 'txt' => "Certificate OIDs size" },
    'len_publickey' => { 'txt' => "Certificate Public Key size" },

    'len_sigdump'    => { 'txt' => "Certificate Signature Key size" },
    'len_altname'    => { 'txt' => "Certificate Subject Altname size" },
    'len_chain'      => { 'txt' => "Certificate Chain size" },
    'len_sernumber'  => { 'txt' => "Certificate Serial Number size" },
    'cnt_altname'    => { 'txt' => "Certificate Subject Altname count" },
    'cnt_wildcard'   => { 'txt' => "Certificate Wildcards count" },
    'cnt_chaindepth' => { 'txt' => "Certificate Chain Depth count" },
    'cnt_ciphers'    => { 'txt' => "Total number of checked ciphers" },
    'cnt_totals'     => { 'txt' => "Total number of accepted ciphers" },
    'cnt_checks_noo' => { 'txt' => "Total number of check results 'no(<<)'" },
    'cnt_checks_no'  => { 'txt' => "Total number of check results 'no'" },
    'cnt_checks_yes' => { 'txt' => "Total number of check results 'yes'" },
    'cnt_exitcode'   => { 'txt' => "Total number of insecure checks" },

);

our %check_http = (

    'sts_maxage0d'   => { 'txt' => "STS max-age not reset" },
    'sts_maxage1d'   => { 'txt' => "STS max-age less than one day" },
    'sts_maxage1m'   => { 'txt' => "STS max-age less than one month" },
    'sts_maxage1y'   => { 'txt' => "STS max-age less than one year" },
    'sts_maxagexy'   => { 'txt' => "STS max-age more than one year" },
    'sts_maxage18'   => { 'txt' => "STS max-age more than 18 weeks" },
    'sts_expired'    => { 'txt' => "STS max-age < certificate's validity" },
    'hsts_sts'       => { 'txt' => "Target sends STS header" },
    'sts_maxage'     => { 'txt' => "Target sends STS header with proper max-age" },
    'sts_subdom'     => { 'txt' => "Target sends STS header with includeSubdomain" },
    'sts_preload'    => { 'txt' => "Target sends STS header with preload" },
    'hsts_is301'     => { 'txt' => "Target redirects with status code 301" },
    'hsts_is30x'     => { 'txt' => "Target redirects not with 30x status code" },
    'hsts_fqdn'      => { 'txt' => "Target redirect matches given host" },
    'http_https'     => { 'txt' => "Target redirects HTTP to HTTPS" },
    'hsts_location'  => { 'txt' => "Target sends STS and no Location header" },
    'hsts_refresh'   => { 'txt' => "Target sends STS and no Refresh header" },
    'hsts_redirect'  => { 'txt' => "Target redirects HTTP without STS header" },
    'hsts_samehost'  => { 'txt' => "Target redirects HTTP to HTTPS same host" },
    'hsts_ip'        => { 'txt' => "Target does not send STS header for IP" },
    'hsts_httpequiv' => { 'txt' => "Target does not send STS in meta tag" },
    'https_pins'     => { 'txt' => "Target sends Public-Key-Pins header" },
);

our %shorttexts = (
    'ip'            => "IP for hostname",
    'DNS'           => "DNS for hostname",
    'reversehost'   => "Reverse hostname",
    'hostname'      => "Hostname equals Subject",
    'expired'       => "Not expired",
    'certfqdn'      => "Valid for hostname",
    'wildhost'      => "Wilcard for hostname",
    'wildcard'      => "No wildcards",
    'sni'           => "Not SNI based",
    'sernumber'     => "Size Serial Number",
    'sha2signature' => "Signature is SHA2",
    'rootcert'      => "Not root CA",
    'ocsp_uri'      => "OCSP URL",
    'ocsp_valid'    => "OCSP valid",
    'hastls10_old'  => "TLSv1",
    'hastls11_old'  => "TLSv1.1",
    'hassslv2'      => "No SSLv2",
    'hassslv3'      => "No SSLv3",
    'hastls10'      => "No TLSv1",
    'hastls11'      => "No TLSv1.1",
    'hastls12'      => "TLSv1.2",
    'hastls13'      => "TLSv1.3",
    'hasdtls1'      => "DTLSv1",
    'hasdtls12'     => "DTLSv1.2",
    'hasdtls13'     => "DTLSv1.3",
    'hasalpn'       => "Supports ALPN",
    'hasnpn'        => "Supports  NPN",
    'alpn'          => "Selected ALPN",
    'npn'           => "Selected  NPN",
    'alpns'         => "Supported ALPNs",
    'npns'          => "Supported  NPNs",
    'master_secret' => "Supports Extended Master Secret",
    'next_protocols'      => "(NPN) Protocols",
    'cipher_strong'       => "Strongest cipher selected",
    'cipher_order'        => "Client's cipher order",
    'cipher_weak'         => "Weak cipher selected",
    'cipher_null'         => "No NULL ciphers",
    'cipher_adh'          => "No ADH ciphers",
    'cipher_exp'          => "No EXPORT ciphers",
    'cipher_cbc'          => "No CBC ciphers",
    'cipher_des'          => "No DES ciphers",
    'cipher_rc4'          => "No RC4 ciphers",
    'cipher_edh'          => "EDH ciphers",
    'cipher_pfs'          => "PFS (selected cipher)",
    'cipher_pfsall'       => "PFS (all ciphers)",
    'sgc'                 => "SGC supported",
    'cps'                 => "CPS supported",
    'crl'                 => "CRL supported",
    'cps_valid'           => "CPS valid",
    'crl_valid'           => "CRL valid",
    'dv'                  => "DV supported",
    'ev+'                 => "EV supported (strict)",
    'ev-'                 => "EV supported (lazy)",
    'ev_chars'            => "No invalid characters in extensions",
    'beast'               => "Safe to BEAST (cipher)",
    'breach'              => "Safe to BREACH",
    'ccs'                 => "Safe to CCS",
    'crime'               => "Safe to CRIME",
    'drown'               => "Safe to DROWN",
    'time'                => "Safe to TIME",
    'freak'               => "Safe to FREAK",
    'heartbleed'          => "Safe to Heartbleed",
    'lucky13'             => "Safe to Lucky 13",
    'logjam'              => "Safe to Logjam",
    'poodle'              => "Safe to POODLE",
    'rc4'                 => "Safe to RC4 attack",
    'robot'               => "Safe to ROBOT",
    'sloth'               => "Safe to SLOTH",
    'sweet32'             => "Safe to Sweet32",
    'scsv'                => "SCSV not supported",
    'constraints'         => "Basic Constraints is false",
    'modulus_exp_1'       => "Modulus Exponent <>1",
    'modulus_size_oldssl' => "Modulus >16385 bits",
    'modulus_exp_65537'   => "Modulus Exponent =65537",
    'modulus_exp_oldssl'  => "Modulus Exponent >65537",
    'pub_encryption'      => "Public Key with Encryption",
    'pub_enc_known'       => "Public Key Encryption known",
    'sig_encryption'      => "Private Key with Encryption",
    'sig_enc_known'       => "Private Key Encryption known",
    'rfc_6125_names'      => "Names according RFC 6125",
    'rfc_2818_names'      => "subjectAltNames according RFC 2818",
    'closure'             => "TLS closure alerts",
    'fallback'            => "Fallback from TLSv1.1",
    'zlib'                => "ZLIB extension",
    'lzo'                 => "GnuTLS extension",
    'open_pgp'            => "OpenPGP extension",
    'ism'                 => "ISM compliant",
    'pci'                 => "PCI compliant",
    'fips'                => "FIPS-140 compliant",
    'sstp'                => "SSTP",
    'tr_02102+'            => "TR-02102-2 compliant (strict)",
    'tr_02102-'            => "TR-02102-2 compliant (lazy)",
    'tr_03116+'            => "TR-03116-4 compliant (strict)",
    'tr_03116-'            => "TR-03116-4 compliant (lazy)",
    'rfc_7525'             => "RFC 7525 compliant",
    'resumption'           => "Resumption",
    'renegotiation'        => "Renegotiation",
    'hsts_sts'             => "STS header",
    'sts_maxage'           => "STS long max-age",
    'sts_maxage0d'         => "STS max-age not reset",
    'sts_maxage1d'         => "STS max-age < 1 day",
    'sts_maxage1m'         => "STS max-age < 1 month",
    'sts_maxage1y'         => "STS max-age < 1 year",
    'sts_maxagexy'         => "STS max-age > 1 year",
    'sts_maxage18'         => "STS max-age > 18 weeks",
    'sts_expired'          => "STS max-age < certificate's validity",
    'sts_subdom'           => "STS includeSubdomain",
    'sts_preload'          => "STS preload",
    'hsts_httpequiv'       => "STS not in meta tag",
    'hsts_ip'              => "STS header not for IP",
    'hsts_location'        => "STS and Location header",
    'hsts_refresh'         => "STS and no Refresh header",
    'hsts_redirect'        => "STS within redirects",
    'http_https'           => "Redirects HTTP",
    'hsts_fqdn'            => "Redirects to same host",
    'hsts_is301'           => "Redirects with 301",
    'hsts_is30x'           => "Redirects not with 30x",
    'https_pins'           => "Public-Key-Pins",
    'selfsigned'           => "Validity (signature)",
    'chain'                => "Certificate chain",
    'verify'               => "Chain verified",
    'chain_verify'         => "CA Chain trace",
    'error_verify'         => "CA Chain error",
    'error_depth'          => "CA Chain error in level",
    'nonprint'             => "No non-printables",
    'crnlnull'             => "No CR, NL, NULL",
    'compression'          => "Compression",
    'expansion'            => "Expansion",
    'krb5'                 => "Krb5 Principal",
    'psk_hint'             => "PSK Identity Hint",
    'psk_identity'         => "PSK Identity",
    'ocsp_stapling'        => "OCSP Stapling",
    'ocsp_response'        => "OCSP Response",
    'ocsp_response_data'   => "OCSP Response Data",
    'ocsp_response_status' => "OCSP Response Status",
    'ocsp_cert_status'     => "OCSP Response Cert Status",
    'ocsp_next_update'     => "OCSP Response Next Update",
    'ocsp_this_update'     => "OCSP Response This Update",
    'srp'                  => "SRP Username",
    'master_key'           => "Master-Key",
    'public_key_len'       => "Server public key length",
    'resumption_psk'       => "Resumption PSK",
    'session_id'           => "Session-ID",
    'session_id_ctx'       => "Session-ID-ctx",
    'session_protocol'     => "Selected SSL Protocol",
    'session_ticket'       => "TLS Session Ticket",
    'session_lifetime'     => "TLS Session Ticket Lifetime",
    'session_random'       => "TLS Session Ticket random",
    'session_timeout'      => "TLS Session Timeout",
    'session_startdate'    => "TLS Session Start Time locale",
    'session_starttime'    => "TLS Session Start Time EPOCH",
    'dh_parameter'         => "DH Parameter",
    'dh_512'               => "DH Parameter >= 512",
    'dh_2048'              => "DH Parameter >= 2048",
    'ecdh_256'             => "DH Parameter >= 256 (ECDH)",
    'ecdh_512'             => "DH Parameter >= 512 (ECDH)",
    'ext_authority'        => "Authority Information Access",
    'ext_authorityid'      => "Authority key Identifier",
    'ext_constraints'      => "Basic Constraints",
    'ext_cps'              => "Certificate Policies",
    'ext_cps_cps'          => "Certificate Policies: CPS",
    'ext_cps_policy'       => "Certificate Policies: Policy",
    'ext_cps_notice'       => "Certificate Policies: User Notice",
    'ext_crl'              => "CRL Distribution Points",
    'ext_subjectkeyid'     => "Subject Key Identifier",
    'ext_keyusage'         => "Key Usage",
    'ext_extkeyusage'      => "Extended Key Usage",
    'ext_certtype'         => "Netscape Cert Type",
    'ext_issuer'           => "Issuer Alternative Name",
    'fallback_protocol'    => "Fallback SSL Protocol",
    'len_pembase64'        => "Size PEM (base64)",
    'len_pembinary'        => "Size PEM (binary)",
    'len_subject'          => "Size subject",
    'len_issuer'           => "Size issuer",
    'len_cps'              => "Size CPS",
    'len_crl'              => "Size CRL",
    'len_crl_data'         => "Size CRL data",
    'len_ocsp'             => "Size OCSP",
    'len_oids'             => "Size OIDs",
    'len_altname'          => "Size altname",
    'len_publickey'        => "Size pubkey",
    'len_sigdump'          => "Size signature key",
    'len_chain'            => "Size certificate chain",
    'len_sernumber'        => "Size serial number",
    'cnt_altname'          => "Count altname",
    'cnt_wildcard'         => "Count wildcards",
    'cnt_chaindepth'       => "Count chain depth",
    'cnt_ciphers'          => "Checked ciphers",
    'cnt_totals'           => "Accepted ciphers",
    'cnt_checks_noo'       => "Checks 'no(<<)'",
    'cnt_checks_no'        => "Checks 'no'",
    'cnt_checks_yes'       => "Checks 'yes'",
    'pem'               => "PEM",
    'text'              => "PEM decoded",
    'cn'                => "Common Name",
    'subject'           => "Subject",
    'issuer'            => "Issuer",
    'altname'           => "Subject AltNames",
    'ciphers'           => "Client Ciphers",
    'ciphers_local'     => "SSLlib Ciphers",
    'cipher_selected'   => "Selected Cipher",
    'dates'             => "Validity (date)",
    'before'            => "Valid since",
    'after'             => "Valid until",
    'tlsextdebug'       => "TLS Extensions (debug)",
    'tlsextensions'     => "TLS Extensions",
    'extensions'        => "Extensions",
    'heartbeat'         => "Heartbeat",
    'aux'               => "Trust",
    'email'             => "Email",
    'pubkey'            => "Public Key",
    'pubkey_algorithm'  => "Public Key Algorithm",
    'pubkey_value'      => "Public Key Value",
    'modulus_len'       => "Public Key Length",
    'modulus'           => "Public Key Modulus",
    'modulus_exponent'  => "Public Key Exponent",
    'serial'            => "Serial Number",
    'serial_hex'        => "Serial Number (hex)",
    'serial_int'        => "Serial Number (int)",
    'certversion'       => "Certificate Version",
    'sslversion'        => "SSL Protocol",
    'signame'           => "Signature Algorithm",
    'sigdump'           => "Signature (hexdump)",
    'sigkey_len'        => "Signature Key Length",
    'sigkey_value'      => "Signature Key Value",
    'trustout'          => "Trusted",
    'ocspid'            => "OCSP Hashes",
    'ocsp_subject_hash' => "OCSP Subject Hash",
    'ocsp_public_hash'  => "OCSP Public Hash",
    'subject_hash'      => "Subject Hash",
    'issuer_hash'       => "Issuer Hash",
    'fp_not_md5'        => "Fingerprint not MD5",
    'cert_type'         => "Certificate Type (bitmask)",
    'verify_hostname'   => "Hostname valid",
    'verify_altname'    => "AltNames valid",
    'fingerprint_hash'  => "Fingerprint Hash",
    'fingerprint_type'  => "Fingerprint Algorithm",
    'fingerprint_sha2'  => "Fingerprint SHA2",
    'fingerprint_sha1'  => "Fingerprint SHA1",
    'fingerprint_md5'   => "Fingerprint  MD5",
    'fingerprint'       => "Fingerprint:",
    'https_protocols'   => "HTTPS Alternate-Protocol",
    'https_body'        => "HTTPS Body",
    'https_svc'         => "HTTPS Alt-Svc header",
    'https_status'      => "HTTPS Status line",
    'https_server'      => "HTTPS Server banner",
    'https_location'    => "HTTPS Location header",
    'https_alerts'      => "HTTPS Error alerts",
    'https_refresh'     => "HTTPS Refresh header",
    'https_pins'        => "HTTPS Public-Key-Pins header",
    'https_sts'         => "HTTPS STS header",
    'hsts_maxage'       => "HTTPS STS MaxAge",
    'hsts_subdom'       => "HTTPS STS sub-domains",
    'hsts_preload'      => "HTTPS STS preload",
    'hsts_is301'        => "HTTP Status code is 301",
    'hsts_is30x'        => "HTTP Status code not 30x",
    'hsts_samehost'     => "HTTP redirect to same host",
    'http_protocols'    => "HTTP Alternate-Protocol",
    'http_svc'          => "HTTP Alt-Svc header",
    'http_status'       => "HTTP Status line",
    'http_location'     => "HTTP Location header",
    'http_refresh'      => "HTTP Refresh header",
    'http_sts'          => "HTTP STS header",
    'options'           => "internal SSL bitmask",
);

sub __SSLinfo {

    my ( $cmd, $host, $port ) = @_;
    my $val = "<<__SSLinfo: unknown command: '$cmd'>>";
    my $ext = "";
    $val = SSLinfo::fingerprint( $host, $port )      if ( $cmd eq 'fingerprint' );
    $val = SSLinfo::fingerprint_hash( $host, $port ) if ( $cmd eq 'fingerprint_hash' );
    $val = SSLinfo::fingerprint_sha2( $host, $port ) if ( $cmd eq 'fingerprint_sha2' );
    $val = SSLinfo::fingerprint_sha1( $host, $port ) if ( $cmd eq 'fingerprint_sha1' );
    $val = SSLinfo::fingerprint_md5( $host, $port )  if ( $cmd eq 'fingerprint_md5' );
    $val = SSLinfo::pubkey_value( $host, $port )     if ( $cmd eq 'pubkey_value' );
    $val = SSLinfo::sigkey_value( $host, $port )     if ( $cmd eq 'sigkey_value' );
    $val = SSLinfo::heartbeat( $host, $port )        if ( $cmd eq 'heartbeat' );
    $val = SSLinfo::extensions( $host, $port )       if ( $cmd =~ /^ext(?:ensions|_)/ );
    $val = SSLinfo::tlsextdebug( $host, $port )      if ( $cmd eq 'tlsextdebug' );

    if ( $cmd eq 'tlsextensions' ) {
        $val = SSLinfo::tlsextensions( $host, $port );
        $val =~ s/^\s*//g;
        $val =~ s/([\n\r])/; /g;
    }
    if ( $cmd =~ /ocspid/ ) {
        $val = SSLinfo::ocspid( $host, $port );
        $val =~ s/^\n?\s+//g;
        $val =~ s/([\n\r])/; /g;
    }
    if ( $cmd =~ /ocsp_subject_hash/ ) {
        $val = SSLinfo::ocspid( $host, $port );
        $val =~ s/^[^:]+:\s*//;
        $val =~ s/.ublic[^:]+:\s*.*//;
    }
    if ( $cmd =~ /ocsp_public_hash/ ) {
        $val = SSLinfo::ocspid( $host, $port );
        $val =~ s/^[^:]+:\s*//;
        $val =~ s/^[^:]+:\s*//;
    }
    if ( $cmd =~ m/ext_/ ) {
        $val .= " X509";
        my $rex = '\s*(.*?)(?:X509|Authority|Netscape|CT Precertificate).*';
        $ext = $val;
        $val =~ s#.*?Authority Information Access:$rex#$1#ms          if ( $cmd eq 'ext_authority' );
        $val =~ s#.*?Authority Key Identifier:$rex#$1#ms              if ( $cmd eq 'ext_authorityid' );
        $val =~ s#.*?Basic Constraints:$rex#$1#ms                     if ( $cmd eq 'ext_constraints' );
        $val =~ s#.*?Key Usage:$rex#$1#ms                             if ( $cmd eq 'ext_keyusage' );
        $val =~ s#.*?Subject Key Identifier:$rex#$1#ms                if ( $cmd eq 'ext_subjectkeyid' );
        $val =~ s#.*?Certificate Policies:$rex#$1#ms                  if ( $cmd =~ /ext_cps/ );
        $val =~ s#.*?CPS\s*:\s*([^\s\n]*).*#$1#ms                     if ( $cmd eq 'ext_cps_cps' );
        $val =~ s#.*?Policy\s*:\s*(.*?)(?:\n|CPS|User).*#$1#ims       if ( $cmd eq 'ext_cps_policy' );
        $val =~ s#.*?User\s*Notice:\s*(.*?)(?:\n|CPS|Policy).*#$1#ims if ( $cmd eq 'ext_cps_notice' );
        $val =~ s#.*?CRL Distribution Points:$rex#$1#ms               if ( $cmd eq 'ext_crl' );
        $val =~ s#.*?Extended Key Usage:$rex#$1#ms                    if ( $cmd eq 'ext_extkeyusage' );
        $val =~ s#.*?Netscape Cert Type:$rex#$1#ms                    if ( $cmd eq 'ext_certtype' );
        $val =~ s#.*?Issuer Alternative Name:$rex#$1#ms               if ( $cmd eq 'ext_issuer' );

        if ( $cmd eq 'ext_crl' ) {
            $val =~ s#\s*Full Name:\s*##imsg;
            $val =~ s#(\s*URI\s*:)# #msg;
        }
        $val = "" if ( $ext eq $val );
    }
    if ( $cmd =~ /ext(?:ensions|debug|_)/ ) {
        if ( $OCfg::cfg{'format'} ne "raw" ) {
            $val =~ s/([0-9a-f]):([0-9a-f])/$1$2/ig;

            $val =~ s/(keyid)/$1:/i;
            $val =~ s/(CA)(FALSE)/$1:$2/i;
            if ( $cmd eq 'extensions' ) {
                $val =~ s/\n\n+/\n/g;
            }
            else {
                $val =~ s/\s\s+/ /g;
            }
        }
        return $val;
    }
    if ( $OCfg::cfg{'format'} ne "raw" ) {
        $val = "" if not defined $val;
        $val =~ s/^\s+//g;
        $val =~ s/\n\s+//g;
        $val =~ s/\n/ /g;
        $val =~ s/\s\s+/ /g;
        $val =~ s/([0-9a-f]):([0-9a-f])/$1$2/ig;
    }
    return $val;
}


sub show {
    my $arg = shift;
    printf("= %%$arg\n");
    if ( 'data' eq $arg ) {
        printf( "%21s -\t%s\n", $_, $data{$_}->{txt} ) foreach ( sort keys %data );
    }
    if ( 'checks' eq $arg ) {
        printf( "%21s -\t%s\n", $_, $checks{$_}->{txt} ) foreach ( sort keys %checks );
    }
    if ( 'check_cert' eq $arg ) {
        printf( "%21s -\t%s\n", $_, $check_cert{$_}->{txt} ) foreach ( sort keys %check_cert );
    }
    if ( 'check_conn' eq $arg ) {
        printf( "%21s -\t%s\n", $_, $check_conn{$_}->{txt} ) foreach ( sort keys %check_conn );
    }
    if ( 'check_dest' eq $arg ) {
        printf( "%21s -\t%s\n", $_, $check_dest{$_}->{txt} ) foreach ( sort keys %check_dest );
    }
    if ( 'check_size' eq $arg ) {
        printf( "%21s -\t%s\n", $_, $check_size{$_}->{txt} ) foreach ( sort keys %check_size );
    }
    if ( 'check_http' eq $arg ) {
        printf( "%21s -\t%s\n", $_, $check_http{$_}->{txt} ) foreach ( sort keys %check_http );
    }
    if ( $arg =~ m/shorttexts?$/ ) {
        printf( "%21s -\t%s\n", $_, $shorttexts{$_} ) foreach ( sort keys %shorttexts );
    }
    return if ( $arg =~ /check_/ );

    print <<"EoHelp";

= Please use  o-saft.pl --help=$arg  for formated output.
EoHelp
    return;
}

sub _init_checks_val {
    my %_text = (
        'undef'  => "<<undefined>>",
        'na_STS' => "<<N/A as STS not set>>",
    );
    foreach my $key ( keys %checks ) { $checks{$key}->{val} = ""; }
    foreach my $key (qw(heartbeat krb5 psk_hint psk_identity srp session_ticket session_lifetime)) {
        $checks{$key}->{val} = $_text{'undef'};
    }
    foreach my $key ( keys %checks ) {
        $checks{$key}->{val} = 0 if ( $key =~ m/$OCfg::cfg{'regex'}->{'cmd-sizes'}/ );
        $checks{$key}->{val} = 0 if ( $key =~ m/$OCfg::cfg{'regex'}->{'SSLprot'}/ );
    }
    $checks{'sts_maxage0d'}->{val} = 1;
    $checks{'sts_maxage1d'}->{val} = 86400;
    $checks{'sts_maxage1m'}->{val} = 2592000;
    $checks{'sts_maxage1y'}->{val} = 31536000;
    $checks{'sts_maxagexy'}->{val} = 99999999;
    $checks{'sts_maxage18'}->{val} = 10886400;

    foreach my $key (
        qw(
        sts_maxage sts_expired sts_preload sts_subdom
        hsts_location hsts_refresh hsts_fqdn hsts_samehost hsts_sts
        )
      )
    {
        $checks{$key}->{val} = $_text{'na_STS'};
    }
    foreach my $key ( @{ $OCfg::cfg{'cmd-vulns'} } ) {
        $checks{$key}->{val} = $_text{'undef'};
    }
    foreach my $key (
        qw(
        cipher_null cipher_adh cipher_exp cipher_cbc cipher_des cipher_rc4
        cipher_edh  cipher_pfs cipher_pfsall
        beast breach freak logjam lucky13 rc4 robot sloth sweet32
        ism pci fips tr_02102+ tr_02102- tr_03116+ tr_03116- rfc_7525
        )
      )
    {
        $checks{$key}->{val} = "";
    }
    return;
}

sub _init {

    foreach my $key ( keys %check_conn ) { $checks{$key}->{txt} = $check_conn{$key}->{txt}; $checks{$key}->{typ} = 'connection'; }
    foreach my $key ( keys %check_cert ) { $checks{$key}->{txt} = $check_cert{$key}->{txt}; $checks{$key}->{typ} = 'certificate'; }
    foreach my $key ( keys %check_dest ) { $checks{$key}->{txt} = $check_dest{$key}->{txt}; $checks{$key}->{typ} = 'destination'; }
    foreach my $key ( keys %check_size ) { $checks{$key}->{txt} = $check_size{$key}->{txt}; $checks{$key}->{typ} = 'sizes'; }
    foreach my $key ( keys %check_http ) { $checks{$key}->{txt} = $check_http{$key}->{txt}; $checks{$key}->{typ} = 'https'; }
    _init_checks_val();

    $data{'fallback_protocol'}->{'val'} = sub { return $OCfg::prot{'fallback'}->{val} };
    foreach my $ssl ( keys %OCfg::prot ) {
        my $key = lc($ssl);
        $data{$key}->{val} = sub { return $OCfg::prot{$ssl}->{'default'}; };
        $data{$key}->{txt} = "Target default $OCfg::prot{$ssl}->{txt} cipher";
        $shorttexts{$key}  = "Default $OCfg::prot{$ssl}->{txt} cipher";
    }

    return;
}

sub _main {
    my @argv = @_;
    push( @argv, "--help" ) if ( 0 > $#argv );
    my %usage = (
        '# commands to show internal data' => {
            'info'       => 'show %data',
            'data'       => 'show %data',
            'checks'     => 'show %checks',
            'check_cert' => 'show %check_cert',
            'check_conn' => 'show %check_conn',
            'check_http' => 'show %check_http',
            'check_dest' => 'show %check_dest',
            'check_size' => 'show %check_size',
            'shorttexts' => 'show %shorttexts',
        },
    );
    while ( my $arg = shift @argv ) {
        if ( $arg =~ m/^--?h(?:elp)?$/x ) { OText::print_pod( $0, __PACKAGE__, $SID_odata ); exit 0; }
        if ( $arg eq '--usage' )          { OText::usage_show( "", \%usage );                exit 0; }
        if ( $arg =~ /^version$/x )        { print "$SID_odata\n"; next; }
        if ( $arg =~ /^[-+]?V(ERSION)?$/ ) { print "$VERSION\n";   next; }
        $arg =~ s/^\+test[_.-]?//x;
        if ( $arg eq 'info' ) { $arg = "data"; }
        show($arg);
    }
    exit 0;
}

sub done { }

_init();


package Ciphers;

our @CARP_NOT = qw(Ciphers);

my $SID_ciphers = "@(#) Ciphers.pm 3.63 24/09/05 10:48:08";
our $VERSION = "24.09.24";

BEGIN {
    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##x;
    if ( exists $ENV{'PWD'} and not( grep { /^$ENV{'PWD'}$/ } @INC ) ) {
        unshift( @INC, $ENV{'PWD'} );
    }
    unshift( @INC, $_path ) if not( grep { /^$_path$/ } @INC );
    unshift( @INC, "lib" )  if not( grep { /^lib$/ } @INC );
    our @EXPORT_OK = qw(
      %ciphers
      %ciphers_desc
      $cipher_results
      %ciphers_notes
      %ciphers_cfg
    );
}

$::osaft_standalone = 0 if not defined $::osaft_standalone;



our %ciphers = (

);

our %ciphers_desc = (
    'head' => [qw( openssl sec  ssl  keyx auth enc  bits mac  rfc  names const notes)],
    'hex' => 'Hex Code',

    'openssl' => 'OpenSSL STRENGTH',

    'sec' => 'Security',

    'ssl' => 'SSL/TLS Version',

    'keyx' => 'Key Exchange',

    'auth'     => 'Authentication',
    'enc'      => 'Encryption Type',
    'bits'     => 'Encryption Size',
    'enc_size' => 'Block Size',
    'mac'      => 'MAC/Hash Type',
    'mac_size' => 'MAC/Hash Size',
    'rfc'   => 'RFC(s)',
    'pfs'   => 'PFS',
    'suite' => 'Cipher Suite',
    'name'  => 'OpenSSL Name',
    'names' => 'Names, Aliases',
    'const' => 'Constant Names',
    'notes' => 'Notes/Comments',

    'sample' => {
        '0x0300003D' =>
          [ split /\s+/x, q(HIGH HIGH TLSv12 RSA  RSA  AES  256  SHA256 5246 AES256-SHA256,Alias RSA_WITH_AES_256_SHA256,RSA_WITH_AES_256_CBC_SHA256 L ) ],
    },
    'additional_notes' => <<'EoNOTE',

Note about constant names:
  Depending on the source of the constant, a different prefix in the name is
  used, such as TLS_ or SSL_ or SSL_CK_ or SSL3_CK_ or TLS1_CK_ .
  Hence no prefix at all is used here.
Note about TLS version:
  Usually the lowest/oldest protocol version is shown. But this cipher suite
  may also be used in a newer protocol version also.
  Following normalised strings are used for protocol versions:
      SSLv2, SSLv3, DTLS0.9, DTLS1.0, TLSv10, TLSv11, TLSv12, TLSv13, PCT
  SSL/TLS  is used for pseudo cipher suites.
EoNOTE
);

our $cipher_results = {};
my $cipher_results_desc = <<'EoDESC';
=---------------+------+----------------------------+----------------------+
=  ssl       => {
=       key    => [ supported, cipher parameters ], # cipher suite name
=  }
=---------------+------+----------------------------+----------------------+
= 'SSLv3'    => {
=      '0x02010080' => [ yes, '' ],                 # RC4-MD5
=      '0x03000004' => [ yes, '' ],                 # RC4-MD5
=      '0x02FF0810' => [ no,  '' ],                 # NULL
= },
= 'TLSv12'   => {
=      '0x0300006B' => [ yes, 'dh: 2048 bits' ],    # DHE-RSA-AES256-SHA256
=      '0x0300003D' => [ yes, '' ],                 # AES256-SHA256
= },
= '_admin'   => {   # for internal use
=      'TLSv12'     => { ... },  # TODO: ...
= },
=---------------+------+----------------------------+----------------------+
EoDESC

our %ciphers_notes = (

);

our %ciphers_cfg = (

);

*_trace = sub { print( join( " ", "#${0}::", @_ ), "\n" ) if ( 0 < $OCfg::cfg{'trace'} ); return; }
  if not defined &_trace;
*_trace2 = sub { print( join( " ", "#${0}::", @_ ), "\n" ) if ( 2 < $OCfg::cfg{'trace'} ); return; }
  if not defined &_trace2;
*_v_print = sub { print( join( " ", "#${0}: ", @_ ), "\n" ) if ( 0 < $OCfg::cfg{'verbose'} ); return; }
  if not defined &_v_print;
*_v2print = sub { print( join( " ", "#${0}: ", @_ ), "\n" ) if ( 1 < $OCfg::cfg{'verbose'} ); return; }
  if not defined &_v2print;


sub is_valid_intern_key {
    my $key = uc(shift);
    $key =~ s/^0X//g;
    return "" if $key !~ m/^[0-9A-F]{8}(?:-BUG|-C)?$/;
    return "0x$key";
}

sub is_valid_key {
    my $key = uc(shift);
    $key =~ s/^0X//g;
    return "" if $key !~ m/^[0-9A-F]{8}$/;
    return "0x$key";
}

sub text2key {
    my $txt = shift;
    my $key = uc($txt);
    $key =~ s/(,|0X)//g;
    return $txt     if ( $key !~ m/^[0-9A-F]+$/ );
    return "0x$key" if ( 8 == length($key) );
    if ( 4 < length($key) ) {
        $key = "0x02$key";
    }
    else {
        while ( 6 > length($key) ) { $key = "0$key"; }
        $key = "0x03$key";
    }
    return $key;
}

sub key2text {
    my $key = shift;
    return $key if not is_valid_key($key);

    $key =~ s/0x//i;
    if ( 6 < length($key) ) {
        $key =~ s/^42//;
        $key =~ s/^02//;
        $key =~ s/^0300//;
    }
    $key =~ s/(..)/,0x$1/g;
    $key =~ s/^,//;
    $key = "     $key" if ( 10 > length($key) );
    return "$key";
}

sub set_sec { my ( $key, $val ) = @_; $ciphers{$key}->{'sec'} = $val; return; }


sub get_param {
    my ( $hex, $key ) = @_;
    $hex = text2key($hex);

    if ( 'ARRAY' eq ref( $ciphers{$hex}->{$key} ) ) {
        return wantarray ? @{ $ciphers{$hex}->{$key} } : join( ' ', @{ $ciphers{$hex}->{$key} } );
    }
    else {
        return $ciphers{$hex}->{$key} || "";
    }
    return $OText::STR{UNDEF};
}

sub get_openssl { return get_param( shift, 'openssl' ); }
sub get_sec     { return get_param( shift, 'sec' ); }
sub get_ssl     { return get_param( shift, 'ssl' ); }
sub get_keyx    { return get_param( shift, 'keyx' ); }
sub get_auth    { return get_param( shift, 'auth' ); }
sub get_enc     { return get_param( shift, 'enc' ); }
sub get_bits    { return get_param( shift, 'bits' ); }
sub get_mac     { return get_param( shift, 'mac' ); }
sub get_rfc     { return get_param( shift, 'rfc' ); }
sub get_name    { return ( get_param( shift, 'names' ) )[0]; }
sub get_names   { return get_param( shift, 'names' ); }
sub get_aliases { my @a = get_names(shift); return @a[ 1 .. $#a ]; }
sub get_const  { return ( get_param( shift, 'const' ) )[0]; }
sub get_consts { return get_param( shift, 'const' ); }
sub get_note   { return ( get_param( shift, 'notes' ) )[0]; }
sub get_notes  { return get_param( shift, 'notes' ); }

sub _get_name {
    my $txt = shift;
    return $txt if ( $txt !~ m/0x/ );
    return get_name($txt);
}

sub get_encsize {
    my $name = _get_name(shift);
    return '128' if ( $name =~ m/AES/ );
    return '64'  if ( $name =~ m/Blowfish/i );
    return '128' if ( $name =~ m/CAMELLIA/ );
    return '-'   if ( $name =~ m/-CHACHA/ );
    return '64'  if ( $name =~ m/-CBC3/ );
    return '64'  if ( $name =~ m/-3DES/ );
    return '-'   if ( $name =~ m/DES-CBC/ );
    return '-?-' if ( $name =~ m/DES-CFB/ );
    return '-?-' if ( $name =~ m/GOST/ );
    return '64'  if ( $name =~ m/IDEA/ );
    return '-'   if ( $name =~ m/NULL/ );
    return '64'  if ( $name =~ m/RC2-/ );
    return '-'   if ( $name =~ m/RC4/ );
    return '128' if ( $name =~ m/SEED/ );
    return '-?-';
}


sub get_key {
    my $txt = shift;
    my $key = uc($txt);
    $key =~ s/X/x/g;
    my $typ = 'key';
  TRY: {
        last TRY if defined $ciphers{$key};
        $typ = 'names';
        foreach my $k ( keys %ciphers ) {
            $key = $k;
            my @names = get_names($key);
            last TRY if ( 0 < ( grep { /^$txt$/i } @names ) );
        }
        $typ = 'const';
        $txt =~ s/^(?:SSL[23]?|TLS1?)_//;
        $txt =~ s/^(?:CK|TXT)_//;
        foreach my $k ( keys %ciphers ) {
            $key = $k;
            my @names = get_consts($key);
            last TRY if ( 0 < ( grep { /^$txt$/i } @names ) );
        }
        $typ = 'no key found';
    }
    return $key if ( $typ !~ /^no key/ );
    OCfg::warn("521: $typ for '$txt'");
    return '';
}

sub get_data {
    my $key = shift;
    return $OText::STR{UNDEF} if ( not defined $ciphers{$key} );
    return join(
        "\t",
        get_param( $key, 'openssl' ),
        get_param( $key, 'sec' ),
        get_param( $key, 'ssl' ),
        get_param( $key, 'keyx' ),
        get_param( $key, 'auth' ),
        get_param( $key, 'enc' ),
        get_param( $key, 'bits' ),
        get_param( $key, 'mac' ),
        get_param( $key, 'rfc' ),
        get_param( $key, 'names' ),
        get_param( $key, 'const' ),
        get_param( $key, 'notes' ),
    );
}

sub get_iana {
    my $key = shift;
    $key = text2key($key);
    return ( grep { /$key/i } $OCfg::cfg{'cipherranges'}{'iana'} ) ? "yes" : "no";
}

sub get_pfs {
    my $key  = shift;
    my $name = $key;
    if ( not is_valid_key($key) ) {
        $name = get_name($key);
    }
    return ( ( $name =~ m/^(?:EC)?DHE/ ) or ( $name =~ m/^(?:EXP-)?EDH-/ ) ) ? "yes" : "no";
}

sub get_keys_list {
    my @keys = grep { /^0x[0-9a-fA-F]{8}$/ } keys %ciphers;
    return wantarray ? ( sort @keys ) : join( ' ', ( sort @keys ) );
}

sub get_names_list {
    my @list;
    foreach my $key ( sort keys %ciphers ) {
        next if ( not is_valid_key($key) );
        push( @list, get_name($key) );
    }
    return wantarray ? ( sort @list ) : join( ' ', ( sort @list ) );
}


sub find_consts {
    my $pattern = shift;
    $pattern =~ s/:/|/g;
    my @list;
    _trace("find_consts($pattern)");
    foreach my $key ( sort keys %ciphers ) {
        my @const = grep { /$pattern/ } get_consts($key);
        next if not @const;
        push( @list, @const );
    }
    return @list;
}

sub find_keys {
    my $pattern = shift;
    _trace("find_keys($pattern)");
    return map( { get_key($_); } grep { /$pattern/ } get_names_list() );
}

sub find_keys_any {
    my $pattern = shift;
    $pattern =~ s/:/|/g;
    my @list;
    _trace("find_keys_any: search $pattern");
    foreach my $key ( sort keys %ciphers ) {
        next if $key =~ m/^\s*$/;
        if ( $key =~ m/$pattern/ ) {
            push( @list, $key );
            next;
        }
        my @names = grep { /$pattern/ } get_names($key);
        if (@names) { push( @list, $key ); next; }
        @names = grep { /$pattern/ } get_consts($key);
        if (@names) { push( @list, $key ); next; }
    }
    return @list;
}

sub find_names {
    my $pattern = shift;
    $pattern =~ s/:/|/g;
    _trace("find_names($pattern)");
    return grep { /$pattern/ } get_names_list();
}

sub find_names_any {
    my $pattern = shift;
    $pattern =~ s/:/|/g;
    my @list;
    foreach my $key ( find_keys_any($pattern) ) { push( @list, get_name($key) ); }
    return wantarray ? (@list) : join( ' ', ( sort @list ) );
    return @list;
}

sub find_name {
    my $cipher = shift;
    my @list;
    _trace("find_name: search $cipher");
    my $key = get_key($cipher);
    return $key if $key !~ m/^\s*$/;
    foreach my $key ( sort keys %ciphers ) {
        my $name = get_name($key);
        next if not $name;
        next if $name =~ m/^\s*$/;
        if ( $name !~ m/$cipher/i ) {
            my @const = get_consts($key);
        }
        OCfg::warn("513: partial match for cipher name found '$cipher'");
        push( @list, $key );
    }
    return @list;
}


sub sort_names {
    my @ciphers = @_;
    my @sorted;
    my @latest;
    my $cnt_in = scalar @ciphers;

    _trace("sort_names(){ $cnt_in ciphers: @ciphers }");

    my @insecure = (
        qw((?:RC[24])),  qw((?:CBC|DES)),            qw((?:DSA|DSS)), qw((?:MD[2345])), qw(DH.?(?i:anon)), qw((?:NULL)),
        qw((?:PSK.SHA)), qw((?:SCSV|SSL2_UNFFINED)), qw((?:GREASE-)),
    );
    my @strength = (
        qw(CECPQ1[_-].*?CHACHA),       qw(CECPQ1[_-].*?AES256.GCM),   qw(^(TLS_|TLS13[_-])),         qw((?:ECDHE|EECDH).*?CHACHA),
        qw((?:ECDHE|EECDH).*?512.GCM), qw((?:ECDHE|EECDH).*?384.GCM), qw((?:ECDHE|EECDH).*?256.GCM), qw((?:ECDHE|EECDH).*?128.GCM),
        qw((?:EDH|DHE).*?CHACHA),      qw((?:EDH|DHE).*?PSK),         qw((?:EDH|DHE).*?512.GCM),     qw((?:EDH|DHE).*?384.GCM),
        qw((?:EDH|DHE).*?256.GCM),     qw((?:EDH|DHE).*?128.GCM),     qw(ECDH[_-].*?CHACHA),         qw(ECDH[_-].*?512.GCM),
        qw(ECDH[_-].*?384.GCM),        qw(ECDH[_-].*?256.GCM),        qw(ECDH[_-].*?128.GCM),        qw(ECDHE.*?CHACHA),
        qw(ECDHE.*?512),               qw(ECDHE.*?384),               qw(ECDHE.*?256),               qw(ECDHE.*?128),
        qw(ECDH[_-].*?CHACHA),         qw(ECDH[_-].*?512),            qw(ECDH[_-].*?384),            qw(ECDH[_-].*?256),
        qw(ECDH[_-].*?128),            qw(ECCPWD[_-]),                qw(AES),                       qw(KRB5),
        qw(SRP),                       qw(PSK),                       qw(GOST),                      qw((?:IANA|LEGACY)[_-]GOST2012),
        qw(FZA),                       qw((?:PSK|RSA).*?CHACHA),      qw(CHACHA),                    qw((?:EDH|DHE).*?CHACHA),
        qw((?:EDH|DHE).*?512),         qw((?:EDH|DHE).*?384),         qw((?:EDH|DHE).*?256),         qw((?:EDH|DHE).*?128),
        qw((?:EDH|DHE).*?(?:RSA|DSS)), qw(AEGIS),                     qw(CAMELLIA),                  qw((?:SEED|IDEA|ARIA|SM4)),
        qw(^(?:SHA256-|SHA384-)),      qw(RSA[_-]),                   qw(DH[_-]),                    qw(RC),
        qw(EXP),                       qw(AEC.*?256),                 qw(AEC.*?128),                 qw(AEC),
        qw(ADH.*?256),                 qw(ADH.*?128),                 qw(ADH),                       qw(PCT_),
    );
    foreach my $rex (@insecure) {
        _trace2("sort_names: insecure regex\t= $rex }");
        push( @latest, grep { /$rex/ } @ciphers );
        @ciphers = grep { !/$rex/ } @ciphers;
    }
    foreach my $rex (@strength) {
        $rex = qr/^(?:(?:SSL|TLS)[_-])?$rex/;
        _trace2("sort_names(): regex\t= $rex }");
        push( @sorted, grep { /$rex/ } @ciphers );
        @ciphers = grep { !/$rex/ } @ciphers;
    }
    push( @sorted, @latest );
    my $cnt_out = scalar @sorted;
    if ( $cnt_in != $cnt_out ) {
        my @miss;
        for my $i ( 0 .. $#ciphers ) {
            push( @miss, $ciphers[$i] ) unless grep { $_ eq $ciphers[$i] } @sorted;
        }
        @miss = sort @miss;
        warn $OText::STR{WARN}, "412: missing ciphers in sorted list ($cnt_out < $cnt_in): @miss";
    }
    @sorted = grep { !/^\s*$/ } @sorted;
    _trace("sort_names(){ $cnt_out ciphers\t= @sorted }");
    return @sorted;
}

sub sort_results {

    my $unsorted = shift;
    my @sorted;
    my @tmp_arr;
    foreach my $key ( sort keys %$unsorted ) {
        next if ( $key =~ m/^\s*$/ );
        my $cipher = get_name($key);
        if ( not defined $cipher ) {
            OCfg::warn("862: unknown cipher key '$key'; key ignored");
            next;
        }
        my $sec_osaft = lc( get_sec($key) );
        my $sec_owasp = OCfg::get_cipher_owasp($cipher);
        $sec_owasp = "N/A" if ( '-?-' eq $sec_owasp );

        my $weight = 50;
        $weight = 19 if ( $cipher =~ /^ECDHE/i );
        $weight = 25 if ( $cipher =~ /^ECDHE.ECDS/i );
        $weight = 29 if ( $cipher =~ /^(?:DHE|EDH)/i );
        $weight = 39 if ( $cipher =~ /^ECDH[_-]/i );
        $weight = 59 if ( $cipher =~ /^(?:DES|RC)/i );
        $weight = 69 if ( $cipher =~ /^EXP/i );
        $weight = 89 if ( $cipher =~ /^A/i );
        $weight = 79 if ( $cipher =~ /^AEC/i );
        $weight = 99 if ( $cipher =~ /^NULL/i );
        $weight -= 11 if ( $cipher =~ /^TLS[_-]/ );
        $weight -= 10 if ( $cipher =~ /^TLS13[_-]/ );
        $weight -= 9  if ( $cipher =~ /SHA512$/ );
        $weight -= 8  if ( $cipher =~ /SHA384$/ );
        $weight -= 7  if ( $cipher =~ /SHA256$/ );
        $weight -= 6  if ( $cipher =~ /SHA128$/ );
        $weight -= 5  if ( $cipher =~ /256.SHA$/ );
        $weight -= 4  if ( $cipher =~ /128.SHA$/ );
        $weight -= 3  if ( $cipher =~ /CHACHA/ );
        $weight -= 2  if ( $cipher =~ /256.GCM/ );
        $weight -= 1  if ( $cipher =~ /128.GCM/ );
        push( @tmp_arr, "$sec_owasp.$weight $key " );
    }
    foreach my $line ( sort @tmp_arr ) {
        my @arr = split( " ", $line );
        push( @sorted, $arr[1] );
    }
    return @sorted;
}


sub is_typ {
    my $rex    = shift;
    my $key    = shift;
    my $cipher = "";
  SWITCH: {
        last if ( ref($rex) =~ /^Regexp/i );
        if ( defined $OCfg::cfg{'regex'}->{$rex} ) {
            $rex = $OCfg::cfg{'regex'}->{$rex};
            last;
        }
        if ( 'ADH' eq $rex ) { $rex = $OCfg::cfg{'regex'}->{'ADHorDHA'};  last; }
        if ( 'EDH' eq $rex ) { $rex = $OCfg::cfg{'regex'}->{'DHEorEDH'};  last; }
        if ( 'EXP' eq $rex ) { $rex = $OCfg::cfg{'regex'}->{'EXPORT'};    last; }
        if ( 'FRZ' eq $rex ) { $rex = $OCfg::cfg{'regex'}->{'FRZorFZA'};  last; }
        if ( 'RC4' eq $rex ) { $rex = $OCfg::cfg{'regex'}->{'RC4orARC4'}; last; }
        $rex = qr($rex);
    }

    if ( not is_valid_key($key) ) {
        $key = get_key($key);
    }
    $cipher = get_name($key) if ( grep { /$rex/ } get_consts($key), get_names($key) );
    return $cipher;
}


sub show_getter03 {
    _v_print( ( caller(0) )[3] );

    my $cipher = "0x00,0x03";
    $cipher = text2key("0x00,0x03");
    printf("# testing example: $cipher (aka 0x00,0x03) ...\n");
    printf( "# %s(%s)\t%s\t%-14s\t# %s\n", "function", "cipher key", "key", "value", "(expected)" );
    printf("#----------------------+-------+----------------+---------------\n");
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_bits",    $cipher, "bits",    get_bits($cipher),                    "40" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_enc",     $cipher, "enc",     get_enc($cipher),                     "RC4" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_keyx",    $cipher, "keyx",    get_keyx($cipher),                    "RSA(512)" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_auth",    $cipher, "auth",    get_auth($cipher),                    "RSA" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_mac",     $cipher, "mac",     get_mac($cipher),                     "MD5" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_rfc",     $cipher, "rfc",     get_rfc($cipher),                     "4346,6347" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_sec",     $cipher, "sec",     get_sec($cipher),                     "WEAK" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_ssl",     $cipher, "ssl",     get_ssl($cipher),                     "SSLv3" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_name",    $cipher, "name",    get_name($cipher),                    "EXP-RC4-MD5" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_names",   $cipher, "names",   scalar( get_names($cipher) ),         "EXP-RC4-MD5" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_aliases", $cipher, "aliases", scalar( get_aliases($cipher) ) || "", "" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_const",   $cipher, "const",   get_const($cipher),                   "RSA_WITH_RC4_40_MD5" );
    printf(
        "%-10s(%s)\t%s\t%-14s\t# %s\n",
        "get_consts", $cipher, "consts",
        scalar( get_consts($cipher) ),
        "RSA_WITH_RC4_40_MD5 RSA_RC4_40_MD5 RSA_EXPORT_WITH_RC4_40_MD5 RC4_128_EXPORT40_WITH_MD5"
    );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_note",    $cipher, "note",    get_note($cipher),    "EXPORT" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_notes",   $cipher, "notes",   get_notes($cipher),   "EXPORT" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_iana",    $cipher, "iana",    get_iana($cipher),    "no" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_pfs",     $cipher, "pfs",     get_iana($cipher),    "no" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n", "get_encsize", $cipher, "encsize", get_encsize($cipher), "-" );
    printf( "%-10s(%s)\t%s\t%-14s\t# %s\n",
        "get_data", $cipher, "data", get_data($cipher),
"WEAK WEAK SSLv3 RSA(512) RSA RC4 40 MD5 4346,6347 EXP-RC4-MD5 RSA_WITH_RC4_40_MD5,RSA_RC4_40_MD5,RSA_EXPORT_WITH_RC4_40_MD5,RC4_128_EXPORT40_WITH_MD5 export"
    );
    printf("#----------------------+-------+----------------+---------------\n");
    return;
}

sub show_getter {
    my $key = shift;
    _v_print( ( caller(0) )[3] );
    if ( $key !~ m/^[x0-9a-fA-F,]+$/ ) {
        printf("# unknown cipher key '$key'; using hardcoded default instead\n");
        show_getter03;
        return;
    }
    print "= testing: $key ...\n";
    $key = text2key($key);
    if ( not defined $ciphers{$key} ) {
        OCfg::warn("511: undefined cipher '$key'");
        return;
    }
    printf( "= %s(%s)\t%s\t%s\n", "function", "cipher key", "key", "value" );
    printf("=----------------------+-------+----------------\n");
    printf( "%-10s(%s)\t%s\t%s\n", "get_bits",    $key, "bits",    get_bits($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_enc",     $key, "enc",     get_enc($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_keyx",    $key, "keyx",    get_keyx($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_auth",    $key, "auth",    get_auth($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_mac",     $key, "mac",     get_mac($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_rfc",     $key, "rfc",     get_rfc($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_sec",     $key, "sec",     get_sec($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_ssl",     $key, "ssl",     get_ssl($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_name",    $key, "name",    get_name($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_names",   $key, "names",   join( " ", get_names($key) ) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_aliases", $key, "aliases", join( " ", get_aliases($key) ) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_const",   $key, "const",   get_const($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_consts",  $key, "consts",  join( " ", get_consts($key) ) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_note",    $key, "note",    get_note($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_notes",   $key, "notes",   join( "", get_notes($key) ) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_iana",    $key, "iana",    get_iana($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_pfs",     $key, "pfs",     get_iana($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_encsize", $key, "encsize", get_encsize($key) );
    printf( "%-10s(%s)\t%s\t%s\n", "get_data",    $key, "data",    get_data($key) );
    printf("=----------------------+-------+----------------\n");
    return;
}

sub show_description {
    _v_print( ( caller(0) )[3] );
    local $\ = "\n";
    print << 'EoT';

=== internal data structure: overview of %ciphers ===
EoT

    my $hex = '0x0300003D';
    my $idx = 0;
    print("= %ciphers : example line:\n");
    printf("  '$hex' -> [");
    foreach ( @{ $ciphers_desc{head} } ) {
        printf( "\t%s", $ciphers_desc{sample}->{$hex}[$idx] );
        $idx++;
    }
    print(" ]");
    print("\n= %ciphers : tabular description of above (example) line:\n");
    print("=-------+--------------+-----------------------+--------");
    printf( "= [%s]\t%15s\t%16s\t%s\n", "nr", "key", "description", "example" );
    print("=-------+--------------+-----------------------+--------");
    $idx = 0;
    foreach ( @{ $ciphers_desc{head} } ) {
        my $txt = $ciphers_desc{ $ciphers_desc{head}[$idx] };
        printf( "  [%s]\t%15s\t%-20s\t%s\n", $idx, $ciphers_desc{head}[$idx], $txt, $ciphers_desc{sample}->{$hex}[$idx] );
        $idx++;
    }
    printf("=-------+--------------+-----------------------+--------");

    print("\n\n= %ciphers : description of one line as Perl code:\n");
    print("=------+------------------------------------+---------------+---------------");
    printf( "= varname  %-23s\t# example result# description\n", "%ciphers hash" );
    print("=------+------------------------------------+---------------+---------------");
    $idx = 0;
    foreach my $col ( @{ $ciphers_desc{head} } ) {
        my $var = $ciphers_desc{head}[$idx];
        my $txt = $ciphers_desc{$var};
        my $tab = ( 6 > length($col) ) ? "\t    " : " ";
        printf( "%6s = \$ciphers{'%s'}{%s};$tab# %-7s\t# %s\n", '$' . $var, $hex, $col, $ciphers_desc{sample}->{$hex}[$idx], $txt );
        $idx++;
    }
    printf( "%6s = \$ciphers{'%s'}{%s}[0];  # %-7s\t# %s\n", '$' . 'name',  $hex, 'names', 'AES256-SHA256', $ciphers_desc{'name'} );
    printf( "%6s = \$ciphers{'%s'}{%s}[1];  # %-7s\t# %s\n", '$' . 'alias', $hex, 'alias', 'Alias',         "first alias" );
    print("=------+------------------------------------+---------------+---------------");

    print "\n= \%cipher_results : description of hash:\n";
    print $cipher_results_desc;
    print "= \%cipher_results : description of Perl code:\n";
    print("=-------------------------------------------+---------------");
    print("=           %hash{  ssl   }->{'cipher key'} = [values];");
    print("=-------------------------------------------+---------------");
    print("  %cipher_results{'TLSv12'}->{'0x0300003D'} = ['yes', ''];");
    print("  %cipher_results{'SSLv3'} ->{'0x02FF0810'} = ['no',  ''];");
    print("=-------------------------------------------+---------------");

    return;
}

sub show_sorted {
    _v_print( ( caller(0) )[3] );
    local $\ = "\n";
    my $head = "= OWASP openssl self    IANA    cipher suite";
    my $line = "=------+-------+-------+-------+----------------------------------------------";
    print << 'EoT';

=== internal data structure: ciphers sorted according strength ===
=
= Show overview of all available ciphers sorted according OWASP scoring.
=
=   description of columns:
=       OWASP       - OWASP scoring (A, B, C, D)
=       openssl     - strength given bei OpenSSL
=       self        - strength according own rules
=       IANA        - "yes" if recommended by IANA
=       cipher suite- OpenSSL suite name
EoT
    print($line);
    print($head);
    print($line);
    my @sorted;
    my @unsorted;
    push( @unsorted, get_name($_) ) foreach sort keys %ciphers;

    foreach my $c ( sort_names(@unsorted) ) {
        my $key = get_key($c);
        push( @sorted, sprintf( "%4s\t%s\t%s\t%s\t%s", OCfg::get_cipher_owasp($c), get_openssl($key), get_sec($key), get_iana($key), $c ) );
    }
    print foreach sort @sorted;
    print($line);
    print($head);
    printf("=\n");
    printf( "= %4s sorted ciphers\n",  scalar @sorted );
    printf( "= %4s ignored ciphers\n", ( ( keys %ciphers ) - ( scalar @sorted ) ) );
    return;
}

sub show_overview {
    _v_print( ( caller(0) )[3] );
    local $\ = "\n";
    print << 'EoT';

=== internal data structure: information about ciphers ===
=
= This function prints a simple overview of all available ciphers. The purpose
= is to show if the internal data structure provides all necessary data.
=
=   description of columns:
=       key         - hex key for cipher suite
=       security    - cipher suite security is known
=       name        - cipher suite (OpenSSL) name exists
=       aliases     - cipher suite has other kown cipher suite names
=       const       - cipher suite constant name exists
=       cipher suite- cipher suite name (OpenSSL)
=   description of values:
=       *    value present (also if None or for pseudo ciphers)
=       -    value missing
=       -?-  security unknown/undefined
=       miss security missing in data structure
=
= No Perl or other warnings should be printed.
= Note: following columns should have a  *  in columns
=       security, name, const
EoT

    my $line = sprintf( "=%s+%s+%s+%s+%s+%s",        "-" x 14, "-" x 7,    "-" x 7, "-" x 7,   "-" x 7, "-" x 21 );
    my $head = sprintf( "= %-13s%s\t%s\t%s\t%s\t%s", "key",    "security", "name",  "aliases", "const", "cipher suite" );
    print($line);
    print($head);
    print($line);
    my %err;
    my $cnt = 0;
    foreach my $key ( sort keys %ciphers ) {
        $cnt++;
        my $sec    = $ciphers{$key}->{'sec'};
        my $name   = "-";
        my $alias  = "-";
        my $const  = "-";
        my $cipher = $ciphers{$key}->{'names'}[0];
        $sec   = "*" if ( $sec =~ m/None|weak|low|medium|high/i );
        $sec   = "-" if ( $sec ne "*" and $sec ne "-?-" );
        $name  = "*" if $ciphers{$key}->{'names'}[0] ne "";
        $alias = "*" if $ciphers{$key}->{'names'} ne "-";
        $const = "*" if $ciphers{$key}->{'const'}[0] ne "";
        printf( "%12s\t%s\t%s\t%s\t%s\t%s\n", $key, $sec, $name, $alias, $const, $cipher );
        $err{'security'}++ if ( '*' ne $sec );
        $err{'name'}++     if ( '*' ne $name );
        $err{'const'}++    if ( '*' ne $const );
        $err{'aliases'}++  if ( '*' ne $alias );
    }
    print($line);
    print($head);
    printf( "=\n= %s ciphers\n", $cnt );
    printf("= identified errors: ");
    printf( "%6s=%-2s,", $_, $err{$_} ) foreach sort keys %err;
    printf("\n");
    return;
}

sub show_all_names {
    my $type = shift;
    _v_print( ( caller(0) )[3] );
    my $text = $type;
    $text = "name"     if $type =~ /names/;
    $text = "constant" if $type =~ /const/;
    $text = "RFC"      if $type =~ /rfc/;
    my $txt_cols = "=       cipher name - (most common) cipher suite $text
=       alias names - known aliases for cipher suite $text";
    $txt_cols = "=       cipher name - cipher suite name as used in openssl
=       RFC         - RFC numbers, where cipher suite is described" if ( "rfc" eq $type );
    local $\ = "\n";
    print <<"EoT";

=== internal data structure: overview of various cipher suite ${text}s ===
=
=   description of columns:
=       key         - hex key for cipher suite
$txt_cols
EoT
    my $line = sprintf( "=%s+%s+%s\n", "-" x 14, "-" x 39, "-" x 31 );
    printf("$line");
    printf( "= %-13s\t%-37s\t%s\n", "key", "cipher name", "$text  names" );
    printf("$line");

    foreach my $key ( sort keys %ciphers ) {
        my @names = [];
        my $name  = "";
        if ( 'rfc' eq $type ) {
            $name = $ciphers{$key}->{'names'}[0];
            my $rfc = $ciphers{$key}->{'rfc'};
            next if "-" eq $rfc;
            @names = $rfc;
        }
        else {
            @names = @{ $ciphers{$key}->{$type} };
            $name  = shift @names;
            next if 1 > scalar @names;
        }
        printf( "%s\t%-37s\t@names\n", $key, $name );
    }
    printf("$line");
    printf( "= %-13s\t%-37s\t%s\n", "key", "cipher name", "alias names" );
    return;
}

sub show_ssltest {
    _v_print( ( caller(0) )[3] );
    my $last_k = "";
    foreach my $key ( sort { $ciphers{$a}->{ssl} cmp $ciphers{$b}->{ssl} || $ciphers{$a}->{names} cmp $ciphers{$b}->{names} } keys %ciphers ) {
        if ( $last_k ne $ciphers{$key}->{ssl} ) {
            $last_k = $ciphers{$key}->{ssl};
            printf( "%s Ciphers Supported...\n", $ciphers{$key}->{ssl} );
        }
        my $name = $ciphers{$key}->{'names'}[0];
        my $auth = $ciphers{$key}->{auth};
        $auth = "No" if ( $auth =~ /none/i );
        my $keyx = $ciphers{$key}->{keyx};
        $keyx =~ s/[()]//g;
        my $bits = $ciphers{$key}->{bits};
        if ( $bits =~ m/\d+/ ) {
            $bits = sprintf( "%03d", $ciphers{$key}->{bits} );
        }
        else {
            $bits = "-?-";
            $bits = "000" if ( $ciphers{$key}->{enc} =~ m/None/i );
        }
        printf( "   %s, %s %s bits, %s Auth, %s MAC, %s Kx\n", $name, $ciphers{$key}->{enc}, $bits, $auth, $ciphers{$key}->{mac}, $keyx );
    }
    return;
}

sub show_ciphers {

    my $format = shift;
    _v_print( ( caller(0) )[3] );
    local $\ = "\n";
    if ( $format !~ m/(?:dump|full|osaft|openssl|simple|show)/ ) {
        OCfg::warn("520: unknown format '$format'");
        return;
    }

    my $out_header = 1;
    my $txt_head   = "";
    if ( $format eq "openssl" ) {
        print join( ":", get_names_list() );
        return;
    }
    if ( $format =~ m/openssl/ ) {
        print << "EoT";
= Output is similar (order of columns) but not identical to result of
= 'openssl ciphers -[vV]' command.
EoT
        $out_header = 0;
        $txt_head   = "";
    }
    else {
        my $idx = 0;
        foreach ( @{ $ciphers_desc{head} } ) {
            my $txt = $ciphers_desc{ $ciphers_desc{head}[$idx] };
            $txt_head .= sprintf( "=      %-12s - %s\n", $ciphers_desc{head}[$idx], $txt );
            $idx++;
        }
    }

    my @columns = @{ $ciphers_desc{head} };
    @columns = qw(openssl sec ssl keyx auth enc bits mac rfc names const notes) if ( $format =~ m/^(?:dump|full|osaft)/ );
    @columns = qw(ssl keyx auth enc bits mac)                                   if ( $format =~ m/^(?:openssl)/ );
    @columns = qw(ssl keyx auth enc bits mac sec)                               if ( $format =~ m/^(?:show)/ );
    @columns = qw(sec ssl keyx auth enc bits mac)                               if ( $format =~ m/^(?:simple)/ );

    my $line = sprintf( "=%s\n", "-" x 77 );
    my $head = "";
    if ( $format =~ m/^(?:dump|full|osaft)/ ) {
        $line = sprintf( "=%9s%s%s\n",     "-" x 14, "+-------" x 9, "+---------------" x 3 );
        $head = sprintf( "= %-13s\t%9s\n", "key",    join( "\t", @columns ) );
    }
    if ( $format =~ m/^(?:show)/ ) {
        $line = sprintf( "=%s%s+%s\n",        "-" x 14, "+-------" x 7,         "-" x 15 );
        $head = sprintf( "= %-13s\t%s\t%s\n", "key",    join( "\t", @columns ), "cipher name" );
    }
    if ( $format =~ m/^(?:simple)/ ) {
        $head = sprintf( "= %-13s\t%s\t%s\n", "key", join( " ", @columns ), "cipher name" );
    }
    if ( $format =~ m/^openssl-V/ ) {
        $line = sprintf( "=%s+%s+%s+%s+%s+%s+%s\n", "-" x 19, "-" x 24, "-" x 5, "-" x 11, "-" x 7, "-" x 11, "-" x 7 );
        $head = sprintf( "=% 18s - %-23s %-5s %-11s %-7s %-11s %s\n", "key", "name", @columns[ 0 .. 2 ], "enc(bit)", "mac" );
    }
    if ( 0 < $out_header ) {
        printf( "%s", << "EoT");

=== internal %ciphers data ===
=
= Show a full overview of all available ciphers.
=
=   description of columns (if available):
=      key          - internal hex key for cipher suite
=      hex          - hex key for cipher suite (like openssl)
=      cipher name  - OpenSSL suite name
$txt_head
EoT
        printf($line);
        printf($head);
        printf($line);
    }

    my $cnt = 0;
    foreach my $key ( sort keys %ciphers ) {
        $cnt++;
        my $hex   = key2text($key);
        my $name  = $ciphers{$key}->{'names'}[0];
        my $const = $ciphers{$key}->{'const'}[0];
        my $note  = $ciphers{$key}->{'notes'}[0];
        my @values;
        if ( $format =~ m/^(?:dump|full|osaft)/ ) {
            push( @values, $key );
            push( @values, $ciphers{$key}->{$_} ) foreach @columns[ 0 .. 8 ];
            push( @values, join( ",", @{ $ciphers{$key}->{names} } ) );
            push( @values, join( ",", @{ $ciphers{$key}->{const} } ) );
            push( @values, join( ",", @{ $ciphers{$key}->{notes} } ) );
            printf( "%s\n", join( "\t", @values ) );
        }
        if ( $format =~ m/^(?:show)/ ) {
            push( @values, $key );
            push( @values, $ciphers{$key}->{$_} ) foreach @columns;
            push( @values, $name );
            printf( "%s\n", join( "\t", @values ) );
        }
        if ( $format =~ m/^(?:simple)/ ) {
            push( @values, $ciphers{$key}->{$_} ) foreach @columns;
            push( @values, $name );
            printf( "%s\t%s\n", $key, join( " ", @values ) );
        }
        if ( $format =~ m/^(?:openssl-v)/ ) {
            push( @values, $name );
            push( @values, $ciphers{$key}->{$_} ) foreach @columns;
            printf( "%-23s %-6s Kx=%-8s Au=%-4s Enc=%s(%s) Mac=%s\n", @values );
        }
        if ( $format =~ m/^(?:openssl-V)/ ) {
            push( @values, $hex, $name );
            push( @values, $ciphers{$key}->{$_} ) foreach @columns;
            printf( "%19s - %-23s %-6s Kx=%-8s Au=%-4s Enc=%s(%s) Mac=%s\n", @values );
        }
    }

    if ( 0 < $out_header ) {
        printf($line);
        printf($head);
        printf( "=\n= %s ciphers\n", $cnt );
    }
    return;
}

sub show {

    my $arg = shift;
    $arg =~ s/^\+?test[._-]?ciphers[._-]?//;
    _v_print( ( caller(0) )[3] );
    local $\ = "\n";
    print "$VERSION"        if ( $arg =~ /version$/i );
    show_all_names('const') if ( $arg =~ m/const(?:ants?)?$/ );
    show_all_names('names') if ( $arg =~ m/alias(?:es)?$/ );
    show_all_names('rfc')   if ( $arg =~ m/rfcs?$/ );
    show_description()      if ( $arg eq 'description' );
    show_description()      if ( $arg =~ m/^ciphers.?description/ );
    show_overview()         if ( $arg eq 'overview' );
    show_ssltest()          if ( $arg eq 'ssltest' );
    show_sorted()           if ( $arg =~ m/^(owasp|sort(?:ed)?$)/ );
    show_ciphers('simple')  if ( $arg eq 'list' );
    show_ciphers($1) if ( $arg =~ m/^(show|simple)$/ );
    show_ciphers($1) if ( $arg =~ m/^(dump|full|osaft|openssl(?:-[vV])?)$/ );
    show_getter($1)  if ( $arg =~ m/^getter=?(.*)/ );
    print is_typ( 'ADH', $1 ) if ( $arg =~ m/^is.?adh=(.*)/ );
    print is_typ( 'CBC', $1 ) if ( $arg =~ m/^is.?cbc=(.*)/ );
    print is_typ( 'DES', $1 ) if ( $arg =~ m/^is.?des=(.*)/ );
    print is_typ( 'EDH', $1 ) if ( $arg =~ m/^is.?edh=(.*)/ );
    print is_typ( 'EXP', $1 ) if ( $arg =~ m/^is.?exp=(.*)/ );
    print is_typ( 'FRZ', $1 ) if ( $arg =~ m/^is.?frz=(.*)/ );
    print is_typ( 'RC4', $1 ) if ( $arg =~ m/^is.?rc4=(.*)/ );
    print "** please use any of: is_adh, is_cbc, is_des, is_edh, is_exp, is_frz, is_rc4"
      if ( $arg =~ m/^is.?typ=(.*)/ );
    print is_valid_key($1) if ( $arg =~ m/^is.?valid.?key=(.*)/ );
    print text2key($1)     if ( $arg =~ m/^text2key=(.*)/ );
    print key2text($1)     if ( $arg =~ m/^key2text=(.*)/ );
    print get_key($1)      if ( $arg =~ m/^(?:get.)?key=(.*)/ );
    print get_sec($1)      if ( $arg =~ m/^(?:get.)?sec=(.*)/ );
    print get_ssl($1)      if ( $arg =~ m/^(?:get.)?ssl=(.*)/ );
    print get_keyx($1)     if ( $arg =~ m/^(?:get.)?keyx=(.*)/ );
    print get_auth($1)     if ( $arg =~ m/^(?:get.)?auth=(.*)/ );
    print get_enc($1)      if ( $arg =~ m/^(?:get.)?enc=(.*)/ );
    print get_bits($1)     if ( $arg =~ m/^(?:get.)?bits=(.*)/ );
    print get_mac($1)      if ( $arg =~ m/^(?:get.)?mac=(.*)/ );
    print get_rfc($1)      if ( $arg =~ m/^(?:get.)?rfc=(.*)/ );
    print get_name($1)     if ( $arg =~ m/^(?:get.)?name=(.*)/ );
    print get_const($1)    if ( $arg =~ m/^(?:get.)?const=(.*)/ );
    print get_note($1)     if ( $arg =~ m/^(?:get.)?note=(.*)/ );
    print get_openssl($1)  if ( $arg =~ m/^(?:get.)?openssl=(.*)/ );
    print get_encsize($1)  if ( $arg =~ m/^(?:get.)?encsize=(.*)/ );
    print get_iana($1)     if ( $arg =~ m/^(?:get.)?iana=(.*)/ );
    print get_pfs($1)      if ( $arg =~ m/^(?:get.)?pfs=(.*)/ );
    print find_name($1)    if ( $arg =~ m/^find.?name=(.*)/ );
    print join( " ", find_names_any($1) ) if ( $arg =~ m/^find.?names.?any=(.*)/ );
    print join( " ", find_keys_any($1) )  if ( $arg =~ m/^find.?keys.?any=(.*)/ );
    print join( " ", find_names($1) )     if ( $arg =~ m/^find.?names=(.*)/ );
    print join( " ", find_keys($1) )      if ( $arg =~ m/^find.?keys=(.*)/ );
    print join( " ", find_consts($1) )    if ( $arg =~ m/^find.?consts=(.*)/ );
    print join( " ", get_names($1) )      if ( $arg =~ m/^(?:get.?)?names=(.*)/ );
    print join( " ", get_aliases($1) )    if ( $arg =~ m/^(?:get.?)?aliases=(.*)/ );
    print join( " ", get_consts($1) )     if ( $arg =~ m/^(?:get.?)?consts=(.*)/ );
    print join( " ", get_notes($1) )      if ( $arg =~ m/^(?:get.?)?notes=(.*)/ );
    print join( " ", get_keys_list() )    if ( $arg =~ m/^(?:get.?)?keys.?list$/ );
    print join( " ", get_names_list() )   if ( $arg =~ m/^(?:get.?)?names.?list$/ );

    if ( $arg =~ m/^regex/ ) {
        printf("#$0: direct testing not yet possible here, please try:\n   o-saft.pl +test-ciphers-regex\n");
    }
    return;
}

sub _ciphers_init {
    my $fh   = *DATA;
    my $dumm = *DATA;
    if ( 0 < $::osaft_standalone ) {
        open( $fh, "<", $0 ) or warn( $OText::STR{ERROR}, "013: open '$0' failed with: $!" );
        while (<$fh>) { last if m(^__DATA__); }
    }
    while ( my $line = <$fh> ) {
        chomp $line;
        next if ( $line =~ m/^\s*$/ );
        next if ( $line =~ m/^\s*#/ );
        last if ( $line =~ m/__END/ );
        my @fields = split( /\t/, $line );
        my $len    = $#fields;
        my $key    = $fields[0];
        if ( $key !~ /^0x[0-9A-F]{8}/ ) {
            OCfg::warn( "504: DATA line" . sprintf( "%4d", $. ) . ": wrong hex key '$key'" );
            next;
        }
        if ( 13 != $len + 1 ) {
            OCfg::warn( "505: DATA line" . sprintf( "%4d", $. ) . ": wrong number of TAB-separated fields '$len' != 13" );
            next;
        }
        $ciphers{$key}->{'openssl'} = $fields[1] || '';
        $ciphers{$key}->{'sec'}     = $fields[2] || '';
        $ciphers{$key}->{'ssl'}     = $fields[3] || '';
        $ciphers{$key}->{'keyx'}    = $fields[4] || '';
        $ciphers{$key}->{'auth'}    = $fields[5] || '';
        $ciphers{$key}->{'enc'}     = $fields[6] || '';
        $ciphers{$key}->{'bits'}    = ( $fields[7] || '0 ' );
        $ciphers{$key}->{'mac'}     = $fields[8] || '';
        $ciphers{$key}->{'rfc'}     = $fields[9] || '';
        @{ $ciphers{$key}->{'names'} } = split( /,/, $fields[10] );
        @{ $ciphers{$key}->{'const'} } = split( /,/, $fields[11] );
        @{ $ciphers{$key}->{'notes'} } = split( /,/, $fields[12] );
    }
    push( @{ $OCfg::dbx{'files'} }, "lib/Ciphers.pm" );
    return;
}

sub _main {
    my @argv = @_;
    push( @argv, "--help" ) if ( 0 > $#argv );
    my %usage = (
        '# commands to show internal cipher tables' => {
            'alias'       => '',
            'const'       => '',
            'dump'        => '',
            'description' => '',
            'openssl'     => '',
            'rfc'         => '',
            'simple'      => '',
            'sort'        => '',
            'ssltest'     => '',
            'overview'    => '',
        },
        "# commands to check cipher type\n# (all following call 'is_typ(TYP,KEY)')" => {
            'is_adh=KEY' => 'check if cipher is ADH',
            'is_cbc=KEY' => 'check if cipher is CBC',
            'is_des=KEY' => 'check if cipher is DES',
            'is_edh=KEY' => 'check if cipher is EDH',
            'is_exp=KEY' => 'check if cipher is EXPORT',
            'is_rc4=KEY' => 'check if cipher is RC4',
        },
        '# commands to show cipher data' => {
            'getter=KEY'        => 'show example for get_*() functions',
            'key=CIPHER-NAME  ' => 'show (internal) cipher key if it is known',
            'is_valid_key=KEY'  => 'show (internal) cipher key if it is known',
        },
        '# commands to show cipher data (various examples)' => {
            'getter=0x0300CCA9'                        => '',
            'getter=0xCC,0xA9 '                        => '',
            'getter=0x03,0x00,0xCC,0xA9'               => '',
            'key=ECDHE-ECDSA-CHACHA20-POLY1305-SHA256' => '',
            'is_valid_key=0300Cca9'                    => '',
        },
        "## all commands can also be used as '+test-ciphers-CMD'" => {},
    );
    while ( my $arg = shift @argv ) {
        if ( $arg =~ m/^--?h(?:elp)?$/ ) { OText::print_pod( $0, __FILE__, $SID_ciphers ); exit 0; }
        if ( $arg eq '--usage' )         { OText::usage_show( "", \%usage );               exit 0; }
        if ( $arg eq '--trace' ) { $OCfg::cfg{'trace'}++;   next; }
        if ( $arg eq '--v' )     { $OCfg::cfg{'verbose'}++; next; }
        if ( $arg =~ /^version$/ )         { print "$SID_ciphers\n"; next; }
        if ( $arg =~ /^[-+]?V(ERSION)?$/ ) { $arg = "test-ciphers-version"; }
        $arg =~ s/^[+]//;
        $arg =~ s/^test.?ciphers[_.-]?//;
        show("+test-ciphers$arg");
    }
    exit 0;
}

sub done { }

_ciphers_init();


package error_handler;

my $SID_error = "@(#) error_handler.pm 3.10 24/08/18 12:45:46";
our $VERSION = "24.01.24";

our %OERR = (
    'NO_ERROR'     => 1,
    'UNKNOWN_TYPE' => -9999,

    'UNDEFINED_TXT' => "<<undefined>>",
    'UNKNOWN_TXT'   => "<<unknown>>",

    'SSLHELLO_ABORT_PROGRAM'          => -9000,
    'SSLHELLO_ABORT_HOST'             => -99,
    'SSLHELLO_RETRY_HOST'             => -94,
    'SSLHELLO_ABORT_PROTOCOL'         => -89,
    'SSLHELLO_RETRY_PROTOCOL'         => -84,
    'SSLHELLO_ABORT_CIPHERS'          => -79,
    'SSLHELLO_RETRY_CIPHERS'          => -74,
    'SSLHELLO_ABORT_EXTENSIONS'       => -69,
    'SSLHELLO_RETRY_EXTENSIONS'       => -64,
    'SSLHELLO_TEST_EXTENSIONS'        => -59,
    'SSLHELLO_RETRY_RECORD'           => -49,
    'SSLHELLO_MERGE_RECORD_FRAGMENTS' => -39,
    'SSLHELLO_MERGE_DTLS'             => -29,
    'SSLHELLO_ERROR_MESSAGE_IGNORED'  => -1,
);

my %OERR_map = reverse %OERR;

our @EXPORT_OK = (
    qw( new is_err reset_err get_err_str get_err_val get_err_type
      get_err_type _name get_err_hash get_all_err_types version
      %OERR
    )
);

our %EXPORT_TAGS = (
    subs => [
        qw(new is_err reset_err get_err_str get_err_val get_err_type
          get_err_type_name get_err_hash get_all_err_types
        )
    ],
);

my %err_hash = (
    type    => $OERR{'NO_ERROR'},
    module  => "",
    sub     => $OERR{'UNDEFINED_TXT'},
    id      => "",
    message => $OERR{'UNDEFINED_TXT'},
    print   => 0,
    warn    => 0,
    trace   => 1,
);

sub version { return $SID_error; }

sub _compile_err_str {

    my ($arg_ref) = @_;
    unless ( defined($arg_ref) && ($arg_ref) ) {
        $arg_ref = \%err_hash;
    }
    elsif ( $err_hash{trace} ) {
        print "    \$arg_ref defined: $arg_ref\n";
    }

    my $err_str = "";
    $err_str = $arg_ref->{module} if ( ( exists( $arg_ref->{module} ) ) && ( defined( $arg_ref->{module} ) ) );
    $err_str .= "::" . $arg_ref->{sub}       if ( ( exists( $arg_ref->{sub} ) )     && ( defined( $arg_ref->{sub} ) ) );
    $err_str .= " (" . $arg_ref->{id} . "):" if ( ( exists( $arg_ref->{id} ) )      && ( defined( $arg_ref->{id} ) ) );
    $err_str .= " " . $arg_ref->{message}    if ( ( exists( $arg_ref->{message} ) ) && ( defined( $arg_ref->{message} ) ) );
    if ( ( exists( $arg_ref->{type} ) ) && ( defined( $arg_ref->{type} ) ) ) {

        if ( ( exists( $OERR_map{ $arg_ref->{type} } ) ) && ( defined( $OERR_map{ $arg_ref->{type} } ) ) ) {
            if ( ( exists( $arg_ref->{trace} ) ) && ( 0 < $arg_ref->{trace} ) ) {
                $err_str .= " [Type=" . $OERR_map{ $arg_ref->{type} };
                $err_str .= "(" . $arg_ref->{type} . ")" if ( 2 < $arg_ref->{trace} );
                $err_str .= "]";
            }
        }
        else {
            $err_str .= " [Type= $OERR{'UNKNOWN_TXT'} (" . $arg_ref->{type} . ")]";
        }
    }
    else {
        $err_str .= " [Type=$OERR{'UNDEFINED_TXT'}]";
    }
    return ($err_str);
}

sub new {
    my ( $class, $arg_ref ) = @_;
    my $tmp_err_str = "";
    my $tmp_text    = "";

    unless ( ( exists( $OERR_map{ $err_hash{type} } ) ) && ( defined( $OERR_map{ $err_hash{type} } ) ) ) {
        $tmp_err_str = _compile_err_str();
        $tmp_text    = "error_handler::new: internal error: unknown error type in";
        print qq($tmp_text "$tmp_err_str") if ( $err_hash{trace} );
        Carp::carp(qq($tmp_text "$tmp_err_str"));
        $err_hash{type} = $OERR{'UNKNOWN_TYPE'};
    }
    else {
        unless ( defined($arg_ref) ) {
            $arg_ref->{type}    = $OERR{'UNKNOWN_TYPE'};
            $arg_ref->{module}  = 'error_handler';
            $arg_ref->{sub}     = 'new';
            $arg_ref->{message} = "internal error: undefined \$arg_ref";
            $tmp_err_str        = _compile_err_str($arg_ref);
            print "$tmp_err_str" if ( $err_hash{trace} );
            Carp::carp($tmp_err_str);
            return 0;
        }
        unless ( ( exists( $OERR_map{ $arg_ref->{type} } ) ) && ( defined( $OERR_map{ $arg_ref->{type} } ) ) ) {
            $tmp_err_str = _compile_err_str($arg_ref);
            print qq($tmp_text "$tmp_err_str".) if ( $err_hash{trace} );
            Carp::carp(qq($tmp_text "$tmp_err_str".));
            $arg_ref->{type} = $OERR{'UNKNOWN_TYPE'};
        }
        if ( $err_hash{type} < $arg_ref->{type} ) {
            my $old_err_str = _compile_err_str();
            $tmp_err_str = _compile_err_str($arg_ref);
            $tmp_text    = "error_handler::new: new error type in";
            print qq($tmp_text "$tmp_err_str" is less important than the previous "$old_err_str".) if ( $err_hash{trace} );
            Carp::carp(qq($tmp_text "$tmp_err_str" is less important than the previous "$old_err_str".));
            return 0;
        }
    }
    %err_hash = ( %err_hash, %$arg_ref ) if ($arg_ref);

    my $err_str = _compile_err_str();
    print "$err_str\n"   if ( $err_hash{print} );
    Carp::carp($err_str) if ( $err_hash{warn} );
    return 1;
}

sub reset_err {
    my ( $class, $arg_ref ) = @_;
    %err_hash = (
        type    => $OERR{'NO_ERROR'},
        module  => "",
        sub     => $OERR{'UNKNOWN_TXT'},
        id      => "",
        message => $OERR{'UNKNOWN_TXT'},
        print   => 0,
        warn    => 0,
        trace   => 1,
    );
    %err_hash = ( %err_hash, %$arg_ref ) if ($arg_ref);

    if ( 2 < $err_hash{trace} ) {
        my $err_str = _compile_err_str();
        print "$err_str\n";
    }
    return 1;
}

sub is_err {
    if ( ( exists( $err_hash{type} ) ) && ( defined( $err_hash{type} ) ) ) {
        return ( $err_hash{type} != $OERR{'NO_ERROR'} );
    }
    my $err_str = "error_handler::is_err: internal error: undefined error type in \$error_hash: ";
    $err_str .= _compile_err_str();
    print "$err_str\n" if ( 2 < $err_hash{trace} );
    Carp::carp($err_str);
    return (1);
}

sub get_err_type {
    if ( ( exists( $err_hash{type} ) ) && ( defined( $err_hash{type} ) ) ) {
        return ( $err_hash{type} );
    }
    print "Error type is " . $OERR{'UNDEFINED_TXT'} if ( $err_hash{trace} );
    Carp::carp( "Error type is " . $OERR{'UNDEFINED_TXT'} );
    return (undef);
}

sub get_err_type_name {
    if ( ( exists( $err_hash{type} ) ) && ( defined( $err_hash{type} ) ) ) {
        return ( $OERR_map{ $err_hash{type} } ) if ( ( exists( $OERR_map{ $err_hash{type} } ) ) && ( defined( $OERR_map{ $err_hash{type} } ) ) );
    }
    print "Error type is " . $OERR{'UNDEFINED_TXT'} if ( $err_hash{trace} );
    Carp::carp( "Error type is " . $OERR{'UNDEFINED_TXT'} );
    return ( $OERR{'UNDEFINED_TXT'} );
}

sub get_err_val {
    my ( $class, $key_arg ) = @_;
    return ( $err_hash{$key_arg} ) if ( ( exists( $err_hash{$key_arg} ) ) && ( defined( $err_hash{$key_arg} ) ) );
    return;
}

sub get_err_str {
    unless ( ( exists( $OERR_map{ $err_hash{type} } ) ) && ( defined( $OERR_map{ $err_hash{type} } ) ) ) {
        my $tmp_err_str = _compile_err_str();
        my $tmp_text    = "error_handler::get_err_str: internal error: unknown error type in";
        print qq($tmp_text "$tmp_err_str".\n) if ( $err_hash{trace} );
        Carp::carp(qq($tmp_text "$tmp_err_str".\n));
        $err_hash{type} = $OERR{'UNKNOWN_TYPE'};
    }
    return ( _compile_err_str() );
}

sub get_err_hash {
    my ( $class, $prefix, $hash_ref ) = @_;
    my $err_hash_str = "";
    $prefix   = ""         if ( not defined($prefix) );
    $hash_ref = \%err_hash if ( not defined($hash_ref) );
    print ">get_err_hash\n" if ( 2 < $err_hash{trace} );
    foreach my $err_key ( sort ( keys(%$hash_ref) ) ) {
        $err_hash_str .= $prefix if ( $err_hash_str !~ /^$/x );
        $err_hash_str .= "\$hash->\{$err_key\} => " . $hash_ref->{$err_key} . "\n";
    }
    return ($err_hash_str);
}

sub get_all_err_types {
    my ( $class, $prefix ) = @_;
    my $err_types_str = "";
    print ">get_all_err_types\n" if ( $err_hash{trace} );
    foreach my $key ( sort { $a <=> $b } ( keys(%OERR) ) ) {
        $err_types_str .= $prefix if ( $err_types_str !~ /^$/x );
        $err_types_str .= "OERR\{$key\} => " . $OERR{$key} . "\n";
    }
    return ($err_types_str);
}

package SSLhello;

my $SID_sslhello = "@(#) SSLhello.pm 3.33 24/09/04 10:28:37";
our $VERSION = "24.09.24";
my $SSLHELLO = "SSLhello";

BEGIN {
    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##;
    my $_pwd = $ENV{PWD} || ".";
    unshift( @INC, $_path ) if ( 1 > ( grep { /^$_path$/ } @INC ) );
    unshift( @INC, $_pwd )  if ( 1 > ( grep { /^$_pwd$/ } @INC ) );
    unshift( @INC, "lib" )  if ( 1 > ( grep { /^lib$/ } @INC ) );
    unshift( @INC, "." )    if ( 1 > ( grep { /^\.$/ } @INC ) );
}

my %CST = (
    '_MY_SSL3_MAX_CIPHERS'                    => 64,
    '_MY_PRINT_CIPHERS_PER_LINE'              => 8,
    '_PROXY_CONNECT_MESSAGE1'                 => "CONNECT ",
    '_PROXY_CONNECT_MESSAGE2'                 => " HTTP/1.1\n\n",
    '_MAX_SEGMENT_COUNT_TO_RESET_RETRY_COUNT' => 16,
    '_SLEEP_B4_2ND_READ'                      => 0.5,
    '_DTLS_SLEEP_AFTER_FOUND_A_CIPHER'        => 0.75,
    '_DTLS_SLEEP_AFTER_NO_CIPHERS_FOUND'      => 0.05
);

$SSLhello::verbose               = 0;
$SSLhello::prefix_verbose        = '#' . __PACKAGE__ . ' ';
$SSLhello::prefix_trace          = '#' . __PACKAGE__ . '::';
$SSLhello::trace                 = 0;
$SSLhello::traceTIME             = 0;
$SSLhello::usesni                = 1;
$SSLhello::use_sni_name          = 0;
$SSLhello::sni_name              = "1";
$SSLhello::force_TLS_extensions  = 0;
$SSLhello::timeout               = 2;
$SSLhello::retry                 = 3;
$SSLhello::connect_delay         = 0;
$SSLhello::usereneg              = 0;
$SSLhello::use_signature_alg     = 1;
$SSLhello::useecc                = 1;
$SSLhello::useecpoint            = 1;
$SSLhello::starttls              = 0;
$SSLhello::starttlsType          = "SMTP";
$SSLhello::starttlsPhaseArray    = [];
$SSLhello::starttlsDelay         = 0;
$SSLhello::slowServerDelay       = 0;
$SSLhello::double_reneg          = 0;
$SSLhello::proxyhost             = "";
$SSLhello::proxyport             = "";
$SSLhello::experimental          = 0;
$SSLhello::max_ciphers           = $CST{'_MY_SSL3_MAX_CIPHERS'};
$SSLhello::max_sslHelloLen       = 16388;
$SSLhello::noDataEqNoCipher      = 1;
$SSLhello::extensions_by_prot    = \%{ $cfg{extensions_by_prot} };
$SSLhello::check_extensions      = [qw(supported_groups)];
$SSLhello::extensions_max_values = 50;
my $dumm = $SSLhello::prefix_trace;
$dumm = $SSLhello::prefix_verbose;

BEGIN {
    my $_me = $0;
    $_me =~ s#.*[/\\]##;
    if ( not exists &_trace ) {
        sub __ytime { my $now = 1; return ( 0 >= $SSLhello::traceTIME ) ? "" : sprintf( " [%02s:%02s:%02s]", ( localtime($now) )[ 2, 1, 0 ] ); }
        sub __y_me_ts   { return sprintf( "#%s%s:", $SSLHELLO, __ytime() ); }
        sub __trace     { my @txt = @_; printf( "%s %s", __y_me_ts(), "@txt" ); return; }
        sub _trace($)   { my @txt = @_; __trace( $txt[0] )    if ( $SSLhello::trace > 0 );  return; }
        sub _trace1($)  { my @txt = @_; __trace(@txt)         if ( $SSLhello::trace > 1 );  return; }
        sub _trace2($)  { my @txt = @_; __trace(@txt)         if ( $SSLhello::trace > 2 );  return; }
        sub _trace3($)  { my @txt = @_; __trace(@txt)         if ( $SSLhello::trace == 3 ); return; }
        sub _trace4($)  { my @txt = @_; __trace(@txt)         if ( $SSLhello::trace > 3 );  return; }
        sub _trace5($)  { my @txt = @_; __trace(@txt)         if ( $SSLhello::trace > 4 );  return; }
        sub _trace_($)  { my @txt = @_; printf( " %s", @txt ) if ( $SSLhello::trace > 0 );  return; }
        sub _trace1_($) { my @txt = @_; printf( "%s", @txt )  if ( $SSLhello::trace > 1 );  return; }
        sub _trace2_($) { my @txt = @_; printf( "%s", @txt )  if ( $SSLhello::trace > 2 );  return; }
        sub _trace3_($) { my @txt = @_; printf( "%s", @txt )  if ( $SSLhello::trace == 3 ); return; }
        sub _trace4_($) { my @txt = @_; printf( "%s", @txt )  if ( $SSLhello::trace > 3 );  return; }
        sub _trace5_($) { my @txt = @_; printf( "%s", @txt )  if ( $SSLhello::trace > 4 );  return; }
    }
}


our @EXPORT_OK = qw(
  net_sslhello_done
  checkSSLciphers
  getSSLciphersWithParam
  openTcpSSLconnection
  printCipherStringArray
  printParameters
  version
);

our $dtlsEpoch = 0;
our %_SSLhello;
our %resultHash;
our %extensions_params_hash;
our $my_error = "";

my %RECORD_TYPE = (
    'change_cipher_spec' => 20,
    'alert'              => 21,
    'handshake'          => 22,
    'application_data'   => 23,
    'heartbeat'          => 24,
    '255'                => 255,
    '<<undefined>>'      => -1
);

my %HANDSHAKE_TYPE = (
    'hello_request'          => 0,
    'client_hello'           => 1,
    'server_hello'           => 2,
    'hello_verify_request'   => 3,
    'certificate'            => 11,
    'server_key_exchange'    => 12,
    'certificate_request'    => 13,
    'server_hello_done'      => 14,
    'certificate_verify'     => 15,
    'client_key_exchange'    => 16,
    'finished'               => 20,
    '255'                    => 255,
    '<<undefined>>'          => -1,
    '<<fragmented_message>>' => -99
);

my %PROTOCOL_VERSION = (
    'SSLv2'      => 0x0002,
    'SSLv3'      => 0x0300,
    'TLSv1'      => 0x0301,
    'TLSv11'     => 0x0302,
    'TLSv12'     => 0x0303,
    'TLSv13'     => 0x0304,
    'TLSv1.FF'   => 0x03FF,
    'DTLSv09'    => 0x0100,
    'DTLSfamily' => 0xFE00,
    'DTLSv1'     => 0xFEFF,
    'DTLSv11'    => 0xFEFE,
    'DTLSv12'    => 0xFEFD,
    'DTLSv13'    => 0xFEFC,
    'SCSV'       => 0x03FF
);

my %PROTOCOL_NAME_BY_HEX = reverse %PROTOCOL_VERSION;

my %TLS_AlertDescription = (
    0   => [qw(close_notify  Y  [RFC5246])],
    10  => [qw(unexpected_message  Y  [RFC5246])],
    20  => [qw(bad_record_mac  Y  [RFC5246])],
    21  => [qw(decryption_failed  Y  [RFC5246])],
    22  => [qw(record_overflow  Y  [RFC5246])],
    30  => [qw(decompression_failure  Y  [RFC5246])],
    40  => [qw(handshake_failure  Y  [RFC5246])],
    41  => [qw(no_certificate_RESERVED  Y  [RFC5246])],
    42  => [qw(bad_certificate  Y  [RFC5246])],
    43  => [qw(unsupported_certificate  Y  [RFC5246])],
    44  => [qw(certificate_revoked  Y  [RFC5246])],
    45  => [qw(certificate_expired  Y  [RFC5246])],
    46  => [qw(certificate_unknown  Y  [RFC5246])],
    47  => [qw(illegal_parameter  Y  [RFC5246])],
    48  => [qw(unknown_ca  Y  [RFC5246])],
    49  => [qw(access_denied  Y  [RFC5246])],
    50  => [qw(decode_error  Y  [RFC5246])],
    51  => [qw(decrypt_error  Y  [RFC5246])],
    60  => [qw(export_restriction_RESERVED  Y  [RFC5246])],
    70  => [qw(protocol_version  Y  [RFC5246])],
    71  => [qw(insufficient_security  Y  [RFC5246])],
    80  => [qw(internal_error  Y  [RFC5246])],
    86  => [qw(inappropriate_fallback  Y  [RFC5246_update-Draft-2014-05-31])],
    90  => [qw(user_canceled  Y  [RFC5246])],
    100 => [qw(no_renegotiation  Y  [RFC5246])],
    109 => [qw(missing_extension Y [RFC8446])],
    110 => [qw(unsupported_extension  Y  [RFC5246])],
    111 => [qw(certificate_unobtainable  Y  [RFC6066])],
    112 => [qw(unrecognized_name  Y  [RFC6066])],
    113 => [qw(bad_certificate_status_response  Y  [RFC6066])],
    114 => [qw(bad_certificate_hash_value  Y  [RFC6066])],
    115 => [qw(unknown_psk_identity  Y  [RFC4279])],
    116 => [qw(certificate_required  Y   [RFC8446])],
    120 => [qw(no_application_protocol Y [RFC7301][RFC8447])],
);

my %ECCURVE_TYPE = (
    'explicit_prime' => 1,
    'explicit_char2' => 2,
    'named_curve'    => 3,
    'reserved_248'   => 248,
    'reserved_249'   => 249,
    'reserved_250'   => 250,
    'reserved_251'   => 251,
    'reserved_252'   => 252,
    'reserved_253'   => 253,
    'reserved_254'   => 254,
    'reserved_255'   => 255,
);

my %ECC_NAMED_CURVE = (
    0  => [qw(Reverved_0              0 N   N   [RFC8447])],
    1  => [qw(sect163k1             163 Y   N   [RFC4492])],
    2  => [qw(sect163r1             163 Y   N   [RFC4492])],
    3  => [qw(sect163r2             163 Y   N   [RFC4492])],
    4  => [qw(sect193r1             193 Y   N   [RFC4492])],
    5  => [qw(sect193r2             193 Y   N   [RFC4492])],
    6  => [qw(sect233k1             233 Y   N   [RFC4492])],
    7  => [qw(sect233r1             233 Y   N   [RFC4492])],
    8  => [qw(sect239k1             239 Y   N   [RFC4492])],
    9  => [qw(sect283k1             283 Y   N   [RFC4492])],
    10 => [qw(sect283r1             283 Y   N   [RFC4492])],
    11 => [qw(sect409k1             409 Y   N   [RFC4492])],
    12 => [qw(sect409r1             409 Y   N   [RFC4492])],
    13 => [qw(sect571k1             571 Y   N   [RFC4492])],
    14 => [qw(sect571r1             571 Y   N   [RFC4492])],
    15 => [qw(secp160k1             160 Y   N   [RFC4492])],
    16 => [qw(secp160r1             160 Y   N   [RFC4492])],
    17 => [qw(secp160r2             160 Y   N   [RFC4492])],
    18 => [qw(secp192k1             192 Y   N   [RFC4492])],
    19 => [qw(secp192r1             192 Y   N   [RFC4492])],
    20 => [qw(secp224k1             224 Y   N   [RFC4492])],
    21 => [qw(secp224r1             224 Y   N   [RFC4492])],
    22 => [qw(secp256k1             256 Y   N   [RFC4492])],
    23 => [qw(secp256r1             256 Y   Y   [RFC4492])],
    24 => [qw(secp384r1             384 Y   Y   [RFC4492])],
    25 => [qw(secp521r1             521 Y   N   [RFC4492])],
    26 => [qw(brainpoolP256r1       256 Y   Y   [RFC7027])],
    27 => [qw(brainpoolP384r1       384 Y   Y   [RFC7027])],
    28 => [qw(brainpoolP512r1       512 Y   Y   [RFC7027])],
    29 => [qw(x25519                255 Y   Y   [RFC8446][RFC8422])],
    30 => [qw(x448                  448 Y   Y   [RFC8446][RFC8422])],
    31 => [qw(brainpoolP256r1tls13  256 Y   N   [RFC8734])],
    32 => [qw(brainpoolP384r1tls13  384 Y   N   [RFC8734])],
    33 => [qw(brainpoolP512r1tls13  512 Y   N   [RFC8734])],
    34 => [qw(GC256A                256 Y   N   [draft-smyshlyaev-tls12-gost-suites])],
    35 => [qw(GC256B                256 Y   N   [draft-smyshlyaev-tls12-gost-suites])],
    36 => [qw(GC256C                256 Y   N   [draft-smyshlyaev-tls12-gost-suites])],
    37 => [qw(GC256D                256 Y   N   [draft-smyshlyaev-tls12-gost-suites])],
    38 => [qw(GC512A                512 Y   N   [draft-smyshlyaev-tls12-gost-suites])],
    39 => [qw(GC512B                512 Y   N   [draft-smyshlyaev-tls12-gost-suites])],
    40 => [qw(GC512C                512 Y   N   [draft-smyshlyaev-tls12-gost-suites])],
    41 => [qw(curveSM2              256 N   N   [draft-yang-tls-tls13-sm-suites])],
    256 => [qw(ffdhe2048            2048 Y   N   [RFC7919])],
    257 => [qw(ffdhe3072            3072 Y   N   [RFC7919])],
    258 => [qw(ffdhe4096            4096 Y   N   [RFC7919])],
    259 => [qw(ffdhe6144            6144 Y   N   [RFC7919])],
    260 => [qw(ffdhe8192            8192 Y   N   [RFC7919])],
    508 => [qw(Private_508             NN Y   N   [RFC7919])],
    509 => [qw(Private_509             NN Y   N   [RFC7919])],
    510 => [qw(Private_510             NN Y   N   [RFC7919])],
    511 => [qw(Private_511             NN Y   N   [RFC7919])],
    2570 => [qw(Reserved_2570          NN Y   N   [RFC8701])],
    6682 => [qw(Reserved_6682          NN Y   N   [RFC8701])],
    10794 => [qw(Reserved_10794         NN Y   N   [RFC8701])],
    14906 => [qw(Reserved_14906         NN Y   N   [RFC8701])],
    19018 => [qw(Reserved_19018         NN Y   N   [RFC8701])],
    23130 => [qw(Reserved_23130         NN Y   N   [RFC8701])],
    27242 => [qw(Reserved_27242         NN Y   N   [RFC8701])],
    31354 => [qw(Reserved_31354         NN Y   N   [RFC8701])],
    35466 => [qw(Reserved_35466         NN Y   N   [RFC8701])],
    39578 => [qw(Reserved_39578         NN Y   N   [RFC8701])],
    43690 => [qw(Reserved_43690         NN Y   N   [RFC8701])],
    47802 => [qw(Reserved_47802         NN Y   N   [RFC8701])],
    51914 => [qw(Reserved_51914         NN Y   N   [RFC8701])],
    56026 => [qw(Reserved_56026         NN Y   N   [RFC8701])],
    60138 => [qw(Reserved_60138         NN Y   N   [RFC8701])],
    64250 => [qw(Reserved_64250         NN Y   N   [RFC8701])],
    65281 => [qw(arbitrary_explicit_prime_curves  -variable- Y    N   [RFC8422])],
    65282 => [qw(arbitrary_explicit_char2_curves  -variable- Y    N   [RFC8422])],
);

sub checkSSLciphers ($$$@);
sub printCipherStringArray ($$$$$@);
sub _timedOut;
sub _error;
sub _compileAllBytes ($$$$$$;$$);
sub _decode_val ($$$;$$$$$$);
sub _sprintf_hex_val ($$;$);

sub _trace_array2str {
    my @arr = @_;
    my $str = "";
    my $i   = 0;
    foreach my $item (@arr) {
        $str .= "\n  " if ( ( $i++ ) % $CST{'_MY_PRINT_CIPHERS_PER_LINE'} == 0 );
        $str .= " >$item<";
    }
    return $str;
}

sub _trace_cipher_array {
    my ( $suffix, @ciphers ) = @_;
    my $i = 0;
    return if ( 0 >= $SSLhello::trace );
    if ( 1 == $SSLhello::trace ) {
        printf(" [ @ciphers ]$suffix\n");
        return;
    }
    printf( "%s%s\n", _trace_array2str(@ciphers), $suffix );
    return;
}

sub _sprintf_hex_val ($$;$) {
    my $_format        = shift;
    my $_val_ref       = shift;
    my $_indent        = shift || 0;
    my $_hex_str       = "";
    my $_format_string = $_format || $OText::STR{'UNDEF'};

    _trace5_( " " x $_indent . "#   ---> _sprintf_hex_val: \$_format: '$_format_string' -> " );
    if ( !defined($_format) ) {
        if ( !defined($_val_ref) ) {
            _trace5("$OText::STR{'UNDEF'}\n");
            return ( $OText::STR{'UNDEF'} );
        }
        _trace5_( "if (\$\$_val_ref =~ /\^\\d+\$/); defined (\$\$_val_ref) = " . defined($$_val_ref) . " -> " );
        if ( !defined($$_val_ref) ) {
            _trace5_("'' (empty value)\n");
            return ("");
        }
        if ( $$_val_ref =~ /^\d+$/ ) {
            _trace5_("number (auto) -> ");
            if ( $$_val_ref <= 0xFF ) {
                $_format = "%02X";
            }
            elsif ( $$_val_ref <= 0xFFFF ) {
                $_format = "%04X";
            }
            elsif ( $$_val_ref <= 0xFFFFFFFF ) {
                $_format = "%08X";
            }
            else {
                $_format = "%016X";
            }
        }
        else {
            $_format = "";
        }
    }
    if ( $_format ne "" ) {
        _trace5_("formated string: ");
        $_hex_str = sprintf( $_format, $$_val_ref );
        $_hex_str =~ s/[0-9A-Fa-f]{2}/"$& "/eigx;
    }
    else {
        _trace5_("val: unformated string: ");
        $_hex_str = sprintf( "%*v2.2x", ' ', $$_val_ref );
    }
    $_hex_str =~ s/\s*$//;
    $_hex_str =~ s/((?:[0-9A-Fa-f]{2}\s){16})(?=[0-9A-Fa-f]{2})/"$&\n"." " x $_indent/eigx;
    _trace5_("$_hex_str\n");
    return ($_hex_str);
}

sub _sprintf_val_description ($$;$$) {
    my $_def_hash_ref = shift;
    my $_val_ref      = shift;
    my $_indent       = shift || 0;
    my $_descr_sep    = shift || " ";
    my $_descr_str    = "";
    my $_text_sep     = ": ";
    if ( $SSLhello::trace >= 5 ) {
        my $_val_ref_print =
          ( defined($_val_ref) )
          ? ref($_val_ref) . ": " . _sprintf_hex_val( undef, $_val_ref, $_indent )
          : $OText::STR{'UNDEF'};
        my $_def_hash_ref_print = ( defined($_def_hash_ref) ) ? ref($_def_hash_ref) : $OText::STR{'UNDEF'};
        if ( ( defined($_def_hash_ref) ) && ( ref($_def_hash_ref) eq "HASH" ) ) {
            $_def_hash_ref_print .= ": ->{FORMAT}: ";
            $_def_hash_ref_print .= ( defined( $_def_hash_ref->{FORMAT} ) ) ? "defined" : $OText::STR{'UNDEF'};
            if ( defined($_val_ref) ) {
                $_def_hash_ref_print .= ", ->{$$_val_ref}: ";
                $_def_hash_ref_print .= ( defined( $_def_hash_ref->{$$_val_ref} ) ) ? "defined" : $OText::STR{'UNDEF'};
            }
        }
        print " " x $_indent . "#   ---> _sprintf_val_description: (\$_val_ref = <<$_val_ref_print>>, \$_def_hash_ref = <<$_def_hash_ref_print>>)\n";
    }
    return ("") if ( !defined($_def_hash_ref) );
    return ("") if ( !defined($_val_ref) );
    if ( ref($_def_hash_ref) eq "HASH" ) {

        if ( defined( $_def_hash_ref->{FORMAT} ) ) {
            if ( defined( $_def_hash_ref->{$$_val_ref} ) ) {
                if ( ref( $_def_hash_ref->{FORMAT} ) eq "ARRAY" ) {
                    _trace5_( " " x $_indent . "#   ---> add " . ( @{ $_def_hash_ref->{FORMAT} } ) . " description(s)\n" );
                    $_descr_str .= $_text_sep;
                    for ( my $_j = 0 ; $_j < ( @{ $_def_hash_ref->{FORMAT} } ) ; $_j++ ) {
                        $_descr_str .= $_descr_sep if ( $_j >= 1 );
                        _trace5_( " " x $_indent . "#   ---> \$_descr_str .= sprintf \($_def_hash_ref->{FORMAT}[$_j], $_def_hash_ref->{$$_val_ref}[$_j]\)\n" );
                        $_descr_str .= sprintf( $_def_hash_ref->{FORMAT}[$_j], $_def_hash_ref->{$$_val_ref}[$_j] )
                          if ( defined( $_def_hash_ref->{$$_val_ref}[$_j] ) );
                    }
                }
            }
        }
    }
    elsif ( ref($_def_hash_ref) eq "SCALAR" ) {
        $_descr_str .= $_text_sep . $$_def_hash_ref;
    }
    elsif ( ref( \$_def_hash_ref ) eq "SCALAR" ) {
        $_descr_str .= $_text_sep . $_def_hash_ref;
    }
    _trace5_( " " x ($_indent) . "# ---> _sprintf_val_description: \$_descr_str = '$_descr_str'\n" );
    return ($_descr_str);
}

sub _decode_val ($$$;$$$$$$) {
    my $_format        = shift;
    my $_val_ref       = shift;
    my $_def_hash_ref  = shift;
    my $_first_indent  = shift || 0;
    my $_next_indent   = shift || 0;
    my $_text_sep      = shift || ":\n" . " " x $_next_indent;
    my $_sub_sep       = shift || ", ";
    my $_sub_sub_sep   = shift || " | ";
    my $_sub3_sep      = shift || " / ";
    my $_sub_lines     = 0;
    my $_sub_sub_lines = 0;
    my $_sub3_lines    = 0;
    my $_decode_str    = "";
    my $_format_print  = $_format;
    $_format_print = $OText::STR{'UNDEF'} if ( !defined($_format) );

    _trace5_( " " x $_next_indent
          . "# _decode_val (\$_format: '$_format_print', \$val_ref, \$_def_hash_ref, \$_first_indent: '$_first_indent', \$_next_indent: '$_next_indent', \$_text_sep: '$_text_sep', \$_sub_sep: '$_sub_sep', \$_sub_sub_sep: '$_sub_sub_sep', \$_sub3_sep: '$_sub3_sep ')\n"
    );
    $_decode_str = " " x $_first_indent;
    if ( defined($_def_hash_ref) ) {
        _trace5_( " " x ( $_next_indent + 2 ) . "# --->> def_hash-ref-Type:     " . ref($_def_hash_ref) . "<<\n" );
        _trace5_( " " x ( $_next_indent + 2 ) . "# --->> def_hash-ref-ref-Type: " . ref($$_def_hash_ref) . "<<\n" ) if ( ref($_def_hash_ref) eq 'REF' );
        _trace5_( " " x ( $_next_indent + 2 ) . "# --->> def_hash-val-Type:     " . ref( \$_def_hash_ref ) . "<<\n" );
        $_def_hash_ref = $$_def_hash_ref if ( ref($_def_hash_ref) eq 'REF' );
        if ( ref($_def_hash_ref) eq "HASH" ) {
            $_decode_str .= $_def_hash_ref->{TEXT} . $_text_sep if ( defined( $_def_hash_ref->{TEXT} ) );
        }
        if ( !defined($_val_ref) ) {
            if ( ref($_def_hash_ref) eq "SCALAR" ) {
                $_decode_str .= $$_def_hash_ref . $_text_sep;
            }
            elsif ( ref( \$_def_hash_ref ) eq "SCALAR" ) {
                $_decode_str .= $_def_hash_ref . $_text_sep;
            }
        }
        _trace5_( " " x ( $_next_indent + 2 ) . "# \$_decode_str: $_decode_str\n" );
    }
    return ($_decode_str) if ( !defined($_val_ref) );
    _trace5_( " " x ( $_next_indent + 2 ) . "# ---> val-Type:     " . ref($_val_ref) . "<\n" );
    _trace5_( " " x ( $_next_indent + 2 ) . "# ---> val-ref-Type: " . ref($$_val_ref) . "<\n" ) if ( ref($_val_ref) eq 'REF' );
    $_val_ref = $$_val_ref                                                                      if ( ref($_val_ref) eq 'REF' );
    if ( ref($_val_ref) eq 'SCALAR' ) {
        $_decode_str .= _sprintf_hex_val( $_format, $_val_ref, $_next_indent + 2 );
        $_decode_str .= _sprintf_val_description( $_def_hash_ref, $_val_ref, $_next_indent + 2 );
    }
    elsif ( ref($_val_ref) eq 'ARRAY' ) {
        $_decode_str .= "[ ";
        $_next_indent += 2;
        if ( ( @{$_val_ref} ) >= 1 ) {
            foreach my $ele ( @{$_val_ref} ) {
                _trace5_( " " x ( $_next_indent + 2 ) . "# ---|> val-ref-Type (\$ele): " . ref( \$ele ) . "<|\n" );
                $_decode_str .= $_sub_sep if ( $_sub_lines++ > 0 );
                if ( ref( \$ele ) eq 'SCALAR' ) {
                    $_decode_str .= _sprintf_hex_val( $_format, \$ele, $_next_indent + 2 );
                    $_decode_str .= _sprintf_val_description( $_def_hash_ref, \$ele, $_next_indent + 2 );
                }
                elsif ( ref($ele) eq 'ARRAY' ) {
                    $_decode_str .= "[ ";
                    $_next_indent += 2;
                    if ( ( @{$ele} ) >= 1 ) {
                        $_sub_sub_lines = 0;
                        foreach my $ele_ele ( @{$ele} ) {
                            _trace5_( " " x ( $_next_indent + 2 ) . "# ---||> val-ref-Type (\$ele_ele): " . ref( \$ele_ele ) . "<||\n" );
                            $_decode_str .= $_sub_sub_sep if ( $_sub_sub_lines++ > 0 );
                            if ( ref( \$ele_ele ) eq 'SCALAR' ) {
                                $_decode_str .= _sprintf_hex_val( $_format, \$ele_ele, $_next_indent + 2 );
                                $_decode_str .= _sprintf_val_description( $_def_hash_ref, \$ele_ele, $_next_indent + 2 );
                            }
                            elsif ( ref($ele_ele) eq 'ARRAY' ) {
                                $_decode_str .= "[ ";
                                $_next_indent += 2;
                                if ( ( @{$ele_ele} ) >= 1 ) {
                                    $_sub3_lines = 0;
                                    foreach my $ele3 ( @{$ele_ele} ) {
                                        _trace5_( " " x ( $_next_indent + 2 ) . "# --|||> val-ref-Type (\$ele3):    " . ref( \$ele3 ) . "<|||\n" );
                                        $_decode_str .= $_sub3_sep if ( $_sub3_lines++ > 0 );
                                        if ( ref( \$ele3 ) eq 'SCALAR' ) {
                                            $_decode_str .= _sprintf_hex_val( $_format, \$ele3, $_next_indent + 2 );
                                            $_decode_str .= _sprintf_val_description( $_def_hash_ref, \$ele3, $_next_indent + 2 );
                                        }
                                        else {
                                            _trace2_( " " x ( $_next_indent + 2 )
                                                  . "# --|||> **WARNING: SSLhello::_decode_val: try to print unsupported or deeply nested val type (\$ele3): '"
                                                  . ref( \$ele3 ) . "/"
                                                  . ref($ele3)
                                                  . "' <|||\n" );
                                            Carp::carp(
                                                    "$OText::STR{WARN} SSLhello::_decode_val: try to print unsupported or deeply nested val type (\$ele3): '"
                                                  . ref( \$ele3 ) . "/"
                                                  . ref($ele3)
                                                  . "'\n" );

                                            $_decode_str .=
                                              "[ --- unsupported or deeply nested val type (\$ele3): '" . ref( \$ele3 ) . "/" . ref($ele3) . "' --- ]";
                                        }
                                    }
                                }
                                $_decode_str .= " ]";
                                $_next_indent -= 2;
                            }
                            else {
                                _trace2_( " " x ( $_next_indent + 2 )
                                      . "**WARNING: SSLhello::_decode_val: try to print unsupported val-refref-Type (\$ele_ele):    "
                                      . ref($ele_ele)
                                      . "<\n" );
                                Carp::carp( "$OText::STR{WARN} SSLhello::_decode_val: try to print unsupported val type (\$ele_ele): '"
                                      . ref( \$ele_ele ) . "/"
                                      . ref($ele_ele)
                                      . "'\n" );
                                $_decode_str .= "[ --- unsupported val type (\$ele_ele): '" . ref( \$ele_ele ) . "/" . ref($ele_ele) . "' --- ]";
                            }
                        }
                    }
                    $_decode_str .= " ]";
                    $_next_indent -= 2;
                }
                else {
                    _trace2_( " " x ( $_next_indent + 2 )
                          . "**WARNING: SSLhello::_decode_val: try to print unsupported val-refref-Type (\$ele):    "
                          . ref($ele)
                          . "<\n" );
                    Carp::carp(
                        "$OText::STR{WARN} SSLhello::_decode_val: try to print unsupported val type (\$ele): '" . ref( \$ele ) . "/" . ref($ele) . "'\n" );
                    $_decode_str .= "[ --- unsupported val type (\$ele_ele): '" . ref( \$ele ) . "/" . ref($ele) . "' --- ]";
                }
            }
        }
        $_decode_str .= " ]";
        $_next_indent -= 2;
    }
    else {
        _trace2_(
            " " x ( $_next_indent + 2 ) . "**WARNING: SSLhello::_decode_val: try to print unsupported val-refref-Type:             " . ref($_val_ref) . "<\n" );
        Carp::carp( "$OText::STR{WARN} SSLhello::_decode_val: try to print unsupported val type: '" . ref( \$_val_ref ) . "/" . ref($_val_ref) . "'\n" );
        $_decode_str .= "[ --- unsupported val type: '" . ref( \$_val_ref ) . "/" . ref($_val_ref) . "' --- ]";
    }
    _trace5_( " " x $_next_indent . "#   ---> _decode_val: \$_decode_str: '$_decode_str'\n" );
    return ($_decode_str);
}

my $CHALLENGE = "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20o-saft\xbb\xcc\xdd\xee\xff";

my $SSL_CLIENT_VERSION = 0x0002;
my $SSL_SERVER_VERSION = 0x0002;

my $SSL_MT_ERROR               = 0;
my $SSL_MT_CLIENT_HELLO        = 1;
my $SSL_MT_CLIENT_MASTER_KEY   = 2;
my $SSL_MT_CLIENT_FINISHED     = 3;
my $SSL_MT_SERVER_HELLO        = 4;
my $SSL_MT_SERVER_VERIFY       = 5;
my $SSL_MT_SERVER_FINISHED     = 6;
my $SSL_MT_REQUEST_CERTIFICATE = 7;
my $SSL_MT_CLIENT_CERTIFICATE  = 8;

my $SSL_PE_NO_CIPHER                    = 0x0001;
my $SSL_PE_NO_CERTIFICATE               = 0x0002;
my $SSL_PE_BAD_CERTIFICATE              = 0x0004;
my $SSL_PE_UNSUPPORTED_CERTIFICATE_TYPE = 0x0006;

my $SSL_CT_X509_CERTIFICATE = 0x01;

my $SSL_AT_MD5_WITH_RSA_ENCRYPTION = 0x01;

my $SSL_MAX_MASTER_KEY_LENGTH_IN_BITS   = 256;
my $SSL_MAX_SESSION_ID_LENGTH_IN_BYTES  = 16;
my $SSL_MIN_RSA_MODULUS_LENGTH_IN_BYTES = 64;
my $SSL_MAX_RECORD_LENGTH_2_BYTE_HEADER = 32767;
my $SSL_MAX_RECORD_LENGTH_3_BYTE_HEADER = 16383;

my %cipherHexHash = (
    '0x020700C0' => [qw(DES_192_EDE3_CBC_WITH_MD5                DES-CBC3-MD5)],
    '0x020701C0' => [qw(DES_192_EDE3_CBC_WITH_SHA                DES-CBC3-SHA)],
    '0x02060040' => [qw(DES_CBC_WITH_MD5                         DES-CBC-MD5)],
    '0x02060140' => [qw(DES_CBC_WITH_SHA                         DES-CBC-SHA)],
    '0x02FF0800' => [qw(DES_64_CFB64_WITH_MD5_1                  DES-CFB-M1)],
    '0x02050080' => [qw(IDEA_CBC_WITH_MD5                        IDEA-CBC-MD5)],
    '0x02FF0810' => [qw(NULL                                     NULL)],
    '0x02000000' => [qw(NULL_WITH_MD5                            NULL-MD5)],
    '0x02040080' => [qw(RC2_128_CBC_EXPORT40_WITH_MD5            EXP-RC2-CBC-MD5)],
    '0x02030080' => [qw(RC2_128_CBC_WITH_MD5                     RC2-CBC-MD5)],
    '0x02020080' => [qw(RC4_128_EXPORT40_WITH_MD5                EXP-RC4-MD5)],
    '0x02010080' => [qw(RC4_128_WITH_MD5                         RC4-MD5)],
    '0x02080080' => [qw(RC4_64_WITH_MD5                          RC4-64-MD5)],

    '0x0300001B' => [qw(DH_anon_WITH_3DES_EDE_CBC_SHA            ADH-DES-CBC3-SHA)],
    '0x03000019' => [qw(DH_anon_EXPORT_WITH_DES40_CBC_SHA        EXP-ADH-DES-CBC-SHA)],
    '0x0300001A' => [qw(DH_anon_WITH_DES_CBC_SHA                 ADH-DES-CBC-SHA)],
    '0x03000018' => [qw(DH_anon_WITH_RC4_128_MD5                 ADH-RC4-MD5)],
    '0x03000017' => [qw(DH_anon_EXPORT_WITH_RC4_40_MD5           EXP-ADH-RC4-MD5)],
    '0x0300000D' => [qw(DH_DSS_WITH_3DES_EDE_CBC_SHA             DH-DSS-DES-CBC3-SHA)],
    '0x0300000B' => [qw(DH_DSS_EXPORT_WITH_DES40_CBC_SHA         EXP-DH-DSS-DES-CBC-SHA)],
    '0x0300000C' => [qw(DH_DSS_WITH_DES_CBC_SHA                  DH-DSS-DES-CBC-SHA)],
    '0x03000010' => [qw(DH_RSA_WITH_3DES_EDE_CBC_SHA             DH-RSA-DES-CBC3-SHA)],
    '0x0300000E' => [qw(DH_RSA_EXPORT_WITH_DES40_CBC_SHA         EXP-DH-RSA-DES-CBC-SHA)],
    '0x0300000F' => [qw(DH_RSA_WITH_DES_CBC_SHA                  DH-RSA-DES-CBC-SHA)],
    '0x03000013' => [qw(EDH_DSS_WITH_3DES_EDE_CBC_SHA            EDH-DSS-DES-CBC3-SHA)],
    '0x03000011' => [qw(EDH_DSS_EXPORT_WITH_DES40_CBC_SHA        EXP-EDH-DSS-DES-CBC-SHA)],
    '0x03000012' => [qw(EDH_DSS_WITH_DES_CBC_SHA                 EDH-DSS-DES-CBC-SHA)],
    '0x03000016' => [qw(EDH_RSA_WITH_3DES_EDE_CBC_SHA            EDH-RSA-DES-CBC3-SHA)],
    '0x03000014' => [qw(EDH_RSA_EXPORT_WITH_DES40_CBC_SHA        EXP-EDH-RSA-DES-CBC-SHA)],
    '0x03000015' => [qw(EDH_RSA_WITH_DES_CBC_SHA                 EDH-RSA-DES-CBC-SHA)],
    '0x0300001D' => [qw(FZA_DMS_FZA_SHA                          FZA-FZA-CBC-SHA)],
    '0x0300001C' => [qw(FZA_DMS_NULL_SHA                         FZA-NULL-SHA)],
    '0x0300001E' => [qw(FZA_DMS_RC4_SHA/KRB5_WITH_DES_CBC_SHA    FZA-RC4-SHA/KRB5-DES-SHA)],
    '0x03000023' => [qw(KRB5_WITH_3DES_EDE_CBC_MD5               KRB5-DES-CBC3-MD5)],
    '0x0300001F' => [qw(KRB5_WITH_3DES_EDE_CBC_SHA               KRB5-DES-CBC3-SHA)],
    '0x03000029' => [qw(KRB5_EXPORT_WITH_DES_CBC_40_MD5          EXP-KRB5-DES-CBC-MD5)],
    '0x03000026' => [qw(KRB5_EXPORT_WITH_DES_CBC_40_SHA          EXP-KRB5-DES-CBC-SHA)],
    '0x03000022' => [qw(KRB5_WITH_DES_CBC_MD5                    KRB5-DES-CBC-MD5)],
    '0x0300001E' => [qw(KRB5_WITH_DES_CBC_SHA                    KRB5-DES-CBC-SHA)],
    '0x03000025' => [qw(KRB5_WITH_IDEA_CBC_MD5                   KRB5-IDEA-CBC-MD5)],
    '0x03000021' => [qw(KRB5_WITH_IDEA_CBC_SHA                   KRB5-IDEA-CBC-SHA)],
    '0x0300002A' => [qw(KRB5_WITH_RC2_CBC_40_MD5                 EXP-KRB5-RC2-CBC-MD5)],
    '0x03000027' => [qw(KRB5_EXPORT_WITH_RC2_CBC_40_SHA          EXP-KRB5-RC2-CBC-SHA)],
    '0x03000024' => [qw(KRB5_WITH_RC4_128_MD5                    KRB5-RC4-MD5)],
    '0x03000020' => [qw(KRB5_WITH_RC4_128_SHA                    KRB5-RC4-SHA)],
    '0x0300002B' => [qw(KRB5_EXPORT_WITH_RC4_40_MD5              EXP-KRB5-RC4-MD5)],
    '0x03000028' => [qw(KRB5_EXPORT_WITH_RC4_40_SHA              EXP-KRB5-RC4-SHA)],
    '0x0300000A' => [qw(RSA_WITH_3DES_EDE_CBC_SHA                DES-CBC3-SHA)],
    '0x03000008' => [qw(RSA_EXPORT_WITH_DES40_CBC_SHA            EXP-DES-CBC-SHA)],
    '0x03000009' => [qw(RSA_WITH_DES_CBC_SHA                     DES-CBC-SHA)],
    '0x03000007' => [qw(RSA_WITH_IDEA_SHA                        IDEA-CBC-SHA)],
    '0x03000000' => [qw(NULL_WITH_NULL_NULL                      NULL-NULL)],
    '0x03000001' => [qw(RSA_WITH_NULL_MD5                        NULL-MD5)],
    '0x03000002' => [qw(RSA_WITH_NULL_SHA                        NULL-SHA)],
    '0x03000006' => [qw(RSA_EXPORT_WITH_RC2_CBC_40_MD5           EXP-RC2-CBC-MD5)],
    '0x03000004' => [qw(RSA_WITH_RC4_128_MD5                     RC4-MD5)],
    '0x03000005' => [qw(RSA_WITH_RC4_128_SHA                     RC4-SHA)],
    '0x03000003' => [qw(RSA_EXPORT_WITH_RC4_40_MD5               EXP-RC4-MD5)],
    '0x030000FF' => [qw(EMPTY_RENEGOTIATION_INFO_SCSV            SCSV-RENEG)],
    '0x03005600' => [qw(FALLBACK_SCSV_DRAFT                      SCSV-FALLBACK-DRAFT)],

    '0x030000A6' => [qw(DH_anon_WITH_AES_128_GCM_SHA256          ADH-AES128-GCM-SHA256)],
    '0x03000034' => [qw(DH_anon_WITH_AES_128_CBC_SHA             ADH-AES128-SHA)],
    '0x0300006C' => [qw(DH_anon_WITH_AES_128_CBC_SHA256          ADH-AES128-SHA256)],
    '0x030000A7' => [qw(DH_anon_WITH_AES_256_GCM_SHA384          ADH-AES256-GCM-SHA384)],
    '0x0300003A' => [qw(DH_anon_WITH_AES_256_CBC_SHA             ADH-AES256-SHA)],
    '0x0300006D' => [qw(DH_anon_WITH_AES_256_CBC_SHA256          ADH-AES256-SHA256)],
    '0x03000046' => [qw(DH_anon_WITH_CAMELLIA_128_CBC_SHA        ADH-CAMELLIA128-SHA)],
    '0x03000089' => [qw(DH_anon_WITH_CAMELLIA_256_CBC_SHA        ADH-CAMELLIA256-SHA)],
    '0x0300009B' => [qw(DH_anon_WITH_SEED_CBC_SHA                ADH-SEED-SHA)],
    '0x03000063' => [qw(DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA      EXP1024-DHE-DSS-DES-CBC-SHA)],
    '0x03000065' => [qw(DHE_DSS_EXPORT1024_WITH_RC4_56_SHA       EXP1024-DHE-DSS-RC4-SHA)],
    '0x030000A2' => [qw(DHE_DSS_WITH_AES_128_GCM_SHA256          DHE-DSS-AES128-GCM-SHA256)],
    '0x03000032' => [qw(DHE_DSS_WITH_AES_128_CBC_SHA             DHE-DSS-AES128-SHA)],
    '0x03000040' => [qw(DHE_DSS_WITH_AES_128_CBC_SHA256          DHE-DSS-AES128-SHA256)],
    '0x030000A3' => [qw(DHE_DSS_WITH_AES_256_GCM_SHA384          DHE-DSS-AES256-GCM-SHA384)],
    '0x03000038' => [qw(DHE_DSS_WITH_AES_256_CBC_SHA             DHE-DSS-AES256-SHA)],
    '0x0300006A' => [qw(DHE_DSS_WITH_AES_256_CBC_SHA256          DHE-DSS-AES256-SHA256)],
    '0x03000044' => [qw(DHE_DSS_WITH_CAMELLIA_128_CBC_SHA        DHE-DSS-CAMELLIA128-SHA)],
    '0x03000087' => [qw(DHE_DSS_WITH_CAMELLIA_256_CBC_SHA        DHE-DSS-CAMELLIA256-SHA)],
    '0x03000066' => [qw(DHE_DSS_WITH_RC4_128_SHA                 DHE-DSS-RC4-SHA)],
    '0x03000099' => [qw(DHE_DSS_WITH_SEED_CBC_SHA                DHE-DSS-SEED-SHA)],
    '0x0300009E' => [qw(DHE_RSA_WITH_AES_128_GCM_SHA256          DHE-RSA-AES128-GCM-SHA256)],
    '0x03000033' => [qw(DHE_RSA_WITH_AES_128_CBC_SHA             DHE-RSA-AES128-SHA)],
    '0x03000067' => [qw(DHE_RSA_WITH_AES_128_CBC_SHA256          DHE-RSA-AES128-SHA256)],
    '0x0300009F' => [qw(DHE_RSA_WITH_AES_256_GCM_SHA384          DHE-RSA-AES256-GCM-SHA384)],
    '0x03000039' => [qw(DHE_RSA_WITH_AES_256_CBC_SHA             DHE-RSA-AES256-SHA)],
    '0x0300006B' => [qw(DHE_RSA_WITH_AES_256_CBC_SHA256          DHE-RSA-AES256-SHA256)],
    '0x03000045' => [qw(DHE_RSA_WITH_CAMELLIA_128_CBC_SHA        DHE-RSA-CAMELLIA128-SHA)],
    '0x03000088' => [qw(DHE_RSA_WITH_CAMELLIA_256_CBC_SHA        DHE-RSA-CAMELLIA256-SHA)],
    '0x0300009A' => [qw(DHE_RSA_WITH_SEED_CBC_SHA                DHE-RSA-SEED-SHA)],
    '0x030000A4' => [qw(DH_DSS_WITH_AES_128_GCM_SHA256           DH-DSS-AES128-GCM-SHA256)],
    '0x03000030' => [qw(DH_DSS_WITH_AES_128_CBC_SHA              DH-DSS-AES128-SHA)],
    '0x0300003E' => [qw(DH_DSS_WITH_AES_128_CBC_SHA256           DH-DSS-AES128-SHA256)],
    '0x030000A5' => [qw(DH_DSS_WITH_AES_256_GCM_SHA384           DH-DSS-AES256-GCM-SHA384)],
    '0x03000036' => [qw(DH_DSS_WITH_AES_256_CBC_SHA              DH-DSS-AES256-SHA)],
    '0x03000068' => [qw(DH_DSS_WITH_AES_256_CBC_SHA256           DH-DSS-AES256-SHA256)],
    '0x03000042' => [qw(DH_DSS_WITH_CAMELLIA_128_CBC_SHA         DH-DSS-CAMELLIA128-SHA)],
    '0x03000085' => [qw(DH_DSS_WITH_CAMELLIA_256_CBC_SHA         DH-DSS-CAMELLIA256-SHA)],
    '0x03000097' => [qw(DH_DSS_WITH_SEED_CBC_SHA                 DH-DSS-SEED-SHA)],
    '0x030000A0' => [qw(DH_RSA_WITH_AES_128_GCM_SHA256           DH-RSA-AES128-GCM-SHA256)],
    '0x03000031' => [qw(DH_RSA_WITH_AES_128_CBC_SHA              DH-RSA-AES128-SHA)],
    '0x0300003F' => [qw(DH_RSA_WITH_AES_128_CBC_SHA256           DH-RSA-AES128-SHA256)],
    '0x030000A1' => [qw(DH_RSA_WITH_AES_256_GCM_SHA384           DH-RSA-AES256-GCM-SHA384)],
    '0x03000037' => [qw(DH_RSA_WITH_AES_256_CBC_SHA              DH-RSA-AES256-SHA)],
    '0x03000069' => [qw(DH_RSA_WITH_AES_256_CBC_SHA256           DH-RSA-AES256-SHA256)],
    '0x03000043' => [qw(DH_RSA_WITH_CAMELLIA_128_CBC_SHA         DH-RSA-CAMELLIA128-SHA)],
    '0x03000086' => [qw(DH_RSA_WITH_CAMELLIA_256_CBC_SHA         DH-RSA-CAMELLIA256-SHA)],
    '0x03000098' => [qw(DH_RSA_WITH_SEED_CBC_SHA                 DH-RSA-SEED-SHA)],
    '0x0300C009' => [qw(ECDHE_ECDSA_WITH_AES_128_CBC_SHA         ECDHE-ECDSA-AES128-SHA)],
    '0x0300C02B' => [qw(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256      ECDHE-ECDSA-AES128-GCM-SHA256)],
    '0x0300C023' => [qw(ECDHE_ECDSA_WITH_AES_128_CBC_SHA256      ECDHE-ECDSA-AES128-SHA256)],
    '0x0300C00A' => [qw(ECDHE_ECDSA_WITH_AES_256_CBC_SHA         ECDHE-ECDSA-AES256-SHA)],
    '0x0300C02C' => [qw(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384      ECDHE-ECDSA-AES256-GCM-SHA384)],
    '0x0300C024' => [qw(ECDHE_ECDSA_WITH_AES_256_CBC_SHA384      ECDHE-ECDSA-AES256-SHA384)],
    '0x0300C008' => [qw(ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA        ECDHE-ECDSA-DES-CBC3-SHA)],
    '0x0300C006' => [qw(ECDHE_ECDSA_WITH_NULL_SHA                ECDHE-ECDSA-NULL-SHA)],
    '0x0300C007' => [qw(ECDHE_ECDSA_WITH_RC4_128_SHA             ECDHE-ECDSA-RC4-SHA)],
    '0x0300C013' => [qw(ECDHE_RSA_WITH_AES_128_CBC_SHA           ECDHE-RSA-AES128-SHA)],
    '0x0300C02F' => [qw(ECDHE_RSA_WITH_AES_128_GCM_SHA256        ECDHE-RSA-AES128-GCM-SHA256)],
    '0x0300C027' => [qw(ECDHE_RSA_WITH_AES_128_CBC_SHA256        ECDHE-RSA-AES128-SHA256)],
    '0x0300C014' => [qw(ECDHE_RSA_WITH_AES_256_CBC_SHA           ECDHE-RSA-AES256-SHA)],
    '0x0300C030' => [qw(ECDHE_RSA_WITH_AES_256_GCM_SHA384        ECDHE-RSA-AES256-GCM-SHA384)],
    '0x0300C028' => [qw(ECDHE_RSA_WITH_AES_256_CBC_SHA384        ECDHE-RSA-AES256-SHA384)],
    '0x0300C012' => [qw(ECDHE_RSA_WITH_3DES_EDE_CBC_SHA          ECDHE-RSA-DES-CBC3-SHA)],
    '0x0300C010' => [qw(ECDHE_RSA_WITH_NULL_SHA                  ECDHE-RSA-NULL-SHA)],
    '0x0300C011' => [qw(ECDHE_RSA_WITH_RC4_128_SHA               ECDHE-RSA-RC4-SHA)],
    '0x0300C004' => [qw(ECDH_ECDSA_WITH_AES_128_CBC_SHA          ECDH-ECDSA-AES128-SHA)],
    '0x0300C02D' => [qw(ECDH_ECDSA_WITH_AES_128_GCM_SHA256       ECDH-ECDSA-AES128-GCM-SHA256)],
    '0x0300C025' => [qw(ECDH_ECDSA_WITH_AES_128_CBC_SHA256       ECDH-ECDSA-AES128-SHA256)],
    '0x0300C005' => [qw(ECDH_ECDSA_WITH_AES_256_CBC_SHA          ECDH-ECDSA-AES256-SHA)],
    '0x0300C02E' => [qw(ECDH_ECDSA_WITH_AES_256_GCM_SHA384       ECDH-ECDSA-AES256-GCM-SHA384)],
    '0x0300C026' => [qw(ECDH_ECDSA_WITH_AES_256_CBC_SHA384       ECDH-ECDSA-AES256-SHA384)],
    '0x0300C003' => [qw(ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA         ECDH-ECDSA-DES-CBC3-SHA)],
    '0x0300C001' => [qw(ECDH_ECDSA_WITH_NULL_SHA                 ECDH-ECDSA-NULL-SHA)],
    '0x0300C002' => [qw(ECDH_ECDSA_WITH_RC4_128_SHA              ECDH-ECDSA-RC4-SHA)],
    '0x0300C00E' => [qw(ECDH_RSA_WITH_AES_128_CBC_SHA            ECDH-RSA-AES128-SHA)],
    '0x0300C031' => [qw(ECDH_RSA_WITH_AES_128_GCM_SHA256         ECDH-RSA-AES128-GCM-SHA256)],
    '0x0300C029' => [qw(ECDH_RSA_WITH_AES_128_CBC_SHA256         ECDH-RSA-AES128-SHA256)],
    '0x0300C00F' => [qw(ECDH_RSA_WITH_AES_256_CBC_SHA            ECDH-RSA-AES256-SHA)],
    '0x0300C032' => [qw(ECDH_RSA_WITH_AES_256_GCM_SHA384         ECDH-RSA-AES256-GCM-SHA384)],
    '0x0300C02A' => [qw(ECDH_RSA_WITH_AES_256_CBC_SHA384         ECDH-RSA-AES256-SHA384)],
    '0x0300C00D' => [qw(ECDH_RSA_WITH_3DES_EDE_CBC_SHA           ECDH-RSA-DES-CBC3-SHA)],
    '0x0300C00B' => [qw(ECDH_RSA_WITH_NULL_SHA                   ECDH-RSA-NULL-SHA)],
    '0x0300C00C' => [qw(ECDH_RSA_WITH_RC4_128_SHA                ECDH-RSA-RC4-SHA)],
    '0x0300C018' => [qw(ECDH_anon_WITH_AES_128_CBC_SHA           AECDH-AES128-SHA)],
    '0x0300C019' => [qw(ECDH_anon_WITH_AES_256_CBC_SHA           AECDH-AES256-SHA)],
    '0x0300C017' => [qw(ECDH_anon_WITH_3DES_EDE_CBC_SHA          AECDH-DES-CBC3-SHA)],
    '0x0300C015' => [qw(ECDH_anon_WITH_NULL_SHA                  AECDH-NULL-SHA)],
    '0x0300C016' => [qw(ECDH_anon_WITH_RC4_128_SHA               AECDH-RC4-SHA)],
    '0x0300008B' => [qw(PSK_WITH_3DES_EDE_CBC_SHA                PSK-3DES-EDE-CBC-SHA)],
    '0x0300008C' => [qw(PSK_WITH_AES_128_CBC_SHA                 PSK-AES128-CBC-SHA)],
    '0x0300008D' => [qw(PSK_WITH_AES_256_CBC_SHA                 PSK-AES256-CBC-SHA)],
    '0x0300008A' => [qw(PSK_WITH_RC4_128_SHA                     PSK-RC4-SHA)],
    '0x03000062' => [qw(RSA_EXPORT1024_WITH_DES_CBC_SHA          EXP1024-DES-CBC-SHA)],
    '0x03000061' => [qw(RSA_EXPORT1024_WITH_RC2_CBC_56_MD5       EXP1024-RC2-CBC-MD5)],
    '0x03000060' => [qw(RSA_EXPORT1024_WITH_RC4_56_MD5           EXP1024-RC4-MD5)],
    '0x03000064' => [qw(RSA_EXPORT1024_WITH_RC4_56_SHA           EXP1024-RC4-SHA)],
    '0x0300009C' => [qw(RSA_WITH_AES_128_GCM_SHA256              AES128-GCM-SHA256)],
    '0x0300002F' => [qw(RSA_WITH_AES_128_CBC_SHA                 AES128-SHA)],
    '0x0300003C' => [qw(RSA_WITH_AES_128_CBC_SHA256              AES128-SHA256)],
    '0x0300009D' => [qw(RSA_WITH_AES_256_GCM_SHA384              AES256-GCM-SHA384)],
    '0x03000035' => [qw(RSA_WITH_AES_256_CBC_SHA                 AES256-SHA)],
    '0x0300003D' => [qw(RSA_WITH_AES_256_CBC_SHA256              AES256-SHA256)],
    '0x03000041' => [qw(RSA_WITH_CAMELLIA_128_CBC_SHA            CAMELLIA128-SHA)],
    '0x03000084' => [qw(RSA_WITH_CAMELLIA_256_CBC_SHA            CAMELLIA256-SHA)],
    '0x0300003B' => [qw(RSA_WITH_NULL_SHA256                     NULL-SHA256)],
    '0x03000096' => [qw(RSA_WITH_SEED_CBC_SHA                    SEED-SHA)],
    '0x0300C01C' => [qw(SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA        SRP-DSS-3DES-EDE-CBC-SHA)],
    '0x0300C01F' => [qw(SRP_SHA_DSS_WITH_AES_128_CBC_SHA         SRP-DSS-AES-128-CBC-SHA)],
    '0x0300C022' => [qw(SRP_SHA_DSS_WITH_AES_256_CBC_SHA         SRP-DSS-AES-256-CBC-SHA)],
    '0x0300C01B' => [qw(SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA        SRP-RSA-3DES-EDE-CBC-SHA)],
    '0x0300C01E' => [qw(SRP_SHA_RSA_WITH_AES_128_CBC_SHA         SRP-RSA-AES-128-CBC-SHA)],
    '0x0300C021' => [qw(SRP_SHA_RSA_WITH_AES_256_CBC_SHA         SRP-RSA-AES-256-CBC-SHA)],
    '0x0300C01A' => [qw(SRP_SHA_WITH_3DES_EDE_CBC_SHA            SRP-3DES-EDE-CBC-SHA)],
    '0x0300C01D' => [qw(SRP_SHA_WITH_AES_128_CBC_SHA             SRP-AES-128-CBC-SHA)],
    '0x0300C020' => [qw(SRP_SHA_WITH_AES_256_CBC_SHA             SRP-AES-256-CBC-SHA)],

    '0x03000080' => [qw(GOSTR341094_WITH_28147_CNT_IMIT      GOST94-GOST89-GOST89)],
    '0x03000081' => [qw(GOSTR341001_WITH_28147_CNT_IMIT      GOST2001-GOST89-GOST89)],
    '0x03000082' => [qw(GOSTR341094_WITH_NULL_GOSTR3411      GOST94-NULL-GOST94)],
    '0x03000083' => [qw(GOSTR341001_WITH_NULL_GOSTR3411      GOST2001-NULL-GOST94)],

    '0x0300CC12' => [qw(RSA_WITH_CHACHA20_POLY1305__OLD         RSA-CHACHA20-POLY1305__OLD)],
    '0x0300CC13' => [qw(ECDHE_RSA_WITH_CHACHA20_POLY1305__OLD   ECDHE-RSA-CHACHA20-POLY1305__OLD)],
    '0x0300CC14' => [qw(ECDHE_ECDSA_WITH_CHACHA20_POLY1305__OLD ECDHE-ECDSA-CHACHA20-POLY1305__OLD)],

    '0x0300CC15' => [qw(DHE_RSA_WITH_CHACHA20_POLY1305__OLD     DHE-RSA-CHACHA20-POLY1305__OLD)],
    '0x0300CC16' => [qw(DHE_PSK_WITH_CHACHA20_POLY1305__OLD     DHE-PSK-CHACHA20-POLY1305__OLD)],

    '0x0300CC17' => [qw(PSK_WITH_CHACHA20_POLY1305__OLD         PSK-CHACHA20-POLY1305__OLD)],
    '0x0300CC18' => [qw(ECDHE_PSK_WITH_CHACHA20_POLY1305__OLD   ECDHE-PSK-CHACHA20-POLY1305__OLD)],
    '0x0300CC19' => [qw(RSA_PSK_WITH_CHACHA20_POLY1305__OLD     RSA-PSK-CHACHA20-POLY1305__OLD)],

    '0x0300CC20' => [qw(RSA_WITH_CHACHA20_SHA              RSA-CHACHA20-SHA)],
    '0x0300CC21' => [qw(ECDHE_RSA_WITH_CHACHA20_SHA        ECDHE-RSA-CHACHA20-SHA)],
    '0x0300CC22' => [qw(ECDHE_ECDSA_WITH_CHACHA20_SHA      ECDHE-ECDSA-CHACHA20-SHA)],

    '0x0300CC23' => [qw(DHE_RSA_WITH_CHACHA20_SHA          DHE-RSA-CHACHA20-SHA)],
    '0x0300CC24' => [qw(DHE_PSK_WITH_CHACHA20_SHA          DHE-PSK-CHACHA20-SHA)],

    '0x0300CC25' => [qw(PSK_WITH_CHACHA20_SHA              PSK-CHACHA20-SHA)],
    '0x0300CC26' => [qw(ECDHE_PSK_WITH_CHACHA20_SHA        ECDHE-PSK-CHACHA20-SHA)],
    '0x0300CC27' => [qw(RSA_PSK_WITH_CHACHA20_SHA          RSA-PSK-CHACHA20-SHA)],

    '0x0300CCA0' => [qw(RSA_WITH_CHACHA20_POLY1305         RSA-CHACHA20-POLY1305)],
    '0x0300CCA1' => [qw(ECDHE_RSA_WITH_CHACHA20_POLY1305   ECDHE-RSA-CHACHA20-POLY1305)],
    '0x0300CCA2' => [qw(ECDHE_ECDSA_WITH_CHACHA20_POLY1305 ECDHE-ECDSA-CHACHA20-POLY1305)],

    '0x0300CCA3' => [qw(DHE_RSA_WITH_CHACHA20_POLY1305     DHE-RSA-CHACHA20-POLY1305)],
    '0x0300CCA4' => [qw(DHE_PSK_WITH_CHACHA20_POLY1305     DHE-PSK-CHACHA20-POLY1305)],

    '0x0300CCA5' => [qw(PSK_WITH_CHACHA20_POLY1305         PSK-CHACHA20-POLY1305)],
    '0x0300CCA6' => [qw(ECDHE_PSK_WITH_CHACHA20_POLY1305   ECDHE-PSK-CHACHA20-POLY1305)],
    '0x0300CCA7' => [qw(RSA_PSK_WITH_CHACHA20_POLY1305     RSA-PSK-CHACHA20-POLY1305)],

    '0x0300CCA8' => [qw(ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256   ECDHE-RSA-CHACHA20-POLY1305-SHA256)],
    '0x0300CCA9' => [qw(ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 ECDHE-ECDSA-CHACHA20-POLY1305-SHA256)],
    '0x0300CCAA' => [qw(DHE_RSA_WITH_CHACHA20_POLY1305_SHA256     DHE-RSA-CHACHA20-POLY1305-SHA256)],

    '0x0300CCAB' => [qw(PSK_WITH_CHACHA20_POLY1305_SHA256         PSK-CHACHA20-POLY1305-SHA256)],
    '0x0300CCAC' => [qw(ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256   ECDHE-PSK-CHACHA20-POLY1305-SHA256)],
    '0x0300CCAD' => [qw(DHE_PSK_WITH_CHACHA20_POLY1305_SHA256     DHE-PSK-CHACHA20-POLY1305-SHA256)],
    '0x0300CCAE' => [qw(RSA_PSK_WITH_CHACHA20_POLY1305_SHA256     RSA-PSK-CHACHA20-POLY1305-SHA256)],

    '0x030000BA' => [qw(RSA_WITH_CAMELLIA_128_CBC_SHA256     RSA-CAMELLIA128-SHA256)],
    '0x030000BB' => [qw(DH_DSS_WITH_CAMELLIA_128_CBC_SHA256  DH-DSS-CAMELLIA128-SHA256)],
    '0x030000BC' => [qw(DH_RSA_WITH_CAMELLIA_128_CBC_SHA256  DH-RSA-CAMELLIA128-SHA256)],
    '0x030000BD' => [qw(DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 DHE-DSS-CAMELLIA128-SHA256)],
    '0x030000BE' => [qw(DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 DHE-RSA-CAMELLIA128-SHA256)],
    '0x030000BF' => [qw(DH_anon_WITH_CAMELLIA_128_CBC_SHA256 ADH-CAMELLIA128-SHA256)],

    '0x030000C0' => [qw(RSA_WITH_CAMELLIA_256_CBC_SHA256     RSA-CAMELLIA256-SHA256)],
    '0x030000C1' => [qw(DH_DSS_WITH_CAMELLIA_256_CBC_SHA256  DH-DSS-CAMELLIA256-SHA256)],
    '0x030000C2' => [qw(DH_RSA_WITH_CAMELLIA_256_CBC_SHA256  DH-RSA-CAMELLIA256-SHA256)],
    '0x030000C3' => [qw(DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 DHE-DSS-CAMELLIA256-SHA256)],
    '0x030000C4' => [qw(DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 DHE-RSA-CAMELLIA256-SHA256)],
    '0x030000C5' => [qw(DH_anon_WITH_CAMELLIA_256_CBC_SHA256 ADH-CAMELLIA256-SHA256)],

    '0x0300C072' => [qw(ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256   ECDHE-ECDSA-CAMELLIA128-SHA256)],
    '0x0300C073' => [qw(ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384   ECDHE-ECDSA-CAMELLIA256-SHA384)],
    '0x0300C074' => [qw(ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256    ECDH-ECDSA-CAMELLIA128-SHA256)],
    '0x0300C075' => [qw(ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384    ECDH-ECDSA-CAMELLIA256-SHA384)],
    '0x0300C076' => [qw(ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256     ECDHE-RSA-CAMELLIA128-SHA256)],
    '0x0300C077' => [qw(ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384     ECDHE-RSA-CAMELLIA256-SHA384)],
    '0x0300C078' => [qw(ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256      ECDH-RSA-CAMELLIA128-SHA256)],
    '0x0300C079' => [qw(ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384      ECDH-RSA-CAMELLIA256-SHA384)],

    '0x0300C07A' => [qw(RSA_WITH_CAMELLIA_128_GCM_SHA256           RSA-CAMELLIA128-GCM-SHA256)],
    '0x0300C07B' => [qw(RSA_WITH_CAMELLIA_256_GCM_SHA384           RSA-CAMELLIA256-GCM-SHA384)],
    '0x0300C07C' => [qw(DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256       DHE-RSA-CAMELLIA128-GCM-SHA256)],
    '0x0300C07D' => [qw(DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384       DHE-RSA-CAMELLIA256-GCM-SHA384)],
    '0x0300C07E' => [qw(DH_RSA_WITH_CAMELLIA_128_GCM_SHA256        DH-RSA-CAMELLIA128-GCM-SHA256)],
    '0x0300C07F' => [qw(DH_RSA_WITH_CAMELLIA_256_GCM_SHA384        DH-RSA-CAMELLIA256-GCM-SHA384)],

    '0x0300C080' => [qw(DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256       DHE-DSS-CAMELLIA128-GCM-SHA256)],
    '0x0300C081' => [qw(DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384       DHE-DSS-CAMELLIA256-GCM-SHA384)],
    '0x0300C082' => [qw(DH_DSS_WITH_CAMELLIA_128_GCM_SHA256        DH-DSS-CAMELLIA128-GCM-SHA256)],
    '0x0300C083' => [qw(DH_DSS_WITH_CAMELLIA_256_GCM_SHA384        DH-DSS-CAMELLIA256-GCM-SHA384)],
    '0x0300C084' => [qw(DH_anon_DSS_WITH_CAMELLIA_128_GCM_SHA256   ADH-DSS-CAMELLIA128-GCM-SHA256)],
    '0x0300C085' => [qw(DH_anon_DSS_WITH_CAMELLIA_256_GCM_SHA384   ADH-DSS-CAMELLIA256-GCM-SHA384)],

    '0x0300C086' => [qw(ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256   ECDHE-ECDSA-CAMELLIA128-GCM-SHA256)],
    '0x0300C087' => [qw(ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384   ECDHE-ECDSA-CAMELLIA256-GCM-SHA384)],
    '0x0300C088' => [qw(ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256    ECDH-ECDSA-CAMELLIA128-GCM-SHA256)],
    '0x0300C089' => [qw(ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384    ECDH-ECDSA-CAMELLIA256-GCM-SHA384)],
    '0x0300C08A' => [qw(ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256     ECDHE-RSA-CAMELLIA128-GCM-SHA256)],
    '0x0300C08B' => [qw(ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384     ECDHE-RSA-CAMELLIA256-GCM-SHA384)],
    '0x0300C08C' => [qw(ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256      ECDH-RSA-CAMELLIA128-GCM-SHA256)],
    '0x0300C08D' => [qw(ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384      ECDH-RSA-CAMELLIA256-GCM-SHA384)],

    '0x0300C08E' => [qw(PSK_WITH_CAMELLIA_128_GCM_SHA256           PSK-CAMELLIA128-GCM-SHA256)],
    '0x0300C08F' => [qw(PSK_WITH_CAMELLIA_256_GCM_SHA384           PSK-CAMELLIA256-GCM-SHA384)],
    '0x0300C090' => [qw(DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256       DHE-PSK-CAMELLIA128-GCM-SHA256)],
    '0x0300C091' => [qw(DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384       DHE-PSK-CAMELLIA256-GCM-SHA384)],
    '0x0300C092' => [qw(RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256       RSA-PSK-CAMELLIA128-GCM-SHA256)],
    '0x0300C093' => [qw(RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384       RSA-PSK-CAMELLIA256-GCM-SHA384)],

    '0x0300C094' => [qw(PSK_WITH_CAMELLIA_128_CBC_SHA256           PSK-CAMELLIA128-SHA256)],
    '0x0300C095' => [qw(PSK_WITH_CAMELLIA_256_CBC_SHA384           PSK-CAMELLIA256-SHA384)],
    '0x0300C096' => [qw(DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256       DHE-PSK-CAMELLIA128-SHA256)],
    '0x0300C097' => [qw(DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384       DHE-PSK-CAMELLIA256-SHA384)],
    '0x0300C098' => [qw(RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256       RSA-PSK-CAMELLIA128-SHA256)],
    '0x0300C099' => [qw(RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384       RSA-PSK-CAMELLIA256-SHA384)],

    '0x0300C09A' => [qw(ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256     ECDHE-PSK-CAMELLIA128-SHA256)],
    '0x0300C09B' => [qw(ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384     ECDHE-PSK-CAMELLIA256-SHA384)],

    '0x0300C09C' => [qw(RSA_WITH_AES_128_CCM        RSA-AES128-CCM)],
    '0x0300C09D' => [qw(RSA_WITH_AES_256_CCM        RSA-AES256-CCM)],
    '0x0300C09E' => [qw(DHE_RSA_WITH_AES_128_CCM    DHE-RSA-AES128-CCM)],
    '0x0300C09F' => [qw(DHE_RSA_WITH_AES_256_CCM    DHE-RSA-AES256-CCM)],
    '0x0300C0A0' => [qw(RSA_WITH_AES_128_CCM_8      RSA-AES128-CCM8)],
    '0x0300C0A1' => [qw(RSA_WITH_AES_256_CCM_8      RSA-AES256-CCM8)],
    '0x0300C0A2' => [qw(DHE_RSA_WITH_AES_128_CCM_8  DHE-RSA-AES128-CCM8)],
    '0x0300C0A3' => [qw(DHE_RSA_WITH_AES_256_CCM_8  DHE-RSA-AES256-CCM8)],
    '0x0300C0A4' => [qw(PSK_WITH_AES_128_CCM        PSK-AES128-CCM)],
    '0x0300C0A5' => [qw(PSK_WITH_AES_256_CCM        PSK-AES256-CCM)],
    '0x0300C0A6' => [qw(DHE_PSK_WITH_AES_128_CCM    DHE-PSK-AES128-CCM)],
    '0x0300C0A7' => [qw(DHE_PSK_WITH_AES_256_CCM    DHE-PSK-AES256-CCM)],
    '0x0300C0A8' => [qw(PSK_WITH_AES_128_CCM_8      PSK-AES128-CCM8)],
    '0x0300C0A9' => [qw(PSK_WITH_AES_256_CCM_8      PSK-AES256-CCM8)],
    '0x0300C0AA' => [qw(PSK_DHE_WITH_AES_128_CCM_8  DHE-PSK-AES128-CCM8)],
    '0x0300C0AB' => [qw(PSK_DHE_WITH_AES_256_CCM_8  DHE-PSK-AES256-CCM8)],

    '0x0300FEE0' => [qw(RSA_FIPS_WITH_3DES_EDE_CBC_SHA      RSA-FIPS-3DES-EDE-SHA)],
    '0x0300FEE1' => [qw(RSA_FIPS_WITH_DES_CBC_SHA           RSA-FIPS-DES-CBC-SHA)],
    '0x0300FEFE' => [qw(RSA_FIPS_WITH_DES_CBC_SHA           RSA-FIPS-DES-CBC-SHA)],
    '0x0300FEFF' => [qw(RSA_FIPS_WITH_3DES_EDE_CBC_SHA      RSA-FIPS-3DES-EDE-SHA)],

    '0x0300002C' => [qw(PSK_WITH_NULL_SHA                 PSK-SHA)],
    '0x0300002D' => [qw(DHE_PSK_WITH_NULL_SHA             DHE-PSK-SHA)],
    '0x0300002E' => [qw(RSA_PSK_WITH_NULL_SHA             RSA-PSK-SHA)],
    '0x0300008E' => [qw(DHE_PSK_WITH_RC4_128_SHA          DHE-PSK-RC4-SHA)],
    '0x0300008F' => [qw(DHE_PSK_WITH_3DES_EDE_CBC_SHA     DHE-PSK-3DES-SHA)],
    '0x03000090' => [qw(DHE_PSK_WITH_AES_128_CBC_SHA      DHE-PSK-AES128-SHA)],
    '0x03000091' => [qw(DHE_PSK_WITH_AES_256_CBC_SHA      DHE-PSK-AES256-SHA)],
    '0x03000092' => [qw(RSA_PSK_WITH_RC4_128_SHA          RSA-PSK-RC4-SHA)],
    '0x03000093' => [qw(RSA_PSK_WITH_3DES_EDE_CBC_SHA     RSA-PSK-3DES-SHA)],
    '0x03000094' => [qw(RSA_PSK_WITH_AES_128_CBC_SHA      RSA-PSK-AES128-SHA)],
    '0x03000095' => [qw(RSA_PSK_WITH_AES_256_CBC_SHA      RSA-PSK-AES256-SHA)],

    '0x030000AA' => [qw(DHE_PSK_WITH_AES_128_GCM_SHA256   DHE-PSK-AES128-GCM-SHA256)],
    '0x030000AB' => [qw(DHE_PSK_WITH_AES_256_GCM_SHA384   DHE-PSK-AES256-GCM-SHA384)],
    '0x030000AC' => [qw(RSA_PSK_WITH_AES_128_GCM_SHA256   RSA-PSK-AES128-GCM-SHA256)],
    '0x030000AD' => [qw(RSA_PSK_WITH_AES_256_GCM_SHA384   RSA-PSK-AES256-GCM-SHA384)],
    '0x030000AE' => [qw(PSK_WITH_AES_128_CBC_SHA256       PSK-AES128-SHA256)],
    '0x030000AF' => [qw(PSK_WITH_AES_256_CBC_SHA384       PSK-AES256-SHA384)],
    '0x030000B0' => [qw(PSK_WITH_NULL_SHA256              PSK-SHA256)],
    '0x030000B1' => [qw(PSK_WITH_NULL_SHA384              PSK-SHA384)],
    '0x030000B2' => [qw(DHE_PSK_WITH_AES_256_CBC_SHA256   DHE-PSK-AES128-SHA256)],
    '0x030000B3' => [qw(DHE_PSK_WITH_AES_256_CBC_SHA384   DHE-PSK-AES256-SHA384)],
    '0x030000B4' => [qw(DHE_PSK_WITH_NULL_SHA256          DHE-PSK-SHA256)],
    '0x030000B5' => [qw(DHE_PSK_WITH_NULL_SHA384          DHE-PSK-SHA384)],
    '0x030000B6' => [qw(RSA_PSK_WITH_AES_256_CBC_SHA256   RSA-PSK-AES128-SHA256)],
    '0x030000B7' => [qw(RSA_PSK_WITH_AES_256_CBC_SHA384   RSA-PSK-AES256-SHA384)],
    '0x030000B8' => [qw(RSA_PSK_WITH_NULL_SHA256          RSA-PSK-SHA256)],
    '0x030000B9' => [qw(RSA_PSK_WITH_NULL_SHA384          RSA-PSK-SHA384)],

    '0x0300C0AC' => [qw(ECDHE_ECDSA_WITH_AES_128_CCM      ECDHE-ECDSA-AES128-CCM)],
    '0x0300C0AD' => [qw(ECDHE_ECDSA_WITH_AES_256_CCM      ECDHE-ECDSA-AES256-CCM)],
    '0x0300C0AE' => [qw(ECDHE_ECDSA_WITH_AES_128_CCM_8    ECDHE-ECDSA-AES128-CCM-8)],
    '0x0300C0AF' => [qw(ECDHE_ECDSA_WITH_AES_256_CCM_8    ECDHE-ECDSA-AES256-CCM-8)],

    '0x030000A8' => [qw(PSK_WITH_AES_128_GCM_SHA256       PSK-AES128-GCM-SHA256)],
    '0x030000A9' => [qw(PSK_WITH_AES_256_GCM_SHA384       PSK-AES256-GCM-SHA384)],

    '0x0300C033' => [qw(ECDHE_PSK_WITH_RC4_128_SHA          ECDHE-PSK-RC4-SHA)],
    '0x0300C034' => [qw(ECDHE_PSK_WITH_3DES_EDE_CBC_SHA     ECDHE-PSK-3DES-SHA)],
    '0x0300C035' => [qw(ECDHE_PSK_WITH_AES_128_CBC_SHA      ECDHE-PSK-AES128-SHA)],
    '0x0300C036' => [qw(ECDHE_PSK_WITH_AES_256_CBC_SHA      ECDHE-PSK-AES256-SHA)],
    '0x0300C037' => [qw(ECDHE_PSK_WITH_AES_128_CBC_SHA256   ECDHE-PSK-AES128-SHA256)],
    '0x0300C038' => [qw(ECDHE_PSK_WITH_AES_256_CBC_SHA384   ECDHE-PSK-AES256-SHA384)],
    '0x0300C039' => [qw(ECDHE_PSK_WITH_NULL_SHA             ECDHE-PSK-SHA)],
    '0x0300C03A' => [qw(ECDHE_PSK_WITH_NULL_SHA256          ECDHE-PSK-SHA256)],
    '0x0300C03B' => [qw(ECDHE_PSK_WITH_NULL_SHA384          ECDHE-PSK-SHA384)],

    '0x0300C03C' => [qw(RSA_WITH_ARIA_128_CBC_SHA256          RSA-ARIA128-SHA256)],
    '0x0300C03D' => [qw(RSA_WITH_ARIA_256_CBC_SHA384          RSA-ARIA256-SHA384)],
    '0x0300C03E' => [qw(DH_DSS_WITH_ARIA_128_CBC_SHA256       DH-DSS-ARIA128-SHA256)],
    '0x0300C03F' => [qw(DH_DSS_WITH_ARIA_256_CBC_SHA384       DH-DSS-ARIA256-SHA384)],
    '0x0300C040' => [qw(DH_RSA_WITH_ARIA_128_CBC_SHA256       DH-RSA-ARIA128-SHA256)],
    '0x0300C041' => [qw(DH_RSA_WITH_ARIA_256_CBC_SHA384       DH-RSA-ARIA256-SHA384)],
    '0x0300C042' => [qw(DHE_DSS_WITH_ARIA_128_CBC_SHA256      DHE-DSS-ARIA128-SHA256)],
    '0x0300C043' => [qw(DHE_DSS_WITH_ARIA_256_CBC_SHA384      DHE-DSS-ARIA256-SHA384)],
    '0x0300C044' => [qw(DHE_RSA_WITH_ARIA_128_CBC_SHA256      DHE-RSA-ARIA128-SHA256)],

    '0x0300C045' => [qw(DHE_RSA_WITH_ARIA_256_CBC_SHA384      DHE-RSA-ARIA256-SHA384)],
    '0x0300C046' => [qw(DH_anon_WITH_ARIA_128_CBC_SHA256      ADH-ARIA128-SHA256)],
    '0x0300C047' => [qw(DH_anon_WITH_ARIA_256_CBC_SHA384      ADH-ARIA256-SHA384)],

    '0x0300C048' => [qw(ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256  ECDHE-ECDSA-ARIA128-SHA256)],
    '0x0300C049' => [qw(ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384  ECDHE-ECDSA-ARIA256-SHA384)],
    '0x0300C04A' => [qw(ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256   ECDH-ECDSA-ARIA128-SHA256)],
    '0x0300C04B' => [qw(ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384   ECDH-ECDSA-ARIA256-SHA384)],
    '0x0300C04C' => [qw(ECDHE_RSA_WITH_ARIA_128_CBC_SHA256    ECDHE-RSA-ARIA128-SHA256)],
    '0x0300C04D' => [qw(ECDHE_RSA_WITH_ARIA_256_CBC_SHA384    ECDHE-RSA-ARIA256-SHA384)],
    '0x0300C04E' => [qw(ECDH_RSA_WITH_ARIA_128_CBC_SHA256     ECDH-RSA-ARIA128-SHA256)],
    '0x0300C04F' => [qw(ECDH_RSA_WITH_ARIA_256_CBC_SHA384     ECDH-RSA-ARIA256-SHA384)],

    '0x0300C050' => [qw(RSA_WITH_ARIA_128_GCM_SHA256          RSA-ARIA128-GCM-SHA256)],
    '0x0300C051' => [qw(RSA_WITH_ARIA_256_GCM_SHA384          RSA-ARIA256-GCM-SHA384)],
    '0x0300C052' => [qw(DHE_RSA_WITH_ARIA_128_GCM_SHA256      DHE-RSA-ARIA128-GCM-SHA256)],
    '0x0300C053' => [qw(DHE_RSA_WITH_ARIA_256_GCM_SHA384      DHE-RSA-ARIA256-GCM-SHA384)],
    '0x0300C054' => [qw(DH_RSA_WITH_ARIA_128_GCM_SHA256       DH-RSA-ARIA128-GCM-SHA256)],
    '0x0300C055' => [qw(DH_RSA_WITH_ARIA_256_GCM_SHA384       DH-RSA-ARIA256-GCM-SHA384)],
    '0x0300C056' => [qw(DHE_DSS_WITH_ARIA_128_GCM_SHA256      DHE-DSS-ARIA128-GCM-SHA256)],
    '0x0300C057' => [qw(DHE_DSS_WITH_ARIA_256_GCM_SHA384      DHE-DSS-ARIA256-GCM-SHA384)],
    '0x0300C058' => [qw(DH_DSS_WITH_ARIA_128_GCM_SHA256       DH-DSS-ARIA128-GCM-SHA256)],
    '0x0300C059' => [qw(DH_DSS_WITH_ARIA_256_GCM_SHA384       DH-DSS-ARIA256-GCM-SHA384)],
    '0x0300C05A' => [qw(DH_anon_WITH_ARIA_128_GCM_SHA256      ADH-ARIA128-GCM-SHA256)],
    '0x0300C05B' => [qw(DH_anon_WITH_ARIA_256_GCM_SHA384      ADH-ARIA256-GCM-SHA384)],

    '0x0300C05C' => [qw(ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256  ECDHE-ECDSA-ARIA128-GCM-SHA256)],
    '0x0300C05D' => [qw(ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384  ECDHE-ECDSA-ARIA256-GCM-SHA384)],
    '0x0300C05E' => [qw(ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256   ECDH-ECDSA-ARIA128-GCM-SHA256)],
    '0x0300C05F' => [qw(ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384   ECDH-ECDSA-ARIA256-GCM-SHA384)],
    '0x0300C060' => [qw(ECDHE_RSA_WITH_ARIA_128_GCM_SHA256    ECDHE-RSA-ARIA128-GCM-SHA256)],
    '0x0300C061' => [qw(ECDHE_RSA_WITH_ARIA_256_GCM_SHA384    ECDHE-RSA-ARIA256-GCM-SHA384)],
    '0x0300C062' => [qw(ECDH_RSA_WITH_ARIA_128_GCM_SHA256     ECDH-RSA-ARIA128-GCM-SHA256)],
    '0x0300C063' => [qw(ECDH_RSA_WITH_ARIA_256_GCM_SHA384     ECDH-RSA-ARIA256-GCM-SHA384)],

    '0x0300C064' => [qw(PSK_WITH_ARIA_128_CBC_SHA256          PSK-ARIA128-SHA256)],
    '0x0300C065' => [qw(PSK_WITH_ARIA_256_CBC_SHA384          PSK-ARIA256-SHA384)],
    '0x0300C066' => [qw(DHE_PSK_WITH_ARIA_128_CBC_SHA256      DHE-PSK-ARIA128-SHA256)],
    '0x0300C067' => [qw(DHE_PSK_WITH_ARIA_256_CBC_SHA384      DHE-PSK-ARIA256-SHA384)],
    '0x0300C068' => [qw(RSA_PSK_WITH_ARIA_128_CBC_SHA256      RSA-PSK-ARIA128-SHA256)],
    '0x0300C069' => [qw(RSA_PSK_WITH_ARIA_256_CBC_SHA384      RSA-PSK-ARIA256-SHA384)],
    '0x0300C06A' => [qw(PSK_WITH_ARIA_128_GCM_SHA256          PSK-ARIA128-GCM-SHA256)],
    '0x0300C06B' => [qw(PSK_WITH_ARIA_256_GCM_SHA384          PSK-ARIA256-GCM-SHA384)],
    '0x0300C06C' => [qw(DHE_PSK_WITH_ARIA_128_GCM_SHA256      DHE-PSK-ARIA128-GCM-SHA256)],
    '0x0300C06D' => [qw(DHE_PSK_WITH_ARIA_256_GCM_SHA384      DHE-PSK-ARIA256-GCM-SHA384)],
    '0x0300C06E' => [qw(RSA_PSK_WITH_ARIA_128_GCM_SHA256      RSA-PSK-ARIA128-GCM-SHA256)],
    '0x0300C06F' => [qw(RSA_PSK_WITH_ARIA_256_GCM_SHA384      RSA-PSK-ARIA256-GCM-SHA384)],
    '0x0300C070' => [qw(ECDHE_PSK_WITH_ARIA_128_CBC_SHA256    ECDHE-PSK-ARIA128-SHA256)],
    '0x0300C071' => [qw(ECDHE_PSK_WITH_ARIA_256_CBC_SHA384    ECDHE-PSK-ARIA256-SHA384)],

    '0x03001301' => [qw(TLS13_AES_128_GCM_SHA256              TLS13-AES-128-GCM-SHA256)],
    '0x03001302' => [qw(TLS13_AES_256_GCM_SHA384              TLS13-AES-256-GCM-SHA384)],
    '0x03001303' => [qw(TLS13_CHACHA20_POLY1305_SHA256        TLS13-CHACHA20-POLY1305-SHA256)],
    '0x03001304' => [qw(TLS13_AES_128_CCM_SHA256              TLS13-AES-128-CCM-SHA256)],
    '0x03001305' => [qw(TLS13_AES_128_CCM_8_SHA256            TLS13-AES-128-CCM-8-SHA256)],

    '0x030000C6' => [qw(TLS13_SM4_GCM_SM3                     TLS13-SM4-GCM)],
    '0x030000C7' => [qw(TLS13_SM4_CCM_SM3                     TLS13-SM4-CCM)],

    '0x0300C100' => [qw(GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC    GOSTR341112-256-KUZNYECHIK-CTR-OMAC)],
    '0x0300C101' => [qw(GOSTR341112_256_WITH_MAGMA_CTR_OMAC         GOSTR341112-256-MAGMA-CTR-OMAC)],
    '0x0300C102' => [qw(GOSTR341112_256_WITH_28147_CNT_IMIT         GOSTR341112-256-28147-CNT-IMIT)],
    '0x0300C103' => [qw(TLS13_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L GOSTR341112-256-KUZNYECHIK-MGM-L)],
    '0x0300C104' => [qw(TLS13_GOSTR341112_256_WITH_MAGMA_MGM_L      GOSTR341112-256-MAGMA-MGM-L)],
    '0x0300C105' => [qw(TLS13_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S GOSTR341112-256-KUZNYECHIK-MGM-S)],
    '0x0300C106' => [qw(TLS13_GOSTR341112_256_WITH_MAGMA_MGM_S      GOSTR341112-256-MAGMA-MGM-S)],

    '0x0300C4B4' => [qw(TLS13_SHA256_SHA256                   TLS13-SHA256-SHA256)],
    '0x0300C4B5' => [qw(TLS13_SHA384_SHA384                   TLS13-SHA384-SHA384)],

    '0x0300D001' => [qw(ECDHE_PSK_WITH_AES_128_GCM_SHA256     ECDHE_PSK_WITH_AES_128_GCM_SHA256)],
    '0x0300D002' => [qw(ECDHE_PSK_WITH_AES_256_GCM_SHA384     ECDHE_PSK_WITH_AES_256_GCM_SHA384)],
    '0x0300D003' => [qw(ECDHE_PSK_WITH_AES_128_CCM_8_SHA256   ECDHE_PSK_WITH_AES_128_CCM_8_SHA256)],
    '0x0300D005' => [qw(ECDHE_PSK_WITH_AES_128_CCM_SHA256     ECDHE_PSK_WITH_AES_128_CCM_SHA256)],

);

my $TLS_CLIENT_HELLO = 1;
my $TLS_SERVER_HELLO = 2;

my %SSL2_CIPHER_STRINGS = (
    '0x020700C0' => [qw(DES_192_EDE3_CBC_WITH_MD5                DES-CBC3-MD5       SSL_CK_DES_192_EDE3_CBC_WITH_MD5)],
    '0x020701C0' => [qw(DES_192_EDE3_CBC_WITH_SHA                DES-CBC3-SHA)],
    '0x02060040' => [qw(DES_CBC_WITH_MD5                         DES-CBC-MD5        SSL_CK_DES_64_CBC_WITH_MD5)],
    '0x02060140' => [qw(DES_CBC_WITH_SHA                         DES-CBC-SHA)],
    '0x02FF0800' => [qw(DES_64_CFB64_WITH_MD5_1                  DES-CFB-M1)],
    '0x02050080' => [qw(IDEA_CBC_WITH_MD5                        IDEA-CBC-MD5       SSL_CK_IDEA_128_CBC_WITH_MD5)],
    '0x02FF0810' => [qw(NULL                                     NULL)],
    '0x02000000' => [qw(NULL_WITH_MD5                            NULL-MD5)],
    '0x02040080' => [qw(RC2_128_CBC_EXPORT40_WITH_MD5            EXP-RC2-CBC-MD5    SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)],
    '0x02030080' => [qw(RC2_128_CBC_WITH_MD5                     RC2-CBC-MD5        SSL_CK_RC2_128_CBC_WITH_MD5)],
    '0x02020080' => [qw(RC4_128_EXPORT40_WITH_MD5                EXP-RC4-MD5        SSL_CK_RC4_128_EXPORT40_WITH_MD5)],
    '0x02010080' => [qw(RC4_128_WITH_MD5                         RC4-MD5            SSL_CK_RC4_128_WITH_MD5)],
    '0x02FFFFFF' => [qw(SSL2_UNFFINED_CIPHER_0x02FFFFFF          SSL2_UNFFINED_CIPHER_0x02FFFFFF             SSL2_UNFFINED_CIPHER_0x02FFFFFF)],
    '0x0300001B' => [qw(DH_anon_WITH_DES_192_CBC_SHA                 ADH-DES-CBC3-SHA)],
    '0x03000019' => [qw(DH_anon_EXPORT_WITH_DES40_CBC_SHA            EXP-ADH-DES-CBC-SHA)],
    '0x0300001A' => [qw(DH_anon_WITH_DES_CBC_SHA                     ADH-DES-CBC-SHA)],
    '0x03000018' => [qw(DH_anon_WITH_RC4_128_MD5                     ADH-RC4-MD5)],
    '0x03000017' => [qw(DH_anon_WITH_RC4_40_MD5                      EXP-ADH-RC4-MD5)],
    '0x0300000D' => [qw(DH_DSS_WITH_3DES_EDE_CBC_SHA                 DH-DSS-DES-CBC3-SHA)],
    '0x0300000B' => [qw(DH_DSS_EXPORT_WITH_DES40_CBC_SHA             EXP-DH-DSS-DES-CBC-SHA)],
    '0x0300000C' => [qw(DH_DSS_WITH_DES_CBC_SHA                      DH-DSS-DES-CBC-SHA)],
    '0x03000010' => [qw(DH_RSA_WITH_3DES_EDE_CBC_SHA                 DH-RSA-DES-CBC3-SHA)],
    '0x0300000E' => [qw(DH_RSA_EXPORT_WITH_DES40_CBC_SHA             EXP-DH-RSA-DES-CBC-SHA)],
    '0x0300000F' => [qw(DH_RSA_WITH_DES_CBC_SHA                      DH-RSA-DES-CBC-SHA)],
    '0x03000013' => [qw(EDH_DSS_WITH_3DES_EDE_CBC_SHA                EDH-DSS-DES-CBC3-SHA)],
    '0x03000011' => [qw(EDH_DSS_EXPORT_WITH_DES40_CBC_SHA            EXP-EDH-DSS-DES-CBC-SHA)],
    '0x03000012' => [qw(EDH_DSS_WITH_DES_CBC_SHA                     EDH-DSS-DES-CBC-SHA)],
    '0x03000016' => [qw(EDH_RSA_WITH_3DES_EDE_CBC_SHA                EDH-RSA-DES-CBC3-SHA)],
    '0x03000014' => [qw(EDH_RSA_EXPORT_WITH_DES40_CBC_SHA            EXP-EDH-RSA-DES-CBC-SHA)],
    '0x03000015' => [qw(EDH_RSA_WITH_DES_CBC_SHA                     EDH-RSA-DES-CBC-SHA)],
    '0x0300001D' => [qw(FZA_DMS_FZA_SHA                              FZA-FZA-CBC-SHA)],
    '0x0300001C' => [qw(FZA_DMS_NULL_SHA                             FZA-NULL-SHA)],
    '0x03000023' => [qw(KRB5_WITH_3DES_EDE_CBC_MD5                   KRB5-DES-CBC3-MD5)],
    '0x0300001F' => [qw(KRB5_WITH_3DES_EDE_CBC_SHA                   KRB5-DES-CBC3-SHA)],
    '0x03000029' => [qw(KRB5_EXPORT_WITH_DES40_CBC_MD5               EXP-KRB5-DES-CBC-MD5)],
    '0x03000026' => [qw(KRB5_EXPORT_WITH_DES40_CBC_SHA               EXP-KRB5-DES-CBC-SHA)],
    '0x03000022' => [qw(KRB5_WITH_DES_CBC_MD5                        KRB5-DES-CBC-MD5)],
    '0x0300001E' => [qw(KRB5_WITH_DES_CBC_SHA                        KRB5-DES-CBC-SHA)],
    '0x03000025' => [qw(KRB5_WITH_IDEA_CBC_MD5                       KRB5-IDEA-CBC-MD5)],
    '0x03000021' => [qw(KRB5_WITH_IDEA_CBC_SHA                       KRB5-IDEA-CBC-SHA)],
    '0x0300002A' => [qw(KRB5_WITH_RC2_40_CBC_MD5                     EXP-KRB5-RC2-CBC-MD5)],
    '0x03000027' => [qw(KRB5_WITH_RC2_40_CBC_SHA                     EXP-KRB5-RC2-CBC-SHA)],
    '0x03000024' => [qw(KRB5_WITH_RC4_128_MD5                        KRB5-RC4-MD5)],
    '0x03000020' => [qw(KRB5_WITH_RC4_128_SHA                        KRB5-RC4-SHA)],
    '0x0300002B' => [qw(KRB5_WITH_RC4_40_MD5                         EXP-KRB5-RC4-MD5)],
    '0x03000028' => [qw(KRB5_WITH_RC4_40_SHA                         EXP-KRB5-RC4-SHA)],
    '0x0300000A' => [qw(RSA_WITH_3DES_EDE_CBC_SHA                    DES-CBC3-SHA)],
    '0x03000008' => [qw(RSA_EXPORT_WITH_DES40_CBC_SHA                EXP-DES-CBC-SHA)],
    '0x03000009' => [qw(RSA_WITH_DES_CBC_SHA                         DES-CBC-SHA)],
    '0x03000007' => [qw(RSA_WITH_IDEA_SHA                            IDEA-CBC-SHA)],
    '0x03000000' => [qw(NULL_WITH_NULL_NULL                          NULL-NULL)],
    '0x03000001' => [qw(RSA_WITH_NULL_MD5                            NULL-MD5)],
    '0x03000002' => [qw(RSA_WITH_NULL_SHA                        NULL-SHA)],
    '0x030000FF' => [qw(EMPTY_RENEGOTIATION_INFO_SCSV            SCSV-RENEG)],
);

sub version {

    local $\ = "";
    print "NET::SSLhello        ($VERSION)\n";
    return;
}

sub __print { return sprintf( "#%s: %s\n", $SSLHELLO, @_ ); }
sub _yprint { return __print( sprintf( "%21s=%s", $_[0], $_[1] ) ); }

sub printConstants {
    local $\ = "";
    _trace("printConstants() {\n");
    my $line = "#--------------------+-------------------------------------------";
    print __print("#----------------------------------- SSLhello::Constants {");
    print __print($line);
    print("#OCfg::TLS_EXTENSIONS:\n");
    foreach my $key ( sort { lc $a cmp lc $b } keys %OCfg::TLS_EXTENSIONS ) {
        print "TLS_EXTENSIONS\{$key\}:\n";
        print "    \{ID\}          \= ";
        if ( defined( $OCfg::TLS_EXTENSIONS{$key}{ID} ) ) { print "$OCfg::TLS_EXTENSIONS{$key}{ID}\n"; }
        print "    \{CH\}          \= ";
        if ( defined( $OCfg::TLS_EXTENSIONS{$key}{CH} ) ) {
            foreach my $val ( @{ $OCfg::TLS_EXTENSIONS{$key}{CH} } ) { print "$val, "; }
        }
        print "\n";
        print "    \{RX\}          \= ";
        if ( defined( $OCfg::TLS_EXTENSIONS{$key}{RX} ) ) {
            foreach my $val ( @{ $OCfg::TLS_EXTENSIONS{$key}{RX} } ) { print "$val, "; }
        }
        print "\n";
        print "    \{RECOMMENDED\} \= ";
        if ( defined( $OCfg::TLS_EXTENSIONS{$key}{RECOMMENDED} ) ) { print "$OCfg::TLS_EXTENSIONS{$key}{RECOMMENDED}\n"; }
        print "    \{TLS13\}       \= ";
        if ( defined( $OCfg::TLS_EXTENSIONS{$key}{TLS13} ) ) {
            foreach my $val ( @{ $OCfg::TLS_EXTENSIONS{$key}{TLS13} } ) { print "$val, "; }
        }
        print "\n";
        print "    \{RFC\}         \= ";
        if ( defined( $OCfg::TLS_EXTENSIONS{$key}{RFC} ) ) {
            foreach my $val ( @{ $OCfg::TLS_EXTENSIONS{$key}{RFC} } ) { print "$val, "; }
        }
        print "\n";
        print "    \{DEFAULT\}     \= ";
        if ( defined( $OCfg::TLS_EXTENSIONS{$key}{DEFAULT} ) ) {
            foreach my $val ( @{ $OCfg::TLS_EXTENSIONS{$key}{DEFAULT} } ) {
                my $__first_indent = 0;
                _trace2_( ", \n" . " " x $__first_indent ) if ( $__first_indent > 0 );
                my $__decode_str = _decode_val( undef, \$val, \$OCfg::TLS_EXTENSIONS{$key}, $__first_indent, 20, ": ", ", ", " | ", " / " );

                _trace5_( " " x 20 ) if ( $__first_indent < 1 );
                print $__decode_str;
                $__first_indent = 20;
            }
        }
        print "\n";
        print "    \{CHECK\}       \= ";
        if ( defined( $OCfg::TLS_EXTENSIONS{$key}{CHECK} ) ) { print "$OCfg::TLS_EXTENSIONS{$key}{CHECK}\n"; }
        print "    \{COMMENT\}     \= ";
        if ( defined( $OCfg::TLS_EXTENSIONS{$key}{COMMENT} ) ) { print "$OCfg::TLS_EXTENSIONS{$key}{COMMENT}\n"; }
        print "\n";
    }
    print __print($line);
    print __print("#----------------------------------- SSLhello::Constants }");
    print "\n";
    _trace("printConstants() }\n");
    return;
}

sub printParameters {
    local $\ = "";
    _trace("printParameters() {\n");
    my $line = "#--------------------+-------------------------------------------";
    print __print("#---------------------------------- SSLhello::Parameters {");
    print __print($line);
    print _yprint( "retry",             $SSLhello::retry )             if ( defined($SSLhello::retry) );
    print _yprint( "timeout",           $SSLhello::timeout )           if ( defined($SSLhello::timeout) );
    print _yprint( "timeout",           $SSLhello::timeout )           if ( defined($SSLhello::timeout) );
    print _yprint( "connect_delay",     $SSLhello::connect_delay )     if ( defined($SSLhello::connect_delay) );
    print _yprint( "trace",             $SSLhello::trace )             if ( defined($SSLhello::trace) );
    print _yprint( "traceTIME",         $SSLhello::traceTIME )         if ( defined($SSLhello::traceTIME) );
    print _yprint( "usereneg",          $SSLhello::usereneg )          if ( defined($SSLhello::usereneg) );
    print _yprint( "double_reneg",      $SSLhello::double_reneg )      if ( defined($SSLhello::double_reneg) );
    print _yprint( "usesni",            $SSLhello::usesni )            if ( defined($SSLhello::usesni) );
    print _yprint( "use_sni_name",      $SSLhello::use_sni_name )      if ( defined($SSLhello::use_sni_name) );
    print _yprint( "sni_name",          $SSLhello::sni_name )          if ( defined($SSLhello::sni_name) );
    print _yprint( "use_signature_alg", $SSLhello::use_signature_alg ) if ( defined($SSLhello::use_signature_alg) );
    print _yprint( "useecc",            $SSLhello::useecc )            if ( defined($SSLhello::useecc) );
    print _yprint( "useecpoint",        $SSLhello::useecpoint )        if ( defined($SSLhello::useecpoint) );

    if ( %{$SSLhello::extensions_by_prot} ) {
        print __print("extensions_by_prot");
        foreach my $_prot ( sort keys %{$SSLhello::extensions_by_prot} ) {
            print _yprint( "->{$_prot}", join( ", ", @{ $SSLhello::extensions_by_prot->{$_prot} } ) ) if defined( $SSLhello::extensions_by_prot->{$_prot} );
        }
    }
    print _yprint( "check_extensions",      join( ", ", @{$SSLhello::check_extensions} ) ) if ( defined($SSLhello::check_extensions) );
    print _yprint( "extensions_max_values", $SSLhello::extensions_max_values )             if ( defined($SSLhello::extensions_max_values) );
    print _yprint( "starttls",              $SSLhello::starttls )                          if ( defined($SSLhello::starttls) );
    print _yprint( "starttlsType",          $SSLhello::starttlsType )                      if ( defined($SSLhello::starttlsType) );
    for my $i ( 1 .. 5 ) {
        print _yprint( "starttlsPhaseArray[$i]", $SSLhello::starttlsPhaseArray[$i] ) if ( defined( $SSLhello::starttlsPhaseArray[$i] ) );
    }
    for my $i ( 6 .. 8 ) {
        print _yprint( "starttlsErrorArray[" . ( $i - 5 ) . "]", $SSLhello::starttlsPhaseArray[$i] . " = starttlsPhaseArray[$i] (internally)" )
          if ( defined( $SSLhello::starttlsPhaseArray[$i] ) );
    }
    print _yprint( "starttlsDelay",   $SSLhello::starttlsDelay )   if ( defined($SSLhello::starttlsDelay) );
    print _yprint( "slowServerDelay", $SSLhello::slowServerDelay ) if ( defined($SSLhello::slowServerDelay) );
    print _yprint( "experimental",    $SSLhello::experimental )    if ( defined($SSLhello::experimental) );
    print _yprint( "proxyhost",       $SSLhello::proxyhost )       if ( defined($SSLhello::proxyhost) );
    print _yprint( "proxyport",       $SSLhello::proxyport )       if ( defined($SSLhello::proxyport) );
    print _yprint( "max_ciphers",     $SSLhello::max_ciphers )     if ( defined($SSLhello::max_ciphers) );
    print _yprint( "max_sslHelloLen", $SSLhello::max_sslHelloLen ) if ( defined($SSLhello::max_sslHelloLen) );
    print __print("# information about the OS and some socket constants and functions");
    print __print($line);
    print _yprint( "OS", $^O ) if ( defined($^O) );
    my $_pf_inet = Socket::PF_INET;
    print _yprint( "socket::PF" . "_INET", $_pf_inet );
    my $_af_inet = Socket::AF_INET;
    print _yprint( "socket::AF" . "_INET", $_af_inet );
    my $_sock_stream = ( defined(Socket::SOCK_STREAM) ) ? Socket::SOCK_STREAM : $OText::STR{'UNDEF'};
    print _yprint( "socket::SOCK_STREAM", $_sock_stream );
    my $_sol_socket = ( defined(Socket::SOL_SOCKET) ) ? Socket::SOL_SOCKET : $OText::STR{'UNDEF'};
    print _yprint( "socket::SOL_SOCKET", $_sol_socket );
    my $_so_sndtimeo = ( defined(Socket::SO_SNDTIMEO) ) ? Socket::SO_SNDTIMEO : $OText::STR{'UNDEF'};
    print _yprint( "socket::SO_SNDTIMEO", $_so_sndtimeo );
    my $_so_rcvtimeo = ( defined(Socket::SO_RCVTIMEO) ) ? Socket::SO_RCVTIMEO : $OText::STR{'UNDEF'};
    print _yprint( "socket::SO_RCVTIMEO", $_so_rcvtimeo );
    my ( $_dummy1, $_dummy2, $_protocol ) = getprotobyname('tcp');

    if ( !$_protocol ) {
        $_protocol = Socket::IPPROTO_TCP;
    }
    print _yprint( "socket::getprotobyname('tcp')", $_protocol );
    print __print($line);
    print __print("#---------------------------------- SSLhello::Parameters }");
    _trace("printParameters() }\n");
    return;
}

sub printCipherStringArray ($$$$$@) {

    my ( $legacy, $host, $port, $ssl, $usesni, @cipherArray ) = @_;

    my $arrayLen    = @cipherArray;
    my $cipherOrder = "";
    my $sni         = "";
    my $sep         = ", ";
    my $sep_sep     = " | ";
    my $protocol    = $PROTOCOL_VERSION{$ssl};
    my $cipher      = "";
    local $\ = "";

    _trace("printCipherStringArray($legacy, $host, $port, $ssl, $usesni, ...) {\n");
    $legacy = "csv" if ( $legacy eq "compact" );
    if ($usesni) {
        $sni = "SNI";
        $SSLhello::use_sni_name = 1 if ( ( $SSLhello::use_sni_name == 0 ) && ($SSLhello::sni_name) && ( $SSLhello::sni_name ne "1" ) );
        $sni .= " ($SSLhello::sni_name)" if ( ($SSLhello::use_sni_name) && ($SSLhello::sni_name) );
    }
    else {
        $sni = "no SNI";
    }

    my $firstEle = 0;
    if ( $arrayLen > 1 ) {
        if ( ( $cipherArray[0] eq $cipherArray[1] ) ) {
            if ( $legacy eq 'csv' ) { $cipherOrder = "Server Order"; }
            else                    { print "# cipher suites in server-preferred order:\n"; }
            $firstEle = 1;
        }
        else {
            if ( $legacy eq 'csv' ) { $cipherOrder = "No Order"; }
            else                    { print "# server has NO preferred order for cipher suites\n"; }
        }
    }
    elsif ( $arrayLen == 0 ) {
        if ( $legacy eq 'csv' ) {
            printf "%s%s%s%s%-6s%s%-8s%s%-12s%s%8s%s\n", $host, $sep, $port, $sep, $ssl, $sep, $sni, $sep, "", $sep, "", $sep;
        }
    }

    foreach my $protocolCipher ( @cipherArray[ $firstEle .. $#cipherArray ] ) {
        if ($usesni) {
            if ( $protocol != 0x0304 ) {
                if ( exists( $_SSLhello{$protocolCipher}{param}{server_name}{RX}{values} ) ) {
                    $sni = "SNI: yes";
                }
                else {
                    $sni = "SNI: no";
                }
            }
            else {
                $sni = "SNI";
            }
            $SSLhello::use_sni_name = 1      if ( ( $SSLhello::use_sni_name == 0 ) && ($SSLhello::sni_name) && ( $SSLhello::sni_name ne "1" ) );
            $sni .= " ($SSLhello::sni_name)" if ( ($SSLhello::use_sni_name) && ($SSLhello::sni_name) );
        }
        else {
            $sni = "no SNI";
        }
        if ( $protocol > 0x0002 ) {
            $cipher = "0x" . substr( $protocolCipher, -4, 4 );
        }
        else {
            $cipher = "0x" . substr( $protocolCipher, -6, 6 );
        }
        if ( $legacy eq 'csv' ) {
            printf "%s%s%s%s%-6s%s%-8s%s%-12s%s%8s%s", $host, $sep, $port, $sep, $ssl, $sep, $sni, $sep, $cipherOrder, $sep, $cipher, $sep;
            if ( ( defined( $cipherHexHash{$protocolCipher} ) ) && ( $#{ $cipherHexHash{$protocolCipher} } > 0 ) ) {
                printf "%-36s%s%-41s", $cipherHexHash{$protocolCipher}[1], $sep, $cipherHexHash{$protocolCipher}[0];
            }
            else {
                printf "%-36s%s%-41s\n", "NO-RFC-" . $cipher, $sep, "NO-RFC-" . $cipher;
            }
            print $sep;
            print getCipherParameter( $protocolCipher, "Paramters: ", $sep_sep ) . "\n";
        }
        else {
            printf "# Cipher-String: >%s<,", $cipher;
            if ( ( defined( $cipherHexHash{$protocolCipher} ) ) && ( $#{ $cipherHexHash{$protocolCipher} } > 0 ) ) {
                printf " %-36s, %s", $cipherHexHash{$protocolCipher}[1], $cipherHexHash{$protocolCipher}[0];
                print getCipherParameter( $protocolCipher, ", Paramters: ", $sep_sep );
            }
            else {
                print " NO-RFC-" . $cipher;
            }
            print "\n";
        }
    }
    if ( $legacy eq 'csv' ) {
        print "\n";
    }
    _trace("printCipherStringArray() }\n");
    return;
}

sub checkSSLciphers ($$$@) {
    my ( $host, $port, $ssl, @cipher_str_array ) = @_;
    my $cipher_spec               = "";
    my $acceptedCipher            = "";
    my @cipherSpecArray           = ();
    my @acceptedCipherArray       = ();
    my @acceptedCipherSortedArray = ();
    my $arrayLen                  = 0;
    my $i                         = 0;
    my $protocol                  = $PROTOCOL_VERSION{$ssl};
    my $maxCiphers                = $SSLhello::max_ciphers;
    local $\ = "";
    %_SSLhello = ();
    printConstants()  if ( $SSLhello::trace > 3 );
    printParameters() if ( $SSLhello::trace > 3 );

    error_handler->reset_err( { module => "#$SSLHELLO", sub => 'checkSSLciphers', print => ( $SSLhello::trace > 3 ), trace => $SSLhello::trace } );

    _trace("checkSSLciphers($host, $port, $ssl,");
    _trace_cipher_array( " ) {", @cipher_str_array );

    if ( $protocol == $PROTOCOL_VERSION{'SSLv2'} ) {
        _trace4_("\n");
        foreach my $cipher_str (@cipher_str_array) {
            _trace4(" checkSSLciphers: Cipher-String: >$cipher_str< -> ");
            ($cipher_str) =~ s/(?:0x03|0x02|0x)?\s?([a-fA-F0-9]{2})\s?/chr(hex $1)/egx;
            _trace4_( " >" . hexCodedCipher($cipher_str) . "<\n" );

            $cipher_spec .= $cipher_str;
        }
        _trace4_("\n");
        $acceptedCipher = _doCheckSSLciphers( $host, $port, $protocol, $cipher_spec );
        my $anzahl = int length($acceptedCipher) / 3;
        _trace( " checkSSLciphers: Accepted " . $anzahl . " Ciphers:" );
        _trace_cipher_array( "", compileSSL2CipherArray($acceptedCipher) );
        _trace("checkSSLciphers() }\n");
        return ( compileSSL2CipherArray($acceptedCipher) );
    }
    else {
        $cipher_spec = "";
        _trace4_("\n");
        my $tot = scalar(@cipher_str_array);
        my $cnt = 0;
        my $len = 0;
        foreach my $cipher_str (@cipher_str_array) {
            $cnt++;
            $len = ( $len < length($cipher_str) ) ? 1 : ( $len - length($cipher_str) );
            printf( "$OText::STR{'INFO'}  cipher %4d/%d %s%s\n", $cnt, $tot, $cipher_str, " " x $len ) if ( 1 < $SSLhello::verbose );
            _trace5(" checkSSLciphers: add cipher >$cipher_str< to cipher-string -> ");
            if ( $cipher_str !~ /0x02/x ) {
                ($cipher_str) =~ s/(?:0x0[3-9a-fA-F]00|0x)?\s?([a-fA-F0-9]{2})\s?/chr(hex $1)/egx;
                _trace5_( "  >" . hexCodedCipher($cipher_str) . "<" );
            }
            else {
                _trace5_("  SSL2-Cipher suppressed\n");
                next;
            }
            _trace5_("\n");

            push( @cipherSpecArray, $cipher_str );
            $arrayLen = @cipherSpecArray;
            if ( $arrayLen >= $maxCiphers ) {
                $my_error = "";

                error_handler->reset_err( { module => "#$SSLHELLO", sub => 'checkSSLciphers', print => ( $SSLhello::trace > 3 ), trace => $SSLhello::trace } );
                $cipher_spec = join( "", @cipherSpecArray );

                if ( $SSLhello::trace > 1 ) {
                    $i = 0;
                    my $txt = "";
                    $txt = " (STARTTLS)" if $SSLhello::starttls;
                    _trace1( " checkSSLciphers:$txt Checking " . scalar(@cipherSpecArray) . " Ciphers, this round (1):" );
                    _trace4_("\n");
                    _trace_( _trace_array2str( compileTLSCipherArray($cipher_spec) ) . "\n" );
                }
                $acceptedCipher = _doCheckSSLciphers( $host, $port, $protocol, $cipher_spec, $dtlsEpoch );
                _trace2_("       ");
                if ($acceptedCipher) {
                    _trace1_( "=> found >0x0300" . hexCodedCipher($acceptedCipher) . "<\n" );
                    if ( grep { $_ eq $acceptedCipher } @cipherSpecArray ) {
                        @cipherSpecArray = grep { $_ ne $acceptedCipher } @cipherSpecArray;
                    }
                    else {
                        Carp::carp( "$OText::STR{WARN} Server replied (again) with cipher '0x"
                              . hexCodedCipher($acceptedCipher)
                              . "' that has not been requested this time (1): ('0x"
                              . hexCodedCipher( $cipherSpecArray[0] )
                              . " ... 0x"
                              . hexCodedCipher( $cipherSpecArray[-1] )
                              . "'." );
                        @cipherSpecArray = ();
                    }
                    push( @acceptedCipherArray, $acceptedCipher );
                }
                else {
                    _trace1_("=> no Cipher found\n");
                    if (   ( ( error_handler->get_err_type() ) <= $OERR{'SSLHELLO_RETRY_HOST'} )
                        || ( $my_error =~ /Fatal Exit/ )
                        || ( $my_error =~ /make a connection/ )
                        || ( $my_error =~ /create a socket/ ) )
                    {

                        _trace(" checkSSLciphers (1.1): '$my_error'\n") if ($my_error);
                        _trace( "**WARNING: checkSSLciphers => Exit loop (1.1): -> Abort '$host:$port' caused by " . error_handler->get_err_str . "\n" );
                        @cipherSpecArray = ();
                        last;
                    }
                    elsif (( ( error_handler->get_err_type() ) <= $OERR{'SSLHELLO_RETRY_PROTOCOL'} )
                        || ( $my_error =~ /answer ignored/ )
                        || ( $my_error =~ /protocol_version.*?not supported/ )
                        || ( $my_error =~ /check.*?aborted/x ) )
                    {
                        _trace2(" checkSSLciphers (1.2): '$my_error'\n") if ($my_error);
                        @cipherSpecArray = ();
                        last;
                    }
                    elsif (( $my_error =~ /target.*?ignored/x )
                        || ( $my_error =~ /protocol.*?ignored/x ) )
                    {
                        _trace2(" checkSSLciphers (1.3): \'$my_error\'\n") if ($my_error);
                        Carp::carp("$OText::STR{WARN} checkSSLciphers => Exit loop (1.3)");
                        @cipherSpecArray = ();
                        last;
                    }
                    elsif ( ( ( error_handler->get_err_type() ) <= $OERR{'SSLHELLO_RETRY_CIPHERS'} ) || ( $my_error =~ /\-> Received NO Data/ ) ) {
                        if ( $SSLhello::noDataEqNoCipher == 1 ) {
                            _trace2(
" checkSSLciphers (1.4): Ignore error messages for TLS intolerant servers that do not respond if non of the ciphers are supported. Ignored: '$my_error'\n"
                            );
                            @cipherSpecArray = ();
                            $my_error        = "";
                            next;
                        }
                        else {
                            _trace2(
" checkSSLciphers (1.5): \'$my_error\', => Please use the option \'--noDataEqNoCipher\' for servers not answeing if none of the requested ciphers are supported. Retry to test the following cipheres individually:\n"
                            );
                            Carp::carp(
"$OText::STR{WARN} checkSSLciphers (1.5): \'$my_error\', => Please use the option \'--noDataEqNoCipher\' for servers not answeing if none of the requested ciphers are supported."
                            );
                        }
                    }
                    elsif ( ( ( error_handler->get_err_type() ) <= $OERR{'SSLHELLO_RETRY_RECORD'} ) || ( $my_error =~ /Error 1: too many requests/ ) ) {
                        _trace2(" checkSSLciphers (1.6): \'$my_error\', => Please use the option \'--starttls_delay=SEC\' to slow down\n");
                        Carp::carp("$OText::STR{WARN} checkSSLciphers (1.6): \'$my_error\', => Please use the option \'--starttls_delay=SEC\' to slow down");
                        next;
                    }
                    elsif ( ( error_handler->is_err ) || $my_error ) {
                        unless ( error_handler->is_err ) {
                            error_handler->new(
                                {
                                    type    => $OERR{'SSLHELLO_ERROR_MESSAGE_IGNORED'},
                                    id      => '(1.9)',
                                    message => "Unexpected Error Message ignored: \'$my_error\'",
                                    warn    => 1,
                                }
                            );
                        }
                        $my_error = "";

                        error_handler->reset_err(
                            { module => "#$SSLHELLO", sub => 'checkSSLciphers', print => ( $SSLhello::trace > 3 ), trace => $SSLhello::trace } );
                    }
                    @cipherSpecArray = ();
                }
            }
        }

        while ( ( @cipherSpecArray > 0 ) && ( !error_handler->is_err ) && ( !$my_error ) ) {
            $cipher_spec = join( "", @cipherSpecArray );
            if ( $SSLhello::trace > 1 ) {
                $i = 0;
                _trace( " checkSSLciphers: Checking " . scalar(@cipherSpecArray) . " Ciphers, this round (2):" );
                _trace4_("\n");
                _trace_( _trace_array2str( compileTLSCipherArray($cipher_spec) ) . "\n" );
            }
            $acceptedCipher = _doCheckSSLciphers( $host, $port, $protocol, $cipher_spec, $dtlsEpoch );
            _trace2_("       ");
            if ($acceptedCipher) {
                _trace1_( "=> found >0x0300" . hexCodedCipher($acceptedCipher) . "<\n" );
                if ( grep { $_ eq $acceptedCipher } @cipherSpecArray ) {
                    @cipherSpecArray = grep { $_ ne $acceptedCipher } @cipherSpecArray;
                }
                else {
                    Carp::carp( "$OText::STR{WARN} Server replied (again) with cipher '0x"
                          . hexCodedCipher($acceptedCipher)
                          . "' that has not been requested this time (2): ('0x"
                          . hexCodedCipher( $cipherSpecArray[0] )
                          . " ... 0x"
                          . hexCodedCipher( $cipherSpecArray[-1] )
                          . "'." );
                    @cipherSpecArray = ();
                }
                push( @acceptedCipherArray, $acceptedCipher );
            }
            else {
                _trace1_("=> no cipher found\n");
                if ( ( $my_error =~ /Fatal Exit/ ) || ( $my_error =~ /make a connection/ ) || ( $my_error =~ /create a socket/ ) ) {
                    _trace2(" checkSSLciphers (2.1): '$my_error'\n");
                    Carp::carp("$OText::STR{WARN} checkSSLciphers => Exit loop (2.1)");
                    @cipherSpecArray = ();
                    last;
                }
                elsif ( ( $my_error =~ /answer ignored/ ) || ( $my_error =~ /protocol_version.*?not supported/ ) || ( $my_error =~ /check.*?aborted/ ) ) {
                    _trace1(" checkSSLciphers (2.2): Exit loop");
                    @cipherSpecArray = ();
                    last;
                }
                elsif ( ( $my_error =~ /target.*?ignored/x ) || ( $my_error =~ /protocol.*?ignored/x ) ) {
                    _trace2(" checkSSLciphers (2.3): '$my_error'\n");
                    Carp::carp("$OText::STR{WARN} checkSSLciphers => Exit loop (2.3)");
                    @cipherSpecArray = ();
                    last;
                }
                elsif ( $my_error =~ /\-> Received NO Data/ ) {
                    if ( $SSLhello::noDataEqNoCipher == 1 ) {
                        _trace1(
" checkSSLciphers (2.4): Ignore Error Messages for TLS intolerant Servers that do not respond if non of the Ciphers are supported. Ignored: '$my_error'\n"
                        );
                        @cipherSpecArray = ();
                        $my_error        = "";
                        next;
                    }
                    else {
                        _trace2(
" checkSSLciphers (2.5): '$my_error', => Please use the option \'--noDataEqNoCipher\' for Servers not answering if none of the requested Ciphers are supported. Retry to test the following Cipheres individually:\n"
                        );
                        Carp::carp(
"$OText::STR{WARN} checkSSLciphers (2.5): '$my_error', => Please use the option \'--noDataEqNoCipher\' for Servers not answering if none of the requested Ciphers are supported."
                        );
                    }
                }
                elsif ( $my_error =~ /Error 1: too many requests/ ) {
                    _trace2(" checkSSLciphers (1.6): \'$my_error\', => Please use the option \'--starttls_delay=SEC\' to slow down\n");
                    Carp::carp("$OText::STR{WARN} checkSSLciphers (1.6): \'$my_error\', => Please use the option \'--starttls_delay=SEC\' to slow down");
                    next;
                }
                elsif ($my_error) {
                    _trace2(" checkSSLciphers (2.6): Unexpected Error Message ignored: '$my_error'\n");
                    Carp::carp("$OText::STR{WARN} checkSSLciphers (2.6): Unexpected Error Message ignored: '$my_error'\n");
                    $my_error = "";
                }
                @cipherSpecArray = ();
            }
        }

        _trace( " checkSSLciphers: Accepted " . scalar(@acceptedCipherArray) . " Ciphers (unsorted):" );
        _trace_cipher_array( "", compileTLSCipherArray( join( "", @acceptedCipherArray ) ) );

        my $cipher_str = join( "", @acceptedCipherArray );
        printTLSCipherList($cipher_str) if ( $SSLhello::trace > 3 );

        while ($cipher_str) {
            _trace2( " checkSSLciphers: Check Cipher Priority for Cipher-Spec >" . hexCodedString($cipher_str) . "<\n" );
            $my_error       = "";
            $acceptedCipher = _doCheckSSLciphers( $host, $port, $protocol, $cipher_str, $dtlsEpoch, 1 );
            _trace2_( "# -->" . hexCodedCipher($acceptedCipher) . "<\n" );
            if ($my_error) {
                _trace2(" checkSSLciphers (3): '$my_error'\n");
                my $str = _trace_array2str( compileTLSCipherArray( join( "", @acceptedCipherArray ) ) );
                if (   ( $my_error =~ /Fatal Exit/ )
                    || ( $my_error =~ /make a connection/ )
                    || ( $my_error =~ /create a socket/ )
                    || ( $my_error =~ /target.*?ignored/x )
                    || ( $my_error =~ /protocol.*?ignored/x ) )
                {
                    _trace1(
" checkSSLciphers (3.1): => Unexpected Loss of Connection while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'\n"
                    );
                    Carp::carp(
"$OText::STR{WARN} checkSSLciphers (3.1): => Unexpected Loss of Connection while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'"
                    );
                    $my_error = "";
                    last;
                }
                elsif ( ( $my_error =~ /answer ignored/ ) || ( $my_error =~ /protocol_version.*?not supported/ ) || ( $my_error =~ /check.*?aborted/x ) ) {
                    _trace1(
" checkSSLciphers (3.2): => Unexpected Lack of Data or unexpected Answer while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'\n"
                    );
                    Carp::carp(
"$OText::STR{WARN} checkSSLciphers (3.2): => Unexpected Lack of Data or unexpected Answer while checking the priority of the ciphers \'$str\' -     > Exit loop. Reason: '$my_error'"
                    );
                    OCfg::hint("The server may have an IPS in place. To slow down the test, consider adding the option '--connect-delay=SEC'.");
                    $my_error = "";
                    last;
                }
                else {
                    _trace1(" checkSSLciphers (3.3): => Received no cipher while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: ''\n");
                    Carp::carp(
"$OText::STR{WARN} checkSSLciphers (3.3): => Received no cipher while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'"
                    );
                    OCfg::hint("The server may have an IPS in place. To slow down the test, consider adding the option '--connect-delay=SEC'.");
                    $my_error = "";
                    last;
                }
            }
            if ($acceptedCipher) {
                push( @acceptedCipherSortedArray, $acceptedCipher );
                _doCheckAllExtensions( $host, $port, $protocol, $acceptedCipher, $dtlsEpoch, 1 );
                $arrayLen = @acceptedCipherSortedArray;
                if ( $arrayLen == 1 ) {
                    if ( $acceptedCipher eq ( $acceptedCipherArray[0] ) ) {
                        _trace3_( "# --> Got back 1st cipher of unsorted List => Check again with this Cipher >"
                              . hexCodedTLSCipher($acceptedCipher)
                              . "< at the end of the List\n" );
                        shift(@acceptedCipherArray);
                        $cipher_str = join( "", @acceptedCipherArray ) . $acceptedCipher;
                        _trace3_( "# --> Check Cipher Prioity for Cipher-S(2) > " . hexCodedString($cipher_str) . "<\n" );
                        _trace4_( "# ---> backup parameters to values of the first check of cipher " . hexCodedTLSCipher($acceptedCipher) . "\n" );
                        my %_param_tmp_hash = ();
                        %_param_tmp_hash = %{ $_SSLhello{ '0x0300' . hexCodedCipher($acceptedCipher) }{param} }
                          if ( exists( $_SSLhello{ '0x0300' . hexCodedCipher($acceptedCipher) }{param} ) );
                        $acceptedCipher = _doCheckSSLciphers( $host, $port, $protocol, $cipher_str, $dtlsEpoch, 1 );
                        _trace3_( "# -->" . hexCodedCipher($acceptedCipher) . "<\n" );
                        _trace4_( "# --->" . hexCodedCipher($acceptedCipher) . "<\n" );

                        if ($acceptedCipher) {
                            push( @acceptedCipherSortedArray, $acceptedCipher );
                            if ( $acceptedCipher eq $acceptedCipherSortedArray[0] ) {
                                _trace4_( "# ---> restore stored parameters to values of first check of cipher " . hexCodedTLSCipher($acceptedCipher) . "\n" );
                                $_SSLhello{ '0x0300' . hexCodedCipher($acceptedCipher) }{param} = \%_param_tmp_hash;
                            }
                            else {
                                _trace4_("# ---> is a new cipher => noi preferred order by the server\n");
                                _doCheckAllExtensions( $host, $port, $protocol, $acceptedCipher, $dtlsEpoch, 1 );
                            }
                        }
                    }
                    else {
                        push( @acceptedCipherSortedArray, $acceptedCipher );
                    }
                }

                if ( ( grep { $_ eq $acceptedCipher } @acceptedCipherArray ) || ( ( $arrayLen == 1 ) && ( $acceptedCipher eq $acceptedCipherSortedArray[1] ) ) )
                {
                    @acceptedCipherArray = grep { $_ ne $acceptedCipher } @acceptedCipherArray;
                }
                else {
                    Carp::carp( "$OText::STR{WARN} checkSSLciphers: Server replied (again) with cipher '0x"
                          . hexCodedCipher($acceptedCipher)
                          . "' that has not been requested this time (3): ('0x"
                          . hexCodedCipher( $acceptedCipherArray[0] )
                          . " ... 0x"
                          . hexCodedCipher( $acceptedCipherArray[-1] )
                          . "'. Untested Ciphers:" );
                    my $str = _trace_array2str( compileTLSCipherArray( join( "", @acceptedCipherArray ) ) );
                    @acceptedCipherArray = ();
                }

                $cipher_str = join( "", @acceptedCipherArray );
            }
            else {
                _trace2(" checkSSLciphers (6): '$my_error'\n");
                my $str = _trace_array2str( compileTLSCipherArray( join( "", @acceptedCipherArray ) ) );
                if (   ( $my_error =~ /Fatal Exit/ )
                    || ( $my_error =~ /make a connection/ )
                    || ( $my_error =~ /create a socket/ )
                    || ( $my_error =~ /target.*?ignored/x )
                    || ( $my_error =~ /protocol.*?ignored/x ) )
                {
                    _trace1(
" checkSSLciphers (6.1): => Unexpected Loss of Connection while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'\n"
                    );
                    Carp::carp(
"$OText::STR{WARN} checkSSLciphers (6.1): => Unexpected Loss of Connection while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'"
                    );
                    $my_error = "";
                    last;
                }
                elsif ( $my_error =~ /Error 1: too many requests/ ) {
                    _trace2(" checkSSLciphers (1.6): \'$my_error\', => Please use the option \'--starttls_delay=SEC\' to slow down\n");
                    Carp::carp("$OText::STR{WARN} checkSSLciphers (1.6): \'$my_error\', => Please use the option \'--starttls_delay=SEC\' to slow down");
                    next;
                }
                elsif ($my_error) {
                    _trace1(
" checkSSLciphers (6.2): => Unexpected Lack of Data or unexpected Answer while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: ''\n"
                    );
                    Carp::carp(
"$OText::STR{WARN} checkSSLciphers (6.2): => Unexpected Lack of Data or unexpected Answer while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'"
                    );
                    OCfg::hint("The server may have an IPS in place. To slow down the test, consider adding the option '--connect-delay=SEC'.");
                    $my_error = "";
                    last;
                }
                else {
                    _trace1(" checkSSLciphers (6.3): => Received no cipher while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: ''\n");
                    Carp::carp(
"$OText::STR{WARN} checkSSLciphers (6.3): => Received no cipher while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'"
                    );
                    OCfg::hint("The server may have an IPS in place. To slow down the test, consider adding the option '--connect-delay=SEC'.");
                    $my_error = "";
                    last;
                }
            }
        }

        _trace("checkSSLciphers() }\n");
        return ( compileTLSCipherArray( join( "", @acceptedCipherSortedArray ) ) );
    }
}

sub getSSLciphersWithParam {
    my ( $host, $port, $ssl, @cipher_str_array ) = @_;
    my %ciphers;
    my $_i      = 0;
    my $lastkey = "";
    _trace("getSSLciphersWithParam($host, $port, $ssl, ...) {\n");
    @{ $ciphers{0} } = checkSSLciphers( $host, $port, $ssl, @cipher_str_array );
    foreach my $key ( @{ $ciphers{0} } ) {
        next if ( $lastkey eq $key );
        $lastkey = $key;
        $_i++;
        $ciphers{$_i} = [ $key, getCipherParameter( $key, "", " | " ) ];
    }
    _trace("getSSLciphersWithParam()\t= %ciphers }\n");
    return %ciphers;
}

sub openTcpSSLconnection ($$) {
    my $host = shift || "";
    my $port = shift || "";
    my $socket;
    my $connect2ip;
    my $alarmTimeout    = $SSLhello::timeout + 1;
    my $proxyConnect    = "";
    my $clientHello     = "";
    my $input           = "";
    my $input2          = "";
    my $retryCnt        = 0;
    my $sleepSecs       = $SSLhello::starttlsDelay   || 0;
    my $slowServerDelay = $SSLhello::slowServerDelay || 0;
    my $suspendSecs     = 0;
    my $firstMessage    = "";
    my $secondMessage   = "";
    my $starttlsType    = 0;
    _trace2("openTcpSSLconnection($host, $port) {\n");

    my @starttls_matrix = (
        [
            "SMTP",         ".*?(?:^|\\n)220\\s", "EHLO o-saft.localhost\r\n",  ".*?(?:^|\\n)250\\s",
            "STARTTLS\r\n", ".*?(?:^|\\n)220\\s", ".*?(?:^|\\n)(?:421|450)\\s", ".*?(?:^|\\n)4[57]4\\s",
            ".*?(?:^|\\n)(?:451|50[023]|554)\\s",
        ],
        [
            "SMTP_2",

            ".*?(?:^|\\n)220",
            "EHLO o-saft.localhost\r\n",
            ".*?(?:^|\\n)250",
            "STARTTLS\r\n",
            ".*?(?:^|\\n)220",
            ".*?(?:^|\\n)(?:421|450)",
            ".*?(?:^|\\n)4[57]4",
            ".*?(?:^|\\n)(?:451|50[023]|554)",
        ],
        [
            "IMAP",
            ".*?(?:^|\\n)\\*\\s*OK.*?IMAP(?:\\s|\\d)",
            "",
            "",
            "a001 STARTTLS\r\n",
            ".*?(?:^|\\n)(?:\\*|a001)\\s*OK\\s",
            "",
            "",
".*?(?:^|\\n)(?:\\*|a00\\d)\\s*(?:BAD|NO)\\s.*?(?:invalid.+?command|unrecognized.+?command|TLS.*?(?:isn\\'t|not)|\\s+no\\s+.*?(?:SSL|TLS)|authoriz)",
        ],
        [
            "IMAP_CAPACITY",
            ".*?(?:^|\\n)\\*\\s*OK.*?IMAP(?:\\s|\\d)",
            "a001 CAPABILITY\r\n",
            ".*?(?:^|\\n)\\*\\s*CAPABILITY",
            "a002 STARTTLS\r\n",
            ".*?(?:^|\\n)(?:\\*|a002)\\s*OK\\s",
            "",
            "",
".*?(?:^|\\n)(?:\\*|a00\\d)\\s*(?:BAD|NO)\\s.*?(?:invalid.+?command|unrecognized.+?command|TLS.*?(?:isn\\'t|not)|\\s+no\\s+.*?(?:SSL|TLS)|authoriz)",
        ],
        [
            "IMAP_2",
            ".*?(?:^|\\n)\\*\\sOK.*?IMAP(?:\\s|\\d)",
            "",
            "",
            ". STARTTLS\r\n",
            ".*?(?:^|\\n). OK\\s",
            "",
            "",
".*?(?:^|\\n)(?:\\*|a00\\d)\\s*(?:BAD|NO)\\s.*?(?:invalid.+?command|unrecognized.+?command|TLS.*?(?:isn\\'t|not)|\\s+no\\s+.*?(?:SSL|TLS)|authoriz)",
        ],
        [
            "POP3", ".*?(?:^|\\n)\\+\\s*OK(?:\\s+|.*?ready|\\r|\\n)",
            "",     "", "STLS\r\n", ".*?(?:^|\\n)\\+\\s*OK", "", "",
            ".*?(?:^|\\n)\\-\\s*ERR.*?(?:invalid command|TLS.*?(?:isn\\'t|not)|\\s+no\\s+.*?(?:SSL|TLS)|authoriz)",
        ],
        [
            "POP3_CAPACITY", ".*?(?:^|\\n)\\+\\s*OK(?:\\s+|.*?ready|\\r|\\n)",
            "CAPA\r\n", ".*?(?:^|\\n)\\+\\s*OK", "STLS\r\n", ".*?(?:^|\\n)\\+\\s*OK", "", "",
            ".*?(?:^|\\n)\\-\\s*ERR.*?(?:invalid command|TLS.*?(?:isn\\'t|not)|\\s+no\\s+.*?(?:SSL|TLS)|authoriz)",
        ],
        [ "FTPS", ".*?(?:^|\\n)220\\s", "", "", "AUTH TLS\r\n", ".*?(?:^|\\n)234\\s+", "", "", "", ],
        [
            "LDAP", "", "", "",
            "0\x1d\x02\x01\x01w\x18\x80\x161.3.6.1.4.1.1466.20037",
            "0\\x24\\x02\\x01\\x01\\x78\\x1F\\x0A\\x01\\x00\\x04\\x00\\x04\\x00\\x8A\\x161\\.3\\.6\\.1\\.4\\.1\\.1466\\.20037",
            "", "", "",
        ],
        [
            "RDP", "", "", "",
            "\x03\x00\x00\x13\x0E\xE0\x00\x00\x00\x00\x00\x01\x00\x08\x00\x0B\x00\x00\x00",
            "\\x03\\x00\\x00\\x13\\x0E\\xD0.....\\x02.\\x08\\x00[\\x01\\x02\\x08]\\x00\\x00\\x00",
            "", "", "\\x03\\x00\\x00\\x13\\x0E\\xD0.....\\x03.\\x08\\x00[\\x01\\x02\\x08]\\x00\\x00\\x00",
        ],

        [
            "RDP_SSL", "", "", "",
            "\x03\x00\x00\x13\x0E\xE0\x00\x00\x00\x00\x00\x01\x00\x08\x00\x01\x00\x00\x00",
            "\\x03\\x00\\x00\\x13\\x0E\\xD0.....\\x02.\\x08\\x00[\\x01\\x02\\x08]\\x00\\x00\\x00",
            "", "", "\\x03\\x00\\x00\\x13\\x0E\\xD0.....\\x03.\\x08\\x00[\\x01\\x02\\x08]\\x00\\x00\\x00",
        ],
        [
            "XMPP",
            "",
            "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'"
              . " to='"
              . $host
              . "' xml:lang='en' version='1.0'>",
            "<stream:stream.*?>",
            "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>",
            "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>",

            "",
            "",
            "",
        ],
        [ "ACAP", ".*?(?:^|\\n)\\*\\s*OK.*?ACAP(?:\\s|\\d)", "", "", "a001 STARTTLS\r\n", ".*?(?:^|\\n)(?:\\*|a001)\\s*OK\\s", "", "", "", ],
        [
            "IRC",

            ".*?NOTICE.*?",
            "",
            "",
            "STARTTLS\r\n",
            ".*?(?:^|\n)\\:.*?\\s670\\s+\\:STARTTLS\\s",
            ".*?(?:^|\n)ERROR\\s.*?too.*?(?:fast|much|many)",
            "",
            ".*?(?:^|\\n)421\\s",
        ],
        [
            "IRC_CAPACITY",

            ".*?NOTICE.*?",
            "CAP LS\r\n",
            ".*?(?:^|\n)\\:.*?\\sCAP\\s.*?\\:.*tls(?:\\s|$|\r\n)",
            "STARTTLS\r\n",
            ".*?(?:^|\n)\\:.*?\\s670\\s+\\:STARTTLS\\s",
            ".*?(?:^|\n)ERROR\\s.*?too.*?(?:fast|much|many)",
            "",
            ".*?(?:^|\\n)421\\s",
        ],
        [ "CUSTOM", "", "", "", "", "", "", "", "", ],
    );

    my %startTlsTypeHash;
    local $my_error = "";
    error_handler->reset_err( { module => "#$SSLHELLO", sub => 'openTcpSSLconnection', print => ( $SSLhello::trace > 3 ), trace => $SSLhello::trace } );
    if ( ($SSLhello::proxyhost) && ($SSLhello::proxyport) ) {
        _trace2(" openTcpSSLconnection: Try to connect and open a SSL connection to $host:$port via proxy "
              . $SSLhello::proxyhost . ":"
              . $SSLhello::proxyport
              . "\n" );
    }
    else {
        _trace2(" openTcpSSLconnection: Try to connect and open a SSL connection to $host:$port\n");
    }
    $retryCnt = 0;
    if ($SSLhello::starttls) {
        $startTlsTypeHash{ $starttls_matrix[$_][0] } = $_ for 0 .. scalar(@starttls_matrix) - 1;
        _trace4(" openTcpSSLconnection: nr of Elements in starttlsTypeMatrix: "
              . scalar(@starttls_matrix)
              . "; looking for starttlsType $SSLhello::starttlsType\n" );

        if ( defined( $startTlsTypeHash{ uc($SSLhello::starttlsType) } ) ) {
            $starttlsType = $startTlsTypeHash{ uc($SSLhello::starttlsType) };
            _trace4(" openTcpSSLconnection: Index-Nr of StarttlsType $SSLhello::starttlsType is $starttlsType\n");
            if ( grep { /^$starttlsType$/x } ( '12', '13', '14', '15' ) ) {
                if ( $SSLhello::experimental > 0 ) {
                    _trace_("\n");
                    _trace(
" openTcpSSLconnection: WARNING: use of STARTTLS type $starttls_matrix[$starttlsType][0] is experimental! Send us feedback to o-saft (at) lists.owasp.org, please\n"
                    );
                }
                else {
                    if ( grep { /^$starttlsType$/x } ( '12', '13', '14', '15' ) ) {
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_ABORT_PROGRAM'},
                                id      => 'ckeck starttls type (1)',
                                message =>
"WARNING: use of STARTTLS type $starttls_matrix[$starttlsType][0] is experimental and *untested*!! Please take care! Please add option '--experimental' to use it. Please send us your feedback to o-saft (at) lists.owasp.org",
                                warn => 1,
                            }
                        );
                    }
                    else {
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_ABORT_PROGRAM'},
                                id      => 'ckeck starttls type (2)',
                                message =>
"WARNING: use of STARTTLS type $starttls_matrix[$starttlsType][0] is experimental! Please add option '--experimental' to use it. Please send us your feedback to o-saft (at) lists.owasp.org",
                                warn => 1,
                            }
                        );
                    }
                    exit(1);
                }
            }
            if ( $starttls_matrix[$starttlsType][0] eq 'CUSTOM' ) {
                for my $i ( 1 .. 8 ) {
                    if ( defined( $SSLhello::starttlsPhaseArray[$i] ) ) {
                        _trace4(
                            " openTcpSSLconnection: Customise starttls_matrix: \$SSLhello::starttlsPhaseArray[$i]= >$SSLhello::starttlsPhaseArray[$i]< = hex: >"
                              . unpack( "H*", $SSLhello::starttlsPhaseArray[$i] )
                              . "<\n" );
                        if ( ( $i == 2 ) || ( $i == 4 ) ) {
                            $starttls_matrix[$starttlsType][$i] = "$SSLhello::starttlsPhaseArray[$i]";
                            ( $starttls_matrix[$starttlsType][$i] ) =~ s/\\r/chr(13)/egx;
                            ( $starttls_matrix[$starttlsType][$i] ) =~ s/\\n/chr(10)/egx;
                            ( $starttls_matrix[$starttlsType][$i] ) =~ s/\\t/chr(9)/egx;
                            ( $starttls_matrix[$starttlsType][$i] ) =~ s/\\e/chr(27)/egx;
                            ( $starttls_matrix[$starttlsType][$i] ) =~ s/\\x([a-fA-F0-9]{2})/chr(hex $1)/egx;
                            ( $starttls_matrix[$starttlsType][$i] ) =~ s/\\\\/\\/gx;
                        }
                        else {
                            $starttls_matrix[$starttlsType][$i] = $SSLhello::starttlsPhaseArray[$i];
                        }
                        _trace2(" openTcpSSLconnection: Customise \$starttls_matrix[$starttlsType][$i]= >$starttls_matrix[$starttlsType][$i]< = hex: >"
                              . unpack( "H*", $starttls_matrix[$starttlsType][$i] )
                              . "<\n" );
                    }
                }
            }
        }
        else {
            $starttlsType = 0;
            Carp::carp("$OText::STR{WARN} openTcpSSLconnection: Undefined StarttlsType, use $starttls_matrix[$starttlsType][0] instead");
        }
    }

  RETRY_TO_OPEN_SSL_CONNECTION: {
        do {
            error_handler->reset_err( { module => "#$SSLHELLO", sub => 'openTcpSSLconnection', print => ( $SSLhello::trace > 3 ), trace => $SSLhello::trace } );
            if ( defined($SSLhello::connect_delay) && ( $SSLhello::connect_delay > 0 ) ) {
                _trace_("\n");
                _trace(" openTcpSSLconnection: connect delay $cfg{'connect_delay'} second(s)\n");
                sleep($SSLhello::connect_delay);
                _trace4(" openTcpSSLconnection: connect delay $cfg{'connect_delay'} second(s) [End]\n");
            }
            alarm(0);
            if ( $retryCnt > 0 ) {
                _trace1_("\n") if ( ( $retryCnt == 1 ) && ( $SSLhello::trace < 3 ) );
                if ( ($SSLhello::proxyhost) && ($SSLhello::proxyport) ) {
                    _trace1(" openTcpSSLconnection: $retryCnt. Retry to connect and open a SSL connection to $host:$port via proxy "
                          . $SSLhello::proxyhost . ":"
                          . $SSLhello::proxyport );
                    if ( $retryCnt > $SSLhello::retry ) {
                        _trace1_(" (this is an additional retry after suspension)");
                    }
                    _trace1_("\n");
                }
                else {
                    _trace1(" openTcpSSLconnection: $retryCnt. Retry to connect and open a SSL connection to $host:$port\n");
                }
            }
            if ($SSLhello::starttls) {
                _trace(" openTcpSSLconnection: $host:$port: wait $sleepSecs sec(s) to prevent too many connects\n")
                  if ( ( $SSLhello::trace > 2 ) || ( $sleepSecs > 0 ) );
                sleep($sleepSecs);
            }

            {
                local $@ = "";
                eval {
                    local $SIG{ALRM} = "SSLhello::_timedOut";
                    alarm($alarmTimeout);
                    my ( $_dummy1, $_dummy2, $_protocol ) = getprotobyname('tcp');
                    if ( !$_protocol ) {
                        $_protocol = Socket::IPPROTO_TCP;
                    }
                    socket( $socket, Socket::PF_INET, Socket::SOCK_STREAM, $_protocol ) or croak("Can't create a socket \'$!\' -> target $host:$port ignored ");
                    setsockopt( $socket, Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, pack( 'L!L!', $SSLhello::timeout, 0 ) )
                      or croak("Can't set socket Sent-Timeout \'$!\' -> target $host:$port ignored");
                    setsockopt( $socket, Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, pack( 'L!L!', $SSLhello::timeout, 0 ) )
                      or croak("Can't set socket Receive-Timeout \'$!\' -> target $host:$port ignored");
                    alarm(0);
                } or do {
                    if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                        $my_error = $@;
                        alarm(0);
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_RETRY_HOST'},
                                id      => 'socket (1)',
                                message => $my_error,
                                warn    => 0,
                            }
                        );
                        next RETRY_TO_OPEN_SSL_CONNECTION;
                    }
                };
                alarm(0);
            }

            if ( ($SSLhello::proxyhost) && ($SSLhello::proxyport) ) {
              GET_PROXY_IP: {
                    $my_error   = "";
                    $connect2ip = Socket::inet_aton($SSLhello::proxyhost);
                    if ( !defined($connect2ip) ) {
                        $my_error =
                          "$OText::STR{WARN} Can't get the IP address of the proxy $SSLhello::proxyhost:$SSLhello::proxyport -> target $host:$port ignored";
                        Carp::carp($my_error);
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_ABORT_HOST'},
                                id      => 'get proxy IP',
                                message => $my_error,
                                warn    => 0,
                            }
                        );
                        last RETRY_TO_OPEN_SSL_CONNECTION;
                    }
                }

                {
                    $my_error = "";
                    local $@ = "";
                    eval {
                        local $SIG{ALRM} = "SSLhello::_timedOut";
                        alarm($alarmTimeout);
                        connect( $socket, Socket::pack_sockaddr_in( $SSLhello::proxyport, $connect2ip ) )
                          or croak("Can't make a connection to proxy $SSLhello::proxyhost:$SSLhello::proxyport -> target $host:$port ignored");
                        alarm(0);
                    } or do {
                        if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                            $my_error = $@;
                            alarm(0);
                            error_handler->new(
                                {
                                    type    => $OERR{'SSLHELLO_RETRY_HOST'},
                                    id      => 'connection via proxy (1)',
                                    message => $my_error,
                                    warn    => 0,
                                }
                            );
                            close($socket) or Carp::carp( "$OText::STR{WARN} " . error_handler->get_err_str() . "; Can't close socket, too: $!" );

                            sleep(1);
                            next RETRY_TO_OPEN_SSL_CONNECTION;
                        }
                    };
                    alarm(0);
                }

                {
                    $my_error = "";
                    local $@ = "";
                    eval {
                        $proxyConnect = $CST{'_PROXY_CONNECT_MESSAGE1'} . $host . ":" . $port . $CST{'_PROXY_CONNECT_MESSAGE2'};
                        _trace4(" openTcpSSLconnection: ## ProxyConnect-Message: >$proxyConnect<\n");
                        local $SIG{ALRM} = "SSLhello::_timedOut";
                        alarm($alarmTimeout);
                        defined( send( $socket, $proxyConnect, 0 ) )
                          || croak( "Can't make a connection to $host:$port via proxy $SSLhello::proxyhost:$SSLhello::proxyport ["
                              . Socket::inet_ntoa($connect2ip)
                              . ":$SSLhello::proxyport] -> target $host:$port ignored" );
                        alarm(0);
                    } or do {
                        if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                            $my_error = $@;
                            alarm(0);
                            error_handler->new(
                                {
                                    type    => $OERR{'SSLHELLO_RETRY_HOST'},
                                    id      => 'connection via proxy (2)',
                                    message => $my_error,
                                    warn    => 0,
                                }
                            );
                            close($socket) or Carp::carp( "$OText::STR{WARN} " . error_handler->get_err_str() . "; Can't close socket, too: $!" );
                            if ( defined($slowServerDelay) && ( $slowServerDelay > 0 ) ) {
                                _trace2(" openTcpSSLconnection: via proxy $host:$port: wait $slowServerDelay sec(s) to wait for slow proxies\n");
                                sleep($slowServerDelay);
                            }
                            next RETRY_TO_OPEN_SSL_CONNECTION;
                        }
                    };
                    alarm(0);
                }

                {
                    $my_error = "";
                    local $@ = "";
                    eval {
                        $input = "";
                        _trace2(
" openTcpSSLconnection ## CONNECT via proxy: try to receive the Connected-Message from the proxy $SSLhello::proxyhost:$SSLhello::proxyport, Retry = $retryCnt\n"
                        );
                        OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} ) if ( $retryCnt > 0 );
                        local $SIG{ALRM} = "SSLhello::_timedOut";
                        alarm($alarmTimeout);
                        recv( $socket, $input, 32767, 0 );
                        if ( length($input) == 0 ) {
                            _trace4(" openTcpSSLconnection: ... Received Connected-Message from proxy (1a): received NO Data\n");
                            sleep(1) if ( $retryCnt > 0 );
                            OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} );
                            recv( $socket, $input, 32767, 0 );
                        }
                        alarm(0);
                    } or do {
                        if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                            $my_error = $@;
                            alarm(0);
                            error_handler->new(
                                {
                                    type    => $OERR{'SSLHELLO_RETRY_HOST'},
                                    id      => 'connection via proxy (3)',
                                    message => $my_error,
                                    warn    => 0,
                                }
                            );
                            close($socket) or Carp::carp( "$OText::STR{WARN} " . error_handler->get_err_str() . "; Can't close socket, too: $!" );
                            if ( defined($slowServerDelay) && ( $slowServerDelay > 0 ) ) {
                                _trace2(" openTcpSSLconnection: via proxy $host:$port: wait $slowServerDelay sec(s) to wait for slow proxies\n");
                                sleep($slowServerDelay);
                            }
                            next RETRY_TO_OPEN_SSL_CONNECTION;
                        }
                    };
                    alarm(0);
                }

                if ( length($input) > 0 ) {
                    _trace3(" openTcpSSLconnection: ... Received data via proxy: "
                          . length($input)
                          . " bytes\n          >"
                          . substr( _chomp_r($input), 0, 64 )
                          . "< ...\n" );
                    _trace4( " openTcpSSLconnection: ... Received data via proxy: " . length($input) . " bytes\n          >" . _chomp_r($input) . "<\n" );
                    if ( $input =~ /(?:^|\s)200\s/x ) {
                        $my_error = "";
                        _trace2(" openTcpSSLconnection: Connection established to $host:$port via proxy "
                              . $SSLhello::proxyhost . ":"
                              . $SSLhello::proxyport
                              . "\n" );
                    }
                    else {
                        if ( $SSLhello::trace == 0 ) {
                            $input =~ /^((?:.+?(?:\r?\n|$)){1,4})/x;
                            $input = _chomp_r($1);
                        }
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_RETRY_HOST'},
                                id      => 'connection via proxy (4)',
                                message =>
                                  "Can't make a connection to $host:$port via proxy $SSLhello::proxyhost:$SSLhello::proxyport; target ignored. Proxy error: "
                                  . $input,
                                warn => 0,
                            }
                        );
                        close($socket) or Carp::carp( "$OText::STR{WARN} " . error_handler->get_err_str() . "; Can't close socket, too: $!" );
                        if ( defined($slowServerDelay) && ( $slowServerDelay > 0 ) ) {
                            _trace2(" openTcpSSLconnection: via proxy $host:$port: wait $slowServerDelay sec(s) to wait for slow proxies\n");
                            sleep($slowServerDelay);
                        }
                        next RETRY_TO_OPEN_SSL_CONNECTION;
                    }
                }
            }
            else {
                {
                    $my_error   = "";
                    $connect2ip = Socket::inet_aton($host);
                    if ( !defined($connect2ip) ) {
                        $my_error = "$OText::STR{WARN} Can't get the IP address of $host -> target $host:$port ignored in openTcpSSLconnection";
                        Carp::carp($my_error);
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_ABORT_HOST'},
                                id      => 'get host IP',
                                message => $my_error,
                                warn    => 0,
                            }
                        );
                        last RETRY_TO_OPEN_SSL_CONNECTION;
                    }
                }

                {
                    $my_error = "";
                    local $@ = "";
                    eval {
                        local $SIG{ALRM} = "SSLhello::_timedOut";
                        alarm($alarmTimeout);
                        connect( $socket, Socket::pack_sockaddr_in( $port, $connect2ip ) )
                          or croak( "Can't make a connection to $host:$port [" . Socket::inet_ntoa($connect2ip) . ":$port]; -> target ignored " );
                        alarm(0);
                    } or do {
                        if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                            $my_error = $@;
                            alarm(0);
                            if ( defined($connect2ip) ) {
                                $my_error .=
                                  " -> No connection to $host:$port [" . Socket::inet_ntoa($connect2ip) . ":$port]; -> target ignored in openTcpSSLconnection";
                            }
                            else {
                                $my_error .= " -> No connection to $host:$port; -> target ignored in openTcpSSLconnection";
                            }
                            error_handler->new(
                                {
                                    type    => $OERR{'SSLHELLO_RETRY_HOST'},
                                    id      => 'connect (1)',
                                    message => $my_error,
                                    warn    => 0,
                                }
                            );
                            close($socket) or Carp::carp( "$OText::STR{WARN} " . error_handler->get_err_str() . "; Can't close socket, too: $!" );
                            next RETRY_TO_OPEN_SSL_CONNECTION;
                        }
                    };
                    alarm(0);
                    _trace2(" openTcpSSLconnection: Connected to server $host:$port\n");
                }
            }

            if ( !( error_handler->is_err ) && ($SSLhello::starttls) ) {
                _trace2(" openTcpSSLconnection: try to STARTTLS using the "
                      . $starttls_matrix[$starttlsType][0]
                      . " protocol for server $host:$port, Retry = $retryCnt\n" );
                if ( ( $slowServerDelay > 0 ) || ( $retryCnt > 0 ) ) {
                    _trace2( " openTcpSSLconnection: $host:$port: wait " . ( $slowServerDelay || 1 ) . " sec(s) to cope with slow servers\n" );
                    sleep( $slowServerDelay || 1 );

                }
                if ( $starttls_matrix[$starttlsType][1] ) {
                    local $@ = "";
                    eval {
                        $input = "";
                        _trace2(" openTcpSSLconnection: ## STARTTLS (Phase 1): try to receive the "
                              . $starttls_matrix[$starttlsType][0]
                              . "-Ready-Message from the Server $host:$port\n" );
                        OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} ) if ( $retryCnt > 0 );
                        local $SIG{ALRM} = "SSLhello::_timedOut";
                        alarm($alarmTimeout);
                        recv( $socket, $input, 32767, 0 );
                        alarm(0);
                    } or do {
                        if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                            $my_error = "STARTTLS phase #1 failed): $@";
                            alarm(0);
                            next RETRY_TO_OPEN_SSL_CONNECTION;
                        }
                    };
                    alarm(0);
                    if ( length($input) > 0 ) {
                        _trace2(" openTcpSSLconnection: ## STARTTLS (Phase 1):  ... Received "
                              . $starttls_matrix[$starttlsType][0]
                              . "-Message (1): "
                              . length($input)
                              . " bytes: >"
                              . _chomp_r($input)
                              . "<\n" );
                        if ( $input =~ /$starttls_matrix[$starttlsType][1]/ ) {
                            $my_error = "";
                        }
                        else {
                            $input = _chomp_r($input);
                            if ( ( $starttls_matrix[$starttlsType][6] ) && ( $input =~ /$starttls_matrix[$starttlsType][6]/ ) ) {
                                if ( $retryCnt > $SSLhello::retry ) {
                                    $my_error =
"STARTTLS (Phase 1): Error 1: too many requests: $host:$port \'$input\' -> suspend $suspendSecs second(s) and all subsequent packets will be slowed down by $sleepSecs second(s)";
                                    last RETRY_TO_OPEN_SSL_CONNECTION;
                                }
                                $SSLhello::starttlsDelay = $sleepSecs;
                                $sleepSecs += $retryCnt + 2;
                                $suspendSecs = 60 * ( $retryCnt + 1 );
                                $my_error =
"STARTTLS (Phase 1): Error 1: too many requests: $host:$port \'$input\' -> suspend $suspendSecs second(s) and all subsequent packets will be slowed down by $sleepSecs second(s)";
                                _trace2(" openTcpSSLconnection: $my_error\n");
                                Carp::carp("$OText::STR{WARN} openTcpSSLconnection: ... $my_error");
                                close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                                sleep($suspendSecs);
                                _trace4(" openTcpSSLconnection: STARTTLS (Phase 1): End suspend\n");

                                if ( $retryCnt == $SSLhello::retry ) {
                                    $retryCnt++;
                                    _trace4(
" openTcpSSLconnection: STARTTLS (Phase 1): 1 additional final retry after too many requests => retry number $retryCnt represented by $retryCnt+1\n"
                                    );
                                    redo RETRY_TO_OPEN_SSL_CONNECTION;
                                }
                                next RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                            elsif ( ( $starttls_matrix[$starttlsType][7] ) && ( $input =~ /$starttls_matrix[$starttlsType][7]/ ) ) {
                                error_handler->new(
                                    {
                                        type    => $OERR{'SSLHELLO_ABORT_PROTOCOL'},
                                        id      => 'STARTTLS (Phase 1): Error 2',
                                        message => "unsupported protocol: $host:$port \'$input\'",
                                        warn    => 0,
                                    }
                                );
                                close($socket) or Carp::carp( "$OText::STR{WARN} " . error_handler->get_err_str() . "; Can't close socket, too: $!" );
                                last;
                            }
                            elsif ( ( $starttls_matrix[$starttlsType][8] ) && ( $input =~ /$starttls_matrix[$starttlsType][8]/ ) ) {
                                $my_error = "STARTTLS (Phase 1): Error 3: Fatal Error: $host:$port \'$input\' -> target $host:$port ignored";
                                _trace2(" openTcpSSLconnection: $my_error\n");
                                close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                                last RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                            else {
                                if ( $SSLhello::trace == 0 ) {
                                    $input =~ s/^(.+?)(?:\r?\n|$)/$1/x;

                                }
                                $my_error =
                                    "STARTTLS (Phase 1): Did *NOT* get a "
                                  . $starttls_matrix[$starttlsType][0]
                                  . " Server Ready Message from $host:$port; target ignored. Server-Error: >"
                                  . _chomp_r($input) . "<";
                                _trace2(" openTcpSSLconnection: $my_error\n");
                                close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                                next RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                        }
                    }
                    else {
                        $my_error =
                          (     "STARTTLS (Phase 1): Did *NOT* get any "
                              . $starttls_matrix[$starttlsType][0]
                              . " message from $host:$port -> slow down and try to retry target." );
                        _trace(" openTcpSSLconnection: $my_error\n");
                        $SSLhello::starttlsDelay = $sleepSecs;
                        $sleepSecs += $retryCnt + 2;
                        close($socket) or Carp::carp("$OText::STR{WARN} openTcpSSLconnection: STARTTLS: $my_error; Can't close socket, too: $!");
                        next RETRY_TO_OPEN_SSL_CONNECTION;
                    }
                }
                else {
                    _trace2( " openTcpSSLconnection: ## STARTTLS (Phase 1): Nothing to do for " . $starttls_matrix[$starttlsType][0] . "\n" );
                }

                if ( $starttls_matrix[$starttlsType][2] ) {
                    local $@ = "";
                    eval {
                        _trace2(" openTcpSSLconnection: ## STARTTLS (Phase 2): send $starttls_matrix[$starttlsType][0] Message: >"
                              . _chomp_r( $starttls_matrix[$starttlsType][2] )
                              . "<\n" );
                        local $SIG{ALRM} = "SSLhello::_timedOut";
                        alarm($alarmTimeout);
                        defined( send( $socket, $starttls_matrix[$starttlsType][2], 0 ) )
                          || die
                          "Could *NOT* send $starttls_matrix[$starttlsType][0] message '$starttls_matrix[$starttlsType][2]' to $host:$port; target ignored\n";
                        alarm(0);
                    } or do {
                        if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                            $my_error = "STARTTLS phase #2 failed): $@";
                            alarm(0);
                            _trace2(" openTcpSSLconnection: $my_error\n");
                            close($socket) or Carp::carp("$OText::STR{WARN} openTcpSSLconnection: $my_error Can't close socket, too: $!");
                            next RETRY_TO_OPEN_SSL_CONNECTION;
                        }
                    };
                    alarm(0);

                    OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} ) if ( $sleepSecs > 0 ) || ( $retryCnt > 0 );

                    OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} ) if ( $retryCnt > 1 );
                }
                else {
                    _trace2( " openTcpSSLconnection: ## STARTTLS (Phase 2): Nothing to do for " . $starttls_matrix[$starttlsType][0] . "\n" );
                }

                if ( $starttls_matrix[$starttlsType][3] ) {
                    local $@ = "";
                    eval {
                        $input = "";
                        _trace2(
" openTcpSSLconnection: ## STARTTLS (Phase 3): try to receive the $starttls_matrix[$starttlsType][0] Hello Answer from the Server $host:$port\n"
                        );
                        OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} ) if ( $retryCnt > 0 );

                        local $SIG{ALRM} = "SSLhello::_timedOut";
                        alarm($alarmTimeout);
                        recv( $socket, $input, 32767, 0 );
                        alarm(0);
                    } or do {
                        if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                            $my_error = "STARTTLS phase #3 failed): $@";
                            alarm(0);
                            next RETRY_TO_OPEN_SSL_CONNECTION;
                        }
                    };
                    alarm(0);
                    if ( length($input) > 0 ) {
                        _trace3(" openTcpSSLconnection: ## STARTTLS (Phase 3): ... Received  $starttls_matrix[$starttlsType][0]-Hello: "
                              . length($input)
                              . " bytes\n      >"
                              . substr( _chomp_r($input), 0, 64 )
                              . " ...<\n" );
                        _trace4(" openTcpSSLconnection: ## STARTTLS (Phase 3):  ... Received  $starttls_matrix[$starttlsType][0]-Hello: "
                              . length($input)
                              . " bytes\n      >"
                              . _chomp_r($input)
                              . "<\n" );
                        if ( $input =~ /$starttls_matrix[$starttlsType][3]/ ) {
                            $my_error = "";
                            _trace2(
" openTcpSSLconnection: ## STARTTLS (Phase 3): received a $starttls_matrix[$starttlsType][0] Hello Answer from the Server $host:$port: >"
                                  . _chomp_r($input)
                                  . "<\n" );
                        }
                        else {
                            $input = _chomp_r($input);
                            if ( ( $starttls_matrix[$starttlsType][6] ) && ( $input =~ /$starttls_matrix[$starttlsType][6]/ ) ) {
                                if ( $retryCnt > $SSLhello::retry ) {
                                    $my_error = "STARTTLS (Phase 3): Error 1: too many requests: $host:$port \'$input\' -> too many retries ($retryCnt)";
                                    last RETRY_TO_OPEN_SSL_CONNECTION;
                                }
                                $SSLhello::starttlsDelay = $sleepSecs;
                                $sleepSecs += $retryCnt + 2;
                                $suspendSecs = 60 * ( $retryCnt + 1 );
                                $my_error =
"STARTTLS (Phase 3): Error 1: too many requests: $host:$port \'$input\' -> suspend $suspendSecs second(s) and all subsequent packets will be slowed down by $sleepSecs second(s)";
                                _trace2(" openTcpSSLconnection: $my_error\n");
                                Carp::carp("$OText::STR{WARN} openTcpSSLconnection: ... $my_error");
                                close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                                sleep($suspendSecs);
                                _trace4(" openTcpSSLconnection: STARTTLS (Phase 3): End suspend\n");

                                if ( $retryCnt == $SSLhello::retry ) {
                                    $retryCnt++;
                                    _trace4(
" openTcpSSLconnection: STARTTLS (Phase 3): 1 additional final retry after too many requests => retry number $retryCnt represented by $retryCnt+1\n"
                                    );
                                }
                                next RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                            elsif ( ( $starttls_matrix[$starttlsType][7] ) && ( $input =~ /$starttls_matrix[$starttlsType][7]/ ) ) {
                                error_handler->new(
                                    {
                                        type    => $OERR{'SSLHELLO_ABORT_PROTOCOL'},
                                        id      => 'STARTTLS (Phase 3): Error 2',
                                        message => "unsupported protocol: $host:$port \'$input\'",
                                        warn    => 0,
                                    }
                                );
                                close($socket) or Carp::carp( "$OText::STR{WARN} " . error_handler->get_err_str() . "; Can't close socket, too: $!" );
                                last RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                            elsif ( ( $starttls_matrix[$starttlsType][8] ) && ( $input =~ /$starttls_matrix[$starttlsType][8]/ ) ) {
                                $my_error = "STARTTLS (Phase 3): Error 3: Fatal Error: $host:$port \'$input\' -> target $host:$port ignored";
                                _trace2(" openTcpSSLconnection: $my_error\n");
                                close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                                last RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                            else {
                                if ( $SSLhello::trace == 0 ) {
                                    $input =~ s/^(.+?)(?:\r?\n|$)/$1/x;

                                }
                                $my_error =
"STARTTLS (Phase 3): Did *NOT* get a $starttls_matrix[$starttlsType][0] Server Hello Answer from $host:$port; target ignored. Server-Error: >"
                                  . _chomp_r($input) . "<";
                                _trace2(" openTcpSSLconnection: $my_error; try to retry\n");
                                close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                                next RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                        }
                    }
                    else {
                        $my_error =
                          (     "STARTTLS (Phase 3): Did *NOT* get any answer to"
                              . $starttls_matrix[$starttlsType][0]
                              . " client message from $host:$port -> slow down and try to retry target." );
                        _trace(" openTcpSSLconnection: $my_error\n");
                        $SSLhello::starttlsDelay = $sleepSecs;
                        $sleepSecs += $retryCnt + 2;
                        close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                        next RETRY_TO_OPEN_SSL_CONNECTION;
                    }
                }
                else {
                    _trace2( " openTcpSSLconnection: ## STARTTLS (Phase 3): Nothing to do for " . $starttls_matrix[$starttlsType][0] . "\n" );
                }

                if ( $starttls_matrix[$starttlsType][4] ) {
                    local $@ = "";
                    eval {
                        _trace2(" openTcpSSLconnection: ## STARTTLS (Phase 4): $starttls_matrix[$starttlsType][0] Do STARTTLS message: >"
                              . _chomp_r( $starttls_matrix[$starttlsType][4] )
                              . "<\n" );
                        local $SIG{ALRM} = "SSLhello::_timedOut";
                        alarm($SSLhello::timeout);
                        defined( send( $socket, $starttls_matrix[$starttlsType][4], 0 ) )
                          || die "Could *NOT* send a STARTTLS message to $host:$port; target ignored\n";
                        alarm(0);
                    } or do {
                        if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                            $my_error = "STARTTLS phase #4 failed): $@";
                            alarm(0);
                            _trace2(" openTcpSSLconnection: $my_error\n");
                            close($socket) or Carp::carp("$OText::STR{WARN} openTcpSSLconnection: ## $my_error Can't close socket, too: $!");
                            next RETRY_TO_OPEN_SSL_CONNECTION;
                        }
                    };

                    OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} ) if ( $sleepSecs > 0 ) || ( $retryCnt > 0 );

                    OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} ) if ( $retryCnt > 1 );

                }
                else {
                    _trace2( " openTcpSSLconnection: ## STARTTLS (Phase 4): Nothing to do for " . $starttls_matrix[$starttlsType][0] . "\n" );
                }

                if ( $starttls_matrix[$starttlsType][5] ) {
                    local $@ = "";
                    eval {
                        $input = "";
                        _trace2(
" openTcpSSLconnection: ## STARTTLS (Phase 5): Try to receive the $starttls_matrix[$starttlsType][0] STARTTLS answer from the server $host:$port\n"
                        );
                        OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} ) if ( $retryCnt > 0 );

                        local $SIG{ALRM} = "SSLhello::_timedOut";
                        alarm($alarmTimeout);
                        recv( $socket, $input, 32767, 0 );
                        alarm(0);
                    } or do {
                        if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                            $my_error = "STARTTLS phase #5 failed): $@";
                            alarm(0);
                            next RETRY_TO_OPEN_SSL_CONNECTION;
                        }
                    };
                    alarm(0);
                    if ( length($input) > 0 ) {
                        _trace3(" openTcpSSLconnection: ## STARTTLS (Phase 5): ... Received STARTTLS-Answer: "
                              . length($input)
                              . " bytes\n      >"
                              . substr( _chomp_r($input), 0, 64 )
                              . " ...<\n" );
                        _trace4(" openTcpSSLconnection: ## STARTTLS (Phase 5): ... Received STARTTLS-Answer: "
                              . length($input)
                              . " bytes\n      >"
                              . _chomp_r($input)
                              . "<\n" );
                        if ( $input =~ /$starttls_matrix[$starttlsType][5]/ ) {
                            $my_error = "";
                            _trace2(" openTcpSSLconnection: ## STARTTLS: Server is ready to do SSL/TLS\n");
                        }
                        else {
                            $input = _chomp_r($input);
                            if ( ( $starttls_matrix[$starttlsType][6] ) && ( $input =~ /$starttls_matrix[$starttlsType][6]/ ) ) {
                                if ( $retryCnt > $SSLhello::retry ) {
                                    $my_error =
"STARTTLS (Phase 5): Error 1: Too many requests: $host:$port \'$input\' -> suspend $suspendSecs second(s) and all subsequent packets will be slowed down by $sleepSecs second(s)";
                                    last RETRY_TO_OPEN_SSL_CONNECTION;
                                }
                                $SSLhello::starttlsDelay = $sleepSecs;
                                $sleepSecs += $retryCnt + 2;
                                $suspendSecs = 60 * ( $retryCnt + 1 );
                                $my_error =
"STARTTLS (Phase 5): Error 1: Too many requests: $host:$port \'$input\' -> suspend $suspendSecs second(s) and all subsequent packets will be slowed down by $sleepSecs second(s)";
                                _trace2(" openTcpSSLconnection: $my_error\n");
                                Carp::carp("$OText::STR{WARN} openTcpSSLconnection: ... $my_error");
                                close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                                sleep($suspendSecs);
                                _trace4(" openTcpSSLconnection: STARTTLS (Phase 5): End suspend\n");

                                if ( $retryCnt == $SSLhello::retry ) {
                                    $retryCnt++;
                                    _trace4(
" openTcpSSLconnection: STARTTLS (Phase 5): 1 additional final retry after too many requests => retry number $retryCnt represented by $retryCnt+1\n"
                                    );
                                }
                                next RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                            elsif ( ( $starttls_matrix[$starttlsType][7] ) && ( $input =~ /$starttls_matrix[$starttlsType][7]/ ) ) {
                                error_handler->new(
                                    {
                                        type    => $OERR{'SSLHELLO_ABORT_PROTOCOL'},
                                        id      => 'STARTTLS (Phase 5): Error 2',
                                        message => "unsupported protocol: $host:$port \'$input\'",
                                        warn    => 0,
                                    }
                                );
                                close($socket) or Carp::carp( "$OText::STR{WARN} " . error_handler->get_err_str() . "; Can't close socket, too: $!" );
                                last RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                            elsif ( ( $starttls_matrix[$starttlsType][8] ) && ( $input =~ /$starttls_matrix[$starttlsType][8]/ ) ) {
                                $my_error = "STARTTLS (Phase 5): Error 3: Fatal Error: $host:$port \'$input\' -> target $host:$port ignored";
                                _trace2(" openTcpSSLconnection: $my_error\n");
                                close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                                last RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                            else {
                                if ( $SSLhello::trace == 0 ) {
                                    $input =~ s/^(.+?)(?:\r?\n|$)/$1/x;

                                }
                                $my_error =
"STARTTLS (Phase 5): Did *NOT* get a server SSL/TLS confirmation from $host:$port (retry: $retryCnt); target ignored. Server-Error: >"
                                  . _chomp_r($input) . "<";
                                _trace2(" openTcpSSLconnection: ## $my_error; try to retry;\n");
                                close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                                next RETRY_TO_OPEN_SSL_CONNECTION;
                            }
                        }
                    }
                    else {
                        $my_error =
                          (     "STARTTLS (Phase 5): Did *NOT* get any answer to"
                              . $starttls_matrix[$starttlsType][0]
                              . " STARTTLS request from $host:$port -> slow down and try to retry target." );
                        _trace(" openTcpSSLconnection: $my_error\n");
                        $SSLhello::starttlsDelay = $sleepSecs;
                        $sleepSecs += $retryCnt + 2;
                        close($socket) or Carp::carp("$OText::STR{WARN} STARTTLS: $my_error; Can't close socket, too: $!");
                        next RETRY_TO_OPEN_SSL_CONNECTION;
                    }
                }
                else {
                    _trace2( " openTcpSSLconnection: ## STARTTLS (Phase 5): Nothing to do for " . $starttls_matrix[$starttlsType][0] . "\n" );
                }
            }
        } while ( ($my_error) && ( ( $retryCnt++ < $SSLhello::retry ) || ( $retryCnt == $SSLhello::retry + 2 ) ) );
    }
    if ($my_error) {
        chomp($my_error);
        Carp::carp("$OText::STR{WARN} openTcpSSLconnection: $my_error");
        _trace2("openTcpSSLconnection: Exit openTcpSSLconnection }\n");
        return (undef);
    }
    alarm(0);
    _trace2("openTcpSSLconnection: Connected to '$host:$port' }\n");
    return ($socket);
}

sub _doCheckSSLciphers ($$$$;$$) {
    my $host            = shift || "";
    my $port            = shift || 443;
    my $protocol        = shift || 0;
    my $cipher_spec     = shift || "";
    my $dtls_epoch      = shift || 0;
    my $parseAllRecords = shift || 0;
    my $socket;
    my $connect2ip;
    my $proxyConnect = "";
    my $clientHello  = "";
    my $input        = "";
    my $input2       = "";
    my $pduLen       = 0;
    my $v2len        = 0;
    my $v2type       = 0;
    my $v3len        = 0;
    my $v3type       = 0;
    my $v3version    = 0;
    my ( $recordType, $recordVersion, $recordLen, $recordEpoch, $recordSeqNr ) = ( 0, 0, 0, 0, 0 );
    my $recordData       = "";
    my $acceptedCipher   = "";
    my $dummy            = "";
    my $retryCnt         = 0;
    my $firstMessage     = "";
    my $secondMessage    = "";
    my $segmentCnt       = 0;
    my $dtlsSequence     = 0;
    my $dtlsCookieLen    = 0;
    my $dtlsCookie       = "";
    my $dtlsNewCookieLen = 0;
    my $dtlsNewCookie    = "";
    my $alarmTimeout     = $SSLhello::timeout + 1;
    my $isUdp            = 0;
    my $buffer           = "";
    my $lastMsgType      = $HANDSHAKE_TYPE{'<<undefined>>'};
    my $lastRecordType   = $RECORD_TYPE{'<<undefined>>'};
    my $lastRecordData   = "";

    my $ssl = $PROTOCOL_NAME_BY_HEX{$protocol};
    if ( !defined $ssl ) {
        $ssl = "--unknown protocol--";
    }

    _trace4( sprintf( "_doCheckSSLciphers ($host, $port, $ssl: >0x%04X<\n          >", $protocol ) . hexCodedString( $cipher_spec, "           " ) . "<) {\n" );
    local $my_error = "";
    error_handler->reset_err( { module => "#$SSLHELLO", sub => '_doCheckSSLciphers', print => ( $SSLhello::trace > 3 ), trace => $SSLhello::trace } );
    $isUdp = ( ( ( $protocol & 0xFF00 ) == $PROTOCOL_VERSION{'DTLSfamily'} ) || ( $protocol == $PROTOCOL_VERSION{'DTLSv09'} ) );

    unless ($isUdp) {
        $socket = openTcpSSLconnection( $host, $port );
        if ( ( !defined($socket) ) || ( error_handler->is_err() ) || ($@) ) {
            if ( ( error_handler->get_err_type ) == $OERR{'SSLHELLO_RETRY_HOST'} ) {
                error_handler->new(
                    {
                        type => $OERR{'SSLHELLO_ABORT_HOST'},
                    }
                );
            }
            unless ( error_handler->is_err ) {
                error_handler->new(
                    {
                        type    => $OERR{'SSLHELLO_ABORT_HOST'},
                        id      => 'open TCP SSL connection (1)',
                        message => "WARNING: Did not get a valid SSL-socket from function openTcpSSLconnection -> fatal exit of openTcpSSLconnection",
                    }
                );
            }
            return ("");
        }
    }
    else {
        if ( defined($SSLhello::connect_delay) && ( $SSLhello::connect_delay > 0 ) ) {
            _trace_("\n");
            _trace(" _doCheckSSLciphers (udp): connect delay $cfg{'connect_delay'} second(s)\n");
            sleep($SSLhello::connect_delay);
            _trace4(" _doCheckSSLciphers (udp): connect delay $cfg{'connect_delay'} second(s) [End]\n");
        }
        {
            $my_error = "";
            $socket   = IO::Socket::INET->new(
                Proto    => "udp",
                PeerAddr => "$host:$port",
                Timeout  => $SSLhello::timeout,
            ) or $my_error = " \'$@\', \'$!\'";
            if ( ( !defined($socket) ) || ($my_error) ) {
                error_handler->new(
                    {
                        type    => $OERR{'SSLHELLO_ABORT_HOST'},
                        id      => 'open UDP socket (1)',
                        message => "WARNING: Did not get a valid socket for UDP: $my_error -> fatal exit of _doCheckSSLciphers (udp)",
                    }
                );
                return ("");
            }
        }
        _trace4(" _doCheckSSLciphers: ## New UDP socket to >$host:$port<\n");
    }

    $retryCnt = 0;
    $my_error = "";
    binmode( $socket, ":raw" );
  RETRY_TO_EXCHANGE_CLIENT_AND_SERVER_HELLO: while ( $retryCnt++ < $SSLhello::retry ) {
        $clientHello = compileClientHello( $protocol, $protocol, $cipher_spec, $host, $dtls_epoch, $dtlsSequence++, $dtlsCookieLen, $dtlsCookie );

        _trace3(" _doCheckSSLciphers: sending Client_Hello\n      >"
              . hexCodedString( substr( $clientHello, 0, 64 ), "        " )
              . " ...< ("
              . length($clientHello)
              . " bytes)\n\n" );
        _trace4(" _doCheckSSLciphers: sending Client_Hello\n          >"
              . hexCodedString( $clientHello, "           " ) . "< ("
              . length($clientHello)
              . " bytes)\n\n" );
        local $@ = "";
        eval {
            local $SIG{ALRM} = "SSLhello::_timedOut";
            alarm($alarmTimeout);
            defined( send( $socket, $clientHello, 0 ) ) || die "Could *NOT* send ClientHello to $host:$port; $! -> target ignored\n";
            alarm(0);
        } or do {
            if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                $my_error = "send client hello failed: $@";
                alarm(0);
                error_handler->new(
                    {
                        type    => $OERR{'SSLHELLO_ABORT_HOST'},
                        id      => 'send client hello failed',
                        message => $my_error,
                        warn    => 0,
                    }
                );
                return ("");
            }
        };
        alarm(0);

        ( $recordType, $recordVersion, $recordLen, $recordData, $recordEpoch, $recordSeqNr, $my_error ) =
          _readRecord( $socket, $isUdp, \$input, $host, $port, $protocol );
        if ( ( error_handler->get_err_type() ) <= $OERR{'SSLHELLO_RETRY_PROTOCOL'} ) {
            if ( ( error_handler->get_err_type() ) == $OERR{'SSLHELLO_RETRY_HOST'} ) {
                error_handler->new(
                    {
                        type => $OERR{'SSLHELLO_ABORT_HOST'},
                    }
                );
            }
            _trace( "**WARNING: " . error_handler->get_err_str . "\n" );
            return ("");
        }
        if ( ($my_error) && ( ( length($input) == 0 ) && ( $SSLhello::noDataEqNoCipher == 0 ) ) ) {
            _trace2(" _doCheckSSLciphers: ... Received Data: Got a timeout receiving Data from $host:$port (protocol: $ssl "
                  . sprintf( "(0x%04X)", $protocol ) . ", "
                  . length($input)
                  . " bytes): Eval-Message: >$my_error<\n" );
            Carp::carp( "$OText::STR{WARN} _doCheckSSLciphers: ... Received Data: Got a timeout receiving Data from $host:$port (protocol: $ssl "
                  . sprintf( "(0x%04X)", $protocol ) . ", "
                  . length($input)
                  . " bytes): Eval-Message: >$my_error<\n" );
            return ("");
        }
        elsif ( length($input) == 0 ) {
            $my_error =
                "... Received NO Data from $host:$port (protocol: $ssl "
              . sprintf( "(0x%04X)", $protocol )
              . ") after $SSLhello::retry retries; This may occur if the server responds by closing the TCP connection instead with an Alert. -> Received NO Data";
            _trace2("_doCheckSSLciphers: $my_error }\n");
            return ("");
        }
        elsif ($my_error) {
            _trace2("_doCheckSSLciphers: Error-Message: $my_error }\n");
            return ("");
        }
        _trace2(" _doCheckSSLciphers: Server '$host:$port': (protocol $ssl ["
              . sprintf( "0x%04X", $protocol )
              . "], (record) type $recordType: received a record with "
              . length($recordData)
              . " bytes payload (recordData) >"
              . hexCodedString( substr( $recordData, 0, 48 ), "       " )
              . "< ...)     \n" );

        if ( $recordVersion <= 0 ) {

            ( $input, $my_error ) = _readText( $socket, $isUdp, $input, "" );

            if ($SSLhello::starttls) {
                if ( $input =~ /(?:^|\s)554(?:\s|-)security.*?$/ix ) {
                    _trace2(
" _doCheckSSLciphers ## STARTTLS: received SMTP Reply Code '554 Security failure': (Is the STARTTLS command issued within an existing TLS session?) -> input ignored and try to Retry\n"
                    );
                    $my_error = "";
                    $input    = "";
                    $pduLen   = 0;
                    next;
                }
            }
            elsif ( $input =~ /(?:^|\s)220(?:\s|-).*?$/x ) {
                $my_error =
"$OText::STR{WARN} _doCheckSSLciphers: $host:$port looks like an SMTP-Service, probably the option '--starttls' is needed -> target ignored\n";
                Carp::carp($my_error);
                return ("");
            }
            $my_error =
              "$OText::STR{WARN} _doCheckSSLciphers: $host:$port dosen't look like a SSL or a SMTP-Service (1) -> Received data ignored -> target ignored\n";
            Carp::carp($my_error);
            _trace_("\n") if ( $retryCnt <= 1 );
            _trace( "_doCheckSSLciphers: Ignored data: "
                  . length($input)
                  . " bytes\n        >"
                  . hexCodedString( $input, "        " )
                  . "<\n        >"
                  . _chomp_r($input)
                  . "< }\n" );
            $input  = "";
            $pduLen = 0;
            return ("");
        }
        if ( length($input) > 0 ) {
            _trace2( "_doCheckSSLciphers: Total data received: " . length($input) . " bytes }\n" );
            ( $buffer, $lastMsgType, $dtlsNewCookieLen, $dtlsNewCookie, $acceptedCipher ) =
              parseHandshakeRecord( $host, $port, $recordType, $recordVersion, $recordLen, $recordData, "", $protocol );
            if ( ( error_handler->get_err_type() ) <= $OERR{'SSLHELLO_RETRY_PROTOCOL'} ) {
                if ( ( error_handler->get_err_type() ) == $OERR{'SSLHELLO_RETRY_HOST'} ) {
                    error_handler->new(
                        {
                            type => $OERR{'SSLHELLO_ABORT_HOST'},
                        }
                    );
                }
                _trace( "**WARNING: " . error_handler->get_err_str . "\n" );
                return ("");
            }

            if ( ( $acceptedCipher ne "" ) && ( $parseAllRecords > 0 ) && ( $lastMsgType != $HANDSHAKE_TYPE{'server_hello_done'} ) ) {
                _trace4(" _doCheckSSLciphers: Try to get and parse next records\n");
                while ( ( length($input) > 0 ) && ( $lastMsgType != $HANDSHAKE_TYPE{'server_hello_done'} ) ) {
                    _trace4(" _doCheckSSLciphers: receive next record\n");
                    $input  = $buffer;
                    $buffer = "";
                    ( $recordType, $recordVersion, $recordLen, $recordData, $recordEpoch, $recordSeqNr, $my_error ) =
                      _readRecord( $socket, $isUdp, \$input, $host, $port, $protocol );
                    last if ( ( length($input) == 0 ) || ($my_error) );
                    _trace4(" _doCheckSSLciphers: record type '$recordType' is no handshake record -> stop receiving records\n")
                      if ( $recordType ne $RECORD_TYPE{'handshake'} );
                    last if ( $recordType eq $RECORD_TYPE{'application_data'} );
                    if ( ( $lastMsgType == $HANDSHAKE_TYPE{'<<fragmented_message>>'} ) && ( $recordType == $lastRecordType ) ) {
                        $recordData = $lastRecordData . $recordData;
                        $recordLen += length($lastRecordData);
                        $lastRecordData = "";
                        _trace4(" _doCheckSSLciphers: recompiled fragmented message -> compiled RecordLen: $recordLen\n");
                    }
                    ( $buffer, $lastMsgType, $dtlsNewCookieLen, $dtlsNewCookie, $dummy ) =
                      parseHandshakeRecord( $host, $port, $recordType, $recordVersion, $recordLen, $recordData, $acceptedCipher, $protocol );
                    $lastRecordType = $recordType;
                    if ( $lastMsgType == $HANDSHAKE_TYPE{'<<fragmented_message>>'} ) {
                        $lastRecordData = $buffer;
                        $buffer         = "";
                    }
                }
            }

            if ( ( $acceptedCipher ne "" ) || ( !$isUdp ) ) {
                last;
            }
            if ( $my_error ne "" ) {
                _trace4("_doCheckSSLciphers: Exit with error: '$my_error' }\n");
                return ("");
            }
            if ( ( $dtlsNewCookieLen > 0 ) && $isUdp ) {
                $dtlsCookieLen    = $dtlsNewCookieLen;
                $dtlsCookie       = $dtlsNewCookie;
                $dtlsNewCookieLen = 0;
                $dtlsNewCookie    = "";
                _trace2( " _doCheckSSLciphers: received a cookie ($dtlsCookieLen bytes): >" . hexCodedString( $dtlsCookie, "        " ) . "<\n" );
                $retryCnt--;
            }
            _trace4( " _doCheckSSLciphers: DTLS: sleep " . $CST{'_DTLS_SLEEP_AFTER_NO_CIPHERS_FOUND'} . " sec(s) after *NO* cipher found\n" );
            OCfg::ocfg_sleep( $CST{'_DTLS_SLEEP_AFTER_NO_CIPHERS_FOUND'} );

        }
    }

    if ($isUdp) {
        local $@ = "";
        eval {
            local $SIG{ALRM} = "SSLhello::_timedOut";
            my $level       = 2;
            my $description = 90;
            alarm($alarmTimeout);
            defined( send( $socket, compileAlertRecord( $protocol, $host, $level, $description, $dtls_epoch, $dtlsSequence++ ), 0 ) )
              || die "Could *NOT* send an alert record to $host:$port; $! -> Error ignored\n";
            alarm(0);
        } or do {
            if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                $my_error = "reset DTLS failed: $@";
                alarm(0);
                error_handler->new(
                    {
                        type    => $OERR{'SSLHELLO_RETRY_PROTOCOL'},
                        id      => 'reset DTLS failed',
                        message => $my_error,
                        warn    => 0,
                    }
                );
                return ("");
            }
        };
        alarm(0);
    }

    unless ( close($socket) ) {
        Carp::carp("$OText::STR{WARN} _doCheckSSLciphers: Can't close socket: $!");
    }
    if ( ($isUdp) && ( defined($acceptedCipher) ) && ( $acceptedCipher ne "" ) ) {
        _trace4(" _doCheckSSLciphers: DTLS: sleep "
              . $CST{'_DTLS_SLEEP_AFTER_FOUND_A_CIPHER'}
              . " sec(s) after received cipher >"
              . hexCodedCipher($acceptedCipher)
              . "<\n" );
        OCfg::ocfg_sleep( $CST{'_DTLS_SLEEP_AFTER_FOUND_A_CIPHER'} );
    }
    _trace2("_doCheckSSLciphers: }\n");
    return ($acceptedCipher);
}

sub _readRecord ($$$;$$$$) {

    my $socket          = shift || "";
    my $isUdp           = shift || 0;
    my $input_ref       = shift;
    my $host            = shift || "";
    my $port            = shift || "";
    my $client_protocol = shift || -1;

    my $MAXLEN  = 16384;
    my $pduLen  = 0;
    my $readLen = ($isUdp) ? $MAXLEN : 7;
    my $recordType       = 0;
    my $recordVersion    = 0;
    my $recordEpoch      = 0;
    my $recordSeqNr_null = 0;
    my $recordSeqNr      = 0;
    my $my_local_error   = "";
    my $recordLen        = 0;
    my $recordData       = "";
    my $recordHeaderLen  = 0;
    my ( $rin, $rout );
    my $alarmTimeout = $SSLhello::timeout + 1;
    my $retryCnt     = 0;
    my $segmentCnt   = 0;

    return ( $recordType, $recordVersion, $recordLen, $recordData, $recordEpoch, $recordSeqNr, $my_local_error ) if ( !defined($input_ref) );
    my $input2       = "";
    my @socketsReady = ();
    my $len          = length($$input_ref);

    require IO::Select if ( $SSLhello::trace > 0 );
    my $select;
    $select = IO::Select->new if ( $SSLhello::trace > 0 );
    my $success = 0;
    $select->add($socket) if ( $SSLhello::trace > 0 );

    error_handler->reset_err( { module => "#$SSLHELLO", sub => '_readRecord', print => ( $SSLhello::trace > 3 ), trace => $SSLhello::trace } );

    vec( $rin = '', fileno($socket), 1 ) = 1;
  RETRY_TO_RECEIVE_A_RECORD: while ( ( ( length($$input_ref) < $pduLen ) || ( $pduLen == 0 ) ) && ( $retryCnt++ <= $SSLhello::retry ) ) {
        if ($isUdp) {
            $my_local_error = "";
            local $@ = "";
            eval {

                local $SIG{ALRM} = "SSLhello::_timedOut";
                alarm($alarmTimeout);
                $success = select( $rout = $rin, undef, undef, $SSLhello::timeout );
                alarm(0);
            } or do {
                if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                    $my_local_error = "failed to select data: $@";
                    alarm(0);
                    error_handler->new(
                        {
                            type    => $OERR{'SSLHELLO_RETRY_CIPHERS'},
                            id      => '_readRecord (udp): unknown Timeout error (1)',
                            message => $my_local_error,
                            warn    => 0,
                        }
                    );
                    Carp::carp("_readRecord (udp): $my_local_error");
                    _trace4("_readRecord (udp) from Server '$host:$port' -> LAST: Received (record) type $recordType, -version: "
                          . sprintf( "(0x%04X)", $recordVersion )
                          . " with "
                          . length($$input_ref)
                          . " bytes (from $pduLen expected) after $retryCnt tries:\n" );
                    last RETRY_TO_RECEIVE_A_RECORD;
                }
            };
            alarm(0);
            if ( !$success ) {
                error_handler->new(
                    {
                        type    => $OERR{'SSLHELLO_RETRY_CIPHERS'},
                        id      => '_readRecord (udp): Timeout error (1)',
                        message => $my_local_error,
                        warn    => 0,
                    }
                );
                _trace4("_readRecord (udp): Server '$host:$port' -> Timeout (received nor data NEITHER special event) while reading a record with"
                      . length($$input_ref)
                      . " bytes (from $pduLen expected) after $retryCnt tries:\n" );
                last RETRY_TO_RECEIVE_A_RECORD;
            }
            if ( vec( $rout, fileno($socket), 1 ) ) {
                local $@ = "";
                eval {

                    local $SIG{ALRM} = "SSLhello::_timedOut";
                    alarm($alarmTimeout);
                    @socketsReady = $select->can_read(0) if ( $SSLhello::trace > 3 );
                    _trace4( "_readRecord (udp): can read (1): (Segement: $segmentCnt, retry: $retryCnt, position: " . length($$input_ref) . " bytes)\n" )
                      if ( scalar(@socketsReady) );
                    $success = sysread( $socket, $$input_ref, $readLen - length($$input_ref), length($$input_ref) );
                    alarm(0);
                } or do {
                    if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                        $my_local_error = "failed to read data with sysread: $@";
                        alarm(0);
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_RETRY_CIPHERS'},
                                id      => '_readRecord (udp): unknown Timeout error (2)',
                                message => $my_local_error,
                                warn    => 0,
                            }
                        );
                        Carp::carp("_readRecord (udp): $my_local_error");
                        _trace4("_readRecord (udp) -> LAST: Received (record) type $recordType, -version: "
                              . sprintf( "(0x%04X)", $recordVersion )
                              . " with "
                              . length($$input_ref)
                              . " bytes (from $pduLen expected) after $retryCnt tries:\n" );
                        last RETRY_TO_RECEIVE_A_RECORD;
                    }
                };
                alarm(0);
                @socketsReady = $select->can_read(0) if ( $SSLhello::trace > 3 );
                _trace4( "_readRecord (udp) can read (2): (Segement: $segmentCnt, retry: $retryCnt, position: " . length($$input_ref) . " bytes)\n" )
                  if ( scalar(@socketsReady) );
                if ( !$success ) {
                    if ( length($$input_ref) == 0 ) {
                        $my_local_error = "Server '$host:$port': received EOF (Disconnect), no Data\n";
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_RETRY_CIPHERS'},
                                id      => '_readRecord (udp): no Data',
                                message => $my_local_error,
                                warn    => 0,
                            }
                        );
                        _trace4("_readRecord (udp) : $my_local_error\n");
                        last RETRY_TO_RECEIVE_A_RECORD;
                    }
                    else {
                        $my_local_error =
                          "Server '$host:$port': No data (EOF) after " . length($$input_ref) . " of expected $pduLen bytes: '$!' -> Retry to read\n";
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_RETRY_CIPHERS'},
                                id      => '_readRecord (udp): EOF',
                                message => $my_local_error,
                                warn    => 0,
                            }
                        );
                        _trace1("_readRecord (udp): $my_local_error\n");
                        @socketsReady = $select->can_read(0) if ( $SSLhello::trace > 1 );
                        _trace1( "_readRecord (udp): can read (3): (Segement: $segmentCnt, retry: $retryCnt, position: " . length($$input_ref) . " bytes)\n" )
                          if ( scalar(@socketsReady) );
                        OCfg::ocfg_sleep( $CST{'_SLEEP_B4_2ND_READ'} );
                        next RETRY_TO_RECEIVE_A_RECORD;
                    }
                }
            }
            else {
                $my_local_error = "Server '$host:$port': No data in _readRecord after reading $len of $pduLen expected bytes; $!";
                error_handler->new(
                    {
                        type    => $OERR{'SSLHELLO_RETRY_CIPHERS'},
                        id      => '_readRecord (udp): Received (no more) data',
                        message => $my_local_error,
                        warn    => 0,
                    }
                );
                _trace1("_readRecord (udp): ... Received data: $my_local_error\n");
                _trace4("_readRecord (udp) :-> LAST: Received (record) type $recordType, -version: "
                      . sprintf( "(0x%04X)", $recordVersion )
                      . " with "
                      . length($$input_ref)
                      . " bytes (from $pduLen expected) after $retryCnt tries:\n" );
                last RETRY_TO_RECEIVE_A_RECORD;
            }
        }
        else {
            local $@ = "";
            eval {

                local $SIG{ALRM} = "SSLhello::_timedOut";
                alarm($alarmTimeout);
                my $_missing_readLen = $readLen - length($$input_ref);
                _trace4("_readRecord (tcp): try to recv (1): (Segement: $segmentCnt, retry: $retryCnt, position: "
                      . length($$input_ref)
                      . " bytes, missing Bytes: $_missing_readLen)\n" );
                $success = ( $_missing_readLen <= 0 ) || ( recv( $socket, $input2, $readLen - length($$input_ref), 0 ) );
                alarm(0);
            } or do {
                if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                    $my_local_error = "failed to receive data (recv) $@";
                    alarm(0);
                    error_handler->new(
                        {
                            type    => $OERR{'SSLHELLO_RETRY_CIPHERS'},
                            id      => '_readRecord (tcp): recv: unknown Timeout error',
                            message => $my_local_error,
                            warn    => 0,
                        }
                    );
                    Carp::carp("_readRecord (tcp): $my_local_error");
                    _trace4("_readRecord (tcp): recv -> LAST: Received (record) type $recordType, -version: "
                          . sprintf( "(0x%04X)", $recordVersion )
                          . " with "
                          . length($$input_ref)
                          . " bytes (from $pduLen expected) after $retryCnt tries:\n" );
                    last RETRY_TO_RECEIVE_A_RECORD;
                }
            };
            alarm(0);
            $$input_ref .= $input2;
            $success = length($input2);
            _trace4( "_readRecord (tcp): recv: (Segement: $segmentCnt, retry: $retryCnt, position: " . length($$input_ref) . " bytes)\n" );
        }
        $len = length($$input_ref);
        if ($success) {
            if ( $pduLen == 0 ) {
                _trace4("_readRecord (tcp): Server '$host:$port': ... Received first $len bytes to detect PduLen\n");
                if ( ( !$isUdp ) && ( $len >= 5 ) ) {

                    ( $recordType, $recordVersion, $recordLen, ) = unpack( "C n n", $$input_ref );

                    if ( ( $recordType < 0x80 ) && ( ( $recordVersion & 0xFF00 ) == $PROTOCOL_VERSION{'SSLv3'} || $recordVersion == 0x0000 ) ) {

                        _trace2_(
                            sprintf(
                                "# -->    => SSL3/TLS record type: >%02X<):\n" . "# -->    record_version:  >%04X<\n" . "# -->    record_len:      >%04X<\n",
                                $recordType, $recordVersion, $recordLen,
                            )
                        );
                        $recordHeaderLen = 5;
                        _trace2("_readRecord (tcp): Server '$host:$port': ... Received data: Expected SSLv3/TLS-PDU-Len:");
                    }
                    else {
                        ( $recordLen, $recordType, ) = unpack( "n C", $$input_ref );
                        if ( ( $recordLen > 0x8000 ) && ( ( $recordType == $SSL_MT_SERVER_HELLO ) || ( $recordType == $SSL_MT_ERROR ) ) ) {
                            $recordLen -= 0x8000;
                            $recordHeaderLen = 2;
                            $pduLen          = $recordLen + $recordHeaderLen;
                            $recordVersion   = $PROTOCOL_VERSION{'SSLv2'};
                            _trace2("_readRecord (tcp): Server '$host:$port': ... Received data: Expected SSLv2-PDU-Len:");
                        }
                        else {
                            $my_local_error = "no known SSL/TLS PDU type";
                            $recordType     = 0;
                            $recordVersion  = 0;
                            $recordLen      = 0;
                            _trace1("_readRecord (tcp): $my_local_error\n");
                            _trace4("_readRecord (tcp): -> LAST: received (record) type $recordType, -version: "
                                  . sprintf( "(0x%04X)", $recordVersion )
                                  . " with "
                                  . length($$input_ref)
                                  . " bytes (from $pduLen expected) after $retryCnt tries:\n" );
                            last RETRY_TO_RECEIVE_A_RECORD;
                        }
                    }

                }
                elsif ( ($isUdp) && ( $len >= 13 ) ) {

                    _trace2("_readRecord (udp): Server '$host:$port': Protocol: DTLS\n");
                    ( $recordType, $recordVersion, $recordEpoch, $recordSeqNr_null, $recordSeqNr, $recordLen, ) = unpack( "C n n n N n", $$input_ref );

                    _trace2_(
                        sprintf(
                            "# -->    => DTLS record type: Handshake  (%02X):\n"
                              . "# -->    record_version:    >%04X<\n"
                              . "# -->    record_epoch:      >%04X<\n"
                              . "# -->    record_seqNr_null: >%04X<\n"
                              . "# -->    record_seqNr:  >%08X<\n"
                              . "# -->    record_len:        >%04X<\n",
                            $recordType, $recordVersion, $recordEpoch, $recordSeqNr_null, $recordSeqNr, $recordLen,
                        )
                    );
                    if (
                        ( $recordType < 0x80 )
                        && (   ( ( $recordVersion & 0xFF00 ) == $PROTOCOL_VERSION{'DTLSfamily'} )
                            || ( $recordVersion == $PROTOCOL_VERSION{'DTLSv09'} ) )
                      )
                    {
                        $recordHeaderLen = 13;
                        _trace2("_readRecord (udp): Server '$host:$port': ... Received data: Expected DTLS-PDU-Len:");
                    }
                    else {
                        $my_local_error = "Server '$host:$port': no known DTLS PDU type -> unknown protocol";
                        _trace1("_readRecord (udp): $my_local_error\n");
                        _trace1("_readRecord (udp): -> LAST: Received (record) type $recordType, -version: "
                              . sprintf( "(0x%04X)", $recordVersion )
                              . " with "
                              . length($$input_ref)
                              . " bytes (from $recordLen expected) after $retryCnt tries: reset all the mentioned parameters to 0\n" );
                        $recordType      = 0;
                        $recordVersion   = 0;
                        $recordLen       = 0;
                        $pduLen          = 0;
                        $recordHeaderLen = 0;
                        last RETRY_TO_RECEIVE_A_RECORD;
                    }
                }

                $pduLen = $recordLen + $recordHeaderLen;
                _trace2_(" $pduLen (including the SSL/TLS header)\n");
                if ( $recordLen > $MAXLEN ) {
                    _trace1(
"_readRecord: Server '$host:$port': Expected len of the SSL/TLS record ($recordLen) is higher than the maximum ($MAXLEN) -> cut at maximum length!"
                    );
                    Carp::carp(
"_readRecord: Server '$host:$port': Expected len of the SSL/TLS record ($recordLen) is higher than the maximum ($MAXLEN) -> cut at maximum length!"
                    );
                    $pduLen += -$recordLen + $MAXLEN;
                }
                $readLen  = $pduLen;
                $retryCnt = 0 if ( $readLen > 0 );
            }
            else {
                $segmentCnt++;
                _trace4("_readRecord: Server '$host:$port': ... Received $len bytes in $segmentCnt segment(s)\n");
                $retryCnt = 0 if ( $segmentCnt <= $CST{'_MAX_SEGMENT_COUNT_TO_RESET_RETRY_COUNT'} );
            }
            if ( defined($client_protocol) ) {
                my $client_ssl = $PROTOCOL_NAME_BY_HEX{$client_protocol};
                if ( !defined $client_ssl ) {
                    $client_ssl = "--unknown protocol--";
                }
                if ( $recordVersion == 0 ) {
                    error_handler->new(
                        {
                            type    => $OERR{'SSLHELLO_ABORT_PROTOCOL'},
                            id      => 'check record protocol (1)',
                            message =>
                              sprintf( "unsupported protocol $client_ssl (0x%04X) by $host:$port, answered with (0x%04X)", $client_protocol, $recordVersion ),
                            warn => 0,
                        }
                    );
                    last RETRY_TO_RECEIVE_A_RECORD;
                }
            }
        }
    }
    if ( !($my_local_error) && ( length($$input_ref) < $pduLen ) ) {
        $my_local_error =
            "Server '$host:$port': Overrun the maximal number of $retryCnt retries in _readRecord after reading $len of $pduLen expected bytes in the "
          . $segmentCnt
          . "th segment; $!";
        _trace1("_readRecord ... Error receiving data: $my_local_error\n");
        _trace4("_readRecord -> LAST: Received (record) type $recordType, -version: "
              . sprintf( "(0x%04X)", $recordVersion )
              . " with "
              . length($$input_ref)
              . " bytes (from $pduLen expected) after $retryCnt tries:\n" );
    }
    chomp($my_local_error);

    if ( $client_protocol >= 0 ) {
        _trace3("_readRecord: Server '$host:$port': (expected protocol= >"
              . sprintf( "%04X", $client_protocol )
              . "<,\n      (record) type $recordType, -version: "
              . sprintf( "(0x%04X)", $recordVersion )
              . " with "
              . length($$input_ref)
              . " bytes >"
              . hexCodedString( substr( $$input_ref, 0, 48 ), "       " )
              . "< ...)\n" );
    }
    else {
        _trace4("_readRecord: Server '$host:$port': (any protocol, (record) type $recordType, -version: "
              . sprintf( "(0x%04X)", $recordVersion )
              . " with "
              . length($$input_ref)
              . " bytes\n       Data="
              . hexCodedString( $$input_ref, "       " )
              . ")\n" );
    }

    ($recordData) = unpack( "x[$recordHeaderLen] a*", $$input_ref );
    if ( length($recordData) < $recordLen ) {
        _trace1("_readRecord: Server '$host:$port': (expected protocol= >"
              . sprintf( "%04X", $client_protocol )
              . "<, (record) type $recordType, -version: "
              . sprintf( "(0x%04X)", $recordVersion )
              . ": recordLen "
              . sprintf( "%04X", length($recordData) )
              . " is smaller than the expected value "
              . sprintf( "%04X", $recordLen )
              . "\n" );
        Carp::carp( "_readRecord: Server '$host:$port': (expected protocol= >"
              . sprintf( "%04X", $client_protocol )
              . "<, (record) type $recordType, -version: "
              . sprintf( "(0x%04X)", $recordVersion )
              . ": recordLen "
              . sprintf( "%04X", length($recordData) )
              . " is smaller than the expected value "
              . sprintf( "%04X", $recordLen )
              . "\n" );
    }
    return ( $recordType, $recordVersion, $recordLen, $recordData, $recordEpoch, $recordSeqNr, $my_local_error );
}

sub _readText ($;$) {
    my $socket         = shift || "";
    my $isUdp          = shift || 0;
    my $input          = shift || "";
    my $my_local_error = "";
    my $untilFound     = shift || "";
    my $len            = 0;
    my $MAXLEN         = 32767;
    my $alarmTimeout   = $SSLhello::timeout + 1;
    my ( $rin, $rout );
    my $input2   = "";
    my $retryCnt = 0;

    vec( $rin = '', fileno($socket), 1 ) = 1;
  RECEVICE_ANSWER:
    while ( ($untilFound) && ( !m {\A$untilFound\Z} ) ) {
        {
            $my_local_error = "";
            local $@ = "";
            eval {

                local $SIG{ALRM} = "SSLhello::_timedOut";
                alarm($alarmTimeout);
                if ( !select( $rout = $rin, undef, undef, $SSLhello::timeout ) ) {
                    alarm(0);
                    $my_local_error = "Timeout in _readText $!";
                    last RECEVICE_ANSWER;
                }
                alarm(0);
            } or do {
                if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                    $my_local_error = "failed to select text: $@";
                }
            };

            alarm(0);
            if ($my_local_error) {
                $my_local_error = "_readText: unknown Timeout-Error (1): $my_error";
                Carp::carp("_readText: $my_local_error");
                return ( $input, $my_local_error );
            }
            if ( vec( $rout, fileno($socket), 1 ) ) {
                local $@ = "";
                eval {

                    local $SIG{ALRM} = "SSLhello::_timedOut";
                    alarm($alarmTimeout);
                    my $_missing_readLen = $MAXLEN - length($input);
                    _trace4( "_readText: try to recv (1): (retry: $retryCnt, position: " . length($input) . " bytes, missing Bytes: $_missing_readLen)\n" );
                    my $success = ( $_missing_readLen <= 0 ) || ( recv( $socket, $input2, $MAXLEN - length($input), 0 ) );
                    $input .= $input2;
                    alarm(0);
                } or do {
                    if ( ($@) or ( $^O !~ m/MSWin32/ ) ) {
                        $my_error = "failed to receice text: $@";
                    }
                };

                alarm(0);
                if ($my_local_error) {
                    $my_local_error = "_readText unknown Timeout-Error (2): $my_error";
                    Carp::carp("_readText: $my_local_error");
                    last RECEVICE_ANSWER;
                }
                $len = length($input2);
                if ( $len <= 0 ) {
                    $my_local_error = "NULL-Len-Data in _readText $!";
                    _trace1("_readText: $my_local_error\n");
                    last RECEVICE_ANSWER;
                }
            }
            else {
                last RECEVICE_ANSWER;
            }
            if ( $retryCnt++ < $SSLhello::retry ) {
                $my_local_error = "Retry-Counter exceeded $SSLhello::retry while reading Text";
                _trace1("_readText: $my_local_error\n");
                last;
            }
        }
    }
    alarm(0);
    chomp($my_local_error);
    $my_error = $my_local_error if ( defined($my_error) );
    return ( $input, $my_local_error );
}

sub compileClientHello ($$$$;$$$$) {
    my $record_version         = shift || "";
    my $version                = shift || "";
    my $ciphers                = shift || "";
    my $host                   = shift || "";
    my $dtls_epoch             = shift || 0;
    my $dtls_sequence          = shift || 0;
    my $dtls_cookieLen         = shift || 0;
    my $dtls_cookie            = shift || "";
    my $clientHello            = "";
    my $clientHello_tmp        = "";
    my $clientHello_extensions = "";
    my $challenge              = $CHALLENGE;
    my $i;

    my $ssl = $PROTOCOL_NAME_BY_HEX{$version};
    if ( !defined $ssl ) {
        $ssl = "--unknown protocol--";
    }
    _trace4(
        sprintf( "compileClientHello (%04X, %04X,\n          >%s<, %s) {\n", $record_version, $version, hexCodedString( $ciphers, "           " ), $host ) );

    $challenge = pack( "Na[28]", time(), $challenge );
    _trace4_( "#   --->   challenge >" . hexCodedString($challenge) . "<\n" );

    my $handshake_version = $version;
    if ( ( $version > $PROTOCOL_VERSION{'TLSv12'} ) && ( $version < $PROTOCOL_VERSION{'DTLSv12'} ) ) {
        $handshake_version = $PROTOCOL_VERSION{'TLSv12'};
        $record_version    = $PROTOCOL_VERSION{'TLSv12'};
    }

    my %clientHello = (
        'record_type'            => $RECORD_TYPE{'handshake'},
        'record_version'         => $record_version,
        'record_epoch'           => 0x0000,
        'record_seqNr'           => 0x000000,
        'record_len'             => 0x0000,
        'msg_type'               => $SSL_MT_CLIENT_HELLO,
        'msg_len'                => 0x000000,
        'msg_seqNr'              => 0x0000,
        'fragment_offset'        => 0x000000,
        'fragment_len'           => 0x000000,
        'version'                => $handshake_version,
        'cipher_spec_len'        => length($ciphers),
        'session_id_len'         => 0x0000,
        'cookie_len'             => 0x00,
        'cookie'                 => "",
        'challenge_len'          => length($challenge),
        'cipher_spec'            => $ciphers,
        'session_id'             => "",
        'challenge'              => $challenge,
        'compression_method_len' => 0x01,
        'compression_method'     => 0x00,
    );

    if ( $version == $PROTOCOL_VERSION{'SSLv2'} ) {
        _trace2("compileClientHello: Protocol: SSL2\n");

        $clientHello_tmp = pack(
            "C n n n n a* a*",
            $clientHello{'msg_type'},
            $clientHello{'version'},
            $clientHello{'cipher_spec_len'},
            $clientHello{'session_id_len'},
            $clientHello{'challenge_len'},
            $clientHello{'cipher_spec'},
            $clientHello{'challenge'},
        );

        $clientHello{'msg_len'} = length($clientHello_tmp) | 0x8000;

        _trace2_(
            sprintf(
                "# --> msg_len \| 0x8000 (added): >%04X<\n"
                  . "# --> msg_type:          >%02X<\n"
                  . "# --> version:         >%04X< (%s)\n"
                  . "# --> cipher_spec_len: >%04X<\n"
                  . "# --> session_id_len:  >%04X<\n"
                  . "# --> challenge_len:   >%04X<\n"
                  . "# --> cipher_spec:     >%s<\n"
                  . "# --> session_id:      >%s<\n"
                  . "# --> challenge:       >%s<\n",
                $clientHello{'msg_len'},                      $clientHello{'msg_type'},
                $clientHello{'version'},                      $ssl,
                $clientHello{'cipher_spec_len'},              $clientHello{'session_id_len'},
                $clientHello{'challenge_len'},                hexCodedString( $clientHello{'cipher_spec'}, "                       >" ),
                hexCodedString( $clientHello{'session_id'} ), hexCodedString( $clientHello{'challenge'} )
            )
        );

        if ( ( $SSLhello::trace > 3 ) ) {
            printSSL2CipherList( $clientHello{'cipher_spec'} );
        }

        $clientHello = pack( "n a*", $clientHello{'msg_len'}, $clientHello_tmp, );

        _trace4( sprintf( "compileClientHello:   ClientHello(Version= %04X)\n          >%s<\n", $version, hexCodedString( $clientHello, "           " ) ) );

    }
    elsif ( ( $record_version & 0xFF00 ) == $PROTOCOL_VERSION{'SSLv3'} ) {
        _trace2("compileClientHello: Protocol: SSL3/TLS1.x\n");

        $clientHello_extensions = _compileClientHelloExtensions( $record_version, $version, $ciphers, $host, %clientHello );

        $clientHello{'extensions_total_len'} = length($clientHello_extensions);

        _trace4("compileClientHello (SSL3/TLS) (1):\n");

        $clientHello_tmp = pack(
            "n a[32] C n a[$clientHello{'cipher_spec_len'}] C C[$clientHello{'compression_method_len'}] a[$clientHello{'extensions_total_len'}]",
            $clientHello{'version'},
            $clientHello{'challenge'},
            $clientHello{'session_id_len'},
            $clientHello{'cipher_spec_len'},
            $clientHello{'cipher_spec'},
            $clientHello{'compression_method_len'},
            $clientHello{'compression_method'},
            $clientHello_extensions
        );

        _trace4_( "          >" . hexCodedString( $clientHello_tmp, "           " ) . "<\n" );

        $clientHello{'msg_len'}    = length($clientHello_tmp);
        $clientHello{'record_len'} = $clientHello{'msg_len'} + 4;

        $clientHello = pack( "C n n C C n a*",
            $clientHello{'record_type'},
            $clientHello{'record_version'},
            $clientHello{'record_len'},
            $clientHello{'msg_type'},
            0x00, $clientHello{'msg_len'}, $clientHello_tmp );

        _trace3( "compileClientHello (SSL3/TLS) (2):\n       >" . hexCodedString( $clientHello, "        " ) . "<\n" );
        _trace2_(
            sprintf(
                "# -->SSL3/TLS-clientHello:\n"
                  . "# -->   record_type:       >%02X<\n"
                  . "# -->   record_version:  >%04X< (%s)\n"
                  . "# -->   record_len:      >%04X<\n"
                  . "# -->   Handshake protocol: \n"
                  . "# -->       msg_type:                >%02X<\n"
                  . "# -->       msg_len:             >00%04X<\n"
                  . "# -->       version:               >%04X< (%s)\n"
                  . "# -->       challenge/random:      >%s<\n"
                  . "# -->       session_id_len:          >%02X<\n"
                  . "# -->       cipher_spec_len:       >%04X<\n"
                  . "# -->       cipher_spec:           >%s<\n",
                $clientHello{'record_type'},    $clientHello{'record_version'},  $ssl,
                $clientHello{'record_len'},     $clientHello{'msg_type'},        $clientHello{'msg_len'},
                $clientHello{'version'},        $ssl,                            hexCodedString( $clientHello{'challenge'} ),
                $clientHello{'session_id_len'}, $clientHello{'cipher_spec_len'}, hexCodedString( $clientHello{'cipher_spec'} ),
            )
        );

        if ( $SSLhello::trace > 3 ) {
            printTLSCipherList( $clientHello{'cipher_spec'} );
        }

        _trace2_(
            sprintf(
                "# -->       compression_method_len:  >%02X<\n" . "# -->       compression_method:      >%02X<\n",
                $clientHello{'compression_method_len'},
                $clientHello{'compression_method'},
            )
        );

        _trace5_(
            sprintf(
                "#   --->    extensions_total_len:  >%04X<\n" . "#   --->    extensions:            >%s<\n",
                $clientHello{'extensions_total_len'},
                hexCodedString($clientHello_extensions),
            )
        );

        _parseExtensions( "CH", undef, \$clientHello_extensions, -1 ) if ( $SSLhello::trace > 2 );
        _trace4( sprintf( "compileClientHello (%04X)\n          >", $record_version ) . hexCodedString( $clientHello, "           " ) . "<\n" );

    }
    elsif ( ( ( $record_version & 0xFF00 ) == $PROTOCOL_VERSION{'DTLSfamily'} ) || ( $version == $PROTOCOL_VERSION{'DTLSv09'} ) ) {
        _trace2("compileClientHello: Protocol: DTLS\n");

        $clientHello_extensions = _compileClientHelloExtensions( $record_version, $version, $ciphers, $host, %clientHello );
        $clientHello{'extensions_total_len'} = length($clientHello_extensions);

        $clientHello{'cookie_len'} = $dtls_cookieLen;
        $clientHello{'cookie'}     = $dtls_cookie;
        _trace4("compileClientHello (DTLS) (1):\n");

        $clientHello_tmp = pack(
"n a[32] C C A[$clientHello{'cookie_len'}] n a[$clientHello{'cipher_spec_len'}] C C[$clientHello{'compression_method_len'}] a[$clientHello{'extensions_total_len'}]",
            $clientHello{'version'},     $clientHello{'challenge'},              $clientHello{'session_id_len'},
            $clientHello{'cookie_len'},  $clientHello{'cookie'},                 $clientHello{'cipher_spec_len'},
            $clientHello{'cipher_spec'}, $clientHello{'compression_method_len'}, $clientHello{'compression_method'},
            $clientHello_extensions
        );

        _trace4_( "          >" . hexCodedString( $clientHello_tmp, "           " ) . "<\n" );

        $clientHello{'msg_len'}      = length($clientHello_tmp);
        $clientHello{'fragment_len'} = $clientHello{'msg_len'};
        $clientHello{'record_len'}   = $clientHello{'msg_len'} + 12;
        $clientHello{'record_epoch'} = $dtls_epoch;
        $clientHello{'record_seqNr'} = $dtls_sequence;
        $clientHello{'msg_seqNr'}    = $dtls_sequence;

        $clientHello = pack(
            "C n n n N n C C n n C n C n a*",
            $clientHello{'record_type'},  $clientHello{'record_version'}, $clientHello{'record_epoch'}, 0x0000,
            $clientHello{'record_seqNr'}, $clientHello{'record_len'},     $clientHello{'msg_type'},     0x00,
            $clientHello{'msg_len'},      $clientHello{'msg_seqNr'},      0x00,                         $clientHello{'fragment_offset'},
            0x00,                         $clientHello{'fragment_len'},   $clientHello_tmp
        );

        _trace2( "compileClientHello (DTLS) (2):\n       >" . hexCodedString( $clientHello, "        " ) . "<\n" );
        _trace2_(
            sprintf(
                "# --> DTLS-clientHello (Record):\n"
                  . "# -->   record_type:       >%02X<\n"
                  . "# -->   record_version:  >%04X< (%s)\n"
                  . "# -->   record_epoch:    >%04X<\n"
                  . "# -->   record_seqNr:    >%012X<\n"
                  . "# -->   record_len:      >%04X<\n"
                  . "# -->   Handshake protocol: \n"
                  . "# -->       msg_type:                >%02X<\n"
                  . "# -->       msg_len:             >%06X<\n"
                  . "# -->       msg_seqNr:             >%04X<\n"
                  . "# -->       fragment_offset:     >%06X<\n"
                  . "# -->       fragment_len:        >%06X<\n"
                  . "# -->       version:               >%04X< (%s)\n"
                  . "# -->       challenge/random:      >%s<\n"
                  . "# -->       session_id_len:          >%02X<\n"
                  . "# -->       cookie_len:              >%02X<\n"
                  . "# -->       cookie:                >%s<\n"
                  . "# -->       cipher_spec_len:       >%04X<\n"
                  . "# -->       cipher_spec:           >%s<\n",
                $clientHello{'record_type'},                          $clientHello{'record_version'},
                $ssl,                                                 $clientHello{'record_epoch'},
                $clientHello{'record_seqNr'},                         $clientHello{'record_len'},
                $clientHello{'msg_type'},                             $clientHello{'msg_len'},
                $clientHello{'msg_seqNr'},                            $clientHello{'fragment_offset'},
                $clientHello{'fragment_len'},                         $clientHello{'version'},
                $ssl,                                                 hexCodedString( $clientHello{'challenge'} ),
                $clientHello{'session_id_len'},                       $clientHello{'cookie_len'},
                hexCodedString( $clientHello{'cookie'}, "        " ), $clientHello{'cipher_spec_len'},
                hexCodedString( $clientHello{'cipher_spec'}, "        " ),
            )
        );

        if ( $SSLhello::trace > 3 ) {
            printTLSCipherList( $clientHello{'cipher_spec'} );
        }

        _trace2_(
            sprintf(
                "# -->       compression_method_len:  >%02X<\n" . "# -->       compression_method:      >%02X<\n",
                $clientHello{'compression_method_len'},
                $clientHello{'compression_method'},
            )
        );

        _trace5_(
            sprintf(
                "#   --->    extensions_total_len:  >%04X<\n" . "#   --->    extensions:            >%s<\n",
                $clientHello{'extensions_total_len'},
                hexCodedString($clientHello_extensions),
            )
        );

        _parseExtensions( "CH", undef, \$clientHello_extensions, -1 ) if ( $SSLhello::trace > 2 );

        _trace4( sprintf( "compileClientHello (%04X)\n          >", $record_version ) . hexCodedString( $clientHello, "           " ) . "<\n" );
    }
    else {
        if ( !defined $ssl ) {
            $ssl = "--unknown protocol--";
        }
        local $my_error =
            "$OText::STR{WARN} compileClientHello: protocol version $ssl (0x"
          . sprintf( "%04X", $record_version )
          . ") not (yet) defined in SSLhello.pm -> protocol ignored";
        Carp::carp($my_error);
    }
    if ( ( $SSLhello::max_sslHelloLen > 0 ) && ( length($clientHello) > $SSLhello::max_sslHelloLen ) ) {
        if ( !defined $ssl ) {
            $ssl = "--unknown protocol--";
        }
        if ( $SSLhello::experimental > 0 ) {
            _trace_("\n");
            _trace(
"compileClientHello: WARNING: Server $host (protocol: $ssl): use of ClientHellos > $SSLhello::max_sslHelloLen bytes did cause some virtual servers to stall in the past. This protection is overridden by '--experimental'"
            );
        }
        else {
            local $my_error =
"$OText::STR{WARN} compileClientHello: Server $host: the ClientHello is longer than $SSLhello::max_sslHelloLen bytes, this caused sometimes virtual servers to stall, e.g. 256 bytes: https://code.google.com/p/chromium/issues/detail?id=245500;\n    Please add '--experimental' to override this protection; -> This time the protocol $ssl is ignored";
            Carp::carp($my_error);
        }
    }
    return ($clientHello);
}

sub compileAlertRecord ($$$$;$$) {
    my $record_version = shift || "";
    my $host           = shift || "";
    my $level          = shift || "";
    my $description    = shift || "";
    my $dtls_epoch     = shift || 0;
    my $dtls_sequence  = shift || 0;
    my $alertRecord    = "";
    my $ssl            = $PROTOCOL_NAME_BY_HEX{$record_version};

    if ( !defined $ssl ) {
        $ssl = "--unknown protocol--";
    }

    _trace4("compileAlertRecord ($host) {\n");

    local $my_error = "";

    my %alertRecord = (
        'record_type'    => $RECORD_TYPE{'handshake'},
        'record_version' => $record_version,
        'record_epoch'   => 0x0000,
        'record_seqNr'   => 0x000000,
        'record_len'     => 0x0002,
        'level'          => $level,
        'description'    => $description,
    );

    if ( $record_version == $PROTOCOL_VERSION{'SSLv2'} ) {
        $my_error = "$OText::STR{WARN} compileAlert for SSL2 is not yet supported";
        _trace1("compileAlertRecord: $my_error\n");
        Carp::carp($my_error);

    }
    elsif ( ( $record_version & 0xFF00 ) == $PROTOCOL_VERSION{'SSLv3'} ) {
        _trace2("compileAlertRecord (SSL3/TLS) (1):\n");
        $alertRecord{'record_type'} = $RECORD_TYPE{'alert'};

        $alertRecord = pack( "C n n C C",
            $alertRecord{'record_type'},
            $alertRecord{'record_version'},
            $alertRecord{'record_len'},
            $alertRecord{'level'}, $alertRecord{'description'} );

        if ( $TLS_AlertDescription{ $alertRecord{'description'} } ) {
            $description = $TLS_AlertDescription{ $alertRecord{'description'} }[0] . " " . $TLS_AlertDescription{ $alertRecord{'description'} }[2];
        }
        else {
            $description = "Unknown/Undefined";
        }

        _trace2_(
            sprintf(
                "# -->SSL3/TLS-AlertRecord:\n"
                  . "# -->   record_type:       >%02X<\n"
                  . "# -->   record_version:  >%04X< (%s)\n"
                  . "# -->   record_len:      >%04X<\n"
                  . "# -->   Alert Message:\n"
                  . "# -->       Level:                >%02X<\n"
                  . "# -->       Description:          >%02X< (%s)\n",
                $alertRecord{'record_type'}, $alertRecord{'record_version'}, $ssl, $alertRecord{'record_len'},
                $alertRecord{'level'},       $alertRecord{'description'},    $description,
            )
        );

        _trace4( sprintf( "compileAlertRecord (%04X)\n          >", $record_version ) . hexCodedString( $alertRecord, "           " ) . "<\n" );

    }
    elsif ( ( ( $record_version & 0xFF00 ) == $PROTOCOL_VERSION{'DTLSfamily'} ) || ( $record_version == $PROTOCOL_VERSION{'DTLSv09'} ) ) {
        _trace2("compileAlertRecord: Protocol: DTLS\n");

        $alertRecord{'record_type'}  = $RECORD_TYPE{'alert'};
        $alertRecord{'record_epoch'} = $dtls_epoch;
        $alertRecord{'record_seqNr'} = $dtls_sequence;

        $alertRecord = pack( "C n n n N n C C",
            $alertRecord{'record_type'},
            $alertRecord{'record_version'},
            $alertRecord{'record_epoch'},
            0x0000,
            $alertRecord{'record_seqNr'},
            $alertRecord{'record_len'},
            $alertRecord{'level'}, $alertRecord{'description'} );
        if ( $TLS_AlertDescription{ $alertRecord{'description'} } ) {
            $description = $TLS_AlertDescription{ $alertRecord{'description'} }[0] . " " . $TLS_AlertDescription{ $alertRecord{'description'} }[2];
        }
        else {
            $description = "Unknown/Undefined";
        }

        _trace2( "compileAlertRecord (DTLS) (2):\n       >" . hexCodedString( $alertRecord, "        " ) . "<\n" );
        _trace2_(
            sprintf(
                "# --> DTLS-Record (Alert):\n"
                  . "# -->   record_type:       >%02X<\n"
                  . "# -->   record_version:  >%04X< (%s)\n"
                  . "# -->   record_epoch:    >%04X<\n"
                  . "# -->   record_seqNr:    >%012X<\n"
                  . "# -->   record_len:      >%04X<\n"
                  . "# -->   Alert Message:\n"
                  . "# -->       Level:                >%02X<\n"
                  . "# -->       Description:          >%02X< (%s)\n",
                $alertRecord{'record_type'},  $alertRecord{'record_version'}, $ssl,
                $alertRecord{'record_epoch'}, $alertRecord{'record_seqNr'},   $alertRecord{'record_len'},
                $alertRecord{'level'},        $alertRecord{'description'},    $description,
            )
        );

        _trace4( sprintf( "compileAlertRecord (%04X)\n          >", $record_version ) . hexCodedString( $alertRecord, "           " ) . "<\n" );
    }
    else {
        if ( !defined $ssl ) {
            $ssl = "--unknown protocol--";
        }
        $my_error =
            "$OText::STR{WARN} compileAlertRecord protocol version $ssl (0x"
          . sprintf( "%04X", $record_version )
          . ") not (yet) defined in SSLhello.pm -> protocol ignored";
        Carp::carp($my_error);
    }
    if ( ( $SSLhello::max_sslHelloLen > 0 ) && ( length($alertRecord) > $SSLhello::max_sslHelloLen ) ) {
        if ( !defined $ssl ) {
            $ssl = "--unknown protocol--";
        }
        if ( $SSLhello::experimental > 0 ) {
            _trace_("\n");
            _trace(
"compileAlertRecord: WARNING: Server $host (protocol: $ssl): use of alert message > $SSLhello::max_sslHelloLen bytes did cause some virtual servers to stall in the past. This protection is overridden by '--experimental'"
            );
        }
        else {
            $my_error =
"$OText::STR{WARN} compileAlertRecord: Server $host: the alert message is longer than $SSLhello::max_sslHelloLen bytes, this caused sometimes virtual servers to stall, e.g. 256 bytes: https://code.google.com/p/chromium/issues/detail?id=245500;\n    Please add '--experimental' to override this protection; -> This time the protocol $ssl is ignored";
            Carp::carp($my_error);
        }
    }
    return ($alertRecord);
}

sub __compile_bytes_len1 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $_size             = 0;
    my $_next_data        = "";

    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_len1 for '$__pdu_name'     ($$__format_pos_ref, $$__param_pos_ref)\n" );
    $$__format_pos_ref++;
    $_size = _compileAllBytes( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, \$_next_data, $__format_text_ref, $__indent );
    my $__len = length($_next_data);
    _trace4_(
        _decode_val(
            "%02X",    \$__len, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . " (len1)\n"
    );
    Carp::carp("$OText::STR{WARN} SSLhello::__compile_bytes_len1 for '$__pdu_name': Length $__len too big\n") if ( $__len > 0xFF );
    $$__buffer_ref .= pack( "C a*", $__len, $_next_data, );
    $_size += 1;
    return ($_size);
}

sub __compile_bytes_len2 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $_size             = 0;
    my $_next_data        = "";

    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_len2 for '$__pdu_name'     ($$__format_pos_ref, $$__param_pos_ref)\n" );
    $$__format_pos_ref++;
    $_size = _compileAllBytes( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, \$_next_data, $__format_text_ref, $__indent );
    my $__len = length($_next_data);
    _trace4_(
        _decode_val(
            "%04X",    \$__len, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . " (len2)\n"
    );
    Carp::carp("$OText::STR{WARN} SSLhello::__compile_bytes_len2 for '$__pdu_name': Length $__len too big\n") if ( $__len > 0xFFFF );
    $$__buffer_ref .= pack( "n a*", $__len, $_next_data, );
    $_size += 2;
    return ($_size);
}

sub __compile_bytes_len3 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $_size             = 0;
    my $_next_data        = "";

    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_len3 for '$__pdu_name'     ($$__format_pos_ref, $$__param_pos_ref)\n" );
    $$__format_pos_ref++;
    $_size = _compileAllBytes( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, \$_next_data, $__format_text_ref, $__indent );
    my $__len  = length($_next_data);
    my $__len1 = $__len >> 16;
    my $__len2 = $__len & 0xFFFF;
    _trace4_(
        _decode_val(
            "%06X",    \$__len, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . " (len3)\n"
    );
    Carp::carp("$OText::STR{WARN} SSLhello::_compile_bytes_len3 for '$__pdu_name': Length $__len too big\n") if ( $__len > 0xFFFFFF );
    $$__buffer_ref .= pack( "C n a*", $__len1, $__len2, $_next_data, );
    $_size += 3;
    return ($_size);
}

sub __compile_bytes_raw ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $_size             = 0;

    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_raw  for '$__pdu_name'     ($$__format_pos_ref, $$__param_pos_ref)\n" );
    return (0) if ( !defined( $__param_ref->[$$__param_pos_ref] ) );
    my $__val = $__param_ref->[ $$__param_pos_ref++ ];
    _trace4_(
        _decode_val( "", \$__val, $__format_text_ref->[$$__format_pos_ref], $__indent, $__indent, ":\n" . " " x $__indent, ",\n", " | ", " / " ) . " (raw)\n" );
    $$__buffer_ref .= pack( "a*", $__val, );
    $_size = length($__val);
    return ($_size);
}

sub __compile_bytes_sequence ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    my $_size               = 0;
    my $_sequence_data      = "";
    my $_sequence_param_pos = 0;
    my $_sequence_param_list_ref;

    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_sequence for '$__pdu_name' ($$__format_pos_ref, $$__param_pos_ref)\n" );
    _trace4_(
        _decode_val(
            undef, undef, $__format_text_ref->[$$__format_pos_ref],
            $__indent,
            $__indent + 3,
            ":\n" . " " x ( $__indent + 3 ),
            ",\n" . " " x ( $__indent + 3 ),
            " | ", " / "
        )
    );
    _trace5_("\n");
    $$__format_pos_ref++;
    my $_sequence_format_pos = $$__format_pos_ref;
    $__indent += 3;

    while ( defined( $__param_ref->[$$__param_pos_ref] ) ) {
        $_sequence_param_list_ref = \@{ $__param_ref->[$$__param_pos_ref] };
        _trace5_( " " x $__indent );
        _trace4_(
            "sequence parameter for '$__pdu_name':\n"
              . _decode_val(
                undef, $_sequence_param_list_ref, undef,
                $__indent + 3,
                $__indent + 3,
                ":\n" . " " x ( $__indent + 3 ),
                ",\n" . " " x ( $__indent + 3 ),
                " | ", " / "
              )
              . "\n"
        );
        $$__format_pos_ref   = $_sequence_format_pos;
        $_sequence_param_pos = 0;
        $_sequence_data      = "";
        $_size += _compileAllBytes( $__pdu_name, $__format_ref, $_sequence_param_list_ref, $__format_pos_ref, \$_sequence_param_pos, \$_sequence_data,
            $__format_text_ref, $__indent );
        $$__buffer_ref .= $_sequence_data;
        _trace4_( " " x $__indent );
        _trace4_( "=> size of sequence(s) for '$__pdu_name': " . _sprintf_hex_val( "%04X", \$_size, $__indent ) . "\n" );
        $$__param_pos_ref++;
    }
    return ($_size);
}

sub __compile_bytes_size1 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $_size             = 0;
    my $_next_data        = "";

    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_size1 for '$__pdu_name'    ($$__format_pos_ref, $$__param_pos_ref)\n" );
    $$__format_pos_ref++;
    $_size = _compileNextByte( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, \$_next_data, $__format_text_ref, $__indent );
    _trace4_(
        _decode_val( "%02X", \$_size, $__format_text_ref->[$$__format_pos_ref], $__indent, $__indent, ",\n" . " " x $__indent, " | ", " / " ) . " (size1)\n" );
    Carp::carp("$OText::STR{WARN} SSLhello::__compile_bytes_size1 for '$__pdu_name': Length $_size too big\n") if ( $_size > 0xFF );
    $$__buffer_ref .= pack( "C a*", $_size, $_next_data, );
    $_size += 1;
    return ($_size);
}

sub __compile_bytes_size2 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    my $_size      = 0;
    my $_next_data = "";
    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_size2 for '$__pdu_name'    ($$__format_pos_ref, $$__param_pos_ref)\n" );
    $$__format_pos_ref++;
    $_size = _compileNextByte( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, \$_next_data, $__format_text_ref, $__indent );
    _trace4_(
        _decode_val(
            "%04X",    \$_size, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . " (size2)\n"
    );
    Carp::carp("$OText::STR{WARN} SSLhello::__compile_bytes_size2 for '$__pdu_name': Length $_size too big\n") if ( $_size > 0xFFFF );
    $$__buffer_ref .= pack( "n a*", $_size, $_next_data, );
    $_size += 2;
    return ($_size);
}

sub __compile_bytes_val1 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    my $_size = 0;
    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_val1 for '$__pdu_name'     ($$__format_pos_ref, $$__param_pos_ref)\n" );
    return (0) if ( !defined( $__param_ref->[$$__param_pos_ref] ) );
    my $__val = $__param_ref->[ $$__param_pos_ref++ ];
    _trace4_(
        _decode_val(
            "%02X",    \$__val, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . " (val1)\n"
    );
    Carp::carp("$OText::STR{WARN} SSLhello::__compile_bytes_val1 for '$__pdu_name': value $__val too big\n") if ( $__val > 0xFF );
    $$__buffer_ref .= pack( "C", $__val, );
    $_size += 1;
    return ($_size);
}

sub __compile_bytes_val2 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    my $_size = 0;
    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_val2 for '$__pdu_name'     ($$__format_pos_ref, $$__param_pos_ref)\n" );
    return (0) if ( !defined( $__param_ref->[$$__param_pos_ref] ) );
    my $__val = $__param_ref->[ $$__param_pos_ref++ ];
    _trace4_(
        _decode_val(
            "%04X",    \$__val, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . " (val2)\n"
    );
    Carp::carp("$OText::STR{WARN} SSLhello::__compile_bytes_val2 for '$__pdu_name': value $__val too big\n") if ( $__val > 0xFFFF );
    $$__buffer_ref .= pack( "n", $__val, );
    $_size += 2;
    return ($_size);
}

sub __compile_bytes_val4 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    my $_size = 0;
    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_val4 for '$__pdu_name'     ($$__format_pos_ref, $$__param_pos_ref)\n" );
    return (0) if ( !defined( $__param_ref->[$$__param_pos_ref] ) );
    my $__val = $__param_ref->[ $$__param_pos_ref++ ];
    _trace4_(
        _decode_val(
            "%04X",    \$__val, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . " (val4)\n"
    );
    Carp::carp("$OText::STR{WARN} SSLhello::__compile_bytes_val4 for '$__pdu_name': value $__val too big\n") if ( $__val > 0xFFFFFFFF );
    $$__buffer_ref .= pack( "N", $__val, );
    $_size += 4;
    return ($_size);
}

sub __compile_bytes_val1List ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    my $_size = 0;
    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_val1List for '$__pdu_name' ($$__format_pos_ref, $$__param_pos_ref)\n" );
    return (0) if ( !defined( $__param_ref->[$$__param_pos_ref] ) );
    my @__list = @{ $__param_ref->[ $$__param_pos_ref++ ] };
    _trace4_(
        _decode_val(
            "%02X",    \@__list, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x ( $__indent + 2 ),
            " | ", " / "
          )
          . " (val1List)\n"
    );
    $$__buffer_ref .= pack( "C" x (@__list), @__list, );
    $_size += (@__list);
    return ($_size);
}

sub __compile_bytes_val2List ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    my $_size = 0;
    _trace4_( " " x $__indent . "# SSLhello: __compile_bytes_val2List for '$__pdu_name' ($$__format_pos_ref, $$__param_pos_ref)\n" );
    return (0) if ( !defined( $__param_ref->[$$__param_pos_ref] ) );
    my @__list = @{ $__param_ref->[ $$__param_pos_ref++ ] };
    _trace4_(
        _decode_val(
            "%04X",    \@__list, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x ( $__indent + 2 ),
            " | ", " / "
          )
          . " (val2List)\n"
    );
    $$__buffer_ref .= pack( "n" x (@__list), @__list, );
    $_size += 2 * (@__list);
    return ($_size);
}

my %__compile_bytes_subs = (
    len1     => \&__compile_bytes_len1,
    len2     => \&__compile_bytes_len2,
    len3     => \&__compile_bytes_len3,
    raw      => \&__compile_bytes_raw,
    sequence => \&__compile_bytes_sequence,
    size1    => \&__compile_bytes_size1,
    size2    => \&__compile_bytes_size2,
    val1     => \&__compile_bytes_val1,
    val2     => \&__compile_bytes_val2,
    val4     => \&__compile_bytes_val4,
    val1List => \&__compile_bytes_val1List,
    val2List => \&__compile_bytes_val2List,
);

sub _compileNextByte ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $_size             = 0;
    return (0) if ( !defined( $__format_ref->[$$__format_pos_ref] ) );
    if ( $__compile_bytes_subs{ $__format_ref->[$$__format_pos_ref] } ) {
        $_size = $__compile_bytes_subs{ $__format_ref->[$$__format_pos_ref] }
          ->( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref, $__format_text_ref, $__indent );
        _trace5_("\n");
    }
    else {
        Carp::carp(
"$OText::STR{WARN} SSLhello::_compileNextByte  for '$__pdu_name': \'No such format sub: $__format_ref->[$$__format_pos_ref]\', => Please verify hash 'CH'-format definition in OCfg.pm e.g. in \%TLS_EXTENSIONS"
        );
    }
    _trace4_(
        " " x ( $__indent + 3 ) . "# SSLhello: _CompileNextByte for '$__pdu_name' ->" . _sprintf_hex_val( "", $__buffer_ref, ( $__indent + 31 ) ) . "\n" );
    $$__format_pos_ref++;
    return ($_size);
}

sub _compileAllBytes ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    my $_size = 0;
    while ( defined( $__format_ref->[$$__format_pos_ref] ) ) {
        $_size +=
          _compileNextByte( $__pdu_name, $__format_ref, $__param_ref, $__format_pos_ref, $__param_pos_ref, $__buffer_ref, $__format_text_ref, $__indent );
    }
    return ($_size);
}

sub _compileClientHelloExtensions ($$$$@) {
    my ( $record_version, $version, $ciphers, $host, %clientHello ) = @_;
    my $clientHello_extensions = "";

    my $ssl = $PROTOCOL_NAME_BY_HEX{$version};
    if ( !defined $ssl ) {
        $ssl = "--unknown protocol--";
    }
    my $_ext_format_pos  = 0;
    my $_ext_param_pos   = 0;
    my $_extensions_data = "";
    my $_extensions_params_ref;

    if ( ( ( $version == $PROTOCOL_VERSION{'SSLv3'} ) && ( !$SSLhello::force_TLS_extensions ) ) || ( $version == $PROTOCOL_VERSION{'SSLv2'} ) ) {
        _trace2("compileClientHelloExtensions: Protocol $ssl does not support TLS extensions including SNI -> no extension added\n");
        return ("");
    }

    if ($SSLhello::usereneg) {
        my $anzahl      = int length( $clientHello{'cipher_spec'} ) / 2;
        my @cipherTable = unpack( "a2" x $anzahl, $clientHello{'cipher_spec'} );
        unless ( ( $SSLhello::double_reneg == 0 ) && ( grep { /\x00\xff/x } @cipherTable ) ) {

            if ( !grep { /^renegotiation_info$/x } @{ $SSLhello::extensions_by_prot->{$ssl} } ) {
                unshift @{ $SSLhello::extensions_by_prot->{$ssl} }, 'renegotiation_info';
            }
            _trace2("compileClientHelloExtensions ($ssl): extension renegotiation_info will be added\n");
        }
        else {
            _trace2(
"compileClientHelloExtensions ($ssl): Extension renegotiation_info will *NOT* be sent as the cipher_spec includes already the Signalling Cipher Suite Value (TLS_EMPTY_RENEGOTIATION_INFO_SCSV {0x00, 0xFF})\n"
            );
            @{ $SSLhello::extensions_by_prot->{$ssl} } = grep { $_ ne 'renegotiation_info' } @{ $SSLhello::extensions_by_prot->{$ssl} };
        }
    }

    if ($SSLhello::usesni) {
        $SSLhello::sni_name =~ s/\s*(.*?)\s*\r?\n?/$1/gx if ($SSLhello::sni_name);
        $SSLhello::use_sni_name = 1                      if ( ( $SSLhello::use_sni_name == 0 ) && ($SSLhello::sni_name) && ( $SSLhello::sni_name ne "1" ) );
        unless ($SSLhello::use_sni_name) {
            $clientHello{'extension_sni_name'} = $host;
        }
        else {
            $clientHello{'extension_sni_name'} = ($SSLhello::sni_name) ? $SSLhello::sni_name : "";
        }
        _trace2("compileClientHelloExtensions ($ssl): extension server_name for '$clientHello{'extension_sni_name'}' will be added\n");
        $OCfg::TLS_EXTENSIONS{server_name}{DEFAULT}[0][1] = $clientHello{'extension_sni_name'};
        if ( !grep { /^server_name$/x } @{ $SSLhello::extensions_by_prot->{$ssl} } ) {
            unshift @{ $SSLhello::extensions_by_prot->{$ssl} }, 'server_name';
        }
    }
    else {
        _trace2("compileClientHelloExtensions ($ssl): NO server_name extension will be added\n");
        @{ $SSLhello::extensions_by_prot->{$ssl} } = grep { $_ ne 'server_name' } @{ $SSLhello::extensions_by_prot->{$ssl} };
    }

    my $_indent = 6;
    _trace4_( " " x $_indent . "Compile extensions ($ssl):\n" );
    foreach my $_extension ( @{ $SSLhello::extensions_by_prot->{$ssl} } ) {
        $_indent = 6;
        _trace4_( " " x $_indent . "extension '$_extension':\n" );
        $_extensions_data = "";
        if ( defined $OCfg::TLS_EXTENSIONS{$_extension} ) {
            _trace4_( " " x $_indent . "ID = ($OCfg::TLS_EXTENSIONS{$_extension}{ID}: CH:" );
            if ( @{ $OCfg::TLS_EXTENSIONS{$_extension}{CH} } ) {
                for ( my $nr = 0 ; $nr < ( @{ $OCfg::TLS_EXTENSIONS{$_extension}{CH} } ) ; $nr++ ) {
                    _trace4_(", ") if $nr > 0;
                    _trace4_("$OCfg::TLS_EXTENSIONS{$_extension}{CH}[$nr]");
                }
                _trace5_( "\n"
                      . " " x $_indent
                      . "#  ---> Use temporary defined parameters for extension '$_extension': "
                      . exists( $SSLhello::extensions_params_hash{$_extension} ) );
                $_extensions_params_ref =
                  ( exists( $SSLhello::extensions_params_hash{$_extension} ) )
                  ? \@{ $SSLhello::extensions_params_hash{$_extension} }
                  : \@{ $OCfg::TLS_EXTENSIONS{$_extension}{DEFAULT} };
                _trace4_(" (");
                if (@$_extensions_params_ref) {
                    for ( my $nr = 0 ; $nr < (@$_extensions_params_ref) ; $nr++ ) {
                        my $val = $_extensions_params_ref->[$nr];
                        _trace5_( "\n" . " " x ( $_indent + 2 ) );
                        _trace4_( _decode_val( undef, \$val, undef, 0, ( $_indent + 2 ), ":\n" . " " x ( $_indent + 2 ), ", ", " | ", " / " ) );
                    }
                }
                _trace4_(")):\n");
                $_ext_format_pos = 0;
                $_ext_param_pos  = 0;
                $_indent += 3;
                _trace5_( " " x $_indent . _sprintf_hex_val( "ID: 0x%04X", \$OCfg::TLS_EXTENSIONS{$_extension}{ID}, $_indent + 3 ) . "\n" );
                $_extensions_data = pack( "n", $OCfg::TLS_EXTENSIONS{$_extension}{ID}, );
                _compileAllBytes(
                    $_extension, \@{ $OCfg::TLS_EXTENSIONS{$_extension}{CH} },
                    $_extensions_params_ref, \$_ext_format_pos, \$_ext_param_pos, \$_extensions_data,
                    \@{ $OCfg::TLS_EXTENSIONS{$_extension}{CH_TEXT} },
                    $_indent + 3
                );
                _trace5_( " " x ( $_indent + 3 ) . _sprintf_hex_val( "", \$_extensions_data, ( $_indent + 6 ) ) . " " x ($_indent) . ")\n" );

                _trace5_("------------- show the compiled extension --------------\n");
                _parseExtensions( "CH", undef, \$_extensions_data, length($_extensions_data) ) if ( $SSLhello::trace > 4 );
                $clientHello_extensions .= $_extensions_data;
            }
            _trace4_("\n");
        }
    }
    $clientHello{'extensions_total_len'} = length($clientHello_extensions);

    if ($clientHello_extensions) {
        $clientHello_extensions = pack( "n a*", length($clientHello_extensions), $clientHello_extensions );
        _trace4(
                sprintf( "_compileClientHelloExtensions ($ssl) (extensions_total_len = %04X)\n          >", $clientHello{'extensions_total_len'} )
              . hexCodedString( $clientHello_extensions, "           " )
              . "<\n" );
    }
    return ($clientHello_extensions);
}

sub __parse_bytes_len1 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $__next_data       = "";
    my $__len;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_len1 for '$__pdu_name'      ($$__format_pos_ref, $__buffer_size)\n");
    return (0) if ( $__buffer_size < 1 );
    ( $__len, $$__buffer_ref ) = unpack( "C a*", $$__buffer_ref );
    _trace2_(
        _decode_val(
            "%02X",    \$__len, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . "\n"
    );
    $$__format_pos_ref++;
    ( $__next_data, $$__buffer_ref ) = unpack( "a[$__len] a*", $$__buffer_ref );
    _parseAllBytes( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, \$__next_data, $__len, $__format_text_ref, $__indent + 3 )
      if ( $__len > 0 );
    $__buffer_size -= ( 1 + $__len );
    return ($__buffer_size);
}

sub __parse_bytes_len2 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $__next_data       = "";
    my $__len;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_len2 for '$__pdu_name'      ($$__format_pos_ref, $__buffer_size)\n");
    return (0) if ( $__buffer_size < 2 );
    ( $__len, $$__buffer_ref ) = unpack( "n a*", $$__buffer_ref );
    _trace2_(
        _decode_val(
            "%04X",    \$__len, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . "\n"
    );
    $$__format_pos_ref++;
    ( $__next_data, $$__buffer_ref ) = unpack( "a[$__len] a*", $$__buffer_ref );
    _parseAllBytes( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, \$__next_data, $__len, $__format_text_ref, $__indent + 3 )
      if ( $__len > 0 );
    $__buffer_size -= ( 2 + $__len );
    return ($__buffer_size);
}

sub __parse_bytes_len3 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $__next_data       = "";
    my $__len;
    my $__len1;
    my $__len2;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_len3 for '$__pdu_name'      ($$__format_pos_ref, $__buffer_size)\n");
    return (0) if ( $__buffer_size < 3 );
    ( $__len1, $__len2, $$__buffer_ref ) = unpack( "C n a*", $$__buffer_ref );
    $__len = $__len1 << 16;
    $__len += $__len2;
    _trace2_(
        _decode_val(
            "%06X",    \$__len, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . "\n"
    );
    $$__format_pos_ref++;
    ( $__next_data, $$__buffer_ref ) = unpack( "a[$__len] a*", $$__buffer_ref );
    _parseAllBytes( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, \$__next_data, $__len, $__format_text_ref, $__indent + 3 )
      if ( $__len > 0 );
    $__buffer_size -= ( 3 + $__len );
    return ($__buffer_size);
}

sub __parse_bytes_raw ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $_hex_str          = "";

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_raw for '$__pdu_name'       ($$__format_pos_ref, $__buffer_size)\n");
    return (0) if ( $__buffer_size < 1 );
    return (0) if ( $__buffer_size > length($$__buffer_ref) );
    ( my $__val, $$__buffer_ref ) = unpack( "a[$__buffer_size] a*", $$__buffer_ref );
    _trace2_(
        _decode_val(
            "", \$__val, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . "\n"
    );

    if ( ( defined($__param_hash_ref) ) && ( ref($__param_hash_ref) eq "HASH" ) ) {
        push @{ $__param_hash_ref->{values} },           $__val;
        push @{ $__param_hash_ref->{format_positions} }, $$__format_pos_ref;
    }
    return (0);
}

sub __parse_bytes_sequence ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $__sequence_pos    = 0;
    my %__sequence_param_hash;
    my $__sequence_param_hash_ref = ( defined($__param_hash_ref) ) ? \%__sequence_param_hash : undef;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_sequence for '$__pdu_name'  ($$__format_pos_ref, $__buffer_size):\n");
    _trace4_(
        _decode_val(
            undef, undef,
            $__format_text_ref->[$$__format_pos_ref],
            ( $__indent + 3 ),
            ( $__indent + 3 ),
            ":\n" . " " x ( $__indent + 3 ),
            ",\n" . " " x ( $__indent + 3 ),
            " | ", " / "
          )
          . "\n"
    );
    $$__format_pos_ref++;
    $__sequence_pos = $$__format_pos_ref;
    my $_counter = 0;

    while ( defined($$__buffer_ref) && ( $__buffer_size > 0 ) ) {
        $$__format_pos_ref = $__sequence_pos;
        _trace4_( " " x $__indent );
        _trace4("__parse_bytes_sequence: (next) sequence elements for '$__pdu_name' ($$__format_pos_ref, $__buffer_size)\n");
        $__buffer_size =
          _parseAllBytes( $__pdu_name, $__format_ref, $__sequence_param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size, $__format_text_ref,
            $__indent );
        if ( ( defined($__param_hash_ref) ) && ( ref($__param_hash_ref) eq "HASH" ) ) {
            push @{ $__param_hash_ref->{values} },           [ $__sequence_param_hash_ref->{values} ];
            push @{ $__param_hash_ref->{format_positions} }, $__sequence_param_hash_ref->{format_positions} if ( $_counter++ == 0 );
        }
    }
    _trace4_( " " x $__indent );
    _trace4_("__parse_bytes_sequence for '$__pdu_name': ------ End of sequence ------\n");
    return ($__buffer_size);
}

sub __parse_bytes_size1 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $__next_data       = "";
    my $__len             = 0;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_size1 for '$__pdu_name'     ($$__format_pos_ref, $__buffer_size)\n");
    return (0) if ( $__buffer_size < 1 );
    ( $__len, $$__buffer_ref ) = unpack( "C a*", $$__buffer_ref );
    _trace2_(
        _decode_val(
            "%02X",    \$__len, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . "\n"
    );
    $$__format_pos_ref++;
    ( $__next_data, $$__buffer_ref ) = unpack( "a[$__len] a*", $$__buffer_ref );
    _parseNextByte( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, \$__next_data, $__len, $__format_text_ref, $__indent + 3 )
      if ( $__len > 0 );
    $__buffer_size -= ( 1 + $__len );
    return ($__buffer_size);
}

sub __parse_bytes_size2 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $__next_data       = "";
    my $__len             = 0;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_size2 for '$__pdu_name'     ($$__format_pos_ref, $__buffer_size)\n");
    return (0) if ( $__buffer_size < 2 );
    ( $__len, $$__buffer_ref ) = unpack( "n a*", $$__buffer_ref );
    _trace2_(
        _decode_val(
            "%04X",    \$__len, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . "\n"
    );
    $$__format_pos_ref++;
    ( $__next_data, $$__buffer_ref ) = unpack( "a[$__len] a*", $$__buffer_ref );
    _parseNextByte( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, \$__next_data, $__len, $__format_text_ref, $__indent + 3 )
      if ( $__len > 0 );
    $__buffer_size -= ( 2 + $__len );
    return ($__buffer_size);
}

sub __parse_bytes_val1 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_val1 for '$__pdu_name':     ($$__format_pos_ref, $__buffer_size)\n");
    return (0) if ( $__buffer_size < 1 );
    ( my $__val, $$__buffer_ref ) = unpack( "C a*", $$__buffer_ref );
    _trace2_(
        _decode_val(
            "%02X",    \$__val, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . "\n"
    );

    if ( ( defined($__param_hash_ref) ) && ( ref($__param_hash_ref) eq "HASH" ) ) {
        push @{ $__param_hash_ref->{values} },           $__val;
        push @{ $__param_hash_ref->{format_positions} }, $$__format_pos_ref;
    }
    $__buffer_size -= 1;
    return ($__buffer_size);
}

sub __parse_bytes_val2 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_val2 for '$__pdu_name':     ($$__format_pos_ref, $__buffer_size)\n");
    return (0) if ( $__buffer_size < 2 );
    ( my $__val, $$__buffer_ref ) = unpack( "n a*", $$__buffer_ref );
    _trace2_(
        _decode_val(
            "%04X",    \$__val, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . "\n"
    );

    if ( ( defined($__param_hash_ref) ) && ( ref($__param_hash_ref) eq "HASH" ) ) {
        push @{ $__param_hash_ref->{values} },           $__val;
        push @{ $__param_hash_ref->{format_positions} }, $$__format_pos_ref;
    }
    $__buffer_size -= 2;
    return ($__buffer_size);
}

sub __parse_bytes_val4 ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_val4 for '$__pdu_name':     ($$__format_pos_ref, $__buffer_size)\n");
    return (0) if ( $__buffer_size < 4 );
    ( my $__val, $$__buffer_ref ) = unpack( "N a*", $$__buffer_ref );
    _trace2_(
        _decode_val(
            "%08X",    \$__val, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x $__indent,
            " | ", " / "
          )
          . "\n"
    );

    if ( ( defined($__param_hash_ref) ) && ( ref($__param_hash_ref) eq "HASH" ) ) {
        push @{ $__param_hash_ref->{values} },           $__val;
        push @{ $__param_hash_ref->{format_positions} }, $$__format_pos_ref;
    }
    $__buffer_size -= 4;
    return ($__buffer_size);
}

sub __parse_bytes_val1List ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_val1List for '$__pdu_name': ($$__format_pos_ref, $__buffer_size):\n");
    return (0) if ( $__buffer_size < 1 );
    ( my $__data, $$__buffer_ref ) = unpack( "a[$__buffer_size] a*", $$__buffer_ref );
    ( my @__list ) = unpack( "C*", $__data );
    _trace2_(
        _decode_val(
            "%02X",    \@__list, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x ( $__indent + 2 ),
            " | ", " / "
          )
          . "\n"
    );

    if ( ( defined($__param_hash_ref) ) && ( ref($__param_hash_ref) eq "HASH" ) ) {
        push @{ $__param_hash_ref->{values} },           [@__list];
        push @{ $__param_hash_ref->{format_positions} }, $$__format_pos_ref;
    }
    $__buffer_size -= (@__list);
    return ($__buffer_size);
}

sub __parse_bytes_val2List ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    _trace4_( " " x $__indent );
    _trace4("__parse_bytes_val2List for '$__pdu_name': ($$__format_pos_ref, $__buffer_size):\n");
    return (0) if ( $__buffer_size < 2 );
    ( my $__data, $$__buffer_ref ) = unpack( "a[$__buffer_size] a*", $$__buffer_ref );
    ( my @__list ) = unpack( "n*", $__data );
    _trace2_(
        _decode_val(
            "%04X",    \@__list, $__format_text_ref->[$$__format_pos_ref],
            $__indent, $__indent,
            ":\n" . " " x $__indent,
            ",\n" . " " x ( $__indent + 2 ),
            " | ", " / "
          )
          . "\n"
    );

    if ( ( defined($__param_hash_ref) ) && ( ref($__param_hash_ref) eq "HASH" ) ) {
        push @{ $__param_hash_ref->{values} },           [@__list];
        push @{ $__param_hash_ref->{format_positions} }, $$__format_pos_ref;
    }
    $__buffer_size -= 2 * (@__list);
    return ($__buffer_size);
}

my %__parse_bytes_subs = (
    len1     => \&__parse_bytes_len1,
    len2     => \&__parse_bytes_len2,
    len3     => \&__parse_bytes_len3,
    raw      => \&__parse_bytes_raw,
    sequence => \&__parse_bytes_sequence,
    size1    => \&__parse_bytes_size1,
    size2    => \&__parse_bytes_size2,
    val1     => \&__parse_bytes_val1,
    val2     => \&__parse_bytes_val2,
    val4     => \&__parse_bytes_val4,
    val1List => \&__parse_bytes_val1List,
    val2List => \&__parse_bytes_val2List,
);

sub _parseNextByte ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;
    my $_hex_str          = "";

    _trace5_( " " x ($__indent) . "_parseNextByte for '$__pdu_name' ($$__format_pos_ref, $__buffer_size)\n" );
    return ($__buffer_size) if ( $$__format_pos_ref >= (@$__format_ref) );
    return (0)              if ( !defined( $__format_ref->[$$__format_pos_ref] ) );
    return (0)              if ( $__buffer_size < 1 );
    if ( $__parse_bytes_subs{ $__format_ref->[$$__format_pos_ref] } ) {
        $__buffer_size = $__parse_bytes_subs{ $__format_ref->[$$__format_pos_ref] }
          ->( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size, $__format_text_ref, $__indent );
    }
    else {
        Carp::carp(
"$OText::STR{WARN} SSLhello::_parseNextByte ($__pdu_name): \'No such format sub: $__format_ref->[$$__format_pos_ref]\', => Please verify hash format definition in OCfg.pm e.g. in \%TLS_EXTENSIONS"
        );
    }
    $$__format_pos_ref++;
    return ($__buffer_size);
}

sub _parseAllBytes ($$$$$$;$$) {
    my ( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size ) = ( shift, shift, shift, shift, shift, shift );
    my $__format_text_ref = shift;
    my $__indent          = shift || 0;

    _trace5_( " " x ($__indent) . "_parseAllBytes for '$__pdu_name' ($$__format_pos_ref, $__buffer_size)\n" );
    while ( ( $$__format_pos_ref < (@$__format_ref) ) && ( defined( $__format_ref->[$$__format_pos_ref] ) ) && ( $__buffer_size > 0 ) ) {
        $__buffer_size =
          _parseNextByte( $__pdu_name, $__format_ref, $__param_hash_ref, $__format_pos_ref, $__buffer_ref, $__buffer_size, $__format_text_ref, $__indent );
    }
    return ($__buffer_size);
}

sub _parseExtensions ($$$$;$$) {
    my ( $_ext_ch_rx, $_param_hash_ref, $_buffer_ref, $_buffer_size ) = ( shift, shift, shift, shift );
    my $protocolCipher      = shift || "";
    my $_indent             = shift || 12;
    my $_format_pos         = 0;
    my $_name               = "_parseExtensions: ";
    my $_extensions_data    = $$_buffer_ref;
    my @__local_format_text = ( \%OCfg::TLS_ID_TO_EXTENSIONS );
    my $__local_format_pos  = 0;

    _trace4_( " " x $_indent . "# SSLhello::_parseExtensions ():\n" );
    if ( ( $_buffer_size < 0 ) && ( length($_extensions_data) > 2 ) ) {
        ( $_buffer_size, $_extensions_data ) = unpack( "n a*", $_extensions_data );
        _trace2_(
            _decode_val( "%04X", \$_buffer_size, "length of extensions", $_indent, $_indent, ":\n" . " " x $_indent, ",\n" . " " x $_indent, " | ", " / " )
              . "\n" );
        $_indent += 3;
    }
  PARSE_EXTENSION: while ( $_buffer_size > 2 ) {
        $__local_format_pos = 0;
        $_format_pos        = 0;
        my %_extension_type_hash;
        $_extension_type_hash{values}           = ();
        $_extension_type_hash{format_positions} = ();
        $_buffer_size =
          __parse_bytes_val2( $_name, undef, \%_extension_type_hash, \$__local_format_pos, \$_extensions_data, $_buffer_size, \@__local_format_text, $_indent );
        if ( !defined( $_extension_type_hash{values}[0] ) ) {
            Carp::carp("$OText::STR{WARN} SSLhello::_parseExtensions: warn: no (more) defined extensionus found parsing the record\n");
            next PARSE_EXTENSION;
        }
        my $_extension = $OCfg::TLS_ID_TO_EXTENSIONS{ $_extension_type_hash{values}[0] }[0];
        $_extension = "-- unknown (" . $_extension_type_hash{values}[0] . ") --" if ( !defined($_extension) );
        _trace5_( " " x ( $_indent + 4 ) . "== Extension '$_extension' ($_extension_type_hash{values}[0]):\n" );

        my %_param_hash;
        $_param_hash{values}           = ();
        $_param_hash{format_positions} = ();
        $_buffer_size                  = _parseAllBytes(
            $_extension,   \@{ $OCfg::TLS_EXTENSIONS{$_extension}{$_ext_ch_rx} },
            \%_param_hash, \$_format_pos, \$_extensions_data, $_buffer_size,
            \@{ $OCfg::TLS_EXTENSIONS{$_extension}{ "$_ext_ch_rx" . "_TEXT" } },
            $_indent + 3
        );
        next PARSE_EXTENSION if ( !defined($_param_hash_ref) );
        _trace5_( " " x ( $_indent + 4 ) . "#      ---> _parseExtensions: ref (\$_param_hash_ref) = " . ref($_param_hash_ref) . "\n" );
        next PARSE_EXTENSION if ( ref($_param_hash_ref) ne "HASH" );
        _trace5_( " " x ( $_indent + 4 )
              . "#      ---> format:           \@{\$_param_hash{format_positions}} = "
              . _decode_val( undef, \@{ $_param_hash{format_positions} }, undef, 0, $_indent + 4, ":\n" . " " x ( $_indent + 4 ), ", ", " | ", " / " )
              . "\n" );
        _trace5_( " " x ( $_indent + 4 )
              . "#      ---> raw value:        \@{\$_param_hash{values}}           = "
              . _decode_val( undef, \@{ $_param_hash{values} }, undef, 0, $_indent + 4, ":\n" . " " x ( $_indent + 4 ), ", ", " | ", " / " )
              . "\n" );
        _trace5_( " " x ( $_indent + 4 )
              . "#      ---> Complex format: (\@{\$_param_hash{format_positions}}) = "
              . ( @{ $_param_hash{format_positions} } )
              . " > 1?\n" );

        if ( ( @{ $_param_hash{format_positions} } ) > 1 ) {
            @{ $_param_hash{values} } = [ @{ $_param_hash{values} } ];
        }
        elsif ( ref( $_param_hash{values}[0] ) eq 'ARRAY' ) {
            @{ $_param_hash{values} } = $_param_hash{values}[0];
        }
        _trace5_( " " x ( $_indent + 4 )
              . "#      ---> standardized value(s): \@{\$_param_hash{values}}      = "
              . _decode_val( undef, \@{ $_param_hash{values} }, undef, 0, $_indent + 4, ":\n" . " " x ( $_indent + 4 ), ", ", " | ", " / " )
              . "\n" );

        if ( !exists( $_param_hash_ref->{$_extension}{$_ext_ch_rx} ) ) {
            _trace4_( " " x ( $_indent + 4 ) . "#      ===> store first results in \%{\$_param_hash_ref->{$_extension}{$_ext_ch_rx}}\n" );
            $_param_hash_ref->{$_extension}{$_ext_ch_rx} = \%_param_hash;
            _trace4_( " " x ( $_indent + 4 )
                  . "#      ===> value(s):     \@{\$_param_hash{values}}           = "
                  . _decode_val( undef, \@{ $_param_hash{values} }, undef, 0, $_indent + 4, ":\n" . " " x ( $_indent + 4 ), ", ", " | ", " / " )
                  . "\n" );
            _trace4_( " " x ( $_indent + 4 )
                  . "#      ===> format(s):    \@{\$_param_hash{format_positions}} = "
                  . _decode_val( undef, \@{ $_param_hash{format_positions} }, undef, 0, $_indent + 4, ":\n" . " " x ( $_indent + 4 ), ", ", " | ", " / " )
                  . "\n" );
        }
        else {
            _trace5_( " " x ( $_indent + 4 )
                  . "#      ---> Check if format is a SCALAR:       ref (\\\$_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions}[0]) = '"
                  . ref( \$_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions}[0] )
                  . "'\n" );
            _trace5_( " " x ( $_indent + 4 )
                  . "#      ---> Check if format is a nested array: ref (\$_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions}[0]) =  '"
                  . ref( $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions}[0] )
                  . "'\n" );
            if ( ref( \$_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions}[0] ) eq 'SCALAR' ) {
                _trace5_( " " x ( $_indent + 4 )
                      . "#      ---> merge new results and format_positions with hash '\$_param_hash_ref->{$_extension}{$_ext_ch_rx}}'\n" );
                my $found = 1;
                _trace5_( " " x ( $_indent + 4 )
                      . "#      ---> (\@{\$_param_hash{format_positions}}) ("
                      . ( @{ $_param_hash{format_positions} } )
                      . ") == (\@{\$_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions}}) ) ("
                      . ( @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions} } )
                      . ")?\n" );
                next PARSE_EXTENSION if ( ( @{ $_param_hash{format_positions} } ) == 0 );
                if ( ( @{ $_param_hash{format_positions} } ) == ( @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions} } ) ) {
                  CHECK_FORMAT: for ( my $__pos = 0 ; $__pos < ( @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions} } ) ; $__pos++ ) {
                        _trace5_( " " x ( $_indent + 4 )
                              . "#      ---> \$_param_hash{format_positions}[$__pos] ("
                              . $_param_hash{format_positions}[$__pos]
                              . ") == \$_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions}[$__pos] ("
                              . $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions}[$__pos]
                              . ")?\n" );
                        if ( $_param_hash{format_positions}[$__pos] ne $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions}[$__pos] ) {

                            _trace4_(
                                    " " x ( $_indent + 4 )
                                  . "#      ---> merging values and format_positions to the result hash with different format sequences is not yet implemented! New values are lost: "
                                  . _decode_val(
                                    undef,
                                    \@{ $_param_hash{values} },
                                    $OCfg::TLS_EXTENSIONS{$_extension}{ "$_ext_ch_rx" . "_TEXT" }[$_format_pos],
                                    0,    $_indent + 17,
                                    ", ", " | ", " / "
                                  )
                                  . "\n"
                            );
                            Carp::carp(
"$OText::STR{WARN} SSLhello::_parseExtensions: merging values and format_positions to the result hash with different format sequences is not yet implemented! New values are lost: "
                                  . _decode_val(
                                    undef,
                                    \@{ $_param_hash{values} },
                                    $OCfg::TLS_EXTENSIONS{$_extension}{ "$_ext_ch_rx" . "_TEXT" }[$_format_pos],
                                    0,    $_indent + 17,
                                    ", ", " | ", " / "
                                  )
                                  . "\n"
                            );
                            $found = 0;
                            last CHECK_FORMAT;
                        }
                    }
                    if ( $found > 0 ) {
                        _trace5_(
                            " " x ( $_indent + 4 ) . "#      ---> check for new results to be stored in '\$_param_hash_ref->{$_extension}{$_ext_ch_rx}}'\n" );
                        _trace5_( " " x ( $_indent + 4 )
                              . "#      ---> number of values to check: (\@{\$_param_hash{values}}) ("
                              . ( @{ $_param_hash{values} } )
                              . ") <=> (\@{\$_param_hash_ref->{$_extension}{$_ext_ch_rx}{values}}) ) ("
                              . ( @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{values} } )
                              . ")?\n" );
                        next PARSE_EXTENSION if ( ( @{ $_param_hash{values} } ) == 0 );
                      FOREACH_NEW_VALUE: foreach my $_new_ele ( @{ $_param_hash{values} } ) {
                            _trace5_( " " x ( $_indent + 4 ) . "#        ---|> new values-ref-type (\\\$new_ele):       >" . ref( \$_new_ele ) . "<|\n" );
                            _trace5_( " " x ( $_indent + 4 ) . "#        --||> new values-ref-type (\$new_ele):        >" . ref($_new_ele) . "<||\n" );
                          FOREACH_STORED_VALUE: foreach my $_stored_ele ( @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{values} } ) {
                                _trace5_(
                                    " " x ( $_indent + 4 ) . "#        ---|> stored values-ref-type (\\\$stored_ele): >" . ref( \$_stored_ele ) . "<|\n" );
                                _trace5_( " " x ( $_indent + 4 ) . "#        --||> stored values-ref-type (\$stored_ele):   >" . ref($_stored_ele) . "<||\n" );
                                if ( ( ref( \$_new_ele ) eq "SCALAR" ) && ( ref( \$_stored_ele ) eq "SCALAR" ) ) {
                                    _trace5_( " " x ( $_indent + 4 )
                                          . "#        ---> check if value is new (\$new_ele): '"
                                          . _decode_val( undef, \$_new_ele, undef, 0, $_indent + 4, ", ", " | ", " / " )
                                          . "' == (\$_stored_ele): '"
                                          . _decode_val( undef, \$_new_ele, undef, 0, $_indent + 4, ", ", " | ", " / " )
                                          . "'?\n" );
                                    next FOREACH_STORED_VALUE if ( $_new_ele ne $_stored_ele );
                                    _trace5_( " " x ( $_indent + 4 )
                                          . "#        ---> \$_new_ele ("
                                          . _decode_val( undef, \$_new_ele, undef, 0, $_indent + 4, ", ", " | ", " / " )
                                          . ") has been alteady stored. Try next ele.\n" );
                                    next FOREACH_NEW_VALUE;
                                }
                                elsif ( ( ref($_new_ele) eq "ARRAY" ) && ( ref($_stored_ele) eq "ARRAY" ) ) {
                                  FOREACH_POSITION: foreach my $__pos ( 0 .. $#{ $_param_hash{format_positions} } ) {
                                        _trace5_( " " x ( $_indent + 4 )
                                              . "#        ---> \$_new_ele[$__pos] ("
                                              . _decode_val( undef, \@{$_new_ele}[$__pos], undef, 0, $_indent + 4, ", ", " | ", " / " )
                                              . ") == \$_stored_ele[$__pos] ("
                                              . _decode_val( undef, \@{$_stored_ele}[$__pos], undef, 0, $_indent + 4, ", ", " | ", " / " )
                                              . ")?\n" );
                                        next FOREACH_STORED_VALUE if ( @{$_new_ele}[$__pos] ne @{$_stored_ele}[$__pos] );
                                    }

                                    next FOREACH_NEW_VALUE;
                                }
                                else {
                                    _trace( " " x ( $_indent + 4 )
                                          . "**WARNING: SSLhello::_parseExtensions: internal error in result hash: new values-ref-type (\$_new_ele):       "
                                          . ref( \$_new_ele )
                                          . "<| !=  (\$_stored_ele): "
                                          . ref( \$_stored_ele )
                                          . "<| OR both neiter a SCALAR nor an ARRAY\n" );
                                    Carp::carp(
                                            "$OText::STR{WARN} SSLhello::_parseExtensions: internal error in result hash: can't compare and store new values: "
                                          . _decode_val( undef, \$_new_ele, undef, 0, $_indent + 4, ", ", " | ", " / " )
                                          . "\n" );
                                    next FOREACH_NEW_VALUE;
                                }
                            }

                            _trace4_( " " x ( $_indent + 4 )
                                  . "#      ===> add new value (array) to the result hash: "
                                  . _decode_val( undef, \$_new_ele, undef, 0, $_indent + 4, ", ", " | ", " / " )
                                  . "\n" );
                            push @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{values} }, $_new_ele;
                        }
                    }
                }
            }
            else {
                _trace4_( " " x ( $_indent + 4 )
                      . "#      ---> merging values and format_positions to the result hash with various formats is not yet implemented! New values are lost: "
                      . _decode_val( undef, \@{ $_param_hash{values} }, undef, 0, $_indent + 17, ", ", " | ", " / " )
                      . "\n" );
                Carp::carp(
"$OText::STR{WARN} SSLhello::_parseExtensions: merging values and format_positions to the result hash with various formats is not yet implemented! New values are lost: "
                      . _decode_val( undef, \@{ $_param_hash{values} }, undef, 0, $_indent + 17, ", ", " | ", " / " )
                      . "\n" );
            }
        }
        _trace5_( " " x ( $_indent + 4 ) . "# -> values           = " . @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{values} } . "\n" );
        _trace5_( " " x ( $_indent + 4 ) . "# -> format_positions = " . @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions} } . "\n" );
        _trace5_(
                " " x ( $_indent + 4 )
              . "# ---> _parseExtensions: \@{\$_param_hash_ref->{$_extension}{$_ext_ch_rx}{values}}          = "
              . _decode_val(
                undef, \@{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{values} },
                undef, 0,
                $_indent + 4,
                ":\n" . " " x ( $_indent + 4 ),
                ", ", " | ", " / "
              )
              . "\n"
        );
        _trace5_(
                " " x ( $_indent + 4 )
              . "# ---> _parseExtensions: \@{\$_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions} = "
              . _decode_val(
                undef, \@{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions} },
                undef, 0,
                $_indent + 4,
                ":\n" . " " x ( $_indent + 4 ),
                ", ", " | ", " / "
              )
              . "\n\n"
        );

        _trace2_(
                " " x ( $_indent + 4 )
              . "# ===> Cipher '$protocolCipher', Extension '$_extension': accumulated $_ext_ch_rx values ("
              . @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{values} } . "): "
              . _decode_val(
                undef, \@{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{values} },
                undef, 0,
                $_indent + 4,
                ":\n" . " " x ( $_indent + 4 ),
                ", ", " | ", " / "
              )
              . "\n"
        );
        _trace2_(
                " " x ( $_indent + 4 )
              . "# ===> Cipher '$protocolCipher', Extension '$_extension': $_ext_ch_rx format positions   ("
              . @{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions} } . "): "
              . _decode_val(
                undef, \@{ $_param_hash_ref->{$_extension}{$_ext_ch_rx}{format_positions} },
                undef, 0,
                $_indent + 4,
                ":\n" . " " x ( $_indent + 4 ),
                ", ", " | ", " / "
              )
              . "\n\n"
        );
    }
    return;
}

sub _doCheckAllExtensions ($$$$;$) {
    my $host            = shift || "";
    my $port            = shift || 443;
    my $protocol        = shift || 0;
    my $cipher          = shift || "";
    my $dtls_epoch      = shift || 0;
    my $parseAllRecords = 1;
    my $found_values    = 0;
    my $acceptedCipher;
    my $_last_extension = "";
    my $protocolCipher  = '0x0300' . hexCodedCipher($cipher);

    _trace4_( "_doCheckAllExtensions {(Cipher: " . hexCodedCipher($cipher) . "):\n" );
    return if ( !$cipher );
    foreach my $_extension ( @{$SSLhello::check_extensions} ) {
        _trace4_("#  ---> _doCheckAllExtensions: extension '$_extension': ");
        next
          if ( ( !exists( $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} ) )
            || ( ( @{ $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} } ) < 1 ) );
        $_last_extension = $_extension;
        for ( my $_i = 0 ; $_i < ( @{ $OCfg::TLS_EXTENSIONS{$_extension}{DEFAULT} } ) ; $_i++ ) {
            for ( my $_j = 0 ; $_j < ( @{ $OCfg::TLS_EXTENSIONS{$_extension}{DEFAULT}[$_i] } ) ; $_j++ ) {
                $SSLhello::extensions_params_hash{$_extension}[$_i][$_j] = $OCfg::TLS_EXTENSIONS{$_extension}{DEFAULT}[$_i][$_j];
            }
            _trace5_( " (copied values         = [$_i][" . ( @{ $SSLhello::extensions_params_hash{$_extension}[$_i] } ) . "], " );
        }
        _trace5_( " parameter arrays       = "
              . ( @{ $SSLhello::extensions_params_hash{$_extension} } ) . "/"
              . ( @{ $OCfg::TLS_EXTENSIONS{$_extension}{DEFAULT} } )
              . ", " );
        _trace5_( " format_positions       = " . ( @{ $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{format_positions} } ) . ", " )
          if ( exists( $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{format_positions} ) );
        _trace5_( " found values           = " . ( @{ $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} } ) )
          if ( exists( $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} ) );
        _trace5_("):\n");
        if ( ( @{ $OCfg::TLS_EXTENSIONS{$_extension}{DEFAULT} } ) == 1 ) {
            _trace5_("#    ---> extension '$_extension':\n");
            $found_values = 0;
            while (( exists( $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} ) )
                && ( ( @{ $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} } ) > $found_values ) )
            {
                _trace5_( "#     ---> parameter arrays[0]    = "
                      . ( @{ $SSLhello::extensions_params_hash{$_extension}[0] } ) . "/"
                      . ( @{ $OCfg::TLS_EXTENSIONS{$_extension}{DEFAULT}[0] } )
                      . "\n" );
                _trace5_( "#     ---> parameter values type  = " . ( ref( $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} ) ) . "\n" );
                last
                  if (
                    !defined(
                        $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values}->[ $#{ $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} } ]
                    )
                  );
                last
                  if (
                    !(
                        grep {
                            $_ eq
                              $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values}->[ $#{ $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} } ]
                        } @{ $SSLhello::extensions_params_hash{$_extension}[0] }
                    )
                  );
                $found_values++;
                _trace5_(
                    "#     ---> extension found $found_values value(s) "
                      . _decode_val(
                        undef,
                        \@{ $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} },
                        \$OCfg::TLS_EXTENSIONS{$_extension},
                        12, 12, ": ", ", ", " | ", " / "
                      )
                      . "\n"
                );
                @{ $SSLhello::extensions_params_hash{$_extension}[0] } = grep {
                    $_ ne $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values}->[ $#{ $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} } ]
                } @{ $SSLhello::extensions_params_hash{$_extension}[0] };
                _trace5_(
                    "#     ---> extensions_params_hash: "
                      . _decode_val(
                        undef,
                        \@{ $SSLhello::extensions_params_hash{$_extension}[0] },
                        \$OCfg::TLS_EXTENSIONS{$_extension},
                        12, 12, ": ", ", ", " | ", " / "
                      )
                      . "\n"
                );

                last if ( ( @{ $SSLhello::extensions_params_hash{$_extension}[0] } ) < 1 );
                if ( $found_values > $SSLhello::extensions_max_values ) {
                    _trace2_(
"**WARNING: SSLhello::_doCheckAllExtension ($_extension): To much checks for this extension. Watchdog aborted checks after getting $found_values values.\n**Hint: Please verify the hash '%OCfg::TLS_EXTENSIONS' and the variable '\$SSLhello::check_extensions', or '\$SSLhello::extensions_max_values' if necessary.\n"
                    );
                    Carp::carp(
"$OText::STR{WARN} SSLhello::_doCheckAllExtension ($_extension): To much checks for this extension. Watchdog aborted checks after getting $found_values values.\n**Hint: Please verify the hash '%OCfg::TLS_EXTENSIONS' and the variable '\$SSLhello::check_extensions', or '\$SSLhello::extensions_max_values' if necessary.\n"
                    );
                    last;
                }
                _trace5_( "#     ---> check next extension '$_extension' parameter for cipher: " . hexCodedCipher($cipher) . ":\n" );
                $acceptedCipher = _doCheckSSLciphers( $host, $port, $protocol, $cipher, $dtls_epoch, $parseAllRecords );
                _trace5_( "#     ---> received cipher: " . hexCodedCipher($acceptedCipher) . " == " . hexCodedCipher($cipher) . "?\n" );
                last if ( $acceptedCipher ne $cipher );
                _trace5_( "#     ---> next while (" . ( @{ $_SSLhello{$protocolCipher}{param}{$_extension}{RX}{values} } ) . " > $found_values)\n" );
            }
            _trace1_( "# _doCheckAllExtensions (Cipher: " . hexCodedCipher($cipher) . ") ==> extension '$_extension': found $found_values values.\n" );
        }
        else {
            _trace_(
"SSLhello::_doCheckAllExtensions ($_extension): Detailled checks for extensions with multiple parameters are not supported, yet. Please check variable '\$SSLhello::check_extensions')."
            );
            Carp::carp(
"$OText::STR{WARN} SSLhello::_doCheckAllExtensions ($_extension): Detailled checks for extensions with multiple parameters are not supported, yet. Please check variable '\$SSLhello::check_extensions')."
            );
        }
        delete( $SSLhello::extensions_params_hash{$_extension} );
    }
    delete( $SSLhello::extensions_params_hash{$_last_extension} ) if ( defined( $SSLhello::extensions_params_hash{$_last_extension} ) );
    return;
}


sub parseServerKeyExchange($$$) {
    my ( $keyExchange, $len, $d ) = @_;
    my ( $_tmpLen, $_null, $_handshake_type, $_bits ) = 0;
    my %_mySSLinfo;
    my $psk = "";
    _trace2("parseServerKeyExchange($keyExchange, $len, ...)\n");
    _trace4( "parseServerKeyExchange(KeyExchange= $keyExchange, Len= $len, Data= " . unpack( "H*", $d ) . "\n" );
    $_tmpLen = length( unpack( "H*", $d ) ) / 2;
    Carp::carp("$OText::STR{WARN} parseServerKeyExchange: Error in ServerKeyExchange Message: unexpected len ($_tmpLen) should be $len bytes")
      if ( $len != $_tmpLen );
    return ( "-- Error in ServerKeyExchange --", "", undef ) if ( $len != $_tmpLen );

    if ( $keyExchange =~ /PSK/ ) {
        ( $_mySSLinfo{'psk_identity_hint_len'}, $d ) = unpack( "n a*", $d );

        ( $_mySSLinfo{'psk_identity_hint'}, $d ) = unpack( "a[$_mySSLinfo{'psk_identity_hint_len'}] a*", $d );
        $_mySSLinfo{'psk_identity_hint'} = unpack( "H*", $_mySSLinfo{'psk_identity_hint'} );
        _trace2(
            sprintf(
                " PSK Key Exchange (len=%d):\n" . "# -->     psk_identity_hint: (len=%4d) >%s<\n",
                $len,
                $_mySSLinfo{'psk_identity_hint_len'},
                $_mySSLinfo{'psk_identity_hint'}
            )
        );
        if ( $keyExchange =~ /^PSK/x ) {
            _trace4("parseServerKeyExchange: PSK_serverParam\n");
            _trace2("parseServerKeyExchange() done.\n");
            return ( "psk", "", undef );
        }
        $psk = "_psk";
        $len -= ( $_mySSLinfo{'psk_identity_hint_len'} + 2 );
        $keyExchange =~ s/^((?:EC)?DH)(?:_PSK)?.*/$1/x;
        _trace2_(" --> KeyExchange (DH, ECDH) = $keyExchange\n");
    }
    if ( $keyExchange eq "DH" ) {

        ( $_mySSLinfo{'DH_ServerParams_p_len'}, $d ) = unpack( "n a*", $d );

        ( $_mySSLinfo{'DH_ServerParams_p'}, $_mySSLinfo{'DH_ServerParams_g_len'}, $d ) = unpack( "a[$_mySSLinfo{'DH_ServerParams_p_len'}] n a*", $d );
        $_mySSLinfo{'DH_ServerParams_p'} = unpack( "H*", $_mySSLinfo{'DH_ServerParams_p'} );

        ( $_mySSLinfo{'DH_ServerParams_g'}, $_mySSLinfo{'DH_ServerParams_PubKeyLen'}, $d ) = unpack( "a[$_mySSLinfo{'DH_ServerParams_g_len'}] n a*", $d );
        $_mySSLinfo{'DH_ServerParams_g'} = unpack( "H*", $_mySSLinfo{'DH_ServerParams_g'} );

        ( $_mySSLinfo{'DH_ServerParams_PubKey'}, $d ) = unpack( "a[$_mySSLinfo{'DH_ServerParams_PubKeyLen'}] a*", $d );
        $_mySSLinfo{'DH_ServerParams_PubKey'} = unpack( "H*", $_mySSLinfo{'DH_ServerParams_PubKey'} );
        _trace2(
            sprintf(
                " DH_ServerParams (len=%d):\n"
                  . "# -->       p: (len=0x%04X=%4d)        >%s<\n"
                  . "# -->       g: (len=0x%04X=%4d)        >%s<\n"
                  . "# -->       PubKey: (len=0x%04X=%4d)   >%s<\n",
                $len,                             $_mySSLinfo{'DH_ServerParams_p_len'},     $_mySSLinfo{'DH_ServerParams_p_len'},
                $_mySSLinfo{'DH_ServerParams_p'}, $_mySSLinfo{'DH_ServerParams_g_len'},     $_mySSLinfo{'DH_ServerParams_g_len'},
                $_mySSLinfo{'DH_ServerParams_g'}, $_mySSLinfo{'DH_ServerParams_PubKeyLen'}, $_mySSLinfo{'DH_ServerParams_PubKeyLen'},
                $_mySSLinfo{'DH_ServerParams_PubKey'}
            )
        );
        $_bits = $_mySSLinfo{'DH_ServerParams_p_len'} * 8;
        $_mySSLinfo{'DH_serverParam'} = $_bits . " bits";
        _trace4( "parseServerKeyExchange: DH_serverParam: " . $_mySSLinfo{'DH_serverParam'} . "\n" );
        _trace2("parseServerKeyExchange() done.\n");
        return ( "dh" . $psk, $_mySSLinfo{'DH_serverParam'}, undef );

    }
    elsif ( $keyExchange eq "ECDH" ) {
        ( $_mySSLinfo{'ECDH_eccurve_type'}, $d ) = unpack( "C a*", $d );
        if ( $_mySSLinfo{'ECDH_eccurve_type'} == $ECCURVE_TYPE{'named_curve'} ) {
            ( $_mySSLinfo{'ECDH_namedCurve'}, $d ) = unpack( "n a*", $d );
            $_mySSLinfo{'ECDH_serverParam'} = "<<unknown: " . $_mySSLinfo{'ECDH_namedCurve'} . ">>";
            $_mySSLinfo{'ECDH_serverParam'} =
                $OCfg::TLS_SUPPORTED_GROUPS{ $_mySSLinfo{'ECDH_namedCurve'} }[0] . " ("
              . $OCfg::TLS_SUPPORTED_GROUPS{ $_mySSLinfo{'ECDH_namedCurve'} }[1]
              . " bits)"
              if ( defined( $OCfg::TLS_SUPPORTED_GROUPS{ $_mySSLinfo{'ECDH_namedCurve'} }[0] ) );
            _trace4( "parseServerKeyExchange: ECDH_serverParam supported group: '" . $_mySSLinfo{'ECDH_serverParam'} . "'\n" );
            _trace2("parseServerKeyExchange() done.\n");
            return ( "ecdh" . $psk . " supported_group(s)", $_mySSLinfo{'ECDH_serverParam'}, $_mySSLinfo{'ECDH_namedCurve'} );
        }
        elsif ( $_mySSLinfo{'ECDH_eccurve_type'} == $ECCURVE_TYPE{'explicit_prime'} ) {
            ( $_mySSLinfo{'ECDH_explicit_prime_p_len'}, $d ) = unpack( "C a*", $d );
            $_bits = $_mySSLinfo{'ECDH_explicit_prime_p_len'} * 8;
            $_mySSLinfo{'ECDH_serverParam'} = "explicite_prime: " . $_bits . " bits";
        }
        elsif ( $_mySSLinfo{'ECDH_eccurve_type'} == $ECCURVE_TYPE{'explicit_char2'} ) {
            $_mySSLinfo{'ECDH_serverParam'} = "explicite_char2: <<not parsed, yet>>";
        }
        else {
            $_mySSLinfo{'ECDH_serverParam'} = "<<unknown ECC Curve type: " . $_mySSLinfo{'ECDH_eccurve_type'} . ">>";
        }
        _trace4( "parseServerKeyExchange: ECDH_serverParam: '" . $_mySSLinfo{'ECDH_serverParam'} . "'\n" );
        _trace2("parseServerKeyExchange() done.\n");
        return ( "ecdh" . $psk, $_mySSLinfo{'ECDH_serverParam'}, undef );
    }
    elsif ( ( $keyExchange =~ /^RSA/x ) || ( $keyExchange =~ /^EXP/x ) ) {
        ( $_mySSLinfo{'RSA_ServerParams_modulus_len'}, $d ) = unpack( "n a*", $d );

        ( $_mySSLinfo{'RSA_ServerParams_modulus'}, $_mySSLinfo{'RSA_ServerParams_exponent_len'}, $d ) =
          unpack( "a[$_mySSLinfo{'RSA_ServerParams_modulus_len'}] n a*", $d );
        $_mySSLinfo{'RSA_ServerParams_modulus'} = unpack( "H*", $_mySSLinfo{'RSA_ServerParams_modulus'} );

        ( $_mySSLinfo{'RSA_ServerParams_exponent'}, $d ) = unpack( "a[$_mySSLinfo{'RSA_ServerParams_exponent_len'}] a*", $d );
        $_mySSLinfo{'RSA_ServerParams_exponent'} = unpack( "H*", $_mySSLinfo{'RSA_ServerParams_exponent'} );
        _trace2(
            sprintf(
                " RSA_ServerParams (len=%d):\n" . "# -->       modulus: (len=0x%04X=%4d)  >%s<\n" . "# -->       exponent: (len=0x%04X=%4d) >%s<\n",
                $len,                                    $_mySSLinfo{'RSA_ServerParams_modulus_len'},  $_mySSLinfo{'RSA_ServerParams_modulus_len'},
                $_mySSLinfo{'RSA_ServerParams_modulus'}, $_mySSLinfo{'RSA_ServerParams_exponent_len'}, $_mySSLinfo{'RSA_ServerParams_exponent_len'},
                $_mySSLinfo{'RSA_ServerParams_exponent'}
            )
        );
        $_bits = $_mySSLinfo{'RSA_ServerParams_modulus_len'} * 8;
        $_mySSLinfo{'RSA_serverParam'} = $_bits . " bits";
        _trace4( "parseServerKeyExchange: RSA_serverParam: " . $_mySSLinfo{'RSA_serverParam'} . "\n" );
        _trace2("parseServerKeyExchange() done.\n");
        return ( "rsa" . $psk, $_mySSLinfo{'RSA_serverParam'}, undef );
    }
    else {
        _trace2("parseServerKeyExchange: The only supported KeyExchange types are DH, ECDH and RSA yet (not $keyExchange)\n");
        _trace2("parseServerKeyExchange() done.\n");
        return ( "-- unsupported KeyExchange --" . $psk, "", undef );
    }
}

sub parseHandshakeRecord ($$$$$$$;$) {
    my $host            = shift || "";
    my $port            = shift || "";
    my $recordType      = shift || 0;
    my $recordVersion   = shift || 0;
    my $recordLen       = shift || 0;
    my $recordData      = shift || "";
    my $lastCipher      = shift || "";
    my $client_protocol = shift || "";

    my $buffer       = "";
    my $rest         = "";
    my $tmp_len      = 0;
    my $message      = "";
    my $nextMessages = "";
    my %serverHello;
    my $cipher             = "";
    my $keyExchange        = "";
    my $description        = "";
    my $lastMsgType        = $HANDSHAKE_TYPE{'<<undefined>>'};
    my $lastProtocolCipher = '0x0300' . hexCodedCipher($lastCipher);

    local $my_error = "";

    my $sni        = "";
    my $client_ssl = $PROTOCOL_NAME_BY_HEX{$client_protocol};
    if ( !defined $client_ssl ) {
        $client_ssl = "--unknown protocol--";
    }

    error_handler->reset_err( { module => "#$SSLHELLO", sub => 'parseHandshakeRecord', print => ( $SSLhello::trace > 3 ), trace => $SSLhello::trace } );

    $SSLhello::use_sni_name = 1 if ( ( $SSLhello::use_sni_name == 0 ) && ($SSLhello::sni_name) && ( $SSLhello::sni_name ne "1" ) );
    unless ($SSLhello::use_sni_name) {
        $sni = "'$host'" if ($SSLhello::use_sni_name);
    }
    else {
        $sni = ($SSLhello::sni_name) ? "'$SSLhello::sni_name'" : "''";
    }

    if ( defined $client_protocol ) {
        _trace2("parseHandshakeRecord: Server '$host:$port': (expected protocol= >"
              . sprintf( "%04X", $client_protocol )
              . "<,\n      (record) type $recordType, -version: "
              . sprintf( "(0x%04X)", $recordVersion )
              . " with "
              . length($recordData)
              . " bytes >"
              . hexCodedString( substr( $recordData, 0, 48 ), "       " )
              . "< ...)\n" );
    }
    else {
        _trace2("parseHandshakeRecord: Server '$host:$port': (any protocol, (record) type $recordType, -version: "
              . sprintf( "(0x%04X)", $recordVersion )
              . " with "
              . length($recordData)
              . " bytes\n       recordData="
              . hexCodedString( substr( $recordData, 0, 48 ), "       " )
              . ")... \n" );
    }

    if ( length($recordData) >= 1 ) {

        if ( $recordVersion == $PROTOCOL_VERSION{'SSLv2'} ) {
            _trace2_("# -->SSL: Message type SSL2-Msg\n");
            $serverHello{'msg_len'}  = $recordLen;
            $serverHello{'msg_type'} = $recordType;
            ($message) = unpack( "x a*", $recordData );
            _trace2_(
                sprintf(
                    "# -->        msg_len:              >%04X<\n" . "# -->        msg_type:               >%02X<\n",
                    $serverHello{'msg_len'}, $serverHello{'msg_type'}
                )
            );
            _trace4( "parseHandshakeRecord: Server '$host:$port': MessageData:\n" . hexCodedString( $message, "             " ) . "\n" );

            $lastMsgType = $serverHello{'msg_type'} || $HANDSHAKE_TYPE{'<<undefined>>'};

            if ( $serverHello{'msg_type'} == $SSL_MT_SERVER_HELLO ) {
                _trace4("    Handshake protocol: SSL2 Server Hello\n");
                _trace4("        Message type: (Server Hello (2)\n");
                return ( "", $lastMsgType, 0, "", parseSSL2_ServerHello( $host, $port, $message, $client_protocol ) );
            }
            elsif ( $serverHello{'msg_type'} == $SSL_MT_ERROR ) {
                ( $serverHello{'err_code'} ) = unpack( "n", $message );

                _trace2("parseHandshakeRecord: Server '$host:$port': received a SSLv2 error message, code: >0x"
                      . hexCodedString( $serverHello{'err_code'} )
                      . "<\n" );
                unless ( $serverHello{'err_code'} == 0x0001 ) {
                    Carp::carp( "$OText::STR{WARN} parseHandshakeRecord: Server '$host:$port': received a SSLv2 error message: , code: >0x"
                          . hexCodedString( $serverHello{'err_code'} )
                          . " -> answer ignored\n" );
                }
                return ( "", $lastMsgType, 0, "", "" );
            }
            else {
                $my_error =
                    "    Unknown SSLv2 message type (Dez): "
                  . $serverHello{'msg_type'}
                  . ", Msg: >"
                  . hexCodedString($message)
                  . "< -> check for SSLv2 is aborted\n";
                return ( "", $lastMsgType, 0, "", "" );
            }
        }
        else {
            if ( $recordType == $RECORD_TYPE{'handshake'} ) {
                ( $nextMessages, $buffer ) = unpack( "a[$recordLen] a*", $recordData );

                while ( $nextMessages ne "" ) {
                    if ( ( $recordVersion & 0xFF00 ) == $PROTOCOL_VERSION{'SSLv3'} ) {
                        ( $serverHello{'msg_type'}, $serverHello{'msg_len_3rd_byte'}, $serverHello{'msg_len'}, $rest ) = unpack( "C C n a*", $nextMessages );

                        _trace2_(
                            sprintf(
                                "# -->     Handshake-Message:\n" . "# -->       msg_type:            >%02X<\n" . "# -->       msg_len:         >%02X%04X<\n",
                                $serverHello{'msg_type'},
                                $serverHello{'msg_len_3rd_byte'},
                                $serverHello{'msg_len'}
                            )
                        );

                        $lastMsgType = $serverHello{'msg_type'} || $HANDSHAKE_TYPE{'<<undefined>>'};

                    }
                    elsif ( ( ( $recordVersion & 0xFF00 ) == $PROTOCOL_VERSION{'DTLSfamily'} ) || ( $recordVersion == $PROTOCOL_VERSION{'DTLSv09'} ) ) {
                        (
                            $serverHello{'msg_type'},              $serverHello{'msg_len_3rd_byte'},         $serverHello{'msg_len'},
                            $serverHello{'msg_seqNr'},             $serverHello{'fragment_offset_3rd_byte'}, $serverHello{'fragment_offset'},
                            $serverHello{'fragment_len_3rd_byte'}, $serverHello{'fragment_len'},             $rest
                        ) = unpack( "C C n n C n C n a*", $nextMessages );

                        _trace2_(
                            sprintf(
                                "# -->     Handshake-Message:\n"
                                  . "# -->       msg_type:             >%02X<\n"
                                  . "# -->       msg_len:          >%02X%04X<\n"
                                  . "# -->       msg_seqNr:          >%04X<\n"
                                  . "# -->       fragment_offset:  >%02X%04X<\n"
                                  . "# -->       fragment_len:     >%02X%04X<\n",
                                $serverHello{'msg_type'},              $serverHello{'msg_len_3rd_byte'},         $serverHello{'msg_len'},
                                $serverHello{'msg_seqNr'},             $serverHello{'fragment_offset_3rd_byte'}, $serverHello{'fragment_offset'},
                                $serverHello{'fragment_len_3rd_byte'}, $serverHello{'fragment_len'},
                            )
                        );

                        $lastMsgType = $serverHello{'msg_type'} || $HANDSHAKE_TYPE{'<<undefined>>'};

                        if (   ( ( defined( $serverHello{'fragment_offset'} ) ) && ( $serverHello{'fragment_offset'} > 0 ) )
                            || ( ( defined( $serverHello{'fragment_offset_3rd_byte'} ) ) && ( $serverHello{'fragment_offset_3rd_byte'} > 0 ) ) )
                        {

                            $serverHello{'fragment_offset'} |= $serverHello{'fragment_offset_3rd_byte'} << 16
                              if ( $serverHello{'fragment_offset_3rd_byte'} > 0 );
                            _trace("parseHandshakeRecord: $host:$port: Received a huge fragment offset of $serverHello{'fragment_offset'} bytes\n")
                              if ( $serverHello{'fragment_offset_3rd_byte'} > 0 );
                            $my_error = "$host:$port: sorry, fragmented DTLS packets are not yet supported -> Retry";
                            _trace2("parseHandshakeRecord: $my_error\n");
                            Carp::carp("parseHandshakeRecord: $my_error");
                            return ( "", $lastMsgType, 0, "", "" );
                        }
                        $serverHello{'fragment_len'} |= $serverHello{'fragment_len_3rd_byte'} << 16 if ( $serverHello{'fragment_len_3rd_byte'} > 0 );
                        _trace("parseHandshakeRecord: >>>WARNING: $host:$port: Received a huge fragment with $serverHello{'fragment_len'} bytes\n")
                          if ( $serverHello{'fragment_len_3rd_byte'} > 0 );
                        Carp::carp("parseHandshakeRecord: >>>WARNING: $host:$port: Received a huge fragment with $serverHello{'fragment_len'} bytes\n")
                          if ( $serverHello{'fragment_len_3rd_byte'} > 0 );
                    }
                    $serverHello{'msg_len'} |= $serverHello{'msg_len_3rd_byte'} << 16 if ( $serverHello{'msg_len_3rd_byte'} > 0 );
                    if ( length($rest) < $serverHello{'msg_len'} ) {

                        _trace2_("parseHandshakeRecord: Server '$host:$port': Received a huge message with $serverHello{'msg_len'} bytes\n")
                          if ( $serverHello{'msg_len_3rd_byte'} > 0 );
                        _trace2_("parseHandshakeRecord: fragmented message with $serverHello{'msg_len'} bytes length -> get next record\n");
                        return ( $nextMessages . $buffer, $HANDSHAKE_TYPE{'<<fragmented_message>>'}, $serverHello{'cookie_length'}, $serverHello{'cookie'},
                            "" );
                    }

                    _trace("parseHandshakeRecord: >>> WARNING: Server '$host:$port': Received a huge message with $serverHello{'msg_len'} bytes\n")
                      if ( $serverHello{'msg_len_3rd_byte'} > 0 );
                    Carp::carp("parseHandshakeRecord: >>> WARNING: Server '$host:$port': Received a huge message with $serverHello{'msg_len'} bytes\n")
                      if ( $serverHello{'msg_len_3rd_byte'} > 0 );

                    ( $message, $nextMessages ) = unpack( "a[$serverHello{'msg_len'}] a*", $rest );
                    _trace4_(
                        sprintf( "# --->      message [len= %d]: >%s<\n", length($message), hexCodedString( $message, "                               " ) ) );

                    if ( $serverHello{'msg_type'} == $HANDSHAKE_TYPE{'server_hello'} ) {
                        _trace2_("# -->     Handshake type:    Server Hello (22)\n");

                        $cipher             = parseTLS_ServerHello( $host, $port, $message, $serverHello{'msg_len'}, $client_protocol );
                        $lastCipher         = $cipher;
                        $lastProtocolCipher = '0x0300' . hexCodedCipher($lastCipher);
                        _trace2_("# ==>       found cipher:      >$lastProtocolCipher<\n");
                    }
                    elsif ( $serverHello{'msg_type'} == $HANDSHAKE_TYPE{'hello_verify_request'} ) {
                        if ( length($message) >= 3 ) {
                            ( $serverHello{'version'}, $serverHello{'cookie_length'}, $rest ) = unpack( "n C a*", $message );

                            $serverHello{'cookie'} = "";
                            ( $serverHello{'cookie'}, $rest ) = unpack( "a[$serverHello{'cookie_length'}] a*", $rest ) if ( $serverHello{'cookie_length'} > 0 );

                            _trace2_(
                                sprintf(
                                    "# -->       version:            >%04X<\n"
                                      . "# -->       cookie_length:        >%02X<\n"
                                      . "# -->       cookie:             >%s<\n",
                                    $serverHello{'version'},
                                    $serverHello{'cookie_length'},
                                    hexCodedString( $serverHello{'cookie'} )
                                )
                            );
                            if ( length( $serverHello{'cookie'} ) != $serverHello{'cookie_length'} ) {
                                $my_error =
                                    "Server '$host:$port': DTLS-HelloVerifyRequest: Len of Cookie ("
                                  . length( $serverHello{'cookie'} )
                                  . ") <> 'cookie_length' ($serverHello{'cookie_length'})";
                                $serverHello{'cookie_length'} = length( $serverHello{'cookie'} );
                                Carp::carp("parseHandshakeRecord: $my_error");
                            }
                            if ( $serverHello{'cookie_length'} > 32 ) {
                                $my_error =
                                  "Server '$host:$port': DTLS-HelloVerifyRequest: 'cookie_length' ($serverHello{'cookie_length'}) out of Range <0..32)";
                                Carp::carp("parseHandshakeRecord: $my_error");
                            }
                            return ( $rest . $buffer, $lastMsgType, $serverHello{'cookie_length'}, $serverHello{'cookie'}, "" );
                        }
                    }
                    elsif ( $serverHello{'msg_type'} == $HANDSHAKE_TYPE{'server_key_exchange'} ) {
                        _trace2( "parseHandshakeRecord: Cipher: " . hexCodedCipher($lastCipher) . "\n" );
                        $keyExchange = $cipherHexHash{$lastProtocolCipher}[0];
                        if ( defined($keyExchange) ) {
                            _trace5_(" --> Cipher(1): $keyExchange\n");
                            $keyExchange =~ s/((?:EC)?DHE?)_anon((_PSK)?).*/A$1$2/x;
                            _trace5_(" --> Cipher(2): $keyExchange\n");
                            $keyExchange =~ s/((?:EC)?DH)E((_PSK)?).*/E$1$2/x;
                            _trace5_(" --> Cipher(3): $keyExchange\n");
                            $keyExchange =~ s/^(?:EXP[_-])?(?:E|A|EA)((?:EC)?DH(?:_PSK)?).*/$1/x;
                            _trace2_(" --> KeyExchange (DH, ECDH, DH_PSK or ECDH_PSK) = $keyExchange\n");

                            my ( $_description, $_param, $_supported_group ) = parseServerKeyExchange( $keyExchange, length($message), $message );
                            if ( defined($_param) ) {
                                _trace2_(
"\n   parseHandshakeRecord: $host:$port, $client_ssl, Cipher: $lastProtocolCipher -> SeverKey Type: $_description: $_param\n"
                                );
                            }
                            _trace5_(
                                "#     ---> values of 'supported_groups': "
                                  . _decode_val(
                                    undef,
                                    \@{ $_SSLhello{$lastProtocolCipher}{param}{supported_groups}{RX}{values} },
                                    \$OCfg::TLS_EXTENSIONS{supported_groups},
                                    0, 12, ": ", ", ", " | ", " / "
                                  )
                                  . "\n"
                            );
                            push( @{ $_SSLhello{$lastProtocolCipher}{param}{supported_groups}{RX}{values} }, $_supported_group )
                              if ( ( defined($_supported_group) )
                                && ( !grep { $_supported_group eq $_ } @{ $_SSLhello{$lastProtocolCipher}{param}{supported_groups}{RX}{values} } ) );
                            _trace4_(
                                "#     ---> found 'supported_groups':     "
                                  . _decode_val(
                                    undef,
                                    \@{ $_SSLhello{$lastProtocolCipher}{param}{supported_groups}{RX}{values} },
                                    \$OCfg::TLS_EXTENSIONS{supported_groups},
                                    0, 12, ": ", ", ", " | ", " / "
                                  )
                                  . "\n"
                            );
                            if ( !exists( $_SSLhello{$lastProtocolCipher}{param}{ServerKey}{description} ) ) {
                                $_SSLhello{$lastProtocolCipher}{param}{ServerKey}{description} = $_description;
                                $_SSLhello{$lastProtocolCipher}{param}{supported_groups}{RX}{format_positions}[0] = 1;
                                @{ $_SSLhello{$lastProtocolCipher}{param}{ServerKey}{values} } = ();
                            }
                            elsif ( $_SSLhello{$lastProtocolCipher}{param}{ServerKey}{description} ne $_description ) {
                                Carp::carp(
"$OText::STR{WARN} SSLhello::parseHandshakeRecord: found different types of ServerKeys for the same Cipher '$lastProtocolCipher': '$_description' != '"
                                      . $_SSLhello{$lastProtocolCipher}{param}{ServerKey}{description}
                                      . "'\n" );
                                $_SSLhello{$lastProtocolCipher}{param}{ServerKey}{description} .= " ## " . $_description;
                                $_param = " ## " . $_param;
                            }
                            push( @{ $_SSLhello{$lastProtocolCipher}{param}{ServerKey}{values} }, $_param )
                              if ( ( defined($_param) )
                                && ( $_param ne "" )
                                && ( !grep { $_param eq $_ } @{ $_SSLhello{$lastProtocolCipher}{param}{ServerKey}{values} } ) );
                        }
                        else {
                            _trace2("parseHandshakeRecord: No name found for cipher: >$lastProtocolCipher< -> counld NOT check the ServerKeyExchange\n");
                            push( @{ $_SSLhello{$lastProtocolCipher}{param}{ServerKey}{values} }, "---unknown---" );
                        }
                    }
                    elsif ( $serverHello{'msg_type'} == $HANDSHAKE_TYPE{'certificate'} ) {
                        _trace2(
                            "parseHandshakeRecord: MessageType \"Certificate\" = " . sprintf( "0x%02X", $serverHello{'msg_type'} ) . " not yet analysed\n" );
                    }
                    elsif ( $serverHello{'msg_type'} == $HANDSHAKE_TYPE{'certificate_request'} ) {
                        _trace2("parseHandshakeRecord: MessageType \"Certificate request\" = "
                              . sprintf( "0x%02X", $serverHello{'msg_type'} )
                              . " not yet analysed\n" );
                    }
                    elsif ( $serverHello{'msg_type'} == $HANDSHAKE_TYPE{'server_hello_done'} ) {
                        _trace4("parseHandshakeRecord: MessageType \"ServerHelloDone\" = "
                              . sprintf( "0x%02X", $serverHello{'msg_type'} )
                              . " -> Final hello Message\n" );
                        last;
                    }
                    else {
                        _trace2( "parseHandshakeRecord: MessageType " . sprintf( "%02X", $serverHello{'msg_type'} ) . " not yet analysed\n" );
                    }
                    _trace2_("\n");
                }
                return ( $nextMessages . $buffer, $lastMsgType, 0, "", $cipher );
            }
            elsif ( $recordType == $RECORD_TYPE{'alert'} ) {
                $serverHello{'msg_type'}          = 0;
                $serverHello{'msg_len_3rd_byte'}  = 0;
                $serverHello{'msg_len'}           = 0;
                $serverHello{'fragment_3rd_byte'} = 0;
                $serverHello{'fragment_offset'}   = 0;
                $serverHello{'fragment_3rd_byte'} = 0;
                $serverHello{'fragment_len'}      = 0;

                ( $serverHello{'level'}, $serverHello{'description'} ) = unpack( "C C", $recordData );

                if ( $TLS_AlertDescription{ $serverHello{'description'} } ) {
                    $description = $TLS_AlertDescription{ $serverHello{'description'} }[0] . " " . $TLS_AlertDescription{ $serverHello{'description'} }[2];
                }
                else {
                    $description = "Unknown/Undefined";
                }

                _trace2_("# -->  Alert Message (Record type 21):\n");
                _trace2_("# -->      Level:       $serverHello{'level'}\n");
                _trace2_("# -->      Description: $serverHello{'description'} ($description)\n");

                unless (
                    ( $serverHello{'level'} == 2 )
                    && (   ( $serverHello{'description'} == 40 )
                        || ( $serverHello{'description'} == 71 ) )
                  )
                {
                    if ( $serverHello{'level'} == 1 ) {
                        if ( $serverHello{'description'} == 112 ) {
                            if (
                                ($SSLhello::usesni)
                                && !(
                                       ( ( $client_protocol == $PROTOCOL_VERSION{'SSLv3'} ) && ( !$SSLhello::force_TLS_extensions ) )
                                    || ( $client_protocol == $PROTOCOL_VERSION{'SSLv2'} )
                                )
                              )
                            {
                                $sni = "";
                                unless ($SSLhello::use_sni_name) {
                                    $sni = "'$host'";
                                }
                                else {
                                    $sni = ($SSLhello::sni_name) ? "'$SSLhello::sni_name'" : "''";
                                }
                                $my_error = sprintf(
"parseHandshakeRecord: Server '$host:$port' ($client_ssl): received SSL/TLS warning: Description: $description ($serverHello{'description'}) -> check of virtual server $sni aborted!\n"
                                );
                                _trace4($my_error);
                                Carp::carp("$OText::STR{WARN} $my_error\n");
                            }
                            else {
                                $my_error = sprintf(
"parseHandshakeRecord: Server '$host:$port' ($client_ssl): received SSL/TLS warning: Description: $description ($serverHello{'description'}), but NO SNI extension has been sent. -> check of server aborted!"
                                );
                                _trace4($my_error);
                                Carp::carp("$OText::STR{WARN} $my_error\n");
                                OCfg::hint(
                                    "Server seens to to be a virtual server, consider adding the option '--sni' (Server Name Indication) for TLSv1 and higher");
                            }
                            return ( "", $lastMsgType, 0, "", "" );
                        }
                        elsif ( $serverHello{'description'} == 0 ) {
                            _trace2_(
"parseHandshakeRecord: Server '$host:$port' ($client_ssl): received SSL/TLS closure alert (1) has been ignored: Description: $description ($serverHello{'description'})\n"
                            );
                        }
                        else {
                            _trace4_(
"**WARNING: parseHandshakeRecord: Server '$host:$port' ($client_ssl): received SSL/TLS warning (1): Description: $description ($serverHello{'description'})\n"
                            );
                            Carp::carp(
"$OText::STR{WARN} parseHandshakeRecord: Server '$host:$port' ($client_ssl): received SSL/TLS warning (1): Description: $description ($serverHello{'description'})\n"
                            );
                        }
                    }
                    elsif ( $serverHello{'level'} == 2 ) {
                        if (   ( $serverHello{'description'} == 70 )
                            or ( $serverHello{'description'} == 80 ) )
                        {
                            error_handler->new(
                                {
                                    type    => $OERR{'SSLHELLO_ABORT_PROTOCOL'},
                                    id      => 'parse alert record (2)',
                                    message =>
                                      sprintf(
"unsupported protocol $client_ssl (0x%04X) by $host:$port: received a SSL/TLS-Warning: Description: $description ($serverHello{'description'})",
                                        $client_protocol ),
                                    warn => 0,
                                }
                            );
                            if ( ( grep { /(:?--v)$/ix } @main::ARGV ) > 0 ) {

                                OCfg::warn("430: SSL version '$client_ssl' not supported by '$host:$port'; no ciphers detected, ignored\n");
                                OCfg::hint("consider using '--ciphermode=openssl' also\n");
                            }
                        }
                        elsif ( $serverHello{'description'} == 112 ) {
                            if (
                                ($SSLhello::usesni)
                                && !(
                                       ( ( $client_protocol == $PROTOCOL_VERSION{'SSLv3'} ) && ( !$SSLhello::force_TLS_extensions ) )
                                    || ( $client_protocol == $PROTOCOL_VERSION{'SSLv2'} )
                                )
                              )
                            {
                                $sni = "";
                                unless ($SSLhello::use_sni_name) {
                                    $sni = "'$host'" if ($SSLhello::usesni);
                                }
                                else {
                                    $sni = ($SSLhello::sni_name) ? "'$SSLhello::sni_name'" : "''";
                                }
                                $my_error = sprintf(
"parseHandshakeRecord: Server '$host:$port' ($client_ssl): received fatal SSL/TLS error (2a): Description: $description ($serverHello{'description'}) -> check of virtual server $sni aborted!\n"
                                );
                                _trace4($my_error);
                                Carp::carp("$OText::STR{WARN} $my_error\n");
                            }
                            else {
                                $my_error = sprintf(
"parseHandshakeRecord: Server '$host:$port' ($client_ssl): received fatal SSL/TLS error (2b): Description: $description ($serverHello{'description'}), but NO SNI extension has been sent. -> check of server aborted!"
                                );
                                _trace4($my_error);
                                Carp::carp("$OText::STR{WARN} $my_error\n");
                                OCfg::hint(
                                    "Server seens to to be a virtual server, consider adding the option '--sni' (Server Name Indication)for TLSv1 and higher");
                            }
                            return ( "", $lastMsgType, 0, "", "" );
                        }
                        else {
                            _trace4($my_error);
                            Carp::carp(
"$OText::STR{WARN} parseHandshakeRecord: Server '$host:$port' ($client_ssl): received fatal SSL/TLS error (2c): Description: $description ($serverHello{'description'})\n"
                            );
                            if ( $serverHello{'description'} == 50 ) {
                                OCfg::hint(
"The server may not support the extension for elliptic curves (ECC) nor discard it silently, consider adding the option '--ssl-nouseecc'."
                                );
                            }
                        }
                    }
                    else {
                        Carp::carp(
"$OText::STR{WARN} parseHandshakeRecord: Server '$host:$port' ($client_ssl): received unknown SSL/TLS error level ($serverHello{'level'}): Description: $description ($serverHello{'description'})\n"
                        );
                    }
                }
                if ( $recordVersion == 0x0000 ) {
                    error_handler->new(
                        {
                            type    => $OERR{'SSLHELLO_ABORT_PROTOCOL'},
                            id      => 'parse alert record (1)',
                            message =>
                              sprintf( "unsupported protocol $client_ssl (0x%04X) by $host:$port, answered with (0x%04X)", $client_protocol, $recordVersion ),
                            warn => 0,
                        }
                    );
                    return ( "", $lastMsgType, 0, "", "" );
                }
            }
            elsif ( $recordType == $RECORD_TYPE{'change_cipher_spec'} ) {
                $serverHello{'msg_type'}          = 0;
                $serverHello{'msg_len_3rd_byte'}  = 0;
                $serverHello{'msg_len'}           = 0;
                $serverHello{'fragment_3rd_byte'} = 0;
                $serverHello{'fragment_offset'}   = 0;
                $serverHello{'fragment_3rd_byte'} = 0;
                $serverHello{'fragment_len'}      = 0;

                ( $serverHello{'payload'}, $rest ) = unpack( "a[$recordLen] a*", $recordData );

                _trace2_(
                    sprintf(
                        "# -->     Record-Type 'change_cipher_spec' [Len = %d]:\n" . "# -->       payload:                >%s<\n",
                        $recordLen, _sprintf_hex_val( "", \$serverHello{'payload'}, 37 ),
                    )
                );
                return ( $rest, $lastMsgType, 0, "", "" );
            }
            elsif ( $recordType == $RECORD_TYPE{'application_data'} ) {
                $serverHello{'msg_type'}          = 0;
                $serverHello{'msg_len_3rd_byte'}  = 0;
                $serverHello{'msg_len'}           = 0;
                $serverHello{'fragment_3rd_byte'} = 0;
                $serverHello{'fragment_offset'}   = 0;
                $serverHello{'fragment_3rd_byte'} = 0;
                $serverHello{'fragment_len'}      = 0;

                ( $serverHello{'application_data'}, $rest ) = unpack( "a[$recordLen] a*", $recordData );

                _trace2_(
                    sprintf(
                        "# -->     Record-Type 'application_data' [Len = %d]:\n" . "# -->       application_data:       >%s<\n",
                        $recordLen, _sprintf_hex_val( "", \$serverHello{'application_data'}, 37 ),
                    )
                );
            }
            else {
                _trace_("\n");
                _trace_(
                    "**WARNING: parseHandshakeRecord: Server '$host:$port': Unknown SSL/TLS record type received that is not (yet) defined in SSLhello.pm:\n");
                _trace_( "#        Record type:     Unknown value (0x" . hexCodedString($recordType) . "), not (yet) defined in SSLhello.pm\n" );
                _trace_( "#        Record version:  $recordVersion (0x" . hexCodedString($recordVersion) . ")\n" );
                _trace_( "#        Record len:      $recordLen (0x" . hexCodedString($recordLen) . ")\n\n" );
                Carp::carp(
"$OText::STR{WARN} parseHandshakeRecord: Server '$host:$port': Unknown SSL/TLS record type received that is not (yet) defined in SSLhello.pm:\n"
                );
                Carp::carp( "#        Record type:     Unknown value (0x" . hexCodedString($recordType) . "), not (yet) defined in SSLhello.pm\n" );
                Carp::carp( "#        Record version:  $recordVersion (0x" . hexCodedString($recordVersion) . ")\n" );
                Carp::carp( "#        Record len:      $recordLen (0x" . hexCodedString($recordLen) . ")\n\n" );
                return ( "", $lastMsgType, 0, "", "" );
            }
            return ( $buffer, $lastMsgType, 0, "", "" );
        }
    }
    else {
        Carp::carp( "$OText::STR{WARN} parseHandshakeRecord: Server '$host:$port': (no SSL/TLS record) : " . hexCodedString($recordData) . "\n" );
        ( $serverHello{'--unknown record type--'}, $rest ) = unpack( "a[$recordLen] a*", $recordData );
        return ( $rest, $lastMsgType, 0, "", "" );
    }
    Carp::carp( "$OText::STR{WARN} parseHandshakeRecord: Server '$host:$port': Internal error: " . hexCodedString($recordData) . "\n" );
    ( $serverHello{'--unknown record type--'}, $rest ) = unpack( "a[$recordLen] a*", $recordData );
    return ( $rest, $lastMsgType, 0, "", "" );
}

sub parseSSL2_ServerHello ($$$;$) {
    my $host            = shift || "";
    my $port            = shift || "";
    my $buffer          = shift || "";
    my $client_protocol = shift || "";
    my $rest;
    my %serverHello;

    $serverHello{'cipher_spec'} = "";

    if ( defined $client_protocol ) {
        _trace3("parseSSL2_ServerHello: Server '$host:$port': (expected protocol="
              . sprintf( "%04X", $client_protocol )
              . ", Data="
              . hexCodedString( substr( $buffer, 0, 48 ), "       " )
              . "...)\n" );
    }
    else {
        _trace4( "parseSSL2_ServerHello: Server '$host:$port': (any protocol, Data=" . hexCodedString( substr( $buffer, 0, 48 ), "         " ) . "...)\n" );
    }

    (
        $serverHello{'session_id_hit'},
        $serverHello{'certificate_type'},
        $serverHello{'version'},
        $serverHello{'certificate_len'},
        $serverHello{'cipher_spec_len'},
        $serverHello{'connection_id_len'}, $rest
    ) = unpack( "C C n n n n a*", $buffer );

    _trace2_(
        sprintf(
            "# -->                      => SSL2: ServerHello (%02X):\n"
              . "# -->        session_id_hit:         >%02X<\n"
              . "# -->        certificate_type:       >%02X<\n"
              . "# -->        version:              >%04X<\n"
              . "# -->        certificate_len:      >%04X<\n"
              . "# -->        cipher_spec_len:      >%04X<\n"
              . "# -->        connection_id_len:    >%04X<\n",
            $SSL_MT_SERVER_HELLO,            $serverHello{'session_id_hit'},  $serverHello{'certificate_type'}, $serverHello{'version'},
            $serverHello{'certificate_len'}, $serverHello{'cipher_spec_len'}, $serverHello{'connection_id_len'}
        )
    );

    _trace4( "Rest: Server '$host:$port': >" . hexCodedString($rest) . "<\n" );

    ( $serverHello{'certificate'}, $serverHello{'cipher_spec'}, $serverHello{'connection_id'} ) =
      unpack( "a[$serverHello{'certificate_len'}] a[$serverHello{'cipher_spec_len'}] a[$serverHello{'connection_id_len'}]", $rest );

    _trace4("parseSSL2_ServerHello(2): Server '$host:$port':\n");

    _trace2_(
        sprintf(
            "# -->       certificate:          >%s<\n"
              . "# -->       cipher_spec:          >%s<\n"
              . "# -->       connection_id:        >%s<\n"
              . "# -->       parseServerHello-Cipher:\n",
            hexCodedString( $serverHello{'certificate'} ),
            hexCodedString( $serverHello{'cipher_spec'}, "     " ),
            hexCodedString( $serverHello{'connection_id'} )
        )
    );

    if ( $SSLhello::trace >= 3 ) {
        printf "## Server Server '$host:$port': accepts the following Ciphers with SSL-Version: >%04X<\n", $serverHello{'version'};
        printSSL2CipherList( $serverHello{'cipher_spec'} );
        print "\n";
    }
    if ( length( $serverHello{'cipher_spec'} ) != int( $serverHello{'cipher_spec_len'} ) ) {
        Carp::carp( "$OText::STR{WARN} parseSSL2_ServerHello: Server '$host:$port': Can't get all Ciphers from Server-Hello (String-Len: "
              . length( $serverHello{'cipher_spec'} )
              . " != cipher_spec_len: "
              . $serverHello{'cipher_spec_len'} . "): >"
              . hexCodedSSL2Cipher( $serverHello{'cipher_spec'} )
              . "<" );
        printf "#                       => SSL2: ServerHello (%02X):\n"
          . "#         session_id_hit:         >%02X<\n"
          . "#         certificate_type:       >%02X<\n"
          . "#         version:              >%04X<\n"
          . "#         certificate_len:      >%04X<\n"
          . "#         cipher_spec_len:      >%04X<\n"
          . "#         connection_id_len:    >%04X<\n",
          $SSL_MT_SERVER_HELLO,
          $serverHello{'session_id_hit'},
          $serverHello{'certificate_type'},
          $serverHello{'version'},
          $serverHello{'certificate_len'},
          $serverHello{'cipher_spec_len'},
          $serverHello{'connection_id_len'};

        printf "##        certificate:          >%s<\n" . "##        cipher_spec:          >%s<\n" . "##        connection_id:        >%s<\n",
          hexCodedString( $serverHello{'certificate'} ),
          hexCodedString( $serverHello{'cipher_spec'}, "   " ),
          hexCodedString( $serverHello{'connection_id'} );
    }
    return ( $serverHello{'cipher_spec'} );
}


sub parseTLS_ServerHello {
    my $host            = shift || "";
    my $port            = shift || "";
    my $buffer          = shift || "";
    my $len             = shift || 0;
    my $client_protocol = shift || "";
    my $server_protocol = "";
    my $rest            = "";
    my $rest2           = "";
    my %serverHello;
    my $protocolCipher = "";

    $serverHello{'cipher_spec'}    = "";
    $serverHello{'extensions_len'} = 0;

    error_handler->reset_err( { module => "#$SSLHELLO", sub => 'parseTLS_ServerHello', print => ( $SSLhello::trace > 3 ), trace => $SSLhello::trace } );

    if ( defined $client_protocol ) {
        _trace3("parseTLS_ServerHello: Server '$host:$port': (expected protocol="
              . sprintf( "%04X", $client_protocol )
              . ",\n     "
              . hexCodedString( substr( $buffer, 0, 48 ), "       " )
              . "...)\n" );
    }
    else {
        _trace4( "parseTLS_ServerHello: Server '$host:$port': (any protocol, Data=" . hexCodedString( substr( $buffer, 0, 48 ), "         " ) . "...)\n" );
    }

    if ( length($buffer) || $len >= 35 ) {
        ( $serverHello{'version'}, $serverHello{'random_gmt_time'}, $serverHello{'random'}, $serverHello{'session_id_len'}, $rest ) =
          unpack( "n N a[28] C a*", $buffer );

        _trace2_(
            sprintf(
                "# -->       (legacy) version:  >%04X<\n"
                  .
                  "# -->       random_gmt_time:   >%08X<\n" . "# -->       random:            >%s<\n" . "# -->       session_id_len:      >%02X<\n",
                $serverHello{'version'},
                $serverHello{'random_gmt_time'},
                hexCodedString( $serverHello{'random'} ),
                $serverHello{'session_id_len'}
            )
        );
        _trace5_( sprintf( "# -->       Rest: (len=%04X)   >%s<\n", length($rest), hexCodedString( $rest, "                                    " ) ) );
        ( $serverHello{'session_id'}, $serverHello{'cipher_spec'}, $serverHello{'compression_method'}, $serverHello{'extensions_len'}, $rest2 ) =
          unpack( "a[$serverHello{'session_id_len'}] a2 C n a*", $rest );

        _trace2_(
            sprintf(
                "# -->       session_id:        >%s<\n" . "# -->       cipher_spec: (len=%2s) >%s<\n",
                hexCodedString( $serverHello{'session_id'} ),
                length( $serverHello{'cipher_spec'} ),
                hexCodedCipher( $serverHello{'cipher_spec'} )
            )
        );

        if ( length( $serverHello{'cipher_spec'} ) != 2 ) {
            Carp::carp( "$OText::STR{WARN} parseTLS_ServerHello: Server '$host:$port': Can't get the Cipher from Server-Hello (String-Len: "
                  . length( $serverHello{'cipher_spec'} )
                  . " != cipher_spec_len: 2): >"
                  . hexCodedString( $serverHello{'cipher_spec'} )
                  . "<" );
        }
        $protocolCipher = '0x0300' . hexCodedCipher( $serverHello{cipher_spec} );
        if ( $SSLhello::trace > 3 ) {
            printTLSCipherList( $serverHello{'cipher_spec'} );
        }

        _trace2_( sprintf( "\n# -->       compression_method:   >%02X<\n", $serverHello{'compression_method'} ) );

        my %_param_tmp_hash = ();
        %_param_tmp_hash = %{ $_SSLhello{$protocolCipher}{param} } if ( exists( $_SSLhello{$protocolCipher}{param} ) );
        if ( $serverHello{'extensions_len'} !~ /(?:^$|[\x00]+)/x ) {
            ( $serverHello{'extensions'}, $rest ) = unpack( "a[$serverHello{'extensions_len'}] a*", $rest2 );

            _trace2_( sprintf( "# -->       extensions_len:     >%04X<\n", $serverHello{'extensions_len'} ) );

            _trace4_(
                sprintf(
                    "# -->       extensions:           >%s<\n" . "# -->       Rest:                 >%s<\n",
                    hexCodedString( $serverHello{'extensions'} ),
                    hexCodedString($rest)
                )
            );
            _parseExtensions( "RX", \%{ $_SSLhello{$protocolCipher}{param} }, \$serverHello{'extensions'}, $serverHello{'extensions_len'}, $protocolCipher );
            if ( length($rest) > 0 ) {
                _trace2(
                    sprintf(
                        "\n\n## parseTLSServerHello Server '$host:$port': did not parse the whole message (rest): >" . hexCodedString($rest) . "< To Be Done\n"
                    )
                );
            }
        }
        $server_protocol =
          (
            defined(
                $_SSLhello{$protocolCipher}{param}{supported_versions}{RX}{values}[ $#{ $_SSLhello{$protocolCipher}{param}{supported_versions}{RX}{values} } ]
            )
          )
          ? $_SSLhello{$protocolCipher}{param}{supported_versions}{RX}{values}[ $#{ $_SSLhello{$protocolCipher}{param}{supported_versions}{RX}{values} } ]
          : $serverHello{'version'};
        _trace5_(
            sprintf(
                "# -->       => check server SSL/TLS-Version: legacy: %04X / effective %04X vs client: %04X\n",
                $serverHello{'version'}, $server_protocol, $client_protocol
            )
        );
        if ( defined($client_protocol) ) {
            if ( $client_protocol != $server_protocol ) {
                my $client_ssl = $PROTOCOL_NAME_BY_HEX{$client_protocol};
                my $server_ssl = $PROTOCOL_NAME_BY_HEX{$server_protocol};
                if ( !defined $client_ssl ) {
                    $client_ssl = "--unknown protocol--";
                }
                if ( !defined $server_ssl ) {
                    $server_ssl = "--unknown protocol--";
                }
                _trace5_( " " x 9
                      . "# --> parseTLSServerHello Server '$host:$port': protocol mismatch (expected $client_ssl != $server_ssl). Restoring hash \%{\$_SSLhello{$protocolCipher}{cipher_spec}{param}}.\n"
                );
                $_SSLhello{$protocolCipher}{param} = \%_param_tmp_hash;
                if ( $server_protocol == 0 ) {
                    error_handler->new(
                        {
                            type    => $OERR{'SSLHELLO_ABORT_PROTOCOL'},
                            id      => 'check record protocol (1)',
                            message => sprintf(
                                "unsupported protocol $client_ssl (0x%04X) by $host:$port, answered with $server_ssl (0x%04X)",
                                $client_protocol, $server_protocol
                            ),
                            warn => 0,
                        }
                    );
                }
                else {
                    error_handler->new(
                        {
                            type    => $OERR{'SSLHELLO_ABORT_PROTOCOL'},
                            id      => 'check record protocol (2)',
                            message => sprintf(
                                "unsupported protocol $client_ssl (0x%04X) by $host:$port, answered with $server_ssl (0x%04X)",
                                $client_protocol, $server_protocol
                            ),
                            warn => 0,
                        }
                    );
                }
                return ("");
            }
        }
        else {
            Carp::carp(
"$OText::STR{WARN} parseTLS_ServerHello: server '$host:$port': internal error: All server protocol versions are accepted, because there is no information provided which version the client has requested.\n"
            );
        }
        _trace2_(
            sprintf(
                "# --> The server '$host:$port': accepts the following cipher(s) with SSL3/TLS-version: >%04X<:\n", $server_protocol
            )
        );
        return ( $serverHello{'cipher_spec'} );
    }
    else {
        return ("");
    }
}

sub _timedOut {
    croak("NET::SSLhello: Receive data timed out -> Received NO data (timeout)");
}

sub _chomp_r {
    my $string = shift || "";
    $string =~ s/(.*?)\r?\n?$/$1/gx;
    if ( $string =~ /[^\x20-\x7E\t\r\n]/x ) {
        $string =~ s/([\x00-\xFF])/sprintf("%02X ", ord($1))/eigx;
    }
    return ($string);
}

sub getCipherParameter {
    my $protocolCipher = shift;
    my $prefix         = shift || "";
    my $sep            = shift || " | ";
    my $string         = "";
    my $param          = $_SSLhello{$protocolCipher}{param}{ServerKey};
    if ( exists( $param->{values} ) ) {

        $string = "$prefix$param->{description}: ";
        for ( my $_i = 0 ; $_i <= $#{ $param->{values} } ; $_i++ ) {
            $string .= $sep if 0 < ($_i);
            $string .= $param->{values}[$_i];
        }
    }
    return $string;
}

sub hexCodedString {
    my $codedString = shift || "";
    my $prefix      = shift;
    return ("")  if ( $codedString eq "" );
    $prefix = "" if not defined($prefix);
    $codedString =~ s/([\x00-\xFF])/sprintf("%02X ", ord($1))/eigx;
    $codedString =~ s/((?:[0-9A-Fa-f]{2}\s){48})(?=[0-9A-Fa-f]{2})/"$1\n$prefix"/eigx;
    chomp($codedString);
    chop($codedString);
    return ($codedString);
}

sub hexCodedCipher {
    my $codedString = shift || "";
    my $prefix      = shift;
    return ("")  if ( $codedString eq "" );
    $prefix = "" if not defined($prefix);
    $codedString =~ s/([\x00-\xFF])/sprintf("%02X", ord($1))/eigx;
    $codedString =~ s/((?:[0-9A-Fa-f]{2}){64})/"$1\n$prefix"/eigx;
    chomp($codedString);
    return ($codedString);
}

sub hexCodedSSL2Cipher {
    my $codedString = shift || "";
    my $prefix      = shift;
    return ("")  if ( $codedString eq "" );
    $prefix = "" if not defined($prefix);
    $codedString =~ s/([\x00-\xFF])([\x00-\xFF])([\x00-\xFF])/sprintf("%02X%02X%02X ", ord($1), ord($2), ord($3))/eigx;
    $codedString =~ s/((?:[0-9A-Fa-f]{6}){16}\s)/"$1\n$prefix"/eigx;
    chomp($codedString);
    return ($codedString);
}

sub hexCodedTLSCipher {
    my $codedString = shift || "";
    my $prefix      = shift;
    return ("")  if ( $codedString eq "" );
    $prefix = "" if not defined($prefix);
    $codedString =~ s/([\x00-\xFF])([\x00-\xFF])/sprintf("%02X%02X ", ord($1), ord($2))/eigx;
    $codedString =~ s/((?:[0-9A-Fa-f]{4}){16}\s)/"$1\n$prefix"/eigx;
    chomp($codedString);
    return ($codedString);
}

sub compileSSL2CipherArray ($) {
    my $cipherList     = shift || "";
    my $protocolCipher = "";
    my $firstByte      = "";
    my @cipherArray    = ();

    my $anzahl      = int length($cipherList) / 3;
    my @cipherTable = unpack( "a3" x $anzahl, $cipherList );

    _trace4("compileSSL2CipherArray ($anzahl) {\n");
    for ( my $i = 0 ; $i < $anzahl ; $i++ ) {
        _trace4_( sprintf( "               Cipher[%2d]: ", $i ) );
        _trace4_( sprintf( " >" . hexCodedSSL2Cipher( $cipherTable[$i] ) . "< -> " ) );
        $firstByte = unpack( "C", $cipherTable[$i] );
        _trace4_( sprintf( "1. Byte: %02X -> ", $firstByte ) );
        if ( $firstByte == 0x00 ) {
            $protocolCipher = pack( "a4a*", "0x03", hexCodedCipher( $cipherTable[$i] ) );
        }
        else {
            $protocolCipher = pack( "a4a*", "0x02", hexCodedCipher( $cipherTable[$i] ) );
        }
        if ( $SSLhello::trace > 3 ) {
            if ( $cipherHexHash{$protocolCipher} ) {
                _trace_( sprintf "%s -> %-32s -> %s", $protocolCipher, $cipherHexHash{$protocolCipher}[1], $cipherHexHash{$protocolCipher}[0] );
            }
            else {
                _trace_( "$protocolCipher" . " -> NO-RFC-" . $protocolCipher );
            }
            _trace4_("\n");
        }
        push( @cipherArray, $protocolCipher );
    }
    _trace4("compileSSL2CipherArray: }\n");
    return (@cipherArray);
}

sub compileTLSCipherArray ($) {
    my $cipherList     = shift || "";
    my $protocolCipher = "";
    my $firstByte      = "";
    my @cipherArray    = ();

    my $anzahl      = int length($cipherList) / 2;
    my @cipherTable = unpack( "a2" x $anzahl, $cipherList );

    _trace4("compileTLSCipherArray ($anzahl):\n");

    for ( my $i = 0 ; $i < $anzahl ; $i++ ) {
        _trace4_( sprintf( "           Cipher[%2d]: ", $i ) );
        _trace4_( sprintf( " >" . hexCodedCipher( $cipherTable[$i] ) . "< -> " ) );
        $protocolCipher = pack( "a6a*", "0x0300", hexCodedCipher( $cipherTable[$i] ) );
        if ( $SSLhello::trace > 3 ) {
            if ( ( defined( $cipherHexHash{$protocolCipher} ) ) && ( $#{ $cipherHexHash{$protocolCipher} } > 0 ) ) {
                _trace4_( sprintf( "%s -> %-32s -> %s", $protocolCipher, $cipherHexHash{$protocolCipher}[1], $cipherHexHash{$protocolCipher}[0] ) );
            }
            else {
                _trace4_( "$protocolCipher -> NO-RFC-" . $protocolCipher );
            }
            _trace4_("\n");
        }
        push( @cipherArray, $protocolCipher );
    }
    _trace4("compileTLSCipherArray: }\n");
    return (@cipherArray);
}

sub printSSL2CipherList ($) {
    my $cipherList     = shift || "";
    my $protocolCipher = "";
    my $firstByte      = "";

    my $anzahl      = int length($cipherList) / 3;
    my @cipherTable = unpack( "a3" x $anzahl, $cipherList );
    local $\ = "";

    if ( $SSLhello::trace > 3 ) {
        _trace4("printSSL2CipherList ($anzahl):\n");
        for ( my $i = 0 ; $i < $anzahl ; $i++ ) {

            _trace4_( sprintf( "           Cipher[%2d]: ", $i ) );
            _trace4_( " >" . hexCodedCipher( $cipherTable[$i] ) . "< -> " );
            $firstByte = unpack( "C", $cipherTable[$i] );
            _trace4_( sprintf( "  1. Byte: %02X -> ", $firstByte ) );
            if ( $firstByte == 0x00 ) {
                $protocolCipher = pack( "a4a*", "0x03", hexCodedCipher( $cipherTable[$i] ) );
            }
            else {
                $protocolCipher = pack( "a4a*", "0x02", hexCodedCipher( $cipherTable[$i] ) );
            }
            if ( ( defined( $cipherHexHash{$protocolCipher} ) ) && ( $#{ $cipherHexHash{$protocolCipher} } > 0 ) ) {
                _trace4_( sprintf( "%s -> %-32s -> %s", $protocolCipher, $cipherHexHash{$protocolCipher}[1], $cipherHexHash{$protocolCipher}[0] ) );
            }
            else {
                _trace4_( "$protocolCipher -> NO-RFC-" . $protocolCipher );
            }
            _trace_ "\n";
        }
        _trace_ "\n";
    }
    return;
}

sub printTLSCipherList ($) {
    my $cipherList     = shift || "";
    my $protocolCipher = "";

    my $anzahl      = int length($cipherList) / 2;
    my @cipherTable = unpack( "a2" x $anzahl, $cipherList );
    local $\ = "";

    if ( $SSLhello::trace > 1 ) {

        _trace4("printTLSCipherList ($anzahl):\n");
        for ( my $i = 0 ; $i < $anzahl ; $i++ ) {
            _trace4_( sprintf( "           Cipher[%2d]: ", $i ) );
            _trace4_( " >" . hexCodedCipher( $cipherTable[$i] ) . "< -> " );
            $protocolCipher = pack( "a6a*", "0x0300", hexCodedCipher( $cipherTable[$i] ) );
            if ( ( defined( $cipherHexHash{$protocolCipher} ) ) && ( $#{ $cipherHexHash{$protocolCipher} } > 0 ) ) {
                _trace_( sprintf "%s -> %-32s -> %s", $protocolCipher, $cipherHexHash{$protocolCipher}[1], $cipherHexHash{$protocolCipher}[0] );
            }
            else {
                _trace_( "$protocolCipher -> NO-RFC-" . $protocolCipher );
            }
            _trace4_ "\n";
        }
        _trace4_("\n");
    }
    return;
}

sub _main_help {
    printf( "# %s %s\n", __PACKAGE__, $VERSION );
    local $\ = "";
    undef $\;
    if ( eval { require Pod::Perldoc; } ) {
        exit( Pod::Perldoc->run( args => [$0] ) );
    }
    if (qx(perldoc -V)) {

        printf("# no Pod::Perldoc installed, please try:\n  perldoc $0\n");
    }
    return;
}

sub _main {
    my @argv = @_;
    if ( $#argv < 0 ) { _main_help(); exit 0; }
    while ( my $arg = shift @argv ) {
        if ( $arg =~ /^--?h(?:elp)?$/x )          { _main_help(); }
        if ( $arg =~ /^version$/x )               { print "$SID_sslhello\n"; }
        if ( $arg =~ /^[+-]?VERSION/x )           { print "$VERSION\n"; }
        if ( $arg =~ /^\+test.?init/x )           { printParameters(); }
        if ( $arg =~ /^\+test.?para(?:meter)?/x ) { printParameters(); }
        if ( $arg =~ /^\+test.?const(?:ant)?/x )  { printConstants(); }
        if ( $arg =~ /^[+-]/ )                    { exit 0; }
    }
    exit 0;
}


sub net_sslhello_done() { }

package SSLinfo;

my $SID_sslinfo = "@(#) SSLinfo.pm 3.34 24/09/06 21:34:46";
our $VERSION = "24.09.24";

BEGIN {
    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##;
    my $_pwd = $ENV{PWD} || ".";
    unshift( @INC, $_path ) if ( 1 > ( grep { /^$_path$/ } @INC ) );
    unshift( @INC, $_pwd )  if ( 1 > ( grep { /^$_pwd$/ } @INC ) );
    unshift( @INC, "lib" )  if ( 1 > ( grep { /^lib$/ } @INC ) );
    unshift( @INC, "." )    if ( 1 > ( grep { /^\.$/ } @INC ) );
}

my %SSLINFO = (
    'ME'      => 'SSLinfo',
    'ERROR'   => '#SSLinfo::errors:',
    'OPENSSL' => '<<openssl>>',
    'UNDEF'   => '<<undefined>>',
    'NO_PEM'  => '<<N/A (no PEM)>>',
);

my $_protos = 'http/1.1,h2c,h2c-14,spdy/1,npn-spdy/2,spdy/2,spdy/3,spdy/3.1,spdy/4a2,spdy/4a4,h2-14,h2-15,http/2.0,h2';
$SSLinfo::timeout       = 'timeout';
$SSLinfo::openssl       = 'openssl';
$SSLinfo::use_openssl   = 1;
$SSLinfo::use_sclient   = 1;
$SSLinfo::use_extdebug  = 1;
$SSLinfo::use_nextprot  = 1;
$SSLinfo::use_reconnect = 1;
$SSLinfo::sclient_opt   = '';
$SSLinfo::file_sclient  = '';
$SSLinfo::sni_name      = '';
$SSLinfo::use_SNI       = 1;
$SSLinfo::use_https     = 1;
$SSLinfo::use_http      = 1;
$SSLinfo::use_alpn      = 1;
$SSLinfo::use_npn       = 1;
$SSLinfo::protos_alpn   = $_protos;
$SSLinfo::protos_npn    = $_protos;
$SSLinfo::no_cert       = 0;

$SSLinfo::no_cert_txt      = 'unable to load certificate';
$SSLinfo::ignore_case      = 1;
$SSLinfo::target_url       = '/';
$SSLinfo::ignore_handshake = 0;
$SSLinfo::timeout_sec      = 3;
$SSLinfo::starttls         = '';
$SSLinfo::proxyhost        = '';
$SSLinfo::proxyport        = '';
$SSLinfo::proxypass        = '';
$SSLinfo::proxyuser        = '';
$SSLinfo::proxyauth        = '';
$SSLinfo::method           = '';
$SSLinfo::socket_reuse     = 1;
$SSLinfo::no_compression   = 0;
$SSLinfo::socket           = undef;
$SSLinfo::ca_crl           = undef;
$SSLinfo::ca_file          = undef;
$SSLinfo::ca_path          = undef;
$SSLinfo::ca_depth         = undef;

$SSLinfo::trace = 0;

$SSLinfo::prefix_trace   = "#$SSLINFO{'ME'}::";
$SSLinfo::prefix_verbose = "#$SSLINFO{'ME'}::";
$SSLinfo::user_agent     = '-';
$SSLinfo::verbose        = 0;
$SSLinfo::linux_debug    = 0;
$SSLinfo::slowly         = 0;

my $dumm_1 = $SSLinfo::linux_debug;
my $dumm_2 = $SSLinfo::proxyport;
my $dumm_3 = $SSLinfo::proxypass;
my $dumm_4 = $SSLinfo::proxyuser;
my $dumm_5 = $SSLinfo::proxyauth;
my $dumm_6 = $SSLinfo::ca_crl;
my $dumm_7 = $SSLinfo::use_nextprot;

BEGIN {
    Net::SSLeay::load_error_strings();
    Net::SSLeay::SSLeay_add_ssl_algorithms();
    Net::SSLeay::randomize();
    if ( 1.45 > $Net::SSLeay::VERSION ) {
        warn("$OText::STR{WARN} 081: ancient Net::SSLeay $Net::SSLeay::VERSION < 1.49; cannot use ::initialize");
    }
    else {
        Net::SSLeay::initialize();
    }
    if ( not exists &_trace ) {
        sub __trace { my @txt = @_; printf( "$SSLinfo::prefix_trace %s\n", "@txt" ); return; }
        sub _trace  { my @txt = @_; __trace(@txt) if ( 0 < $SSLinfo::trace );  return; }
        sub _trace1 { my @txt = @_; __trace(@txt) if ( 1 == $SSLinfo::trace ); return; }
        sub _trace2 { my @txt = @_; __trace(@txt) if ( 1 < $SSLinfo::trace );  return; }
    }
}


my @EXPORT = qw(
  net_sslinfo_done
  ssleay_methods
  test_methods
  test_openssl
  test_sclient
  test_sslmap
  test_ssleay
  datadump
  s_client_check
  s_client_get_optionlist
  s_client_opt_get
  do_ssl_new
  do_ssl_free
  do_ssl_open
  do_ssl_close
  do_openssl
  set_cipher_list
  options
  errors
  PEM
  pem
  text
  fingerprint
  fingerprint_hash
  fingerprint_text
  fingerprint_type
  fingerprint_sha2
  fingerprint_sha1
  fingerprint_md5
  cert_type
  email
  serial
  serial_int
  serial_hex
  modulus
  modulus_len
  modulus_exponent
  subject_hash
  issuer_hash
  aux
  pubkey
  pubkey_algorithm
  pubkey_value
  signame
  sigdump
  sigkey_len
  sigkey_value
  extensions
  tlsextdebug
  tlsextensions
  heartbeat
  trustout
  ocsp_uri
  ocspid
  ocsp_response
  ocsp_response_data
  ocsp_response_status
  ocsp_cert_status
  ocsp_next_update
  ocsp_this_update
  before
  after
  dates
  issuer
  subject
  default
  selected
  cipher_list
  cipher_openssl
  cipher_local
  ciphers
  cn
  commonname
  altname
  subjectaltnames
  authority
  owner
  certificate
  SSLversion
  version
  keysize
  keyusage
  https_protocols
  https_svc
  https_body
  https_status
  https_server
  https_alerts
  https_location
  https_refresh
  https_pins
  https_content_enc
  https_transfer_enc
  http_protocols
  http_svc
  http_status
  http_location
  http_refresh
  http_sts
  hsts
  hsts_httpequiv
  hsts_maxage
  hsts_subdom
  hsts_preload
  verify_hostname
  verify_altname
  verify_alias
  verify
  error_verify
  error_depth
  chain
  chain_verify
  compression
  expansion
  extended_master_secret
  master_secret
  next_protocols
  alpn
  no_alpn
  next_protocol
  krb5
  master_key
  psk_hint
  psk_identity
  public_key_len
  session_id
  session_id_ctx
  session_startdate
  session_starttime
  session_lifetime
  session_ticket
  session_ticket_hint
  session_timeout
  session_protocol
  srp
  renegotiation
  resumption
  dh_parameter
  selfsigned
  s_client
  error
  CTX_method
);

our $HAVE_XS = eval {
    local $SIG{'__DIE__'} = 'DEFAULT';
    eval {
        require XSLoader;
        XSLoader::load( 'SSLinfo', $VERSION );
        1;
    } or do {
        require DynaLoader;
        bootstrap SSLinfo $VERSION;
        1;
    };
} ? 1 : 0;

my $trace    = $SSLinfo::trace;
my $_echo    = '';
my $_timeout = undef;
my $_openssl = undef;

sub do_ssl_open($$$@);
sub do_ssl_close($$);
sub do_openssl($$$$);

sub _vprint  { my $txt = shift; printf("$SSLinfo::prefix_verbose $txt\n") if ( 0 < $SSLinfo::verbose ); return; }
sub _vprint2 { my $txt = shift; printf("$SSLinfo::prefix_verbose $txt\n") if ( 1 < $SSLinfo::verbose ); return; }

sub _traceset {
    $trace              = $SSLinfo::trace;
    $Net::SSLeay::trace = $trace if ( 1 < $trace );
    $Net::SSLeay::linux_debug = 1 if ( 2 < $trace );
    $Net::SSLeay::slowly = $SSLinfo::slowly;
    return;
}

sub _trace_value_or_text {
    my $val = shift || "undef";
    return ( 1 < $trace ) ? $val : "<<use --trace=2 to print pointer>>";
}

sub _setcommand {
    my $command = shift;
    return '' if not $command;
    my $cmd;
    my $opt = "version";
    $opt = "--version" if ( $command =~ m/timeout$/ );
    _trace("_setcommand($command) $opt 2>&1");
    $cmd = qx("$command" $opt 2>&1);

    if ( defined $cmd ) {
        chomp $cmd;
        _trace2("_setcommand: #{ $cmd #}");
        $cmd = "$command";
        if ( $cmd =~ m#timeout$# ) {
            $cmd = "$cmd -t " if ( qx("$cmd" -t 2 pwd 2>&1) !~ m/timeout/ );
        }
    }
    else {
        _trace("_setcommand: $command = ''");
        $cmd = '';
    }
    if ( $^O !~ m/MSWin32/ ) {
        $cmd = '\\' . $cmd if ( ( $cmd ne '' ) and ( $cmd !~ /\// ) );
    }
    _trace("_setcommand cmd=$cmd");
    return $cmd;
}

sub _setcmd {
    return if ( defined $_timeout );
    $_openssl = _setcommand($SSLinfo::openssl);
    $_timeout = _setcommand($SSLinfo::timeout);
    $_timeout .= " $SSLinfo::timeout_sec" if ( defined $_timeout );
    _trace("#_setcmd: _openssl=$_openssl ; _timeout=$_timeout");
    if ( $^O !~ m/MSWin32/ ) {
        $_echo = '\\' . $_echo;
    }
    return;
}

sub _traceSSLbitmasks {
    my $txt  = shift;
    my $mask = shift;
    return if ( 0 >= $trace );
    _traceset();
    foreach my $op (
        sort qw(
        OP_ALL
        OP_MICROSOFT_SESS_ID_BUG
        OP_NETSCAPE_CHALLENGE_BUG
        OP_LEGACY_SERVER_CONNECT
        OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
        OP_TLSEXT_PADDING
        OP_MICROSOFT_BIG_SSLV3_BUFFER
        OP_SAFARI_ECDHE_ECDSA_BUG
        OP_SSLEAY_080_CLIENT_DH_BUG
        OP_TLS_D5_BUG
        OP_TLS_BLOCK_PADDING_BUG
        OP_DONT_INSERT_EMPTY_FRAGMENTS
        OP_NO_QUERY_MTU
        OP_COOKIE_EXCHANGE
        OP_NO_TICKET
        OP_CISCO_ANYCONNECT
        OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
        OP_NO_COMPRESSION
        OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
        OP_SINGLE_ECDH_USE
        OP_SINGLE_DH_USE
        OP_CIPHER_SERVER_PREFERENCE
        OP_TLS_ROLLBACK_BUG
        OP_NO_SSLv2
        OP_NO_SSLv3
        OP_NO_TLSv1
        OP_NO_TLSv1_1
        OP_NO_TLSv1_2
        OP_NO_TLSv1_3
        OP_NO_SSL_MASK
        OP_NETSCAPE_CA_DN_BUG
        OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
        OP_CRYPTOPRO_TLSEXT_BUG
        OP_SSLREF2_REUSE_CERT_TYPE_BUG
        OP_MSIE_SSLV2_RSA_PADDING
        OP_EPHEMERAL_RSA
        OP_PKCS1_CHECK_1
        OP_PKCS1_CHECK_2
        OP_ALLOW_NO_DHE_KEX
        OP_NON_EXPORT_FIRST
        OP_NO_CLIENT_RENEGOTIATION
        OP_NO_ENCRYPT_THEN_MAC
        OP_NO_RENEGOTIATION
        OP_PRIORITIZE_CHACHA
        )
      )
    {
        no strict;

        my $bit;
        my $opt;
        my $_ok = eval { $opt = &{"Net::SSLeay::$op"}; };
        if ( defined $_ok ) {
            $bit = ( ( $mask & $opt ) > 0 ) || 0;
            $bit = sprintf( "0x%08x %s", $opt, $bit );
        }
        else {
            $bit = sprintf("<<$@>>");

        }
        _trace( sprintf( "%s: %-30s %s", $txt, $op, $bit ) );
    }
    return;
}

sub _ssleay_value_get {
    my $type = shift;
    my $func = shift;
    my $val  = "<<undef>>";
    $val = undef if ( 'OP_or_undef' eq $type );
    _traceset();
    _trace("_ssleay_value_get('$type', '$func')");
    if ( defined &$func ) {
        $val = sprintf( '0x%08x', Net::SSLeay::CTX_get_options( &$func() ) )     if ( 'options' eq $type );
        $val = Net::SSLeay::CTX_get_timeout( &$func() )                          if ( 'timeout' eq $type );
        $val = sprintf( '0x%08x', Net::SSLeay::CTX_get_verify_mode( &$func() ) ) if ( 'verify_mode' eq $type );
        $val = Net::SSLeay::CTX_get_verify_depth( &$func() )                     if ( 'verify_depth' eq $type );
        $val = sprintf( '0x%08x', &$func() )                                     if ( 'OP' eq $type );
        $val = sprintf( '0x%08x', &$func() )                                     if ( 'OP_or_undef' eq $type );
    }
    _trace( "_ssleay_value_get ret=" . ( $val || "undef" ) );
    return $val;
}

my %_OpenSSL_opt = (

    'done' => 0,
    'data' => '',

    '-CAfile'                   => 0,
    '-CApath'                   => 0,
    '-alpn'                     => 0,
    '-npn'                      => 0,
    '-nextprotoneg'             => 0,
    '-reconnect'                => 0,
    '-fallback_scsv'            => 0,
    '-comp'                     => 0,
    '-no_comp'                  => 0,
    '-no_ticket'                => 0,
    '-no_tlsext'                => 0,
    '-serverinfo'               => 0,
    '-servername'               => 0,
    '-serverpref'               => 0,
    '-showcerts'                => 0,
    '-curves'                   => 0,
    '-debug'                    => 0,
    '-bugs'                     => 0,
    '-key'                      => 0,
    '-msg'                      => 0,
    '-nbio'                     => 0,
    '-psk'                      => 0,
    '-psk_identity'             => 0,
    '-pause'                    => 0,
    '-prexit'                   => 0,
    '-proxy'                    => 0,
    '-quiet'                    => 0,
    '-sigalgs'                  => 0,
    '-state'                    => 0,
    '-status'                   => 0,
    '-strict'                   => 0,
    '-nbio_test'                => 0,
    '-tlsextdebug'              => 0,
    '-client_sigalgs'           => 0,
    '-record_padding'           => 0,
    '-no_renegotiation'         => 0,
    '-legacyrenegotiation'      => 0,
    '-legacy_renegotiation'     => 0,
    '-legacy_server_connect'    => 0,
    '-no_legacy_server_connect' => 0,
    '-ssl2'      => 0,
    '-ssl3'      => 0,
    '-tls1'      => 0,
    '-tls1_1'    => 0,
    '-tls1_2'    => 0,
    '-tls1_3'    => 0,
    '-dtls'      => 0,
    '-dtls1'     => 0,
    '-dtls1_1'   => 0,
    '-dtls1_2'   => 0,
    '-dtls1_3'   => 0,
    '-no_ssl2'   => 0,
    '-no_ssl3'   => 0,
    '-no_tls1'   => 0,
    '-no_tls1_1' => 0,
    '-no_tls1_2' => 0,
    '-no_tls1_3' => 0,
);

my %_SSLmap = (

    'SSLv2'      => [ 0x0002, undef ],
    'SSLv3'      => [ 0x0300, undef ],
    'TLSv1'      => [ 0x0301, undef ],
    'TLSv11'     => [ 0x0302, undef ],
    'TLSv12'     => [ 0x0303, undef ],
    'TLSv13'     => [ 0x0304, undef ],
    'TLS1FF'     => [ 0x03FF, undef ],
    'DTLSfamily' => [ 0xFE00, undef ],
    'DTLSv09'    => [ 0x0100, undef ],
    'DTLSv1'     => [ 0xFEFF, undef ],
    'DTLSv11'    => [ 0xFEFE, undef ],
    'DTLSv12'    => [ 0xFEFD, undef ],
    'DTLSv13'    => [ 0xFEFF, undef ],
);
$_SSLmap{'SSLv2'}[1]   = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_SSLv2 );
$_SSLmap{'SSLv3'}[1]   = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_SSLv3 );
$_SSLmap{'TLSv1'}[1]   = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_TLSv1 );
$_SSLmap{'TLSv11'}[1]  = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_TLSv1_1 );
$_SSLmap{'TLSv12'}[1]  = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_TLSv1_2 );
$_SSLmap{'TLSv13'}[1]  = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_TLSv1_3 );
$_SSLmap{'DTLSv1'}[1]  = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_DTLSv1 );
$_SSLmap{'DTLSv11'}[1] = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_DTLSv1_1 );
$_SSLmap{'DTLSv12'}[1] = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_DTLSv1_2 );
$_SSLmap{'DTLSv13'}[1] = _ssleay_value_get( 'OP_or_undef', *Net::SSLeay::OP_NO_DTLSv1_3 );
my %_SSLhex = map { $_SSLmap{$_}[0] => $_ } keys %_SSLmap;

sub _SSLversion_get { return $_SSLmap{ $_[0] }[0]; }
sub _SSLbitmask_get { return $_SSLmap{ $_[0] }[1]; }

my %_SSLtemp = (

    'addr'     => undef,
    'socket'   => undef,
    'ctx'      => undef,
    'ssl'      => undef,
    'method'   => '',
    'errors'   => [],
    'PEM_text' => '',

);

sub _SSLtemp_reset {
    foreach my $key ( keys %_SSLtemp ) { $_SSLtemp{$key} = undef; }
    $_SSLtemp{'method'}   = '';
    $_SSLtemp{'errors'}   = [];
    $_SSLtemp{'PEM_text'} = '';
    return;
}

my %_SSLinfo = (
    'key' => 'value',

    'host'       => '',
    'addr'       => undef,
    'ip'         => '',
    'port'       => 443,
    'ctx'        => undef,
    'ssl'        => undef,
    '_options'   => '',
    'errors'     => [],
    'cipherlist' => 'ALL:NULL:eNULL:aNULL:LOW:EXP',
    'verify_cnt' => 0,

    'SSLversion'   => '',
    'version'      => '',
    'error_verify' => '',
    'error_depth'  => '',
    'keysize'      => '',
    'keyusage'     => '',
    'altname'      => '',
    'cn'           => '',
    'subject'      => '',
    'issuer'       => '',
    'before'       => '',
    'after'        => '',
    'PEM'          => '',
    'text'         => '',
    'cert_type'    => '',
    'ciphers'      => [],

    's_client'             => "",
    'ciphers_openssl'      => "",
    'subject_hash'         => "",
    'issuer_hash'          => "",
    'aux'                  => "",
    'ocsp_response'        => "",
    'ocsp_response_data'   => "",
    'ocsp_response_status' => "",
    'ocsp_cert_status'     => "",
    'ocsp_next_update'     => "",
    'ocsp_this_update'     => "",
    'pubkey'               => "",
    'pubkey_algorithm'     => "",
    'pubkey_value'         => "",
    'signame'              => "",
    'sigdump'              => "",
    'sigkey_len'           => "",
    'sigkey_value'         => "",
    'extensions'           => "",
    'tlsextdebug'          => "",
    'tlsextensions'        => "",
    'email'                => "",
    'heartbeat'            => "",
    'serial'               => "",
    'serial_hex'           => "",
    'serial_int'           => "",
    'modulus'              => "",
    'modulus_len'          => "",
    'modulus_exponent'     => "",
    'fingerprint_text'     => "",
    'fingerprint_type'     => "",
    'fingerprint_hash'     => "",
    'fingerprint_sha2'     => "",
    'fingerprint_sha1'     => "",
    'fingerprint_md5'      => "",
    'selected'             => "",

    'verify'            => "",
    'chain'             => "",
    'chain_verify'      => "",
    'dh_parameter'      => "",
    'renegotiation'     => "",
    'resumption'        => "",
    'selfsigned'        => "",
    'compression'       => "",
    'expansion'         => "",
    'next_protocols'    => "",
    'alpn'              => "",
    'no_alpn'           => "",
    'next_protocol'     => "",
    'krb5'              => "",
    'psk_hint'          => "",
    'psk_identity'      => "",
    'srp'               => "",
    'master_key'        => "",
    'master_secret'     => "",
    'public_key_len'    => "",
    'resumption_psk'    => "",
    'session_id'        => "",
    'session_id_ctx'    => "",
    'session_startdate' => "",
    'session_starttime' => "",
    'session_lifetime'  => "",
    'session_ticket'    => "",
    'session_timeout'   => "",
    'session_protocol'  => "",

    'https_protocols'    => "",
    'https_svc'          => "",
    'https_body'         => "",
    'https_status'       => "",
    'https_server'       => "",
    'https_alerts'       => "",
    'https_location'     => "",
    'https_refresh'      => "",
    'https_pins'         => "",
    'https_content_enc'  => "",
    'https_transfer_enc' => "",
    'http_protocols'     => "",
    'http_svc'           => "",
    'http_status'        => "",
    'http_location'      => "",
    'http_refresh'       => "",
    'http_sts'           => "",
    'https_sts'          => "",
    'hsts_httpequiv'     => "",
    'hsts_maxage'        => "",
    'hsts_subdom'        => "",
    'hsts_preload'       => "",

);

my $_SSLinfo_random      = qr/ctx|master_key|resumption_psk|session_(?:startdate|starttime|ticket)|ssl|x509/;
my $_SSLinfo_random_text = $OText::STR{MAKEVAL};

sub _SSLinfo_reset {
    foreach my $key ( keys %_SSLinfo ) { $_SSLinfo{$key} = ''; }
    $_SSLinfo{'key'}             = 'value';
    $_SSLinfo{'ctx'}             = undef;
    $_SSLinfo{'ssl'}             = undef;
    $_SSLinfo{'addr'}            = undef;
    $_SSLinfo{'port'}            = 443;
    $_SSLinfo{'errors'}          = [];
    $_SSLinfo{'ciphers'}         = [];
    $_SSLinfo{'cipherlist'}      = 'ALL:NULL:eNULL:aNULL:LOW:EXP';
    $_SSLinfo{'verify_cnt'}      = 0;
    $_SSLinfo{'ciphers_openssl'} = '';
    return;
}

sub _SSLinfo_print {
    foreach my $key (
        sort qw(
        subject_hash
        issuer_hash
        aux
        ocsp_response
        ocsp_response_data
        ocsp_response_status
        ocsp_cert_status
        ocsp_next_update
        ocsp_this_update
        pubkey
        pubkey_algorithm
        pubkey_value
        signame
        sigdump
        sigkey_len
        sigkey_value
        extensions
        tlsextdebug
        tlsextensions
        email
        heartbeat
        serial
        serial_hex
        serial_int
        modulus
        modulus_len
        modulus_exponent
        fingerprint_text
        fingerprint_type
        fingerprint_hash
        fingerprint_sha2
        fingerprint_sha1
        fingerprint_md5
        selected
        verify
        chain
        chain_verify
        dh_parameter
        renegotiation
        resumption
        selfsigned
        compression
        expansion
        next_protocols
        alpn
        no_alpn
        next_protocol
        krb5
        psk_hint
        psk_identity
        srp
        master_secret
        master_key
        public_key_len
        resumption_psk
        session_id
        session_id_ctx
        session_startdate
        session_starttime
        session_lifetime
        session_ticket
        session_timeout
        session_protocol
        )
      )
    {
        next if ( not defined $_SSLinfo{$key} );
        _trace("$key=$_SSLinfo{$key}");
    }
    return;
}

sub ssleay_methods {
    my @list;
    push( @list, 'TLSv1_3_method' )               if ( defined &Net::SSLeay::TLSv1_3_method );
    push( @list, 'TLSv1_2_method' )               if ( defined &Net::SSLeay::TLSv1_2_method );
    push( @list, 'TLSv1_1_method' )               if ( defined &Net::SSLeay::TLSv1_1_method );
    push( @list, 'TLSv1_method' )                 if ( defined &Net::SSLeay::TLSv1_method );
    push( @list, 'SSLv23_method' )                if ( defined &Net::SSLeay::SSLv23_method );
    push( @list, 'SSLv3_method' )                 if ( defined &Net::SSLeay::SSLv3_method );
    push( @list, 'SSLv2_method' )                 if ( defined &Net::SSLeay::SSLv2_method );
    push( @list, 'DTLSv1_3_method' )              if ( defined &Net::SSLeay::DTLSv1_3_method );
    push( @list, 'DTLSv1_2_method' )              if ( defined &Net::SSLeay::DTLSv1_2_method );
    push( @list, 'DTLSv1_1_method' )              if ( defined &Net::SSLeay::DTLSv1_1_method );
    push( @list, 'DTLSv1_method' )                if ( defined &Net::SSLeay::DTLSv1_method );
    push( @list, 'DTLS_method' )                  if ( defined &Net::SSLeay::DTLS_method );
    push( @list, 'CTX_tlsv1_3_new' )              if ( defined &Net::SSLeay::CTX_tlsv1_3_new );
    push( @list, 'CTX_tlsv1_2_new' )              if ( defined &Net::SSLeay::CTX_tlsv1_2_new );
    push( @list, 'CTX_tlsv1_1_new' )              if ( defined &Net::SSLeay::CTX_tlsv1_1_new );
    push( @list, 'CTX_tlsv1_0_new' )              if ( defined &Net::SSLeay::CTX_tlsv1_0_new );
    push( @list, 'CTX_tlsv1_new' )                if ( defined &Net::SSLeay::CTX_tlsv1_new );
    push( @list, 'CTX_v23_new' )                  if ( defined &Net::SSLeay::CTX_v23_new );
    push( @list, 'CTX_v3_new' )                   if ( defined &Net::SSLeay::CTX_v3_new );
    push( @list, 'CTX_v2_new' )                   if ( defined &Net::SSLeay::CTX_v2_new );
    push( @list, 'CTX_new_with_method' )          if ( defined &Net::SSLeay::CTX_new_with_method );
    push( @list, 'CTX_new' )                      if ( defined &Net::SSLeay::CTX_new );
    push( @list, 'CTX_dtlsv1_3_new' )             if ( defined &Net::SSLeay::CTX_dtlsv1_3_new );
    push( @list, 'CTX_dtlsv1_2_new' )             if ( defined &Net::SSLeay::CTX_dtlsv1_2_new );
    push( @list, 'CTX_dtlsv1_new' )               if ( defined &Net::SSLeay::CTX_dtlsv1_new );
    push( @list, 'CTX_get_options' )              if ( defined &Net::SSLeay::CTX_get_options );
    push( @list, 'CTX_set_options' )              if ( defined &Net::SSLeay::CTX_set_options );
    push( @list, 'CTX_set_timeout' )              if ( defined &Net::SSLeay::CTX_set_timeout );
    push( @list, 'CTX_set_alpn_protos' )          if ( defined &Net::SSLeay::CTX_set_alpn_protos );
    push( @list, 'CTX_set_next_proto_select_cb' ) if ( defined &Net::SSLeay::CTX_set_next_proto_select_cb );
    return @list;
}

sub test_openssl {
    s_client_check();
    my $line = "=-----------------------+----------------";
    my $data = "= using $SSLinfo::openssl\n";
    $data .= "$line\n= _OpenSSL_opt          | 1=available\n$line\n";
    foreach my $_opt ( sort keys %_OpenSSL_opt ) {
        if ( 'data' eq $_opt ) {
            if ( 0 >= $SSLinfo::verbose ) {
                $_OpenSSL_opt{$_opt} = '<<use  --v  or  --trace to see openssl usage>>';
            }
        }
        $data .= sprintf( "%23s\t= %s\n", $_opt, $_OpenSSL_opt{$_opt} );
    }
    $data .= "$line";
    return $data;
}

sub test_methods {
    return join( " ", sort( ssleay_methods() ) );
}

sub test_sclient {
    return join( " ", sort( s_client_get_optionlist() ) );
}

sub test_sslmap {
    my $line = "=-----------------------+--------+-------------";
    my $data = "$line\n= _SSLmap    key        | SSLeay  bitmask\n$line\n";
    foreach my $_ssl ( sort keys %_SSLmap ) {
        my $mask = "<<undef>>";
        $mask = $_SSLmap{$_ssl}[1] if defined $_SSLmap{$_ssl}[1];
        $data .= sprintf( "%23s\t= 0x%04X  %s\n", $_ssl, $_SSLmap{$_ssl}[0], $mask );
    }
    $data .= "$line";
    return $data;
}

sub test_ssleay {
    my @list = ssleay_methods();
    my $line = "=------------+------------------+-------------";
    my $data = "$line
= Net::SSLeay{ function         | 1=available
$line
             ::SSLv2_method     = " . ( ( grep { /^SSLv2_method$/ } @list )   ? 1 : 0 ) . "
             ::SSLv3_method     = " . ( ( grep { /^SSLv3_method$/ } @list )   ? 1 : 0 ) . "
             ::SSLv23_method    = " . ( ( grep { /^SSLv23_method$/ } @list )  ? 1 : 0 ) . "
             ::TLSv1_method     = " . ( ( grep { /^TLSv1_method$/ } @list )   ? 1 : 0 ) . "
             ::TLSv1_1_method   = " . ( ( grep { /^TLSv1_1_method$/ } @list ) ? 1 : 0 ) . "
             ::TLSv1_2_method   = " . ( ( grep { /^TLSv1_2_method$/ } @list ) ? 1 : 0 ) . "
#{ following missing in Net::SSLeay (up to 1.72):
             ::TLSv1_3_method   = " . ( ( grep { /^TLSv1_3_method$/ } @list )  ? 1 : 0 ) . "
             ::DTLSv1_method    = " . ( ( grep { /^DTLSv1_method$/ } @list )   ? 1 : 0 ) . "
             ::DTLSv1_2_method  = " . ( ( grep { /^DTLSv1_2_method$/ } @list ) ? 1 : 0 ) . "
             ::DTLS_method      = " . ( ( grep { /^DTLS_method$/ } @list )     ? 1 : 0 ) . "
#}
             ::CTX_new_with_method  = " .         ( ( grep { /^CTX_new_with_method$/ } @list )          ? 1 : 0 ) . "
             ::CTX_new          = " .             ( ( grep { /^CTX_new$/ } @list )                      ? 1 : 0 ) . "
             ::CTX_v2_new       = " .             ( ( grep { /^CTX_v2_new$/ } @list )                   ? 1 : 0 ) . "
             ::CTX_v3_new       = " .             ( ( grep { /^CTX_v3_new$/ } @list )                   ? 1 : 0 ) . "
             ::CTX_v23_new      = " .             ( ( grep { /^CTX_v23_new$/ } @list )                  ? 1 : 0 ) . "
             ::CTX_tlsv1_new    = " .             ( ( grep { /^CTX_tlsv1_new$/ } @list )                ? 1 : 0 ) . "
             ::CTX_tlsv1_0_new  = " .             ( ( grep { /^CTX_tlsv1_0_new$/ } @list )              ? 1 : 0 ) . "
             ::CTX_tlsv1_1_new  = " .             ( ( grep { /^CTX_tlsv1_1_new$/ } @list )              ? 1 : 0 ) . "
             ::CTX_tlsv1_2_new  = " .             ( ( grep { /^CTX_tlsv1_2_new$/ } @list )              ? 1 : 0 ) . "
             ::CTX_tlsv1_3_new  = " .             ( ( grep { /^CTX_tlsv1_3_new$/ } @list )              ? 1 : 0 ) . "
             ::CTX_dtlsv1_new   = " .             ( ( grep { /^CTX_dtlsv1_new$/ } @list )               ? 1 : 0 ) . "
             ::CTX_dtlsv1_2_new = " .             ( ( grep { /^CTX_dtlsv1_2_new$/ } @list )             ? 1 : 0 ) . "
             ::CTX_dtlsv1_3_new = " .             ( ( grep { /^CTX_dtlsv1_3_new$/ } @list )             ? 1 : 0 ) . "
             ::CTX_get_options  = " .             ( ( grep { /^CTX_get_options$/ } @list )              ? 1 : 0 ) . "
             ::CTX_set_options  = " .             ( ( grep { /^CTX_set_options$/ } @list )              ? 1 : 0 ) . "
             ::CTX_set_timeout  = " .             ( ( grep { /^CTX_set_timeout$/ } @list )              ? 1 : 0 ) . "
             ::CTX_set_alpn_protos  = " .         ( ( grep { /^CTX_set_alpn_protos$/ } @list )          ? 1 : 0 ) . "
             ::CTX_set_next_proto_select_cb = " . ( ( grep { /^CTX_set_next_proto_select_cb$/ } @list ) ? 1 : 0 ) . "
$line
= Net::SSLeay} function\n";
    no warnings 'once';

    $data .= "= Net::SSLeay{ constant           hex value
$line
             ::OP_NO_SSLv2      = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_SSLv2 ) . "
             ::OP_NO_SSLv3      = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_SSLv3 ) . "
             ::OP_NO_TLSv1      = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_TLSv1 ) . "
             ::OP_NO_TLSv1_1    = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_TLSv1_1 ) . "
             ::OP_NO_TLSv1_2    = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_TLSv1_2 ) . "
             ::OP_NO_TLSv1_3    = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_TLSv1_3 ) . "
             ::OP_NO_DTLSv09    = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_DTLSv09 ) . "
             ::OP_NO_DTLSv1     = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_DTLSv1 ) . "
             ::OP_NO_DTLSv1_1   = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_DTLSv1_1 ) . "
             ::OP_NO_DTLSv1_2   = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_DTLSv1_2 ) . "
             ::OP_NO_DTLSv1_3   = " . _ssleay_value_get( 'OP', *Net::SSLeay::OP_NO_DTLSv1_3 ) . "
$line
= Net::SSLeay} constant\n";
    $data .= "= Net::SSLeay{ call
$line
#      experimental ...
  Net::SSLeay::CTX_new {
             ::CTX_get_options(CTX)= " . _ssleay_value_get( 'options', *Net::SSLeay::CTX_new ) . "
  Net::SSLeay::CTX_new }
  Net::SSLeay::CTX_v3_new {
             ::CTX_get_options(CTX)= " . _ssleay_value_get( 'options', *Net::SSLeay::CTX_v3_new ) . "
  Net::SSLeay::CTX_v3_new }
  Net::SSLeay::CTX_v23_new {
             ::CTX_get_options(CTX)= " . _ssleay_value_get( 'options', *Net::SSLeay::CTX_v23_new ) . "
             ::CTX_get_timeout(CTX)= " . _ssleay_value_get( 'timeout', *Net::SSLeay::CTX_v23_new ) . "
             ::CTX_get_verify_mode(CTX) = " . _ssleay_value_get( 'verify_mode',  *Net::SSLeay::CTX_v23_new ) . "
             ::CTX_get_verify_depth(CTX)= " . _ssleay_value_get( 'verify_depth', *Net::SSLeay::CTX_v23_new ) . "
  Net::SSLeay::CTX_v23_new }
  Net::SSLeay::CTX_tlsv1_2_new {
             ::CTX_get_options(CTX)= " . _ssleay_value_get( 'options', *Net::SSLeay::CTX_tlsv1_2_new ) . "
             ::CTX_get_timeout(CTX)= " . _ssleay_value_get( 'timeout', *Net::SSLeay::CTX_tlsv1_2_new ) . "
             ::CTX_get_verify_mode(CTX) = " . _ssleay_value_get( 'verify_mode',  *Net::SSLeay::CTX_tlsv1_2_new ) . "
             ::CTX_get_verify_depth(CTX)= " . _ssleay_value_get( 'verify_depth', *Net::SSLeay::CTX_tlsv1_2_new ) . "
  Net::SSLeay::CTX_tlsv1_2_new }
$line
= Net::SSLeay} call\n";

    return $data;
}

sub _dump {
    my $key = shift;
    my $txt = shift;
    my $val = shift;
    return sprintf( "#{ %-12s=%s%s #}\n", $key, $txt, ( $val || "<<undefined>>" ) );
}

sub datadump {
    my $prefix = shift;
    my $data   = $prefix;
    $data .= " datadump #{\n";
    if ( $SSLinfo::use_sclient > 1 ) {
        $data .= _dump( 's_client', " ", $_SSLinfo{'s_client'} );
    }
    else {
        $data .= _dump( 's_client', " ", "#### please set 'SSLinfo::use_sclient > 1' to dump s_client data also ###" );
    }
    $data .= _dump( 'PEM',     " ", $_SSLinfo{'PEM'} );
    $data .= _dump( 'text',    " ", $_SSLinfo{'text'} );
    $data .= _dump( 'ciphers', " ", join( ' ', @{ $_SSLinfo{'ciphers'} } ) );
    $data .= _dump( 'addr',    " ", join( '.', unpack( 'W4', $_SSLinfo{'addr'} || "" ) ) );
    foreach my $key ( sort keys %_SSLinfo ) {
        next if ( $key =~ m/addr|ciphers|errors|PEM|text|fingerprint_|s_client/ );
        if ( $key =~ m/$_SSLinfo_random/ ) {
            if ( defined $ENV{'OSAFT_MAKE'} ) {
                $data .= _dump( $key, " ", $_SSLinfo_random_text );
                next;
            }
        }
        $data .= _dump( $key, " ", $_SSLinfo{$key} );
    }
    foreach my $key ( sort keys %_SSLinfo ) {
        next if ( $key !~ m/fingerprint_/ );
        $data .= _dump( $key, " ", $_SSLinfo{$key} );
    }
    $data .= _dump( 'errors', "\n", join( "\n ** ", @{ $_SSLinfo{'errors'} } ) );
    $data .= "$SSLinfo::prefix_trace$prefix datadump #}";
    return $data;
}

sub _SSLinfo_get {
    my ( $key, $host, $port ) = @_;
    _traceset();
    _trace2( "_SSLinfo_get('$key'," . ( $host || '' ) . "," . ( $port || '' ) . ")" );
    if ( $key eq 'ciphers_openssl' ) {
        _trace("_SSLinfo_get($key): WARNING: function obsolete, please use cipher_openssl()");
        return '';
    }
    if ( $key eq 'errors' ) {

        return wantarray ? @{ $_SSLinfo{$key} } : join( "\n", @{ $_SSLinfo{$key} } );
    }
    if ( not defined $_SSLinfo{'ssl'} ) {
        return '' if not defined do_ssl_open( $host, $port, '' );
    }
    if ( $key eq 'ciphers' ) {
        return wantarray ? @{ $_SSLinfo{$key} } : join( ' ', @{ $_SSLinfo{$key} } );
        return wantarray ? @{ $_SSLinfo{$key} } : join( ':', @{ $_SSLinfo{$key} } );
    }
    if ( $key eq 'dates' ) {
        _trace2( "_SSLinfo_get 'dates'=" . $_SSLinfo{'before'} . " " . $_SSLinfo{'after'} );
        return ( $_SSLinfo{'before'}, $_SSLinfo{'after'} );
    }
    if ( 0 < $trace ) {
        my $value = $_SSLinfo{$key} || '';
        $value = "<<use --trace=2 to print data>>" if ( $value =~ m/[\r\n]/ );
        _trace2("_SSLinfo_get '$key'=$value");
    }
    return ( grep { /^$key$/ } keys %_SSLinfo ) ? $_SSLinfo{$key} : '';
}

sub _check_host {
    my $host = shift;
    _trace( "_check_host(" . ( $host || '' ) . ")" );
    $host = $_SSLinfo{'host'} unless defined $host;
    my $ip = undef;
    if ( $ip = gethostbyname($host) ) {
        $_SSLinfo{'host'} = $host;
        $_SSLinfo{'addr'} = $ip;
        $_SSLinfo{'ip'}   = join( '.', unpack( 'W4', $ip ) );
    }
    else {
        push( @{ $_SSLinfo{'errors'} }, "_check_host: $!" );
    }
    _trace("_check_host $_SSLinfo{'host'} $_SSLinfo{'ip'}");
    return ( defined $ip ) ? 1 : undef;
}

sub _check_port {
    my $port = shift;
    _trace( "_check_port(" . ( $port || '' ) . ")" );
    $port = $_SSLinfo{'port'}             unless defined $port;
    $port = getservbyname( $port, 'tcp' ) unless $port =~ /^\d+$/;
    push( @{ $_SSLinfo{'errors'} }, "_check_port: $!" ) if ( $! !~ m/^\s*$/ );
    $_SSLinfo{'port'} = $port                           if ( defined $port );
    return ( defined $port ) ? 1 : undef;
}

sub _check_peer {
    my ( $ok, $x509_store_ctx ) = @_;
    _trace( "_check_peer($ok, " . _trace_value_or_text($x509_store_ctx) . ")" );
    $_SSLinfo{'verify_cnt'} += 1;
    return $ok;
}

sub _check_crl {

    my $ssl = shift;
    _trace("_check_crl()");
    return;
}

sub _check_client_cert { print "##check_client_cert\n"; return; }

sub _ssleay_cert_get {
    my ( $key, $x509 ) = @_;
    _traceset();
    _trace("_ssleay_cert_get('$key', x509)");
    if ( 0 != $SSLinfo::no_cert ) {
        _trace("_ssleay_cert_get 'use_cert' $SSLinfo::no_cert .");
        return $SSLinfo::no_cert_txt if ( 2 == $SSLinfo::no_cert );
        return '';
    }

    if ( not $x509 ) {
        return $SSLinfo::no_cert_txt
          if ( $key =~ m/^(PEM|version|md5|sha1|sha2|subject|issuer|before|after|serial_hex|cn|policies|error_depth|cert_type|serial|altname)/ );
    }

    return Net::SSLeay::PEM_get_string_X509($x509) || ''                                                                     if ( $key eq 'PEM' );
    return Net::SSLeay::X509_get_version($x509) + 1                                                                          if ( $key eq 'version' );
    return Net::SSLeay::X509_get_fingerprint( $x509, 'md5' )                                                                 if ( $key eq 'md5' );
    return Net::SSLeay::X509_get_fingerprint( $x509, 'sha1' )                                                                if ( $key eq 'sha1' );
    return Net::SSLeay::X509_get_fingerprint( $x509, 'sha256' )                                                              if ( $key eq 'sha2' );
    return Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name($x509) )                                       if ( $key eq 'subject' );
    return Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name($x509) )                                        if ( $key eq 'issuer' );
    return Net::SSLeay::P_ASN1_UTCTIME_put2string( Net::SSLeay::X509_get_notBefore($x509) )                                  if ( $key eq 'before' );
    return Net::SSLeay::P_ASN1_UTCTIME_put2string( Net::SSLeay::X509_get_notAfter($x509) )                                   if ( $key eq 'after' );
    return Net::SSLeay::P_ASN1_INTEGER_get_hex( Net::SSLeay::X509_get_serialNumber($x509) )                                  if ( $key eq 'serial_hex' );
    return Net::SSLeay::X509_NAME_get_text_by_NID( Net::SSLeay::X509_get_subject_name($x509), &Net::SSLeay::NID_commonName ) if ( $key eq 'cn' );
    return Net::SSLeay::X509_NAME_get_text_by_NID( Net::SSLeay::X509_get_subject_name($x509), &Net::SSLeay::NID_certificate_policies )
      if ( $key eq 'policies' );
    return Net::SSLeay::X509_STORE_CTX_get_error_depth($x509) if ( $key eq 'error_depth' );
    return Net::SSLeay::X509_certificate_type($x509)          if ( $key eq 'cert_type' );
    return Net::SSLeay::X509_subject_name_hash($x509)         if ( $key eq 'subject_hash' );
    return Net::SSLeay::X509_issuer_name_hash($x509)          if ( $key eq 'issuer_hash' );

    my $ret = '';
    if ( $key =~ 'serial' ) {
        $ret = Net::SSLeay::P_ASN1_INTEGER_get_hex( Net::SSLeay::X509_get_serialNumber($x509) );
        return $ret if ( $key eq 'serial_hex' );
        my $int = hex($ret);
        return $int if ( $key eq 'serial_int' );
        return "$int (0x$ret)";
    }

    if ( $key eq 'altname' ) {
        my @altnames = Net::SSLeay::X509_get_subjectAltNames($x509);
        _trace2( "_ssleay_cert_get: Altname: " . join( ' ', @altnames ) );
        while (@altnames) {
            my ( $type, $name ) = splice( @altnames, 0, 2 );
            $type = 'DNS'                              if ( $type eq '2' );
            $type = 'URI'                              if ( $type eq '6' );
            $type = 'X400'                             if ( $type eq '3' );
            $type = 'DIRNAME'                          if ( $type eq '4' );
            $type = 'EDIPARTY'                         if ( $type eq '5' );
            $type = 'IPADD'                            if ( $type eq '7' );
            $type = 'RID'                              if ( $type eq '8' );
            $type = 'email'                            if ( $type eq '1' );
            $name = '<<undefined>>'                    if ( ( $type eq '0' ) && ( $name !~ /^/ ) );
            $type = 'othername'                        if ( $type eq '0' );
            $name = join( '.', unpack( 'W4', $name ) ) if ( $type eq 'IPADD' );
            $ret .= ' ' . join( ':', $type, $name );
        }
    }
    _trace("_ssleay_cert_get '$key'=$ret");
    return $ret;
}

sub _ssleay_socket {
    my $host   = shift;
    my $port   = shift;
    my $socket = shift;
    my $src    = '';
    my $err    = '';
    my $dum    = '';
    _traceset();
    _trace( "_ssleay_socket(" . ( $host || '' ) . "," . ( $port || '' ) . ") {" );
    goto FIN if ( defined $socket );
    $socket = undef;
    local $! = undef;

  TRY: {
        unless ( ($SSLinfo::starttls) || ($SSLinfo::proxyhost) ) {
            $src = '_check_host(' . ( $host || '' ) . ')';
            if ( not defined _check_host($host) ) { $err = $!; last; }
            $src = '_check_port(' . ( $port || '' ) . ')';
            if ( not defined _check_port($port) ) { $err = $!; last; }
            $src = 'socket()';
            socket( $socket, Socket::AF_INET, Socket::SOCK_STREAM, 0 ) or do { $err = $! }
              and last;
            $src = 'connect()';
            $dum = () = connect( $socket, Socket::sockaddr_in( $_SSLinfo{'port'}, $_SSLinfo{'addr'} ) ) or do { $err = $! }
              and last;
        }
        else {
            require SSLhello;
            SSLhello::version() if ( 1 < $trace );
            $src = 'SSLhello::openTcpSSLconnection()';
            ( $socket = SSLhello::openTcpSSLconnection( $host, $port ) ) or do { $err = $! }
              and last;
        }
        select($socket);
        local $| = 1;
        select(STDOUT);
        goto FIN;
    }
    push( @{ $_SSLinfo{'errors'} }, "_ssleay_socket() failed calling $src: $err" );
  FIN:
    _trace( "_ssleay_socket()\t= " . _trace_value_or_text($socket) . " }" );
    return $socket;
}

sub _ssleay_ctx_new {
    my $method = shift;
    my $ctx    = undef;
    my $src    = '';
    my $err    = '';
    my $old    = '';
    _traceset();
    _trace("_ssleay_ctx_new($method) {");
    $src = "Net::SSLeay::$method";
    _trace2(" _ssleay_ctx_new: $src");
    local $! = undef;

  TRY: {
        $_ = $method;
        /CTX_tlsv1_3_new/ && do {
            ( $ctx = Net::SSLeay::CTX_tlsv1_3_new() ) or last;

            if ( defined &Net::SSLeay::TLSv1_3_method ) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(TLSv1_3_method)';
                Net::SSLeay::CTX_set_ssl_version( $ctx, Net::SSLeay::TLSv1_3_method() ) or do { $err = $! }
                  and last;
                $src = '';
            }
            else {
                $src = 'Net::SSLeay::TLSv1_3_method()';
            }
        };
        /CTX_tlsv1_2_new/ && do {
            ( $ctx = Net::SSLeay::CTX_tlsv1_2_new() ) or last;
            if ( defined &Net::SSLeay::TLSv1_2_method ) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(TLSv1_2_method)';
                Net::SSLeay::CTX_set_ssl_version( $ctx, Net::SSLeay::TLSv1_2_method() ) or do { $err = $! }
                  and last;
                $src = '';
            }
            else {
                $src = 'Net::SSLeay::TLSv1_2_method()';
            }
        };
        /CTX_tlsv1_1_new/ && do {
            ( $ctx = Net::SSLeay::CTX_tlsv1_1_new() ) or last;
            if ( defined &Net::SSLeay::TLSv1_1_method ) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(TLSv1_1_method)';
                Net::SSLeay::CTX_set_ssl_version( $ctx, Net::SSLeay::TLSv1_1_method() ) or do { $err = $! }
                  and last;
                $src = '';
            }
            else {
                $src = 'Net::SSLeay::TLSv1_1_method()';
            }
        };
        /CTX_tlsv1_new/ && do {
            ( $ctx = Net::SSLeay::CTX_tlsv1_new() ) or last;
            if ( defined &Net::SSLeay::TLSv1_method ) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(TLSv1_method)';
                Net::SSLeay::CTX_set_ssl_version( $ctx, Net::SSLeay::TLSv1_method() ) or do { $err = $! }
                  and last;
                $src = '';
            }
            else {
                $src = 'Net::SSLeay::TLSv1_2_method()';
            }
        };
        /CTX_v23_new/ && do {
            ( $ctx = Net::SSLeay::CTX_v23_new() ) or last;
            if ( defined &Net::SSLeay::SSLv23_method ) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(SSLv23_method)';
                Net::SSLeay::CTX_set_ssl_version( $ctx, Net::SSLeay::SSLv23_method() ) or do { $err = $! }
                  and last;
                $src = '';
            }
            else {
                $src = 'Net::SSLeay::SSLv23_method()';
            }
        };
        /CTX_v3_new/ && do {
            ( $ctx = Net::SSLeay::CTX_v3_new() ) or last;
            if ( defined &Net::SSLeay::SSLv3_method ) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(SSLv3_method)';
                Net::SSLeay::CTX_set_ssl_version( $ctx, Net::SSLeay::SSLv3_method() ) or do { $err = $! }
                  and last;
                $src = '';
            }
            else {
                $src = 'Net::SSLeay::SSLv3_method()';
            }
        };
        /CTX_v2_new/ && do {
            ( $ctx = Net::SSLeay::CTX_v2_new() ) or last;
            if ( defined &Net::SSLeay::SSLv2_method ) {
                $src = 'Net::SSLeay::CTX_set_ssl_version(SSLv2_method)';
                Net::SSLeay::CTX_set_ssl_version( $ctx, Net::SSLeay::SSLv2_method() ) or do { $err = $! }
                  and last;
                $src = '';
            }
            else {
                $src = 'Net::SSLeay::SSLv2_method()';
            }
        };
        /CTX_dtlsv1_3_new/ && do {
        };
        /CTX_dtlsv1_2_new/ && do {
        };
        /CTX_dtlsv1_1_new/ && do {
        };
        /CTX_dtlsv1_new/ && do {
        };
        goto FIN if not $ctx;
        $_SSLinfo{'CTX_method'} = $method;

        if ( '' ne $src ) {
            push( @{ $_SSLinfo{'errors'} }, "_ssleay_ctx_new() WARNING '$src' not available, using system default for '$method'" );
        }
        my $options = &Net::SSLeay::OP_ALL;
        if ( 0 < $SSLinfo::no_compression ) {
            $options |= &Net::SSLeay::OP_NO_COMPRESSION;
        }
        $src = 'Net::SSLeay::CTX_set_options()';
        Net::SSLeay::CTX_set_options( $ctx, 0 );
        Net::SSLeay::CTX_set_options( $ctx, $options );
        $src = 'Net::SSLeay::CTX_set_timeout()';
        ( $old = Net::SSLeay::CTX_set_timeout( $ctx, $SSLinfo::timeout_sec ) ) or do { $err = $!; }
          and last;
        _trace( " CTX_get_session_cache_mode(CTX)= " . sprintf( '0x%08x', Net::SSLeay::CTX_get_session_cache_mode($ctx) ) );
        _trace( " CTX_get_timeout(CTX)= $old -> " . Net::SSLeay::CTX_get_timeout($ctx) );
        _trace( " CTX_get_options(CTX)= " . sprintf( '0x%08x', Net::SSLeay::CTX_get_options($ctx) ) );
        _traceSSLbitmasks( " CTX options", Net::SSLeay::CTX_get_options($ctx) );
        goto FIN;
    }

    push( @{ $_SSLinfo{'errors'} }, "_ssleay_ctx_new() failed calling $src: $err" );
  FIN:
    return $ctx;
}

sub _ssleay_ctx_ca {
    my $ctx    = shift;
    my $ssl    = undef;
    my $ret    = 0;
    my $src    = '';
    my $err    = '';
    my $cafile = '';
    my $capath = '';
    _traceset();
    _trace( "_ssleay_ctx_ca(" . _trace_value_or_text($ctx) . ") {" );
  TRY: {
        Net::SSLeay::CTX_set_verify( $ctx, &Net::SSLeay::VERIFY_NONE, \&_check_peer );
        $src    = 'Net::SSLeay::CTX_load_verify_locations()';
        $cafile = $SSLinfo::ca_file || '';
        if ( $cafile !~ m#^(?:[a-zA-Z0-9_,.\\/()-])*$# ) {
            $err = "invalid characters for " . '$SSLinfo::ca_file; not used';
            last;
        }
        $capath = $SSLinfo::ca_path || '';
        if ( $capath !~ m#^(?:[a-zA-Z0-9_,.\\/()-]*)$# ) {
            $err = "invalid characters for " . '$SSLinfo::ca_path; not used';
            last;
        }
        if ( ( $capath . $cafile ) ne '' ) {
            Net::SSLeay::CTX_load_verify_locations( $ctx, $cafile, $capath ) or do { $err = $! }
              and last;
        }
        $src = 'Net::SSLeay::CTX_set_verify_depth()';
        if ( defined $SSLinfo::ca_depth ) {
            if ( $SSLinfo::ca_depth !~ m/^[0-9]$/ ) {
                $err = "invalid value '$SSLinfo::ca_depth' for " . '$SSLinfo::ca_depth; not used';
                last;
            }
            Net::SSLeay::CTX_set_verify_depth( $ctx, $SSLinfo::ca_depth );
        }
        $ret = 1;
        goto FIN;
    }
    push( @{ $_SSLinfo{'errors'} }, "_ssleay_ctx_ca() failed calling $src: $err" );
  FIN:
    _trace("_ssleay_ctx_ca()\t= $ret }");
    return $ret;
}

sub _ssleay_ssl_new {
    my $ctx    = shift;
    my $host   = shift;
    my $socket = shift;
    my $cipher = shift;
    my $ssl    = undef;
    my $src    = '';
    my $err    = '';
    _traceset();
    _trace( "_ssleay_ssl_new(" . _trace_value_or_text($ctx) . ") {" );
  TRY: {
        $src = 'Net::SSLeay::new()';
        ( $ssl = Net::SSLeay::new($ctx) ) or do { $err = $! }
          and last;
        $src = 'Net::SSLeay::set_fd()';
        Net::SSLeay::set_fd( $ssl, fileno($socket) ) or do { $err = $! }
          and last;
        $src = "Net::SSLeay::set_cipher_list($cipher)";
        Net::SSLeay::set_cipher_list( $ssl, $cipher ) or do { $err = $! }
          and last;

        if ( 0 < $SSLinfo::use_SNI ) {
            my $sni = $SSLinfo::sni_name;
            _trace(" use SNI");
            if ( 1.45 <= $Net::SSLeay::VERSION ) {
                $src = 'Net::SSLeay::set_tlsext_host_name()';
                my $e = Net::SSLeay::set_tlsext_host_name( $ssl, $sni );
                if ( ( 0 == $e ) and ( $0 !~ /SSLinfo.pm$/ ) ) { $err = $!; last; }
            }
            else {
                $src = 'Net::SSLeay::ctrl()';
                Net::SSLeay::ctrl( $ssl, 55, 0, $sni ) or do { $err = $! }
                  and last;
            }
        }
        goto FIN;
    }
    push( @{ $_SSLinfo{'errors'} }, "_ssleay_ssl_new() failed calling $src: $err" );
  FIN:
    _trace( "_ssleay_ssl_new()\t= " . _trace_value_or_text($ssl) . " }" );
    return $ssl;
}

sub _ssleay_ssl_np {
    my $ctx         = shift;
    my $protos_alpn = shift;
    my $protos_npn  = shift;
    my @protos_alpn = split( /,/, $protos_alpn );
    my @protos_npn  = split( /,/, $protos_npn );
    _trace("_ssleay_ssl_np(ctx, $protos_alpn, $protos_npn)");
    my $src;
    my @err;

    if ( $protos_alpn !~ m/^\s*$/ ) {
        if ( exists &Net::SSLeay::CTX_set_alpn_protos ) {
            $src = 'Net::SSLeay::CTX_set_alpn_protos()';
            Net::SSLeay::CTX_set_alpn_protos( $ctx, [@protos_alpn] ) && do {
                push( @err, "_ssleay_ssl_np(),alpn failed calling $src: $!" );
            };
        }
    }
    if ( $protos_npn !~ m/^\s*$/ ) {
        if ( exists &Net::SSLeay::CTX_set_next_proto_select_cb ) {
            $src = 'Net::SSLeay::CTX_set_next_proto_select_cb()';
            Net::SSLeay::CTX_set_next_proto_select_cb( $ctx, @protos_npn ) && do {
                push( @err, "_ssleay_ssl_np(),npn  failed calling $src: $!" );
            };
        }
    }
    _trace("_ssleay_ssl_np()\t= $#err }");
    return @err;
}

sub _header_get {
    my $head     = shift;
    my $response = shift;
    my $value    = '';
    _trace2("__header_get('$head', <<response>>)");
    if ( $response =~ m/[\r\n]$head\s*:/i ) {
        $value = $response;
        $value =~ s/.*?[\r\n]$head\s*:\s*([^\r\n]*).*$/$1/ims;
    }
    return $value;
}

sub _openssl_MS {
    my $mode = shift;
    my $host = shift;
    my $port = shift;
    my $text = shift;
    my $data = '';
    return '' if ( $^O !~ m/MSWin32/ );

    _trace("_openssl_MS($mode, $host, $port)");
    if ( '' eq $_openssl ) {
        _trace("_openssl_MS($mode): WARNING: no openssl");
        return $SSLINFO{'OPENSSL'};
    }
    $host .= ':' if ( $port ne '' );
    $text = '""' if ( not defined $text );
    chomp $text;
    $text = '""' if ( $text !~ /[\r\n]/ );
    $text =~ s/\n/\n echo /g;
    $text = "(echo $text)";
    my $err = '';
    my $src = 'open';
    my $tmp = '.\\_yeast.bat';
    _trace2("_openssl_MS $mode $host$port: cmd.exe /D /C /S $tmp");
  TRY: {
        my $fh;
        open( $fh, '>', $tmp ) or do { $err = $! }
          and last;
        print $fh "$text | $_openssl $mode $host$port 2>&1";
        close($fh);
        $src = 'cmd.exe';
        ( $data = qx(cmd.exe /D /S /C $tmp) ) or do { $err = $! }
          and last;

        $src = 'unlink';
        unlink $tmp or do { $err = $! }
          and last;
        $data =~ s#^[^)]*[^\r\n]*.##s;
        $data =~ s#WARN.*?openssl.cnf[\r\n]##;
        _trace2("_openssl_MS $mode $host$port : $data #");
    }
    if ( '' ne $err ) {
        $text = "_openssl_MS() failed calling $src: $err";
        _trace2($text);
        push( @{ $_SSLinfo{'errors'} }, $text );
        return '';
    }
    return $data;
}

sub _openssl_x509 {
    my $pem  = shift;
    my $mode = shift;
    my $data = '';
    _trace2("_openssl_x509($mode,...) {");
    _setcmd();
    if ( $_openssl eq '' ) {
        _trace2("_openssl_x509($mode): WARNING: no openssl");
        return $SSLINFO{'OPENSSL'};
    }
    if ( '' eq $pem ) {
        _trace2("_openssl_x509($mode): WARNING: no PEM");
        return $SSLinfo::no_cert_txt;
    }

    if ( $mode =~ m/^-?(version|pubkey|signame|sigdump|aux|extensions)$/ ) {
        my $m = 'no_' . $mode;
        $mode =
'-text -certopt no_header,no_version,no_serial,no_signame,no_validity,no_subject,no_issuer,no_pubkey,no_sigdump,no_aux,no_extensions,ext_default,ext_dump';
        $mode =~ s/$m//;
        $mode =~ s/,,/,/;
    }
    if ( $mode =~ m/^-?ocsp/ ) {
        $mode = "x509 $mode";
    }
    else {
        $mode = "x509 -noout $mode";
    }
    if ( 2 < $trace ) {
        _trace("_openssl_x509: openssl $mode < '$pem'");
    }
    else {
        _trace("_openssl_x509: openssl $mode");
    }
    if ( $^O !~ m/MSWin32/ ) {
        $data = qx(echo '$pem' | $_openssl $mode 2>&1);

    }
    else {
        $data = _openssl_MS( $mode, '', '', $pem );
    }
    chomp $data;
    $data =~ s/\n?-----BEGIN.*$//s if ( $mode =~ m/ -ocsp/ );
    $data =~ s/\s*$//;
    $data =~ s/\s*Version:\s*//i if ( ( $mode =~ m/ -text / ) && ( $mode !~ m/version,/ ) );

    _trace2("_openssl_x509()\t= $data }");
    return $data;
}


sub s_client_check {
    return 1 if ( 0 < $_OpenSSL_opt{'done'} );
    _traceset();
    _trace("s_client_check()");
    _setcmd();
    if ( '' eq $_openssl ) {
        _trace("s_client_check(): WARNING: no openssl");
        return undef;
    }

    if ( $^O =~ m/MSWin32/ ) {
        $_OpenSSL_opt{'data'} = _openssl_MS( 's_client -help', '', '', '' );
    }
    else {
        $_OpenSSL_opt{'data'} = qx($_openssl s_client -help 2>&1);

    }

    foreach my $key ( sort keys %_OpenSSL_opt ) {
        next if ( $key !~ m/^-/ );
        $_OpenSSL_opt{$key} = grep { /^ *$key\s/ } split( "\n", $_OpenSSL_opt{'data'} );
    }
    $_OpenSSL_opt{'-npn'} = $_OpenSSL_opt{'-nextprotoneg'};
    $_OpenSSL_opt{'done'} = 1;
    _trace("s_client_check()\t= 1");
    return 1;
}

sub _OpenSSL_opt_get {
    my $key = shift;
    _traceset();
    if ( 0 <= $_OpenSSL_opt{'done'} ) {
        if ( not defined s_client_check() ) {
            _trace("_OpenSSL_opt_get('$key') undef");
            return $SSLINFO{'OPENSSL'};
        }
    }
    _trace( "_OpenSSL_opt_get('$key')\t= " . ( $_OpenSSL_opt{$key} || 0 ) );
    return ( grep { /^$key$/ } keys %_OpenSSL_opt ) ? $_OpenSSL_opt{$key} : '';
}

sub s_client_get_optionlist {
    return ( grep { /^-/ } keys %_OpenSSL_opt );
}

sub s_client_opt_get { return _OpenSSL_opt_get(shift); }


sub do_ssl_free {
    my ( $ctx, $ssl, $socket ) = @_;
    close($socket)              if ( defined $socket );
    Net::SSLeay::free($ssl)     if ( defined $ssl );
    Net::SSLeay::CTX_free($ctx) if ( defined $ctx );
    return;
}


sub do_ssl_new {
    my ( $host, $port, $sslversions, $cipher, $protos_alpn, $protos_npn, $socket ) = @_;
    my $ctx    = undef;
    my $ssl    = undef;
    my $method = undef;
    my $src;
    my $err      = '';
    my $tmp_sock = undef;

    my $dum = undef;
    $cipher      = '' if ( not defined $cipher );
    $protos_alpn = '' if ( not defined $protos_alpn );
    $protos_npn  = '' if ( not defined $protos_npn );
    _traceset();
    _trace( "do_ssl_new("
          . ( $host        || '' ) . ','
          . ( $port        || '' ) . ','
          . ( $sslversions || '' ) . ','
          . ( $cipher      || '' ) . ','
          . ( $protos_alpn || '' )
          . ',socket) {' );
    _SSLtemp_reset();

  TRY: {

        my @list = ssleay_methods();
        foreach my $ctx_new (@list) {
            next if ( $ctx_new !~ m/^CTX_/ );
            next if ( $ctx_new =~ m/CTX_new$/ );
            next if ( $ctx_new =~ m/_method$/ );
            next if ( $ctx_new =~ m/_options$/ );
            next if ( $ctx_new =~ m/_timeout$/ );
            $method = $ctx_new;
            _trace("do_ssl_new: $method ...");
            $src = $ctx_new;

            do_ssl_free( $ctx, $ssl, $tmp_sock );
            $ctx      = undef;
            $ssl      = undef;
            $tmp_sock = undef;

            ( $tmp_sock = _ssleay_socket( $host, $port, $tmp_sock ) ) or do { $src = '_ssleay_socket()' }
              and last TRY;

            ( $ctx = _ssleay_ctx_new($ctx_new) ) or do { $src = '_ssleay_ctx_new()' }
              and next;

            foreach my $_ssl ( sort keys %_SSLmap ) {
                next if ( $sslversions =~ m/^\s*$/ );
                next if ( grep { /^$_ssl$/ } split( / /, $sslversions ) );
                my $bitmask = _SSLbitmask_get($_ssl);
                if ( defined $bitmask ) {
                    _trace("do_ssl_new: OP_NO_$_ssl");
                    if ( 1.85 <= $Net::SSLeay::VERSION ) {
                        Net::SSLeay::CTX_set_options( $ctx, hex($bitmask) );
                    }
                    else {
                        Net::SSLeay::CTX_set_options( $ctx, $bitmask );
                    }
                }
            }

            ( $dum = _ssleay_ctx_ca($ctx) ) or do { $src = '_ssleay_ctx_ca()' }
              and next;

            my @err = _ssleay_ssl_np( $ctx, $protos_alpn, $protos_npn );
            if ( 0 < $#err ) {
                push( @{ $_SSLtemp{'errors'} }, @err );
            }

            ( $ssl = _ssleay_ssl_new( $ctx, $host, $tmp_sock, $cipher ) ) or do { $src = '_ssleay_ssl_new()' }
              and next;

            local $SIG{PIPE} = 'IGNORE';
            my $ret;
            $src = 'Net::SSLeay::connect() ';
            $ret = Net::SSLeay::connect($ssl);
            if ( 0 > $ret ) {
                $src .= " failed start with $ctx_new()";
                $err = $!;
                push( @{ $_SSLtemp{'errors'} }, "do_ssl_new() $src: $err" );
                next;
            }
            if ( $SSLinfo::ignore_handshake <= 0 ) {
                if ( 0 == $ret ) {
                    $src .= " failed handshake with $ctx_new()";
                    $err = $!;
                    push( @{ $_SSLtemp{'errors'} }, "do_ssl_new() $src: $err" );
                    next;
                }
            }
            $src = '';
            last;
        }
        if ( '' eq $src ) {
            _trace2( join( "\n" . $SSLINFO{'ERROR'} . ' ', '', @{ $_SSLtemp{'errors'} } ) ) if ( -1 < $#{ $_SSLtemp{'errors'} } );
            _trace2(" errors reseted.");
            @{ $_SSLtemp{'errors'} } = ();
            goto FIN;
        }
        else {
            push( @{ $_SSLtemp{'errors'} }, "do_ssl_new() connection failed in '$src': $err" );
            $src = " failed to connect";
            last;
        }
        _trace("do_ssl_new: $method");

    }

    close($tmp_sock) if ( defined $tmp_sock );
    push( @{ $_SSLtemp{'errors'} }, "do_ssl_new() failed calling $src: $err" );
    if ( 1 < $trace ) {
        Net::SSLeay::print_errs( $SSLINFO{'ERROR'} );
        printf( "%s%s\n", $SSLINFO{'ERROR'}, $_ ) foreach @{ $_SSLtemp{'errors'} };
    }
    _trace("do_ssl_new() failed }");
    return;

  FIN:
    _trace("do_ssl_new() done }");
    return wantarray ? ( $ssl, $ctx, $tmp_sock, $method ) : $ssl;
}


sub _X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT () { return 18; }
sub _FLAGS_ALLOW_SELFSIGNED ()                 { return 0x00000001; }

sub do_ssl_open($$$@) {
    my ( $host, $port, $sslversions, $cipher ) = @_;
    $cipher = '' if ( not defined $cipher );

    _traceset();
    _trace( "do_ssl_open(" . ( $host || '' ) . "," . ( $port || '' ) . "," . ( $sslversions || '' ) . "," . ( $cipher || '' ) . ") {" );
    goto FIN if ( defined $_SSLinfo{'ssl'} );

    $SSLinfo::target_url =~ s:^\s*$:/:;
    _vprint( "do_ssl_open " . ( $host || '' ) . ":" . ( $port || '' ) . $SSLinfo::target_url );
    if ( $cipher =~ m/^\s*$/ ) {
        $cipher = $_SSLinfo{'cipherlist'};
    }
    else {
        $_SSLinfo{'cipherlist'} = $cipher;
    }
    _trace("do_ssl_open cipherlist: $_SSLinfo{'cipherlist'}");
    my $ctx    = undef;
    my $ssl    = undef;
    my $socket = undef;
    my $method = undef;
    my $src;
    my $err = '';

    $src = 's_client_check';
    if ( 0 < $SSLinfo::use_openssl ) {
        if ( not defined s_client_check() ) {
            push( @{ $_SSLinfo{'errors'} }, "do_ssl_open() WARNING $src: undefined" );
        }
    }

    {
        no warnings;
        if ( defined $SSLinfo::next_protos ) {
            warn("$OText::STR{WARN} 090: SSLinfo::next_protos no longer supported, please use SSLinfo::protos_alpn instead");
        }
    }

  TRY: {

        Net::SSLeay::free($ssl)     if ( defined $ssl );
        Net::SSLeay::CTX_free($ctx) if ( defined $ctx );
        if ( 1 > $SSLinfo::socket_reuse ) {
            close($SSLinfo::socket) if ( defined $SSLinfo::socket );
            $SSLinfo::socket = undef;
        }

        $src = 'SSinfo::do_ssl_new()';
        ( $ssl, $ctx, $socket, $method ) = do_ssl_new( $host, $port, $sslversions, $cipher, $SSLinfo::protos_alpn, $SSLinfo::protos_npn, $SSLinfo::socket );
        if ( not defined $ssl ) { $err = 'undef $ssl'; last; }
        if ( not defined $ctx ) { $err = 'undef $ctx'; last; }
        $_SSLinfo{'ctx'}    = $ctx;
        $_SSLinfo{'ssl'}    = $ssl;
        $_SSLinfo{'method'} = $method;
        $SSLinfo::method    = $method;
        $SSLinfo::socket    = $socket;
        push( @{ $_SSLinfo{'errors'} }, @{ $_SSLtemp{'errors'} } );
        _trace("do_ssl_open: $SSLinfo::method");

        $src = 'Net::SSLeay::get_peer_certificate()';
        my $x509 = Net::SSLeay::get_peer_certificate($ssl);

        $_SSLinfo{'x509'} = $x509;
        $_SSLinfo{'_options'} .= sprintf( "0x%016x", Net::SSLeay::CTX_get_options($ctx) ) if $ctx;
        $_SSLinfo{'SSLversion'} = $_SSLhex{ Net::SSLeay::version($ssl) };
        $_SSLinfo{'session_protocol'}  = $_SSLinfo{'SSLversion'};
        $_SSLinfo{'session_starttime'} = Net::SSLeay::SESSION_get_time($ssl);
        $_SSLinfo{'session_timeout'}   = Net::SSLeay::SESSION_get_timeout($ssl);

        my $i = 0;
        my $c = '';
        push( @{ $_SSLinfo{'ciphers'} }, $c ) while ( $c = Net::SSLeay::get_cipher_list( $ssl, $i++ ) );
        $_SSLinfo{'selected'} = Net::SSLeay::get_cipher($ssl);

        $_SSLinfo{'certificate'} = Net::SSLeay::dump_peer_certificate($ssl);

        $_SSLinfo{'PEM'} = _ssleay_cert_get( 'PEM', $x509 );
        $_SSLinfo{'subject'}  = _ssleay_cert_get( 'subject',  $x509 );
        $_SSLinfo{'issuer'}   = _ssleay_cert_get( 'issuer',   $x509 );
        $_SSLinfo{'before'}   = _ssleay_cert_get( 'before',   $x509 );
        $_SSLinfo{'after'}    = _ssleay_cert_get( 'after',    $x509 );
        $_SSLinfo{'policies'} = _ssleay_cert_get( 'policies', $x509 );
        if ( 1.45 <= $Net::SSLeay::VERSION ) {
            $_SSLinfo{'version'} = _ssleay_cert_get( 'version', $x509 );
        }
        else {
            warn("$OText::STR{WARN} 651: Net::SSLeay >= 1.45 required for getting version");
        }
        if ( 1.33 <= $Net::SSLeay::VERSION ) {
            $_SSLinfo{'altname'} = _ssleay_cert_get( 'altname', $x509 );
        }
        else {
            warn("$OText::STR{WARN} 652: Net::SSLeay >= 1.33 required for getting subjectAltNames");
        }
        if ( 1.30 <= $Net::SSLeay::VERSION ) {
            $_SSLinfo{'cn'} = _ssleay_cert_get( 'cn', $x509 ) || "";
            $_SSLinfo{'cn'} =~ s{\0$}{};
        }
        else {
            warn("$OText::STR{WARN} 653: Net::SSLeay >= 1.30 required for getting commonName");
        }
        if ( 1.45 <= $Net::SSLeay::VERSION ) {
            $_SSLinfo{'fingerprint_md5'}  = _ssleay_cert_get( 'md5',  $x509 );
            $_SSLinfo{'fingerprint_sha1'} = _ssleay_cert_get( 'sha1', $x509 );
            $_SSLinfo{'fingerprint_sha2'} = _ssleay_cert_get( 'sha2', $x509 );
        }
        else {
            warn("$OText::STR{WARN} 654: Net::SSLeay >= 1.45 required for getting fingerprint_md5");
        }
        if ( 1.46 <= $Net::SSLeay::VERSION ) {

            $_SSLinfo{'error_verify'} = Net::SSLeay::X509_verify_cert_error_string( Net::SSLeay::get_verify_result($ssl) );
            $_SSLinfo{'error_depth'}  = _ssleay_cert_get( 'error_depth', $x509 );
            $_SSLinfo{'serial_hex'}   = _ssleay_cert_get( 'serial_hex',  $x509 );
            $_SSLinfo{'cert_type'}    = sprintf( "0x%x  <<experimental>>", _ssleay_cert_get( 'cert_type', $x509 ) || 0 );
            $_SSLinfo{'subject_hash'} = sprintf( "%x", _ssleay_cert_get( 'subject_hash', $x509 ) || 0 );
            $_SSLinfo{'issuer_hash'}  = sprintf( "%x", _ssleay_cert_get( 'issuer_hash', $x509 ) || 0 );
        }
        else {
            warn("$OText::STR{WARN} 655: Net::SSLeay >= 1.46 required for getting some certificate checks");
        }
        $_SSLinfo{'commonName'} = $_SSLinfo{'cn'};
        $_SSLinfo{'authority'}  = $_SSLinfo{'issuer'};
        $_SSLinfo{'owner'}      = $_SSLinfo{'subject'};

        if ( 0 < $SSLinfo::use_https ) {
            _trace("do_ssl_open HTTPS {");
            my $ua       = "User-Agent: Mozilla/5.0 (quark rv:52.0) Gecko/20100101 Firefox/52.0";
            my $response = '';
            my $request  = "GET $SSLinfo::target_url HTTP/1.1\r\n";
            $request .= "Host:$host\r\nConnection:close\r\n";
            $request .= "Accept-Encoding:gzip,deflate\r\n";
            $request .= "User-Agent:$SSLinfo::user_agent\r\n\r\n";
            $src = 'Net::SSLeay::write()';
            Net::SSLeay::write( $ssl, $request ) or { $err = $! } and last;
            $src = 'Net::SSLeay::ssl_read_all()';
            $response = Net::SSLeay::ssl_read_all($ssl) || "<<GET failed>>";
            _trace("do_ssl_open: request $host:$port");

            if ( 1 == $trace ) {
                _trace("do_ssl_open: request  #{<<use --trace=2 to print data>>#}");
                _trace("do_ssl_open: response #{\n$response #}") if ( $response =~ m/<<GET failed/ );
                _trace("do_ssl_open: response #{<<use --trace=2 to print data>>#}");
            }
            else {
                _trace2("do_ssl_open: request  #{\n$request");
                _trace2("do_ssl_open request #}");
                _trace2("do_ssl_open: response #{\n$response");
                _trace2("do_ssl_open response #}");
            }
            if ( $response =~ /handshake_failed/ ) {
                $response = "<<HTTP handshake failed>>";
            }
            if ( $response =~ /bad client magic byte string/ ) {

                $response = "<<Received bad client magic byte string>>";
            }
            $_SSLinfo{'https_body'} = $response;
            $_SSLinfo{'https_body'} =~ s/.*?\r\n\r\n(.*)/$1/ms;
            $_SSLinfo{'https_location'} = _header_get( 'Location', $response );
            $_SSLinfo{'https_status'} = $response;
            $_SSLinfo{'https_status'} =~ s/[\r\n].*$//ms;
            $_SSLinfo{'https_server'}    = _header_get( 'Server',             $response );
            $_SSLinfo{'https_refresh'}   = _header_get( 'Refresh',            $response );
            $_SSLinfo{'https_pins'}      = _header_get( 'Public-Key-Pins',    $response );
            $_SSLinfo{'https_protocols'} = _header_get( 'Alternate-Protocol', $response );
            $_SSLinfo{'https_svc'}       = _header_get( 'Alt-Svc',            $response );
            $_SSLinfo{'https_svc'} .= _header_get( 'X-Firefox-Spdy', $response );
            $_SSLinfo{'https_svc'} .= _header_get( 'Spdy',           $response );
            $_SSLinfo{'https_sts'}          = _header_get( 'Strict-Transport-Security', $response );
            $_SSLinfo{'https_content_enc'}  = _header_get( 'Content-Encoding',          $response );
            $_SSLinfo{'https_transfer_enc'} = _header_get( 'Transfer-Encoding',         $response );
            $_SSLinfo{'hsts_httpequiv'}     = $_SSLinfo{'https_body'};
            $_SSLinfo{'hsts_httpequiv'} =~ s/.*?(http-equiv=["']?Strict-Transport-Security[^>]*).*/$1/ims;
            $_SSLinfo{'hsts_httpequiv'} = '' if ( $_SSLinfo{'hsts_httpequiv'} eq $_SSLinfo{'https_body'} );
            $_SSLinfo{'hsts_maxage'}    = $_SSLinfo{'https_sts'};
            $_SSLinfo{'hsts_maxage'} =~ s/.*?max-age=([^;" ]*).*/$1/i;
            $_SSLinfo{'hsts_subdom'}  = 'includeSubDomains' if ( $_SSLinfo{'https_sts'} =~ m/includeSubDomains/i );
            $_SSLinfo{'hsts_preload'} = 'preload'           if ( $_SSLinfo{'https_sts'} =~ m/preload/i );
            _trace("do_ssl_open HTTPS }");
        }
        if ( 0 < $SSLinfo::use_http ) {
            _trace("do_ssl_open HTTP {");
            my %headers;
            my $response = '';
            my $request  = '';
            _trace("do_ssl_open ::use_http: $SSLinfo::use_http");
            $src = 'Net::SSLeay::get_http()';
            ( $response, $_SSLinfo{'http_status'}, %headers ) = Net::SSLeay::get_http(
                $host, 80,
                $SSLinfo::target_url,
                Net::SSLeay::make_headers(
                    'Host'       => $host,
                    'User-Agent' => $SSLinfo::user_agent,
                    'Connection' => 'close',
                )
            );
            my $headers = "";
            foreach my $h ( sort( keys %headers ) ) { $headers .= "$h: $headers{$h}\n"; }
            _trace("do_ssl_open: request $host:$port");

            if ( 1 == $trace ) {
                _trace("do_ssl_open: request  #{<<use --trace=2 to print data>>#}");
                _trace("do_ssl_open: response #{\n$response #}") if ( $response =~ m/<<GET failed/ );
                _trace("do_ssl_open: response #{<<use --trace=2 to print data>>#}");
            }
            else {
                _trace2("do_ssl_open: request  #{\n$request");
                _trace2("do_ssl_open request #}");
                _trace2("do_ssl_open: response #{\n$response");
                _trace2("do_ssl_open response #}");
            }

            if ( $_SSLinfo{'http_status'} =~ m:^HTTP/... ([1234][0-9][0-9]|500) : ) {
                $_SSLinfo{'http_location'} = $headers{ ( grep { /^Location$/i } keys %headers )[0]                  || '' };
                $_SSLinfo{'http_refresh'}  = $headers{ ( grep { /^Refresh$/i } keys %headers )[0]                   || '' };
                $_SSLinfo{'http_sts'}      = $headers{ ( grep { /^Strict-Transport-Security$/i } keys %headers )[0] || '' };
                $_SSLinfo{'http_svc'}      = $headers{ ( grep { /^Alt-Svc$/i } keys %headers )[0]                   || '' } || '';
                $_SSLinfo{'http_svc'} .= $headers{ ( grep { /^X-Firefox-Spdy$/i } keys %headers )[0] || '' } || '';
                $_SSLinfo{'http_protocols'} = $headers{ ( grep { /^Alternate-Protocol/i } keys %headers )[0] || '' };
            }
            else {

                push( @{ $_SSLinfo{'errors'} }, "do_ssl_open WARNING $src: " . $_SSLinfo{'http_status'} );
                if ( $_SSLinfo{'http_status'} =~ m:^HTTP/... (50[12345]) : ) {
                    push( @{ $_SSLinfo{'errors'} }, "do_ssl_open WARNING $src: check HTTP gateway" );
                }
                $response = '';
            }
            _trace("do_ssl_open HTTP }");
        }

        if ( 0 == $SSLinfo::use_openssl ) {
            _trace2("do_ssl_open without openssl");
            goto finished;
        }

        my $fingerprint = _openssl_x509( $_SSLinfo{'PEM'}, '-fingerprint' );
        chomp $fingerprint;
        $_SSLinfo{'fingerprint_text'} = $fingerprint;
        $_SSLinfo{'fingerprint'}      = $fingerprint;
        ( $_SSLinfo{'fingerprint_type'}, $_SSLinfo{'fingerprint_hash'} ) = split( /=/, $fingerprint );
        $_SSLinfo{'fingerprint_type'} = $SSLinfo::no_cert_txt if ( not defined $_SSLinfo{'fingerprint_type'} );
        $_SSLinfo{'fingerprint_hash'} = $SSLinfo::no_cert_txt if ( not defined $_SSLinfo{'fingerprint_hash'} );
        $_SSLinfo{'fingerprint_type'} =~ s/\s+.*$//;
        $_SSLinfo{'fingerprint_type'} =~ s/(^[^\s]*).*/$1/ if (m/^[^\s]*/);
        $_SSLinfo{'subject_hash'} = _openssl_x509( $_SSLinfo{'PEM'}, '-subject_hash' );
        $_SSLinfo{'issuer_hash'}  = _openssl_x509( $_SSLinfo{'PEM'}, '-issuer_hash' );
        $_SSLinfo{'version'}      = _openssl_x509( $_SSLinfo{'PEM'}, 'version' );
        $_SSLinfo{'text'}         = _openssl_x509( $_SSLinfo{'PEM'}, '-text' );
        $_SSLinfo{'modulus'}      = _openssl_x509( $_SSLinfo{'PEM'}, '-modulus' );
        $_SSLinfo{'email'}      = _openssl_x509( $_SSLinfo{'PEM'}, '-email' );
        $_SSLinfo{'trustout'}   = _openssl_x509( $_SSLinfo{'PEM'}, '-trustout' );
        $_SSLinfo{'ocsp_uri'}   = _openssl_x509( $_SSLinfo{'PEM'}, '-ocsp_uri' );
        $_SSLinfo{'ocspid'}     = _openssl_x509( $_SSLinfo{'PEM'}, '-ocspid' );
        $_SSLinfo{'aux'}        = _openssl_x509( $_SSLinfo{'PEM'}, 'aux' );
        $_SSLinfo{'pubkey'}     = _openssl_x509( $_SSLinfo{'PEM'}, 'pubkey' );
        $_SSLinfo{'extensions'} = _openssl_x509( $_SSLinfo{'PEM'}, 'extensions' );
        $_SSLinfo{'signame'}    = _openssl_x509( $_SSLinfo{'PEM'}, 'signame' );
        $_SSLinfo{'sigdump'}    = _openssl_x509( $_SSLinfo{'PEM'}, 'sigdump' );
        ( $_SSLinfo{'sigkey_value'}     = $_SSLinfo{'sigdump'} ) =~ s/.*?\n//ms;
        ( $_SSLinfo{'pubkey_algorithm'} = $_SSLinfo{'pubkey'} )  =~ s/^.*?Algorithm: ([^\r\n]*).*/$1/si;
        ( $_SSLinfo{'pubkey_value'}     = $_SSLinfo{'pubkey'} )  =~ s/^.*?Modulus ?([^\r\n]*)//si;
        $_SSLinfo{'pubkey_value'} =~ s/^.*?pub:([^\r\n]*)//si;
        $_SSLinfo{'pubkey_value'} =~ s/(Exponent|ASN1 OID).*//si;
        $_SSLinfo{'modulus_exponent'} = $_SSLinfo{'pubkey'};
        $_SSLinfo{'modulus_exponent'} =~ s/^.*?(?:Exponent|ASN1 OID): (.*)$/$1/si;
        $_SSLinfo{'modulus'}          =~ s/^[^=]*=//i;
        $_SSLinfo{'signame'}          =~ s/^[^:]*: //i;
        $_SSLinfo{'modulus_len'} = 4 * length( $_SSLinfo{'modulus'} );

        if ( $_SSLinfo{'sigkey_value'} ne $SSLinfo::no_cert_txt ) {
            $_SSLinfo{'sigkey_len'} = $_SSLinfo{'sigkey_value'};
            $_SSLinfo{'sigkey_len'} =~ s/[\s\n]//g;
            $_SSLinfo{'sigkey_len'} =~ s/[:]//g;
            $_SSLinfo{'sigkey_len'} = 4 * length( $_SSLinfo{'sigkey_len'} );
        }
        chomp $_SSLinfo{'fingerprint_hash'};
        chomp $_SSLinfo{'modulus'};
        chomp $_SSLinfo{'pubkey'};
        chomp $_SSLinfo{'signame'};

        $_SSLinfo{'s_client'} = do_openssl( 's_client', $host, $port, '' );
        my $eee = $_SSLinfo{'s_client'};
        if ( $eee =~ m/.*(?:\*\*ERROR)/ ) {
            $eee =~ s/.*(\*\*ERROR[^\n]*).*/$1/s;
            push( @{ $_SSLinfo{'errors'} }, "do_ssl_open WARNING openssl: $eee" );
        }
        else {
            $eee = '';
        }

        my %match_map = (
            'session_id'        => "Session-ID:",
            'session_id_ctx'    => "Session-ID-ctx:",
            'master_key'        => "Master-Key:",
            'master_secret'     => "Extended master secret:",
            'resumption_psk'    => "Resumption PSK:",
            'krb5'              => "Krb5 Principal:",
            'psk_identity'      => "PSK identity:",
            'psk_hint'          => "PSK identity hint:",
            'srp'               => "SRP username:",
            'compression'       => "Compression:",
            'expansion'         => "Expansion:",
            'alpn'              => "ALPN protocol:",
            'no_alpn'           => "No ALPN negotiated",
            'next_protocol'     => "Next protocol:",
            'next_protocols'    => "Protocols advertised by server:",
            'session_protocol'  => "Protocol\\s+:",
            'session_timeout'   => "Timeout\\s+:",
            'session_lifetime'  => "TLS session ticket lifetime hint:",
            'session_starttime' => "Start Time:",
            'dh_parameter' => "Server Temp Key:",
        );
        my $d    = '';
        my $data = $_SSLinfo{'text'};
        $d = $data;
        $d =~ s/.*?Serial Number:\s*(.*?)\n.*/$1/si;
        $_SSLinfo{'serial'} = $d;
        $d =~ s/\s.*$//;
        $_SSLinfo{'serial_int'} = $d;

        if ( $d =~ m/[0-9a-f]:/i ) {
            $d =~ s/://g;
            my $b = 8;
            if ( 8 < length($d) ) {

                if ( eval('use Config; $b = $Config{ivsize};') ) {
                }
                else {
                    $err = "use Config";
                    push( @{ $_SSLinfo{'errors'} }, "do_ssl_open Cfg failed calling $src: $err" );
                    $_SSLinfo{'serial_int'} = "<<$err failed>>";
                }
            }
            if (   ( $b < length($d) )
                || ( 16 < length($d) ) )
            {
                if ( eval { require Math::BigInt; } ) {
                    $_SSLinfo{'serial_int'} = Math::BigInt->from_hex($d);
                }
                else {
                    $err = "Math::BigInt->from_hex($d)";
                    push( @{ $_SSLinfo{'errors'} }, "do_ssl_open Big failed calling $src: $err" );
                    $_SSLinfo{'serial_int'} = "<<$err failed>>";
                }
            }
            else {
                $_SSLinfo{'serial_int'} = hex($d);
            }
        }

        $data = $_SSLinfo{'s_client'};
        foreach my $key ( sort keys %match_map ) {
            my $regex = $match_map{$key};
            $d = $data;
            $d =~ s/.*?$regex[ \t]*([^\n\r]*)\n.*/$1/si;
            _trace2("do_ssl_open: match key:   $key\t= $regex");
            if ( $data =~ m/$regex/ ) {
                $_SSLinfo{$key} = $d;
                $_SSLinfo{$key} = $regex if ( $key eq 'no_alpn' );
                _trace2("do_ssl_open: match value: $key\t= $_SSLinfo{$key}");
            }
        }
        my $key = 'session_starttime';
        $_SSLinfo{'session_startdate'} = scalar localtime( $_SSLinfo{$key} );

        $d = $data;
        $d =~ s/.*?OCSP response:\s*([a-zA-Z0-9,. -]+)[\n\r].*/$1/si;
        if ( $d =~ m/^\s*$/ ) {
            $d = $data;
            $d =~ s/.*?OCSP response:\s*[\n\r]+(.*?)[\n\r][\n\r].*/$1/si;
            $d =~ s/^[\n\r]*//;
            if ( $d =~ m/OCSP Response Status:\s*([^\n\r]+)[\n\r]/i ) {
                $_SSLinfo{'ocsp_response_status'} = $1;
            }
            if ( $d =~ m/Cert Status:\s*([^\n\r]+)[\n\r]/i ) {
                $_SSLinfo{'ocsp_cert_status'} = $1;
            }
            if ( $d =~ m/This Update:\s*([^\n\r]+)[\n\r]/i ) {
                $_SSLinfo{'ocsp_this_update'} = $1;
            }
            if ( $d =~ m/Next Update:\s*([^\n\r]+)[\n\r]/i ) {
                $_SSLinfo{'ocsp_next_update'} = $1;
            }
            $_SSLinfo{'ocsp_response'} =
                "Response Status: "
              . $_SSLinfo{'ocsp_response_status'}
              . "; Cert Status: "
              . $_SSLinfo{'ocsp_cert_status'}
              . "; This Update: "
              . $_SSLinfo{'ocsp_this_update'}
              . "; Next Update: "
              . $_SSLinfo{'ocsp_next_update'};
        }
        else {
            $_SSLinfo{'ocsp_response'} = $d;
        }
        $_SSLinfo{'ocsp_response_data'} = $d;

        $d = $data;
        $d =~ s/.*?Server public key is *([^\n\r]*)[\n\r].*/$1/si;
        $_SSLinfo{'public_key_len'} = $d if ( $data =~ m/Server public key is / );

        $d = $data;
        $d =~ s/.*?TLS session ticket:\s*[\n\r]+(.*?)\n\n.*/$1_/si;
        if ( $data =~ m/TLS session ticket:/ ) {
            $d =~ s/\s*[0-9a-f]{4}\s*-\s*/_/gi;
            $d =~ s/^_//g;
            $d =~ s/   .{16}//g;
            $d =~ s/[^0-9a-f]//gi;
            $_SSLinfo{'session_ticket'} = $d;
        }

        $d = $data;
        $d =~ s/.*?((?:Secure\s*)?Renegotiation[^\n]*)\n.*/$1/si;
        $_SSLinfo{'renegotiation'} = $d;

        $d = $data;
        my $cnt = () = $d =~ m/(New|Reused),/g;
        if ( $cnt < 3 ) {
            _trace2("do_ssl_open: slow target server; resumption not detected; try to increase \$SSLinfo::timeout_sec");
        }
        else {
            $cnt = () = $d =~ m/New,/g;
            _trace2("do_ssl_open: checking resumption: found $cnt `New' ");
            if ( $cnt > 2 ) {
                $cnt = () = $d =~ m/Reused,/g;
                _trace2("do_ssl_open: checking resumption: found $cnt `Reused' ");
                $_SSLinfo{'resumption'} = 'no';
            }
            else {
                $d =~ s/.*?(Reused,[^\n]*).*/$1/si;
                $_SSLinfo{'resumption'} = $d if ( $d =~ m/Reused,/ );
            }
        }

        $d = $data;
        $d =~ s/.*?Verify (?:error|return code):\s*((?:num=)?[\d]*[^\n]*).*/$1/si;
        $_SSLinfo{'verify'} = $d;

        $d =~ s/.*?(self signed.*)/$1/si;
        $_SSLinfo{'selfsigned'} = $d;

        $d = $data;
        $d =~ s/.*?Certificate chain[\r\n]+(.*?)[\r\n]+---[\r\n]+.*/$1/si;
        $_SSLinfo{'chain'} = $d;

        $d = $data;
        $d =~ s/.*?(depth=-?[0-9]+.*?)[\r\n]+---[\r\n]+.*/$1/si;
        $_SSLinfo{'chain_verify'} = $d;

        foreach my $line ( split( /[\r\n]+/, $data ) ) {
            next if ( $line !~ m/TLS server extension/i );
            $d = $line;
            $d =~ s/TLS server extension\s*"([^"]*)"/$1/i;
            my $rex = $d;
            $rex =~ s#([(/*)])#\\$1#g;
            next if ( ( grep { /$rex/ } split( /\n/, $_SSLinfo{'tlsextensions'} ) ) > 0 );
            $_SSLinfo{'tlsextdebug'}   .= "\n" . $line;
            $_SSLinfo{'tlsextensions'} .= "\n" . $d;
            $_SSLinfo{'heartbeat'} = $d if ( $d =~ m/heartbeat/ );
            _trace("do_ssl_open: -tlsextdebug  $d") if ( $d =~ m/session ticket/ );
            _trace("do_ssl_open: -tlsextdebug  $d") if ( $d =~ m/renegotiation info/ );
        }
        $_SSLinfo{'tlsextensions'} =~ s/\([^)]*\),?\s+//g;
        $_SSLinfo{'tlsextensions'} =~ s/\s+len=\d+//g;

        _trace1("do_ssl_open <<use --trace=2 to print data collected from openssl>>");
        _trace2( SSLinfo::datadump("do_ssl_open") );
        goto finished;
    }

    push( @{ $_SSLinfo{'errors'} }, "do_ssl_open TRY failed calling $src: $err" );
    if ( 1 < $trace ) {
        Net::SSLeay::print_errs( $SSLINFO{'ERROR'} );
        printf( "%s%s\n", $SSLINFO{'ERROR'}, $_ ) foreach @{ $_SSLtemp{'errors'} };
    }
    _trace("do_ssl_open ()failed }");
    return;

  finished:
    _SSLinfo_print();
  FIN:
    _trace("do_ssl_open() done }");
    return wantarray ? ( $_SSLinfo{'ssl'}, $_SSLinfo{'ctx'} ) : $_SSLinfo{'ssl'};
}


sub do_ssl_close($$) {
    my ( $host, $port ) = @_;
    _trace("do_ssl_close($host,$port)");
    do_ssl_free( $_SSLinfo{'ctx'}, $_SSLinfo{'ssl'}, $SSLinfo::socket );
    _SSLinfo_reset();
    $SSLinfo::socket = undef;
    $SSLinfo::method = '';
    return;
}


sub do_openssl($$$$) {
    my $mode   = shift;
    my $host   = shift;
    my $port   = shift || '';
    my $pipe   = shift || '';
    my $data   = '';
    my $capath = $SSLinfo::ca_path || '';
    my $cafile = $SSLinfo::ca_file || '';
    _trace("do_openssl($mode,$host,$port...).");
    _setcmd();
    _vprint("do_openssl $mode");

    if ( '' eq $_openssl ) {
        _trace("do_openssl($mode): WARNING: no openssl");
        return $SSLINFO{'OPENSSL'};
    }
    if ( $mode =~ m/^-?s_client$/ ) {
        if ( $SSLinfo::file_sclient !~ m/^\s*$/ ) {
            if ( open( my $fh, '<:encoding(UTF-8)', $SSLinfo::file_sclient ) ) {
                undef $/;
                $data = <$fh>;
                close($fh);
                return $data;
            }
            _trace("do_openssl($mode): WARNING: cannot open $SSLinfo::file_sclient");
            return $SSLINFO{'OPENSSL'};
        }
        if ( 0 == $SSLinfo::use_sclient ) {
            _trace2("do_openssl($mode): WARNING: no openssl s_client");
            return $SSLINFO{'OPENSSL'};
        }
        $mode = 's_client' . $SSLinfo::sclient_opt;
        $mode .= ' -CApath ' . $capath if ( '' ne $capath );
        $mode .= ' -CAfile ' . $cafile if ( '' ne $cafile );
        $mode .= ' -reconnect'   if ( 1 == $SSLinfo::use_reconnect );
        $mode .= ' -tlsextdebug' if ( 1 == $SSLinfo::use_extdebug );
        $mode .= ' -status';
    }
    if (   ( $mode =~ m/^-?s_client$/ )
        || ( $mode =~ m/^-?s_client.*?-cipher/ ) )
    {
        $mode .= ' -alpn ' . $SSLinfo::protos_alpn if ( 1 == $SSLinfo::use_alpn );
        if ( $mode !~ m/-tls1_3/ ) {
            $mode .= ' -nextprotoneg ' . $SSLinfo::protos_npn if ( 1 == $SSLinfo::use_npn );
        }
    }
    if ( $mode =~ m/^-?s_client/ ) {
        $mode .= ' -connect' if ( $mode !~ m/-connect/ );
    }
    $host = $port = '' if ( $mode =~ m/^-?(ciphers)/ );
    _trace("do_openssl($mode): echo '' | $_timeout $_openssl $mode $host:$port 2>&1");
    _vprint2("$_timeout $_openssl $mode $host:$port");
    if ( $^O !~ m/MSWin32/ ) {
        $host .= ':'              if ( $port ne '' );
        $pipe = 'HEAD / HTTP/1.1' if ( $pipe =~ m/^$/ );
        $pipe .= "\r\nUser-Agent:$SSLinfo::user_agent\r\n\r";
        $data = qx((echo "$pipe"; sleep 1 ) | $_timeout $_openssl $mode $host$port 2>&1);

        if ( $data =~ m/(\nusage:|unknown option)/s ) {
            my $u1 = $data;
            $u1 =~ s/.*?(unknown option[^\r\n]*).*/$1/s;
            my $u2 = $data;
            $u2 =~ s/.*?\n(usage:[^\r\n]*).*/$1/s;
            $data = "**ERROR: $u1\n**ERROR: $u2\n";
            _trace("do_openssl($mode): WARNING: openssl does not support -nextprotoneg option");
            push( @{ $_SSLinfo{'errors'} }, "do_openssl($mode) failed: $data" );
            $mode = 's_client';
            $mode .= ' -CApath ' . $capath if ( '' ne $capath );
            $mode .= ' -CAfile ' . $cafile if ( '' ne $cafile );
            $mode .= ' -reconnect'         if ( 1 == $SSLinfo::use_reconnect );
            $mode .= ' -connect';
            $data .= qx((echo "$pipe"; sleep 1 ) | $_timeout $_openssl $mode $host$port 2>&1);
        }
    }
    else {
        $data = _openssl_MS( $mode, $host, $port, '' );
        if ( $data =~ m/(\nusage:|unknown option)/s ) {
            _trace("do_openssl($mode): WARNING: openssl does not support -nextprotoneg option");
            $data = _openssl_MS( $mode, $host, $port, '' );
        }
    }
    if ( $mode =~ m/^-?(ciphers)/ ) {
        if ( $data =~ m/^\s*(?:Error|openssl)(?: |:)/i ) {
            push( @{ $_SSLinfo{'errors'} }, "do_openssl($mode) failed: $data" );
            $data = '';
        }
    }
    chomp $data;
    $data =~ s/\s*$//;
    return $data;
}


sub set_cipher_list {
    my $ssl    = shift;
    my $cipher = shift;
    Net::SSLeay::set_cipher_list( $ssl, $cipher ) or return $SSLINFO{'ME'} . '::set_cipher_list(' . $cipher . ')';
    $_SSLinfo{'cipherlist'} = $cipher;
    return '';
}


sub cipher_list {
    my $pattern = shift || $_SSLinfo{'cipherlist'};
    my ( $ctx, $ssl, $cipher );
    my $priority = 0;
    my @list;
    _trace("cipher_list($pattern)");
  TRY: {

        ( $ctx = Net::SSLeay::CTX_new() ) or last;
        ( $ssl = Net::SSLeay::new($ctx) ) or last;
        Net::SSLeay::set_cipher_list( $ssl, $pattern ) or last;
        push( @list, $cipher ) while ( $cipher = Net::SSLeay::get_cipher_list( $ssl, $priority++ ) );
    }
    Net::SSLeay::free($ssl)     if ( defined $ssl );
    Net::SSLeay::CTX_free($ctx) if ( defined $ctx );
    return (wantarray) ? @list : join( ' ', @list );
}

sub cipher_openssl {
    my $pattern = shift || $_SSLinfo{'cipherlist'};
    my $list;
    _trace("cipher_openssl($pattern)");
    _setcmd();
    _trace2("cipher_openssl: openssl ciphers $pattern");
    $list = do_openssl( "ciphers $pattern", '', '', '' );
    chomp $list;
    return (wantarray) ? split( /[:\s]+/, $list ) : $list;
}

sub cipher_local {
    warn("$OText::STR{WARN} 451: function obsolete, please use cipher_openssl()");
    return cipher_openssl(@_);
}

sub ciphers {
    return cipher_list(@_) if ( $SSLinfo::use_openssl == 0 );
    return cipher_openssl(@_);
}



sub errors   { return _SSLinfo_get( 'errors',   $_[0], $_[1] ); }
sub s_client { return _SSLinfo_get( 's_client', $_[0], $_[1] ); }
sub options  { return _SSLinfo_get( '_options', $_[0], $_[1] ); }
sub PEM      { return _SSLinfo_get( 'PEM',      $_[0], $_[1] ); }
sub pem      { return _SSLinfo_get( 'PEM',      $_[0], $_[1] ); }
sub text     { return _SSLinfo_get( 'text',     $_[0], $_[1] ); }
sub before   { return _SSLinfo_get( 'before',   $_[0], $_[1] ); }
sub after    { return _SSLinfo_get( 'after',    $_[0], $_[1] ); }
sub dates    { return _SSLinfo_get( 'dates',    $_[0], $_[1] ); }
sub issuer   { return _SSLinfo_get( 'issuer',   $_[0], $_[1] ); }
sub subject  { return _SSLinfo_get( 'subject',  $_[0], $_[1] ); }
sub selected               { return _SSLinfo_get( 'selected',             $_[0], $_[1] ); }
sub cn                     { return _SSLinfo_get( 'cn',                   $_[0], $_[1] ); }
sub commonname             { return _SSLinfo_get( 'cn',                   $_[0], $_[1] ); }
sub altname                { return _SSLinfo_get( 'altname',              $_[0], $_[1] ); }
sub subjectaltnames        { return _SSLinfo_get( 'altname',              $_[0], $_[1] ); }
sub authority              { return _SSLinfo_get( 'authority',            $_[0], $_[1] ); }
sub owner                  { return _SSLinfo_get( 'owner',                $_[0], $_[1] ); }
sub certificate            { return _SSLinfo_get( 'certificate',          $_[0], $_[1] ); }
sub SSLversion             { return _SSLinfo_get( 'SSLversion',           $_[0], $_[1] ); }
sub version                { return _SSLinfo_get( 'version',              $_[0], $_[1] ); }
sub keysize                { return _SSLinfo_get( 'keysize',              $_[0], $_[1] ); }
sub keyusage               { return _SSLinfo_get( 'keyusage',             $_[0], $_[1] ); }
sub email                  { return _SSLinfo_get( 'email',                $_[0], $_[1] ); }
sub modulus                { return _SSLinfo_get( 'modulus',              $_[0], $_[1] ); }
sub serial_hex             { return _SSLinfo_get( 'serial_hex',           $_[0], $_[1] ); }
sub serial_int             { return _SSLinfo_get( 'serial_int',           $_[0], $_[1] ); }
sub serial                 { return _SSLinfo_get( 'serial',               $_[0], $_[1] ); }
sub aux                    { return _SSLinfo_get( 'aux',                  $_[0], $_[1] ); }
sub extensions             { return _SSLinfo_get( 'extensions',           $_[0], $_[1] ); }
sub tlsextdebug            { return _SSLinfo_get( 'tlsextdebug',          $_[0], $_[1] ); }
sub tlsextensions          { return _SSLinfo_get( 'tlsextensions',        $_[0], $_[1] ); }
sub heartbeat              { return _SSLinfo_get( 'heartbeat',            $_[0], $_[1] ); }
sub trustout               { return _SSLinfo_get( 'trustout',             $_[0], $_[1] ); }
sub ocsp_uri               { return _SSLinfo_get( 'ocsp_uri',             $_[0], $_[1] ); }
sub ocspid                 { return _SSLinfo_get( 'ocspid',               $_[0], $_[1] ); }
sub ocsp_response          { return _SSLinfo_get( 'ocsp_response',        $_[0], $_[1] ); }
sub ocsp_response_data     { return _SSLinfo_get( 'ocsp_response_data',   $_[0], $_[1] ); }
sub ocsp_response_status   { return _SSLinfo_get( 'ocsp_response_status', $_[0], $_[1] ); }
sub ocsp_cert_status       { return _SSLinfo_get( 'ocsp_cert_status',     $_[0], $_[1] ); }
sub ocsp_next_update       { return _SSLinfo_get( 'ocsp_next_update',     $_[0], $_[1] ); }
sub ocsp_this_update       { return _SSLinfo_get( 'ocsp_this_update',     $_[0], $_[1] ); }
sub pubkey                 { return _SSLinfo_get( 'pubkey',               $_[0], $_[1] ); }
sub signame                { return _SSLinfo_get( 'signame',              $_[0], $_[1] ); }
sub sigdump                { return _SSLinfo_get( 'sigdump',              $_[0], $_[1] ); }
sub sigkey_value           { return _SSLinfo_get( 'sigkey_value',         $_[0], $_[1] ); }
sub sigkey_len             { return _SSLinfo_get( 'sigkey_len',           $_[0], $_[1] ); }
sub subject_hash           { return _SSLinfo_get( 'subject_hash',         $_[0], $_[1] ); }
sub issuer_hash            { return _SSLinfo_get( 'issuer_hash',          $_[0], $_[1] ); }
sub verify                 { return _SSLinfo_get( 'verify',               $_[0], $_[1] ); }
sub error_verify           { return _SSLinfo_get( 'error_verify',         $_[0], $_[1] ); }
sub error_depth            { return _SSLinfo_get( 'error_depth',          $_[0], $_[1] ); }
sub chain                  { return _SSLinfo_get( 'chain',                $_[0], $_[1] ); }
sub chain_verify           { return _SSLinfo_get( 'chain_verify',         $_[0], $_[1] ); }
sub compression            { return _SSLinfo_get( 'compression',          $_[0], $_[1] ); }
sub expansion              { return _SSLinfo_get( 'expansion',            $_[0], $_[1] ); }
sub next_protocols         { return _SSLinfo_get( 'next_protocols',       $_[0], $_[1] ); }
sub protocols              { return _SSLinfo_get( 'next_protocols',       $_[0], $_[1] ); }
sub alpn                   { return _SSLinfo_get( 'alpn',                 $_[0], $_[1] ); }
sub no_alpn                { return _SSLinfo_get( 'no_alpn',              $_[0], $_[1] ); }
sub next_protocol          { return _SSLinfo_get( 'next_protocol',        $_[0], $_[1] ); }
sub krb5                   { return _SSLinfo_get( 'krb5',                 $_[0], $_[1] ); }
sub psk_hint               { return _SSLinfo_get( 'psk_hint',             $_[0], $_[1] ); }
sub psk_identity           { return _SSLinfo_get( 'psk_identity',         $_[0], $_[1] ); }
sub srp                    { return _SSLinfo_get( 'srp',                  $_[0], $_[1] ); }
sub master_key             { return _SSLinfo_get( 'master_key',           $_[0], $_[1] ); }
sub master_secret          { return _SSLinfo_get( 'master_secret',        $_[0], $_[1] ); }
sub extended_master_secret { return _SSLinfo_get( 'master_secret',        $_[0], $_[1] ); }
sub resumption_psk         { return _SSLinfo_get( 'resumption_psk',       $_[0], $_[1] ); }
sub public_key_len         { return _SSLinfo_get( 'public_key_len',       $_[0], $_[1] ); }
sub session_id             { return _SSLinfo_get( 'session_id',           $_[0], $_[1] ); }
sub session_id_ctx         { return _SSLinfo_get( 'session_id_ctx',       $_[0], $_[1] ); }
sub session_startdate      { return _SSLinfo_get( 'session_startdate',    $_[0], $_[1] ); }
sub session_starttime      { return _SSLinfo_get( 'session_starttime',    $_[0], $_[1] ); }
sub session_lifetime       { return _SSLinfo_get( 'session_lifetime',     $_[0], $_[1] ); }
sub session_ticket_hint    { return _SSLinfo_get( 'session_lifetime',     $_[0], $_[1] ); }
sub session_ticket         { return _SSLinfo_get( 'session_ticket',       $_[0], $_[1] ); }
sub session_timeout        { return _SSLinfo_get( 'session_timeout',      $_[0], $_[1] ); }
sub session_protocol       { return _SSLinfo_get( 'session_protocol',     $_[0], $_[1] ); }
sub fingerprint_hash       { return _SSLinfo_get( 'fingerprint_hash',     $_[0], $_[1] ); }
sub fingerprint_text       { return _SSLinfo_get( 'fingerprint_text',     $_[0], $_[1] ); }
sub fingerprint_type       { return _SSLinfo_get( 'fingerprint_type',     $_[0], $_[1] ); }
sub fingerprint_sha2       { return _SSLinfo_get( 'fingerprint_sha2',     $_[0], $_[1] ); }
sub fingerprint_sha1       { return _SSLinfo_get( 'fingerprint_sha1',     $_[0], $_[1] ); }
sub fingerprint_md5        { return _SSLinfo_get( 'fingerprint_md5',      $_[0], $_[1] ); }
sub fingerprint            { return _SSLinfo_get( 'fingerprint',          $_[0], $_[1] ); }
sub cert_type              { return _SSLinfo_get( 'cert_type',            $_[0], $_[1] ); }
sub modulus_len            { return _SSLinfo_get( 'modulus_len',          $_[0], $_[1] ); }
sub modulus_exponent       { return _SSLinfo_get( 'modulus_exponent',     $_[0], $_[1] ); }
sub pubkey_algorithm       { return _SSLinfo_get( 'pubkey_algorithm',     $_[0], $_[1] ); }
sub pubkey_value           { return _SSLinfo_get( 'pubkey_value',         $_[0], $_[1] ); }
sub renegotiation          { return _SSLinfo_get( 'renegotiation',        $_[0], $_[1] ); }
sub resumption             { return _SSLinfo_get( 'resumption',           $_[0], $_[1] ); }
sub dh_parameter           { return _SSLinfo_get( 'dh_parameter',         $_[0], $_[1] ); }
sub selfsigned             { return _SSLinfo_get( 'selfsigned',           $_[0], $_[1] ); }
sub https_protocols        { return _SSLinfo_get( 'https_protocols',      $_[0], $_[1] ); }
sub https_body             { return _SSLinfo_get( 'https_body',           $_[0], $_[1] ); }
sub https_svc              { return _SSLinfo_get( 'https_svc',            $_[0], $_[1] ); }
sub https_status           { return _SSLinfo_get( 'https_status',         $_[0], $_[1] ); }
sub https_server           { return _SSLinfo_get( 'https_server',         $_[0], $_[1] ); }
sub https_alerts           { return _SSLinfo_get( 'https_alerts',         $_[0], $_[1] ); }
sub https_location         { return _SSLinfo_get( 'https_location',       $_[0], $_[1] ); }
sub https_refresh          { return _SSLinfo_get( 'https_refresh',        $_[0], $_[1] ); }
sub https_pins             { return _SSLinfo_get( 'https_pins',           $_[0], $_[1] ); }
sub https_content_enc      { return _SSLinfo_get( 'https_content_enc',    $_[0], $_[1] ); }
sub https_transfer_enc     { return _SSLinfo_get( 'https_transfer_enc',   $_[0], $_[1] ); }
sub http_protocols         { return _SSLinfo_get( 'http_protocols',       $_[0], $_[1] ); }
sub http_svc               { return _SSLinfo_get( 'http_svc',             $_[0], $_[1] ); }
sub http_status            { return _SSLinfo_get( 'http_status',          $_[0], $_[1] ); }
sub http_location          { return _SSLinfo_get( 'http_location',        $_[0], $_[1] ); }
sub http_refresh           { return _SSLinfo_get( 'http_refresh',         $_[0], $_[1] ); }
sub http_sts               { return _SSLinfo_get( 'http_sts',             $_[0], $_[1] ); }
sub https_sts              { return _SSLinfo_get( 'https_sts',            $_[0], $_[1] ); }
sub hsts_httpequiv         { return _SSLinfo_get( 'hsts_httpequiv',       $_[0], $_[1] ); }
sub hsts_maxage            { return _SSLinfo_get( 'hsts_maxage',          $_[0], $_[1] ); }
sub hsts_subdom            { return _SSLinfo_get( 'hsts_subdom',          $_[0], $_[1] ); }
sub hsts_preload           { return _SSLinfo_get( 'hsts_preload',         $_[0], $_[1] ); }
sub CTX_method             { return _SSLinfo_get( 'CTX_method',           $_[0], $_[1] ); }


sub verify_hostname {
    my ( $host, $port ) = @_;
    return                       if ( not defined do_ssl_open( $host, $port, '' ) );
    return $SSLinfo::no_cert_txt if ( 0 != $SSLinfo::no_cert );
    my $cname = $_SSLinfo{'cn'};
    my $match = '';
    if ( 1 == $SSLinfo::ignore_case ) {
        $host  = lc($host);
        $cname = lc($cname);
    }
    $match = ( $host eq $cname ) ? 'matches' : 'does not match';
    return sprintf( "Given hostname '%s' %s CN '%s' in certificate", $host, $match, $cname );
}


sub verify_altname {
    my ( $host, $port ) = @_;
    return                       if ( not defined do_ssl_open( $host, $port, '' ) );
    return $SSLinfo::no_cert_txt if ( 0 != $SSLinfo::no_cert );
    _trace("verify_altname($host)");
    my $match = 'does not match';
    my $cname = $_SSLinfo{'altname'};
    return "No alternate name defined in certificate" if ( '' eq $cname );
    _trace("verify_altname: $cname");

    foreach my $alt ( split( / /, $cname ) ) {
        next if ( $alt =~ m/^\s*$/ );
        my ( $type, $name ) = split( /:/, $alt );
        push( @{ $_SSLinfo{'errors'} }, "verify_altname() $type not supported in SNA" ) if ( $type !~ m/DNS/i );
        my $rex = $name;
        if ( 1 == $SSLinfo::ignore_case ) {
            $host = lc($host);
            $rex  = lc($rex);
        }
        $rex =~ s/[.]/\\./g;
        $rex =~ s/([({[])/\\$1/g;
        if ( $name =~ m/[*]/ ) {
            $rex =~ s/(\*)/[^.]*/;
        }
        _trace("verify_altname: $host =~ $rex ");
        if ( $host =~ /^$rex$/ ) {
            $match = 'matches';
            $cname = $alt;
            $cname =~ s/^[a-zA-Z0-9]+://;
            last;
        }
    }
    _trace("verify_altname() done.");
    return sprintf( "Given hostname '%s' %s alternate name '%s' in certificate", $host, $match, $cname );
}

sub verify_alias { return verify_altname( $_[0], $_[1] ); }

sub error {
}

sub _main {
    my @argv = @_;
    push( @argv, "--help" ) if ( 0 > $#argv );
    my %usage = (
        '# commands to show internal data' => {
            'sclient'      => "show available options for 'openssl s_client'",
            'ssleay'       => 'show information about Net::SSLeay capabilities',
            'sslmap'       => 'show constants for SSL protocols',
            'openssl'      => 'show information about openssl capabilities',
            'methods'      => 'show available methods in Net::SSLeay',
            'unknown-host' => 'show empty data structure',
            'your.tld'     => 'show data from your.tld',
        },
        "## some commands can also be used as '+test-CMD'" => {},
    );
    local $\ = "\n";

    while ( my $arg = shift @argv ) {
        if ( $arg =~ m/^--?h(?:elp)?$/ ) { local $\ = undef; OText::print_pod( $0, __PACKAGE__, $SID_sslinfo ); exit 0; }
        if ( $arg eq '--usage' )         { local $\ = undef; OText::usage_show( "", \%usage );                  exit 0; }
        if ( $arg =~ m/^--(?:v|trace.?)/i ) { $SSLinfo::verbose++; next; }
        if ( $arg =~ m/^(test.*)/ )         { $arg = "+$1"; }
        if ( $arg =~ m/^(?:--|,)(test.*)/ ) { $arg = "+$1"; }
        if ( $arg =~ m/^version$/ )               { print "$SID_sslinfo"; next; }
        if ( $arg =~ m/^[+-]?VERSION/i )          { print "$VERSION";     next; }
        if ( $arg =~ m/^(?:\+test)?.?ssleay/ )    { print test_ssleay();  next; }
        if ( $arg =~ m/^(?:\+test)?.?sslmap/ )    { print test_sslmap();  next; }
        if ( $arg =~ m/^(?:\+test)?.?s_?client/ ) { print test_sclient(); next; }
        if ( $arg =~ m/^(?:\+test)?.?methods/ )   { print test_methods(); next; }
        if ( $arg =~ m/^(?:\+test)?.?openssl/ )   { print test_openssl(); next; }
        if ( $arg =~ m/^[+-]/ ) { next; }

        do_ssl_open( $arg, 443, '' );
        print SSLinfo::datadump("main");
    }
    exit 0;
}


sub net_sslinfo_done { }

package ODoc;

my $SID_odoc = "@(#) ODoc.pm 3.38 24/09/05 13:19:29";
our $VERSION = "24.09.24";

BEGIN {

    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##x;
    if ( exists $ENV{'PWD'} and not( grep { /^$ENV{'PWD'}$/ } @INC ) ) {
        unshift( @INC, $ENV{'PWD'} );
    }
    unshift( @INC, $_path ) if not( grep { /^$_path$/ } @INC );
    unshift( @INC, "lib" )  if not( grep { /^lib$/ } @INC );
}


sub _replace_var {
    my ( $name, $version, @arr ) = @_;
    s#\$VERSION#$version#g for @arr;
    s#(?<!`)\$0#$name#g    for @arr;
    return @arr;
}

sub _get_standalone {
    my $file = shift;
    my $name = $file;
    $name =~ s#(.*[/\\]+)##g;
    my $path = __FILE__;
    $path =~ s#/[^/\\]*$##;
    foreach my $f (
        "$file",                                   "$path/$file",
        "$path/$name",                             "$path/$OCfg::cfg{'dirs'}->{'doc'}/$name",
        "$path/$OCfg::cfg{'dirs'}->{'lib'}/$name", "$path/$OCfg::cfg{'dirs'}->{'lib'}/$file",
        "$path/../$OCfg::cfg{'dirs'}->{'lib'}/$file"
      )
    {
        return $f if -e $f;
    }
    OCfg::warn("189: no '$file' found, consider installing");
    return "";
}
if ( 1 == 42 ) { my $dumm = _get_standalone("never called, but keeps Perl::Critic happy"); }

sub _get_filehandle {
    my $file = shift || "";
    my $fh;
    local $\ = "\n";
    if ( "" ne $file ) {
        if ( not -e $file ) {
            my $path = __FILE__;
            $path =~ s#^/($OCfg::cfg{'dirs'}->{'lib'}/.*)#$1#;
            $path =~ s#/[^/\\]*$##;

            if ( not -e "$path/$file" ) {
                $path = $OCfg::cfg{'dirs'}->{'doc'};
            }
            $file = "$path/$file";
            $file = _get_standalone($file);
        }
    }
    if ( "" ne $file and -e $file ) {
        if ( not open( $fh, '<:encoding(UTF-8)', $file ) ) {
            OCfg::warn("190: open('$file'): $!");
        }
    }
    else {
        $fh = __PACKAGE__ . "::DATA";
        OCfg::warn("191: no '$file' found, using '$fh'") if not -e $file;
    }
    return $fh;
}

sub get_egg {
    my $fh  = _get_filehandle(shift);
    my $egg = "";
    while (<$fh>) { $egg .= $_ if ( m/^#begin/ .. m/^#end/ ); }
    $egg =~ s/#(begin|end) .*\n//g;
    close($fh);
    return scalar reverse "\n$egg";
}

sub get { my $fh = _get_filehandle(shift); return <$fh>; }

sub get_custom {
    my $file    = shift;
    my $name    = shift || "o-saft.pl";
    my $version = shift || $VERSION;
    my @txt;
    my $fh = _get_filehandle($file);
    return "" if ( "" eq $fh );
    for (<$fh>) {
        next if ( m/^#begin/ .. m/^#end/ );
        next if (/^#/);
        next if (/^\s*#.*#$/);

        s/ {1,2}((?:Net::SSLeay|ldd|openssl|timeout|IO::Socket(?:::SSL|::INET)?)\(\d(?:pm)?\))/ "$1"/g;

        s/[IX]&([^&]*)&/$1/g;

        push( @txt, $_ );
    }
    return _replace_var( $name, $version, @txt );
}

sub get_markup {
    my $file    = shift;
    my $parent  = shift || "o-saft.pl";
    my $version = shift || $VERSION;
    my @txt;
    my $fh = _get_filehandle($file);
    return "" if ( "" eq $fh );

    for (<$fh>) {

        next if ( m/^#begin/ .. m/^#end/ );
        next if (/^#/);
        next if (/^\s*#.*#$/);
        s/^([A-Z].*)/=head1 $1/;
        s/^ {4}([^ ].*)/=head2 $1/;
        s/^ {6}([^ ].*)/=head3 $1/;
        s/^( +[a-z0-9]+\).*)/=item * $1/;
        s/^( +\*\* .*)/=item $1/;
        s/^( +\* .*)/=item $1/;
        s/^( {11})([^ ].*)/=item * $1$2/;
        s/^( {14})([^ ].*)/S&$1$2&/;
        s/^( {18})([^ ].*)/S&$1$2&/;

        if (    not m/^(?:=|S&|\s+(?:\$0|o-saft|o-saft.tcl|o-saft-docker|checkAllCiphers.pl|perl|perl2exe|perlapp)\s)/
            and not m/X&[^&]*(?:\+|--)/ )
        {
            s#(\s)+(a-zA-Z[^ ]+)(\s+)#$1'$2'$3#g;

            s#(\s)((?:\+|--)[^,\s).]+)([,\s).])#$1I&$2&$3#g;
            s#([A-Z]L)&#$1 &#g;
            s#(\s)((?:\+|--)[^,\s).]+)([,\s).])#$1I&$2&$3#g;
        }
        if ( not m/^S/ and not m/^ {14,}/ ) {
            s/((?:Net::SSLeay|ldd|openssl|timeout|IO::Socket(?:::SSL|::INET)?)\(\d(?:pm)?\))/L&$1&/g;
        }
        s/  (L&[^&]*&)/ $1/g;
        s/(L&[^&]*&)  /$1 /g;
        if (m/^ /) {
            s/ ((?:DEBUG|RC|USER)-FILE)/ X&$1&/g;
            s/ (CONFIGURATION (?:FILE|OPTIONS))/ X&$1&/g;
            s/ (SHELL TWEAKS)/ X&$1&/g;
            s/ (SEE ALSO)/ X&$1&/g;
            s/ (EXIT STATUS)/ X&$1&/g;
            s/ (CIPHER NAMES)/ X&$1&/g;
            s/ (LAZY SYNOPSIS)/ X&$1&/g;
            s/ (KNOWN PROBLEMS)/ X&$1&/g;
            s/ (BUILD DOCKER IMAGE)/ X&$1&/g;
            s/ (BUILD DOCKER IMAGE)/ X&$1&/g;
            s/ (TECHNICAL INFORMATION)/ X&$1&/g;
            s/ (NAME|CONCEPTS|ENVIRONMENT)/ X&$1&/g;
            s/ (COMMANDS|OPTIONS|RESULTS|CHECKS|OUTPUT|CUSTOMISATION) / X&$1& /g;
            s/ (LIMITATIONS|DEPENDENCIES|INSTALLATION|DOCKER|TESTING) / X&$1& /g;
            s/ (SCORING|EXAMPLES|ATTRIBUTION|DOCUMENTATION|VERSION) / X&$1& /g;
            s/ (DESCRIPTION|SYNOPSIS|QUICKSTART|SECURITY|DEBUG|AUTHOR) / X&$1& /g;
        }
        push( @txt, $_ );
    }
    close($fh);
    return _replace_var( $parent, $version, @txt );
}

sub get_section {
    my $file  = shift;
    my $label = lc(shift) || "";
    my $anf   = uc($label);
    my $end   = "[A-Z]";
    my $hlp;
    my $fh  = _get_filehandle($file);
    my $txt = join( "", <$fh> );
    close($fh);
    if ( $label =~ m/^name/i ) { $end = "TODO"; }
    $txt =~ s/.*?\n$anf/$anf/ms;
    $txt =~ s/\n$end.*//ms;

    $txt =~ s/\n#[^\n]*//g;
    $txt =~ s/[IX]&([^&]*)&/$1/g;
    if ( 0 < ( grep { /^--v/ } @ARGV ) ) {

        OCfg::warn("192: using workaround to print large strings.\n\n");
        $hlp .= $_ foreach split( //, $txt );
    }
    else {
        $hlp .= $txt;
    }
    return $hlp;
}

sub get_section_from_pod {
    my $file  = shift;
    my $label = lc(shift) || "";
    my $anf   = uc($label);
    my $end   = "[A-Z]";
    my $hlp;
    my $txt = join( "", get_markup($file) );
    if ( $label =~ m/^name/i ) { $end = "TODO"; }
    $txt =~ s/.*?\n=head1 $anf//ms;
    $txt =~ s/\n=head1 $end.*//ms;

    $txt = "\n=head1 $anf" . $txt;
    $txt =~ s/\n=head2 ([^\n]*)/\n    $1/msg;
    $txt =~ s/\n=head3 ([^\n]*)/\n      $1/msg;
    $txt =~ s/\n=(?:[^ ]+ (?:\* )?)([^\n]*)/\n$1/msg;
    $txt =~ s/\nS&([^&]*)&/\n$1/g;
    $txt =~ s/[IX]&([^&]*)&/$1/g;
    $txt =~ s/L&([^&]*)&/"$1"/g;
    if ( 0 < ( grep { /^--v/ } @ARGV ) ) {

        OCfg::warn("192: using workaround to print large strings.\n\n");
        $hlp .= $_ foreach split( //, $txt );
    }
    else {
        $hlp .= $txt;
    }
    return $hlp;
}

sub list {
    my $dir = shift;
    $dir =~ s#[/\\][^/\\]*$##;
    $dir .= "/$OCfg::cfg{'dirs'}->{'doc'}" if $dir !~ m#$OCfg::cfg{'dirs'}->{'doc'}/?$#;
    $dir = $OCfg::cfg{'dirs'}->{'doc'}     if not -d $dir;
    my @txt;
    opendir( my $dh, $dir ) or return $!;
    while ( my $file = readdir($dh) ) {
        next unless ( -f "$dir/$file" );
        next unless ( $file =~ m/\.txt$/ );
        push( @txt, $file );
    }
    closedir($dh);
    return join( "\n", sort @txt );
}

sub _main {
    my @argv = @_;
    push( @argv, "--help" ) if ( 0 > $#argv );
    my %usage = (
        '# internal commands' => {
            'list' => 'list available files',
        },
        '# commands to get text from file in various formats(examples)' => {
            'get help.txt'                 => 'show own SID',
            'get-custom help.txt'          => 'show text with some variables replaced',
            'get-markup help.txt'          => 'show text with internal makup',
            'get-section help.txt SECTION' => 'show specified section from file',
        },
    );
    while ( my $cmd = shift @argv ) {
        OText::print_pod( $0, __PACKAGE__, $SID_odoc ) if ( $cmd =~ m/^--?h(?:elp)?$/x );
        OText::usage_show( "", \%usage )               if ( $cmd eq '--usage' );
        my $arg = shift @argv;
        my $sec = "NAME";
        $sec = shift @argv || "NAME" if ( $cmd =~ /^get.?section/ );
        print list($0) . "\n"           if ( $cmd =~ /^list$/ );
        print get($arg)                 if ( $cmd =~ /^get$/ );
        print get_custom($arg)          if ( $cmd =~ /^get.?custom$/ );
        print get_markup($arg)          if ( $cmd =~ /^get.?mark(?:up)?/ );
        print get_section( $arg, $sec ) if ( $cmd =~ /^get.?section/ );
        print "$SID_odoc\n"             if ( $cmd =~ /^version$/ );
        print "$VERSION\n"              if ( $cmd =~ /^[-+]?V(?:ERSION)?$/ );
    }
    exit 0;
}

sub done { }


package OMan;

my $SID_oman = "@(#) OMan.pm 3.70 24/09/09 01:33:02";
our $VERSION = "24.09.24";

BEGIN {

    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##;
    if ( exists $ENV{'PWD'} and not( grep { /^$ENV{'PWD'}$/ } @INC ) ) {
        unshift( @INC, $ENV{'PWD'} );
    }
    unshift( @INC, $_path ) if not( grep { /^$_path$/ } @INC );
    unshift( @INC, "lib" )  if not( grep { /^lib$/ } @INC );
    our @EXPORT_OK = qw( man_printhelp man_docs_write );
}

$::osaft_standalone = 0 if not defined $::osaft_standalone;

my $parent = ( caller(0) )[1] || "o-saft.pl";
$parent =~ s:.*/::;
$parent =~ s:\\:/:g;
$parent = $0 if ( 0 < $::osaft_standalone );
my $ich = ( caller(1) )[1];
$ich = "OMan.pm" if ( not defined $ich );
$ich =~ s:.*/::;
my $version = "$SID_oman";
$version =~ s:^.{5}::;
$version = ::_VERSION() if ( defined &::_VERSION );
my $cfg_header = 0;
$cfg_header = 1 if ( 0 < ( grep { /^--header/ } @ARGV ) );
my $mytool = qr/(?:$parent|o-saft.tcl|o-saft|checkAllCiphers.pl)/;
my @help   = ODoc::get_markup( "help.txt", $parent, $version );
my $_trace = 0;
$_trace++ if ( 0 < ( grep { /^--trace(?:=\d+)?$/ } @ARGV ) );
local $\ = "";

my $_cmd_brief = <<'EoBrief';
+info             Overview of most important details of the SSL connection.
+cipher           Check target for ciphers (using libssl).
+check            Check the SSL connection for security issues.
+protocols        Check for protocols supported by target.
+vulns            Check for various vulnerabilities.
EoBrief

my $_commands = <<'EoCmds';
                  Commands for information about this tool
+dump             Dumps internal data for SSL connection and target certificate.
+exec             Internal command; should not be used directly.
+help             Complete documentation.
+list             Show all ciphers supported by this tool.
+libversion       Show version of openssl.
+quit             Show internal data and exit, used for debugging only.
+VERSION          Just show version and exit.
+version          Show version information for program and Perl modules.

                  Commands to check SSL details
+bsi              Various checks according BSI TR-02102-2 and TR-03116-4 compliance.
+check            Check the SSL connection for security issues.
+check_sni        Check for Server Name Indication (SNI) usage.
+ev               Various checks according certificate's extended Validation (EV).
+http             Perform HTTP checks.
+info             Overview of most important details of the SSL connection.
+info--v          More detailled overview.
+quick            Quick overview of checks.
+protocols        Check for protocols supported by target.
+s_client         Dump data retrieved from  "openssl s_client ..."  call.
+sizes            Check length, size and count of some values in the certificate.
+sni              Check for Server Name Indication (SNI) usage.
+sts              Various checks according STS HTTP header.
+vulns            Check for various vulnerabilities.

                  Commands to test ciphers provided by target
+cipher           Check target for ciphers (using libssl).
+cipher-dh        Check target for ciphers (using libssl), prints also DH parameter.
+cipher-default   Check target for (default) selected cipher for each protocol.
+cipher-null      Check if target accepts NULL ciphers.
+cipher-adh       Check if target accepts ciphers with anonymous key exchange.
+cipher-exp       Check if target accepts EXPORT ciphers.
+cipher-cbc       Check if target accepts CBC ciphers.
+cipher-des       Check if target accepts DES ciphers.
+cipher-rc4       Check if target accepts RC4 ciphers.
+cipher-edh       Check if target supports ephemeral ciphers.
+cipher-pfs       Check if target supports ciphers with PFS.
+cipher-strong    Check if target selects strongest cipher.
+cipher-selected  Selected cipher.

EoCmds

my $_voodoo = <<'EoHelp';
# begin voodoo

# Some documentation is plain text, which is  DATA  in Perl sources. As such,
# it  is  not detected as source,  not as comment,  and  not as documentation
# by most tools analysing the source code.
# O-Saft's public user documentation is plain text stored in  separate files.
# These files are usually also not counted as source.
# Unfortunately, some people solely believe in statistics generated by  magic
# tools. They use such statistics to measure for example code quality without
# looking themself at the code.
# Hence the purpose of this file is to provide real comment and documentation
# lines from our documentation in format of the used programming language.
# Hopefully, if these people read this, they change the workflow (means: they
# also review the source code) or adapt their conclusions having in mind that
# statistics can be manipulated in many ways. Here we go ...
#
# Disclaimer: No offence meant anyhow, neither against any analysing tool nor
# against anyone using them. It is just a reminder to use the tools and their
# results in a wise manner. Measuring quality is more than just automatically
# generated statistics!

# end voodoo
EoHelp

my %html = (
    'title' => 'O - S a f t  --  OWASP - SSL advanced forensic tool',

    'nonce'        => '4f2d53616674',
    'script_nonce' => '<script nonce="4f2d53616674">',

    'doctype' => "<!DOCTYPE html>\n",

    'copyright' => << 'EoCOPY',
 <hr><p><span style="display:none">&copy; Achim Hoffmann 2023</span></p>
EoCOPY

    'links' => << 'EoLINK',
 <a href="https://github.com/OWASP/O-Saft/"   target=_github >Repository</a> &nbsp;
 <a href="https://github.com/OWASP/O-Saft/blob/master/o-saft.tgz" target=_tar class=b >Download (stable)</a>
 <a href="https://github.com/OWASP/O-Saft/archive/master.zip" target=_tar class=b >Download (newest)</a><br><br>
 <a href="https://owasp.org/www-project-o-saft/" target=_owasp  >O-Saft Home</a>
EoLINK

    'action' => '__HTML_cgi_bin__',

    'meta' => << 'EoMETA',

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline'">
  <!-- CSP in meta tag is not recommended, but it servs as hint how to set
       the HTTP header Content-Security-Policy -->
  <meta name="viewport" content="width=device-width,initial-scale=0.4">
  <title>__HTML_title__</title>
EoMETA

    'script_func1' => << 'EoFUNC',

  function _i(id){return document.getElementById(id);}
  function toggle_checked(id){
	id=_i(id);
	if(null !== id){ id.checked=(id.checked=='false')?'true':'false'; };
	return false;
  }
  function toggle_display(id){
	if("string" === typeof id){ id=_i(id).style; } else { id=id.style };
	if("" === id.display){ id.display='none';} /* Chrome hack */
	id.display = (id.display=='none')?'block':'none';
	return false;
  }
  function schema_is_file(){
	if (/^file:/.test(location.protocol)===true) { return true; }
	return false;
  }

EoFUNC

    'script_func2' => << 'EoFUNC',

  function osaft_buttons(){
  // generated buttons for most common commands in <table id="osaft_buttons">
	var buttons = ['+quick', '+check', '+cipher', '+info', '+protocols', '+vulns' ];
	var table   = _i('osaft_buttons');
	for (var b in buttons) {
	        // <input type=submit name="--cmd" value="+check" ><div class=q
	        // id='c+check'></div><br>
	        tr = document.createElement('TR');
	        td = document.createElement('TD');
	        cc = document.createElement('INPUT');
	        cc.type   = 'submit'; cc.name='--cmd'; cc.value=buttons[b];
	        cc.title  = 'execute: o-saft.pl ' + buttons[b];
	        //cc.target = 'o-saft.pl_' + buttons[b];
	        td.appendChild(cc);
	        tr.appendChild(td);
	        td = document.createElement('TD');
	        td.setAttribute('class', 'q');
	        td.id='q' + buttons[b];
	        tr.appendChild(td);
	        table.appendChild(tr);
	}
	return;
  }
  function osaft_commands(){
  /* get help texts from generated HTML for commands and add it to command
   * button (generated by osaft_buttons, see above) of cgi-GUI
   * existing  tag of text paragraph containing help text has  id=h+cmd
   * generated tag of  quick button  containing help text has  id=q+cmd
   */
	osaft_buttons();
	var arr = document.getElementsByTagName('p');
	for (var p=0; p<arr.length; p++) {
	    if (/^h./.test(arr[p].id)===true) {
	        var id = arr[p].id.replace(/^h/, 'q');
	        if (_i(id) != undefined) {
	            // button exists, add help text
	            _i(id).innerHTML = _i(arr[p].id).innerHTML;
	        }
	    }
	}
	return;
  }
  function osaft_options(){
  /* get help texts from generated HTML for options and add it to option
   * checkbox of cgi-GUI (actually add it to the parent's title tag)
   * existing  tag of text paragraph containing help text has  id=h--OPT
   * generated tag of quick checkbox containing help text has  id=q--OPT
   */
	var arr = document.getElementsByTagName('p');
	for (var p=0; p<arr.length; p++) {
	    if (/^h./.test(arr[p].id)===true) {
	        var id = arr[p].id.replace(/^h/, 'q');
	        // TODO: *ssl and *tls must use *SSL
	        if (_i(id) != undefined) {
	            obj = _i(id).parentNode;
	            if (/^LABEL$/.test(obj.nodeName)===true) {
	                // checkbox exists, add help text to surrounding
	                // LABEL
	                obj.title = _i(arr[p].id).innerHTML;
	            }
	        }
	    }
	}
	return;
  }
  function osaft_enable(){
  /* check all input fields with type=text if they are disabled, which was set
   * by osaft_submit(), then removes the disabled attribute for these tags
   */
	var arr = document.getElementsByTagName('input');
	for (var tag=0; tag<arr.length; tag++) {
	    if (/^text$/.test(arr[tag].type)===true) {
	        arr[tag].removeAttribute('disabled');
	    }
	}
	return;
  }
  function osaft_submit(){
  /* check all input fields with type=text, if its value is empty the attribute
   * disabled is added to the input tag to ensure that no  name=value  for this
   * input field will be submitted
   * return true (so that the form will be submitted)
   */
	var arr = document.getElementsByTagName('input');
	for (var tag=0; tag<arr.length; tag++) {
	    if (/^text$/.test(arr[tag].type)===true) {
	        if (arr[tag].value === '') {
	            arr[tag].setAttribute('disabled', true);
	        }
	    }
	}
	// ensure that all input fields are enabled again after submit
	setTimeout("osaft_enable()",2000);
	return true;
  }
  function osaft_handler(from,to){
  /* set form's action and a's href attribute if schema is file:
   * replace all href attributes also to new schema
   */
	var rex = new RegExp(from.replace(/\//g, '.'),"");  // lazy convertion to Regex
	var url = document.forms["o-saft"].action;          // in case we need it
	if (/^file:/.test(location.protocol)===false) { return false; } // not a file: schema
	var arr = document.getElementsByTagName('form');
	for (var tag=0; tag<arr.length; tag++) {
	    if (rex.test(arr[tag].action)===true) {
	        arr[tag].action = arr[tag].action.replace(rex, to).replace(/^file:/, 'osaft:');
	    }
	}
	//dbx// alert(document.forms["o-saft"].action);
	var arr = document.getElementsByTagName('a');
	for (var tag=0; tag<arr.length; tag++) {
	    if (rex.test(arr[tag].href)===true) {
	        arr[tag].href = arr[tag].href.replace(rex, to).replace(/^file:/, 'osaft:');
	    }
	}
	return false;
  }
  function osaft_disable_help(){
  // disable help-buttons
	return;  // -- NOT YET WORKING --
	var arr = document.getElementsByTagName('a');
	for (var p=0; p<arr.length; p++) {
	    if (arr[p].className==="b") {
	        arr[p].setAttribute('disabled', true);  // not working
	        arr[p].setAttribute('display', 'none'); // not working
	        //arr[p].disabled = true;  // not working
	        //alert(arr[p].href+" "+arr[p].display);
	    }
	}
	return;
  }
  function toggle_handler(){
  // toggle display of "schema" button
	if (true===schema_is_file()) { return; }
	toggle_display("schema");
	return;
  }
EoFUNC

    'script_endall' => << 'EoFUNC',

 <script nonce="4f2d53616674">
  /* keep JavaScript's DOM happy */
  if (_i('a')){ _i('a').style.display='block'; }
  if (_i('b')){ _i('b').style.display='none';  }
  if (_i('c')){ _i('c').style.display='none';  }
  if (_i('warn')){ _i('warn').style.display='block'; }
  /* adapt display of some buttons (if corresponding function exists) */
  if ("function" === typeof osaft_disable_help) {
    if (true === schema_is_file()) { osaft_disable_help(); }
  }
  if ("function" === typeof toggle_handler) { toggle_handler(); }
 </script>
EoFUNC

    'script_endcgi' => << 'EoFUNC',

 <script nonce="4f2d53616674">
  var osaft_action_http="__HTML_cgi_bin__"; // default action used in FORM and A tags; see osaft_handler()
  var osaft_action_file="/o-saft.cgi";      // default action used if file: ; see osaft_handler()
  osaft_commands("a");              // generate quick buttons
  osaft_options();                  // generate title for quick options
  toggle_handler();                 // show "change schema" button if file:
  toggle_checked("q--header");      // want nice output
  toggle_checked("q--enabled");     // avoid huge cipher lists
  toggle_checked("q--html4");       // is on bey default, switch off
  toggle_checked("q--html5");       // nice output in browser
  toggle_checked("o--header");      // .. also as option ..
  toggle_checked("o--enabled");     // .. also as option ..
				    //toggle_checked("o--html5");       // TODO: noch nicht als Option da
  toggle_checked("q--no-hints");    // disabled by default Hints in CGI-mode
  toggle_checked("q--no-dtlsv1");   // disabled by default because some targets hang
  toggle_checked("q--no-dtlsv11");  // ..
  toggle_checked("q--no-dtlsv12");  // ..
  toggle_checked("q--no-dtlsv13");  // ..
 </script>
EoFUNC

    'style_root' => << 'EoROOT',

/* variable definitions */
 :root {
    /* color and background */
    --bg-osaft:     #fff;
    --bg-black:     #000;
    --bg-blue:      #226;               /* darkblue  */
    --bg-head:      linear-gradient(#000,#fff);    /* black,white */
    --bg-menu:      linear-gradient(#000,#aaa);    /* black,grey */
    --bg-mbox:      rgba(0,0,0,0.9);
    --bg-mdiv:      linear-gradient(#fff,#226);
    --bg-button:    linear-gradient(#d3d3d3,#fff);  /* lightgray */
    --bg-button-h:  linear-gradient(#fff,#d3d3d3);  /* lightgray */
    --bg-start:     linear-gradient(#ffd700,#ff0);  /* gold */
    --bg-start-h:   linear-gradient(#ff0,#ffd700);  /* gold */
    --bg-hover:     #d3d3d3;            /* lightgray */
    --bg-literal:   #d3d3d3;            /* lightgray */
    /* border */
    --border-0:     0px solid #fff;
    --border-1:     1px solid #000;     /* black */
    --border-w:     1px solid #fff;     /* white */
    --radius-10:    0px 10px 10px 10px;
    --radius-20:    0px  0px 20px 20px;
    --shadow:       1px  4px  4px #666;
    /* misc */
    --z-index:      42;
 }
EoROOT

    'style_button' => << 'EoButton',

 [type=submit] {        /* submit/start buttons */
    text-align:     left;
    font-size:      80%;
    font-weight:    bold;
    min-width:      10em;
    background:     var(--bg-start);
    box-shadow:     var(--shadow);
    border:         var(--border-1);
    border-radius:  4px;
 }
 [type="submit"]:hover  { background:var(--bg-start-h); }

 .navdiv div a, .b {    /* help buttons */
    display:        block;
    margin:         0.2em;
    padding:        0px 0.2em 0px 0.2em;
    text-decoration:none;
    font-size:      90%;
    font-weight:    bold;
    color:          #000;
    background:     var(--bg-button);
    box-shadow:     var(--shadow);
    border:         var(--border-1);
    border-radius:  4px;
   }
 .navdiv div a:hover, .b:hover { background: var(--bg-button-h); }
 .b { display: inline-block; }              /* ^top and start button */
EoButton

    'style' => << 'EoSTYLE',

 body   { margin:0px 0.5em 0px 0.5em; background:#f2eff2; font: 16pt Arial, Helvetica, sans-serif; }
/* { page header */
 body > h2          { margin: 0px -0.3em 0px -0.3em; padding:1em; background:var(--bg-head);color:white;border-radius:var(--radius-20); }
 body > h2 > span   { margin-bottom:2em;font-size:120%;border:var(--border-0);}
/* } page header */
/* { help page only */
 h3, h4, h5         { margin-bottom: 0.2em; }
 body > h3          { margin-top:    1.2em; }
 body   h4          { margin-left:     1em; }       /* mainly +cmd and --opt */
/* } help page only */
/* { cgi page only */
 body h4 [class="i"] {margin-left:    -1em; }       /* mainly +cmd and --opt */
 fieldset           { margin:     0px;  }
 fieldset > details:nth-child(2) > div  { z-index:calc(var(--z-index)); } /* "Simple GUI" on top */
 fieldset > details > div       { margin:0.1em 0.4em 0px -0.85em; background:white; overflow-y:scroll; }
/*
fieldset > details > div:focus  { display:block; } // geht nicht
*/
 aside              { border:1px solid black; position:fixed; top:3em; right:0.6em; background:white; z-index:calc(var(--z-index) + 7); box-shadow:var(--shadow); }
 aside details      { background:white; }
 aside summary      { padding:0px  0.5em 0px 0.5em; border-bottom:1px solid black; }
 aside p            { overflow-y:auto; height:80vh; }
 aside p > a        { margin:0.3em 0.3em 0.3em 1em; font-size:80%; display:block;  }
/* for menu bar left vertical instead top horizontal:
 *   .navdiv { float:left; }
 *   .navdiv > details  { min-width:4em; }
*/
 .navdiv            { background:black; color:white; padding:0.3em; min-height:1.5em; font-weight:bold; position:sticky; top: 0px; z-index:calc(var(--z-index) + 5); } /* navigation top-most */
 .navdiv > details:first-child >summary  { list-style:none; font-size:120%; max-width:2em !important; }
 .navdiv > details:first-child { margin-left:0.1em; }
 .navdiv > details       { margin-left: 0.8em; float:left; }
 .navdiv > details   div { margin-left:-0.3em; background:var(--bg-menu); z-index:calc(var(--z-index) + 3);  }
 .navdiv > details > div > input[type="submit"]  { display:block; }
 .navdiv > details > div > label         { font-weight:normal; display:block; }
 .navdiv > details > div > details > div { margin-left:0.8em; } /* submenu */
 .navdiv > details[open]>summary::before { content: ""; position:fixed; top:-1em; right:-1em; bottom:-1em; left:-1em; } /* any click outside closes submenu */
 details > div           { padding:0.5em; border:var(--border-1); border-radius:var(--radius-10); position:absolute; }
 details > div > li      { margin-left: 2.2em; }    /* lists in texts        */
 details > div > table   { font-size:   100%;  }    /* Simple GUI (unsure why necessary)*/
 details[open] > summary { text-decoration:underline; }
/* } cgi page only */
 li                 { margin-left: 2.0em; }         /* lists in texts        */
 li[class="l2"]     { margin-left: 3.0em; list-style-type:square;} /* 2nd level lists in texts */
 li[class="n"]      { margin-left: 2.2em; list-style-type:none; }  /* "comments" in text */
 p                  { margin: 0px 0px 0.5em 1em; }  /* all texts     */
 p > a[class="b"]   { margin-left:-1em; }           /* ^top button only      */
 label[class="i"]   { margin-right:1em; min-width:8em; border:var(--border-w); display:inline-block; } 
 label[class="i"]:hover { background:var(--bg-hover);border-bottom:var(--border-1);}
 b                  { margin-left: 1em; }           /* for discrete commands #FIXME: wrong in cgi page */
 .r                 { float:right;      }           /* help buttons          */
 .l                 { margin-left: 2em; }           /* label for options     */
 .c                 { margin-left: 3em; padding:0.1em 0.3em; font-size:12pt !important; font-family:monospace; background:var(--bg-literal);} /* literal text block; #TODO: white-space:pro   */
 .d                 { min-width: 9em; display:inline-block; } /* label in dt-dd format '/
 .d::after          { content:"–"; } /* #TODO: does not work, reason unknown */
 span[class="c"]    { margin-left:0.1em;}           /* literal text (inline) */
/* dirty hack for mobile-friendly A tag's title= attribute;
 * placed left bound below tag; browser's title still visible
 * does not work for BUTTON and INPUT tags
 */
 [title]            { position:relative; }
 a[class="b"][title]:hover:after,
 a[class="b r"][title]:hover:after {
    content: attr(title);
    position:absolute; z-index:calc(var(--z-index) + 22); top:100%; left:-1em; padding:0.3em;
    border-radius:2px; background:var(--bg-mbox); color:white;
    font-weight:normal;
 }
EoSTYLE

    'style_ciphers' => << 'EoSTYLE_C',

 body                 {padding:   1em;       }
 body > h1            {padding-top:1em;  margin-top:1em; }
 body > h2            {padding:   1em;   margin-top:-0.3em; height:1.5em;width:94%;color:white;background:linear-gradient(#000,#fff);border-radius:0px 0px 20px 20px;box-shadow:0 5px 5px #c0c0c0;position:fixed;top:0px; }
 body > h2 > span     {font-size:120%; }
 h2 > a[class="b"]    {float:right;      margin-top:1em; font-size:70%; border-radius:5px;}
 /* table { border-collapse: collapse; } * nicht verwenden */
 /* table { table-layout: fixed;       } * geht nicht      */
 table th    {background:#aaa;   }
 tbody tr:nth-child(even) {background:#fff; }
 tbody tr:nth-child(odd)  {background:#eee; }
 tbody td:first-child   {text-align:right;  }
 tbody td               {width: 5em;        }
 thead                  {position: sticky; top:3em; }
 details                {padding: 0.2em; font-weight:bold;     }
 details:nth-child(even){background:#fff;   }
 details:nth-child(odd) {background:#eee;   }
 details summary:hover  {background:#ffd700;}
 details span:first-child  {text-align:right; min-width:15em;  }
 details span           {padding:   0.2em; display:inline-block; min-width:6em; border-radius:4px 4px 4px 4px; }
 details div            {margin-top:0.5ex; font-size:90%; border:1px solid #000; border-top:0px solid #000; border-radius:0px 0px 10px 10px; }
 details dl             {padding:   0.2em; display:block;        }
 details dt,dd          {padding:   0.5ex; display:inline-block; }
 details dt             {min-width: 12em;  text-align:left;font-weight:bold;}
 /* automatically generate colour of tag based on the sec attribute */
 [sec="-"]              {background-color:#f00; }
 [sec^="weak"]          {background-color:#f00; }
 [sec^="WEAK"]          {background-color:#f00; }
 [sec="-?-"]            {background-color:#ff0; }
 [sec^="LOW"]           {background-color:#fd8; }
 [sec^="medium"]        {background-color:#ff4; }
 [sec^="MEDIUM"]        {background-color:#ff4; }
 [sec^="high"]          {background-color:#4f4; }
 [sec^="HIGH"]          {background-color:#3f3; }
 [typ="PFS"]            {background-color:#4f4; }
 /* automatically generate content if tag from attribute typ= */
 [typ]::before          {content:attr(typ);     }
 dd[typ]                {border:1px solid #ffd700;}
 td[typ]                {border:1px solid #fff; }
 [typ]:hover            {border:1px solid #aaa; }
 [typ]:hover ::after    {border:1px solid #000; border-radius:3px; position:absolute; margin-left:0.5em; background:#fd8; min-width:19em; }
 /* following definitons should be generated from doc/glossar.txt    */
 /* sequence of following definitions important: more lacy pattern first */
 [typ="-"]:hover       ::after  {content:"\2014  none / null / nothing";}
 [typ="-?-"]:hover     ::after  {content:"\2014  unknown";}
 [typ^="ADH"]:hover    ::after  {content:"\2014  Anonymous Diffie-Hellman";}
 [typ="AEAD"]:hover    ::after  {content:"\2014  Authenticated Encryption with Additional Data";}
 [typ^="AES"]:hover    ::after  {content:"\2014  Advanced Encryption Standard";}
 [typ="AESGCM"]:hover  ::after  {content:"\2014  AEAD algorithms AEAD_AES_128_GCM and AEAD_AES_256_GCM";}
 [typ^="ARIA"]:hover   ::after  {content:"\2014  128-bit symmetric block cipher";}
 [typ="ARIAGCM"]:hover ::after  {content:"\2014  symmetric key block cipher encryption algorithm with GCM";}
 [typ="CAMELLIA"]:hover    ::after  {content:"\2014  symmetric key block cipher encryption algorithm";}
 [typ="CAMELLIAGCM"]:hover ::after  {content:"\2014  CAMELLIA with GCM";}
 [typ="CAST"]:hover    ::after  {content:"\2014  Carlisle Adams and Stafford Tavares, block cipher";}
 [typ="CBC"]:hover     ::after  {content:"\2014  Cyclic Block Chaining (aka Cypher Block Chaining)";}
 [typ^="CECPQ"]:hover  ::after  {content:"\2014  Combined elliptic Curve and Post-Quantum Cryptography Key Exchange";}
 [typ^="ChaCha"]:hover ::after  {content:"\2014  stream cipher algorithm (with 256-bit key)";}
 [typ="DES"]:hover     ::after  {content:"\2014  Data Encryption Standard";}
 [typ="3DES"]:hover    ::after  {content:"\2014  Tripple Data Encryption Standard";}
 [typ="DSS"]:hover     ::after  {content:"\2014  Digital Signature Standard";}
 [typ="DH"]:hover      ::after  {content:"\2014  Diffie-Hellman";}
 [typ^="DHE"]:hover    ::after  {content:"\2014  Diffie-Hellman ephemeral (same as EDH)";}
 [typ="DHEPSK"]:hover  ::after  {content:"\2014  Diffie-Hellman ephemeral with pre-shared key";}
 [typ="DH/DSS"]:hover  ::after  {content:"\2014  Diffie-Hellman with DSS";}
 [typ="DH/RSA"]:hover  ::after  {content:"\2014  Diffie-Hellman with RSA";}
 [typ="DH(512)"]:hover ::after  {content:"\2014  Diffie-Hellman (512 bit)";}
 [typ="ECCPWD"]:hover  ::after  {content:"\2014  Elliptic Curve Cryptography (with password?)";}
 [typ^="ECDH"]:hover   ::after  {content:"\2014  Elliptic Curve Diffie-Hellman";}
 [typ^="ECDHE"]:hover  ::after  {content:"\2014  Ephemeral Elliptic Curve Diffie-Hellman";}
 [typ="ECDH/ECDSA"]:hover  ::after  {content:"\2014  Elliptic Curve Diffie-Hellman with ECDSA";}
 [typ="ECDH/RSA"]:hover    ::after  {content:"\2014  Elliptic Curve Diffie-Hellman with RSA";}
 [typ="ECDHEPSK"]:hover    ::after  {content:"\2014  Elliptic Curve Diffie-Hellman with pre-shared key";}
 [typ="ECDSA"]:hover   ::after  {content:"\2014  Elliptic Curve Digital Signature Algorithm";}
 [typ^="EDH"]:hover    ::after  {content:"\2014  Ephemeral Diffie-Hellman";}
 [typ="FZA"]:hover     ::after  {content:"\2014  Fortezza encryption";}
 [typ^="GOST"]:hover   ::after  {content:"\2014  Gossudarstwenny Standard, block cipher";}
 [typ="IDEA"]:hover    ::after  {content:"\2014  International Data Encryption Algorithm";}
 [typ="KRB"]:hover     ::after  {content:"\2014  Key Exchange Kerberos";}
 [typ="KRB5"]:hover    ::after  {content:"\2014  Key Exchange Kerberos 5";}
 [typ="MD2"]:hover     ::after  {content:"\2014  Message Digest 2";}
 [typ="MD4"]:hover     ::after  {content:"\2014  Message Digest 4";}
 [typ="MD5"]:hover     ::after  {content:"\2014  Message Digest 5";}
 [typ="None"]:hover    ::after  {content:"\2014  no encryption / plain text";}
 [typ="RC2"]:hover     ::after  {content:"\2014  Rivest Cipher 2, block cipher";}
 [typ="RC4"]:hover     ::after  {content:"\2014  Rivest Cipher 4, stream cipher (aka Ron's Code)";} # dumm '
 [typ="RC5"]:hover     ::after  {content:"\2014  Rivest Cipher 5, block cipher";}
 [typ="RIPEMD"]:hover  ::after  {content:"\2014  RACE Integrity Primitives Evaluation Message Digest";}
 [typ="RSA"]:hover     ::after  {content:"\2014  Rivest Sharmir Adelman (public key cryptographic algorithm)";}
 [typ="RSAPSK"]:hover  ::after  {content:"\2014  Rivest Sharmir Adelman with pre-shared key";}
 [typ="RSA(512)"]:hover ::after {content:"\2014  Rivest Sharmir Adelman (512 bit)";}
 [typ="PCT"]:hover     ::after  {content:"\2014  Private Communications Transport";}
 [typ="PSK"]:hover     ::after  {content:"\2014  Pre-shared Key";}
 [typ="SEED"]:hover    ::after  {content:"\2014  128-bit symmetric block cipher";}
 [typ="SHA"]:hover     ::after  {content:"\2014  Secure Hash Algorithm";}
 [typ="SHA1"]:hover    ::after  {content:"\2014  Secure Hash Algorithm";}
 [typ="SHA256"]:hover  ::after  {content:"\2014  Secure Hash Algorithm (256 bit)";}
 [typ="SHA384"]:hover  ::after  {content:"\2014  Secure Hash Algorithm (384 bit)";}
 [typ="SHA512"]:hover  ::after  {content:"\2014  Secure Hash Algorithm (512 bit)";}
 [typ="SRP"]:hover     ::after  {content:"\2014  Secure Remote Password protocol";}
 [typ="SSLv2"]:hover   ::after  {content:"\2014  Secure Socket Layer 2";}
 [typ="SSLv3"]:hover   ::after  {content:"\2014  Secure Socket Layer 3";}
 [typ="TLSv10"]:hover  ::after  {content:"\2014  Transport Level Secure 1.0";}
 [typ="TLSv11"]:hover  ::after  {content:"\2014  Transport Level Secure 1.1";}
 [typ="TLSv12"]:hover  ::after  {content:"\2014  Transport Level Secure 1.2";}
 [typ="TLSv13"]:hover  ::after  {content:"\2014  Transport Level Secure 1.3";}
 /* not yet working: setting CSS variables and then use them
  dd[val]            {--data: attr(val); --index: var(--data);}
 */
EoSTYLE_C

    'body_anf' => << 'EoBODY',
<body>
 <h2 title="__HTML_version__" ><span id="txt" >__HTML_title__</span>
     <button id="schema" style="float: right;" onclick="osaft_handler(osaft_action_http,osaft_action_file);" title="change schema of all&#13;action and href attributes">Change to osaft: schema</button>
 </h2>
EoBODY

    'body_aside' => << 'EoASIDE',

 <aside class="aside"><details><summary>Content</summary><p>
__HTML_aside__
 </p></details></aside>
EoASIDE

    'form_anf' => << 'EoFORM',

 <a name="aFORM"></a>
 <form id="o-saft" action="__HTML_cgi_bin__" method="GET" onsubmit="return osaft_submit()" target="cmd" >
  <noscript><div>
All options, even those without values, are passed to __HTML_cgi_bin__ .
  </div></noscript>
  <input  type="hidden" name="--cgi" value="" >
EoFORM

    'fieldset' => << 'EoFIELDSET',
  <fieldset>
    <p>
    Host[:Port]:: <input type="text" name="--url"  size="40" title="hostname or hostname:port or URL" >
    <input type="submit" name="--cmd" value="+check" title="execute: o-saft.pl +check ..." onclick='this.value="+check";' >
    <input type="reset"  value="clear" title="clear all settings or reset to defaults"/>
    </p>
EoFIELDSET

    'form_end' => << 'EoFORM',
  </fieldset>
 </form>
 <hr>
EoFORM

    'warning_box' => << 'EoWARN',
 <!-- print "Note" text box for CGI usage; only visible with fragment #Note -->
 <style>
  /* message box "Note", if necessary # TODO: font-size not working in firefox */
  .m            {opacity:1; pointer-events:none; position:fixed; transition:opacity 400ms ease-in; background:var(--bg-mbox); top:0; right:0; bottom:0; left:0; z-index:calc(var(--z-index) + 9); }
  .m > div      {position:relative; min-width:10em; margin:4em auto; padding:1em; border-radius:8px;   background:var(--bg-mdiv); font-size:120%; }
  .m > div > a  {opacity:1; pointer-events:auto; }
  .m > div > a  {position:absolute; width:1.1em; top:0.1em;      right:0.2em; line-height:1.1em;   background:var(--bg-blue); color:#fff; text-align:center;  text-decoration:none; font-weight:bold; border-radius:8px; box-shadow:1px 3px 3px #5bb; }
  .m > div > a:hover  {background: #5bb; }
  .m > div > h3       {margin:-0.8em 0px 1em 0px; border-bottom:var(--border-1); }
  .m > div > h3:before{content:"\00a0\00a0\00a0" }
 </style>
 <div id="warn" class="m"> <div>
  <a  id="seen" href="" onclick="toggle_display('warn');return false;" title="I understand">X</a>
  <h3>O-Saft as CGI </h3>
  <p>This is a sample implementation to show O-Saft's functionality.</p>
  <p>It is not intended to be used for regular tests of foreign servers.</p>
  <p>The server may be slow and is short on memory, so please don't expect miracles.</p>
 </div> </div>
EoWARN

);

sub _get_filename {
    my $src = shift || "o-saft.pl";
    foreach my $dir (@INC) {
        if ( -e "$dir/$src" ) {
            $src = "$dir/$src";
            last;
        }
    }
    return $src;
}

sub _man_dbx {

    my @txt = @_;
    my $anf = "";
    my $end = "";
    if ( 0 < ( grep { /^--help=gen.cgi/i } @ARGV ) ) {
        $anf = "<!-- ";
        $end = " -->";
    }
    print $anf . "#" . $ich . ": " . join( ' ', @txt ) . "$end\n" if ( 0 < $_trace );
    return;
}

sub _man_use_tty {

    _man_dbx("_man_use_tty() ...");
    return if not defined $cfg{'tty'}->{'width'};
    my $_len = 80;
    my $cols = $cfg{'tty'}->{'width'};
    if ( 10 > $cols ) {
        $cols = $ENV{COLUMNS} || 0;
        if ( $cols =~ m/^[1-9][0-9]+$/ ) {
            $cfg{'tty'}->{'width'} = $cols;
            return;
        }
        $cols = qx(\\tput cols 2>/dev/null) || undef;
        if ( not defined $cols ) {
            $cols = qx(\\stty size 2>/dev/null)
              || $_len;
            $cols =~ s/^[^ ]* //;
        }
        $cfg{'tty'}->{'width'} = $cols;
    }
    $cfg{'tty'}->{'width'} = 80 if ( 10 > $cfg{'tty'}->{'width'} );
    _man_dbx( "_man_use_tty: " . $cfg{'tty'}->{'width'} );
    return;
}

sub _man_squeeze {

    my $len = shift;
    my $txt = shift;
    return $txt if not defined $cfg{'tty'}->{'width'};
    $txt =~ s/[\t]/    /g;
    my $max   = $cfg{'tty'}->{'width'} - 2;
    my $ident = ' ' x $cfg{'tty'}->{'ident'};
    if ( defined $len ) {
        $ident = "$cfg{'tty'}->{'arrow'}\n" . ' ' x $len;
        $txt =~ s/(.{$max})/$1$ident/g;
    }
    else {
        $txt =~ s/\n {8}/$ident/g;
        $ident = "$cfg{'tty'}->{'arrow'}\n" . $ident;
        $max--;
    }
    $txt =~ s/(.{$max})/$1$ident/g;
    return $txt;
}

sub _man_usr_value {
    my $key = shift;
    $key =~ s/^(?:--|\+)//;
    my @arg = "";
    map( { @arg = split( /=/, $_, 2 ) if /^$key/ } @{ $cfg{'usr_args'} } );

    return $arg[1];
}

sub _man_html_init {
    my $cgi_bin = _man_usr_value('user-action') || _man_usr_value('usr-action') || "/cgi-bin/o-saft.cgi";
    $html{'action'}        =~ s/__HTML_cgi_bin__/$cgi_bin/g;
    $html{'form_anf'}      =~ s/__HTML_cgi_bin__/$cgi_bin/g;
    $html{'script_endcgi'} =~ s/__HTML_cgi_bin__/$cgi_bin/g;
    $html{'body_anf'}      =~ s/__HTML_version__/$version/g;
    $html{'body_anf'}      =~ s/__HTML_title__/$html{'title'}/g;
    $html{'meta'}          =~ s/__HTML_title__/$html{'title'}/g;
    return;
}

sub _get_file {
    my $typ = shift;
    return ODoc::get('glossary.txt') if ( 'abbr' eq $typ );
    return ODoc::get('links.txt')    if ( 'links' eq $typ );
    return ODoc::get('rfc.txt')      if ( 'rfc' eq $typ );
    return '';
}

sub _man_http_head {
    return "" if ( 0 >= ( grep { /--cgi.?trace/ } @ARGV ) );
    return
        "X-Cite: Perl is a mess. But that's okay, because the problem space is also a mess. Larry Wall\r\n"
      . "Content-type: text/html; charset=utf-8\r\n" . "\r\n"
      . _man_dbx("_man_http_head() ...") ;
}

sub _man_html_head {
    _man_dbx("_man_html_head() ...");
    return
        $html{'doctype'}
      . '<html><head>'
      . $html{'meta'}
      . $html{'script_nonce'}
      . $html{'script_func1'}
      . $html{'script_func2'}
      . '</script>' . "\n"
      . '<style>'
      . $html{'style_root'}
      . $html{'style_button'}
      . $html{'style'}
      . '</style>' . "\n"
      . '</head>' . "\n"
      . $html{'body_anf'};
}

sub _man_html_details {
    my $sum  = shift;
    my $open = shift;
    my $txt  = shift;
    return << "EoDetails";
    <details $open><summary>$sum</summary>
      <div>
$txt
      </div>
    </details><!-- $sum -->
EoDetails
}

sub _man_help_button {
    my $cmd   = shift;
    my $class = shift;
    my $title = shift;
    my $href  = $html{'action'};
    my $txt   = $cmd;
    $txt =~ s/.*--.*help=//;
    $txt =~ s/&.*$//;
    $class = qq(class="$class") if ( $class !~ m/^\s*$/ );
    return qq(        <a $class target="_help" href="$href?--cgi&--header&$cmd" title="$title" >$txt</a>\n);
}

sub _man_cmd_button {
    my $cmd = shift;
    return qq(        <input target="_cmd" type="submit" name="--cmd" value="$cmd" title="execute o-saft.pl $cmd" >\n);
}

sub _man_opt_button {
    my $opt = shift;
    my $val = shift;
    return qq(        <label><input type="checkbox" name="$opt" value="$val" >$opt</label>\n);
}

sub _man_menu_bar {
    my $menu =
        _man_help_button( "--help=ciphers-html&--content-type=html", '', "open window with list of cipher suites (html format)" )
      . qq(        <a target="_help" href="doc/o-saft.html#aABOUT%20CGI" >! About (this CGI form)</a>)
      . qq(        <a target="_help" href="doc/o-saft.html" >? Help (complete help)</a>);
    my $cmds;
    $cmds .= _man_cmd_button($_) foreach qw(+check +cipher +info +quick +vulns +protocols);
    my $opts = _man_opt_button( '--format', 'html' );
    $opts .= _man_opt_button( $_, '' ) foreach qw(--header --enabled --no-dns --no-http --no-sni --no-sslv2 --no-sslv3);
    my $help =
        _man_help_button( "--help", '', "open window with complete help (plain text)" )
      . _man_help_button( "--help=command",                          '', "open window with help for commands" )
      . _man_help_button( "--help=checks",                           '', "open window with help for checks" )
      . _man_help_button( "--help=example",                          '', "open window with examples" )
      . _man_help_button( "--help=opt",                              '', "open window with help for options" )
      . _man_help_button( "--help=FAQ",                              '', "open window with FAQs" )
      . _man_help_button( "--help=abbr",                             '', "open window with the glossar" )
      . _man_help_button( "--help=todo",                             '', "open window with help for ToDO" )
      . _man_help_button( "--help=ciphers-text",                     '', "open window with list of cipher suites (text format)" )
      . _man_help_button( "--help=ciphers-html&--content-type=html", '', "open window with list of cipher suites (html format)" );
    return
        qq(  <div  class="navdiv">\n)
      . _man_html_details( "☰",    '', $menu )
      . _man_html_details( "Cmd",  '', $cmds )
      . _man_html_details( "Opt",  '', $opts )
      . _man_html_details( "Help", '', $help )
      . qq(  </div> <!-- class=navdiv -->\n);
}

sub _man_cgi_simple {
    my $txt = qq(       <table id="osaft_buttons">\n       </table>\n);
    $txt .= qq(       <hr>\n);
    $txt .= qq(       <div class="n">\n);
    foreach my $key (
        qw(no-sslv2 no-sslv3 no-tlsv1 no-tlsv11 no-tlsv12 no-tlsv13 BR
        no-dtlsv1 no-dtlsv11 no-dtlsv12 no-dtlsv13   BR
        no-dns dns no-cert BR
        no-sni sni   BR
        no-http http BR
        header  no-header  no-warnings  no-hints     BR
        html4   html5  format=hex       BR
        enabled disabled   legacy=owasp BR
        traceKEY traceCMD  trace v     cgi-no-header BR
        )
      )
    {
        if ( 'BR' eq $key ) { $txt .= "        <br>\n"; next; }
        my $tag_txt = "--$key";
        my $tag_nam = $key;
        my $tag_val = "";
        ( $tag_nam, $tag_val ) = split( /=/, $key ) if ( $key =~ m/=/ );
        $tag_nam = "--$tag_nam";
        $txt .= _man_html_cbox( 'cgi', "        ", "q$tag_txt", $tag_nam, $tag_val, $tag_txt ) . "\n";
    }
    $txt .= "       </div><!-- class=n -->";
    $txt .= _man_html_go("cgi");
    return $txt;
}

sub _man_html_form {
    my $cgi_bin = $html{'action'};
    my $txt;
    _man_dbx("_man_html_form() ...");
    return $html{'form_anf'} . _man_menu_bar() . $html{'fieldset'} . _man_html_details( "Simple GUI", '', _man_cgi_simple() ) . _man_html_details(
        "Full GUI Commands & Options", 'open',
        _man_html( 'cgi', 'COMMANDS', 'LAZY' )
          . '<input type=reset  value="clear" title="clear all settings or reset to defaults"/>'
    ) . $html{'form_end'} . $html{'script_endcgi'};
}

sub _man_html_foot {
    _man_dbx("_man_html_foot() ...");
    return $html{'links'} . $html{'copyright'} . $html{'script_endall'} . '</body></html>';
}

sub _man_html_cbox {

    my ( $mode, $prefix, $tag_id, $tag_nam, $tag_val, $cmd_txt ) = @_;
    my $title = '';
    return $cmd_txt if ( $mode ne 'cgi' );
    return sprintf( qq(%s<label class="i" for="%s"><input type="checkbox" id="%s" name="%s" value="%s" title="%s" >%s</label>&#160;&#160;),
        $prefix, $tag_id, $tag_id, $tag_nam, $tag_val, $title, $cmd_txt );
}

sub _man_html_chck {
    my $mode    = shift;
    my $cmd_opt = shift || "";
    my $tag_nam = $cmd_opt;
    my $tag_val = '';
    return ''       if ( $cmd_opt !~ m/^(?:-|\+)+/ );
    return $cmd_opt if ( $mode ne 'cgi' );

    if ( $cmd_opt =~ m/^(?:\+)/ ) {
        $tag_val = scalar( ( split( /\s+/, $cmd_opt ) )[0] );
        $tag_nam = '--cmd';
    }
    else {

        $tag_val = '';
        $tag_nam = scalar( ( split( /\s+/, $cmd_opt ) )[0] );
        my ( $key, $val ) = split( /=/, $tag_nam );
        if ( defined $val && $val =~ m/^[A-Z0-9:_-]+/ ) {
            my $label = qq(<label class="l" >$key=</label>);
            my $input = qq(<input type="text" id="$tag_nam" name="$key" value="" placeholder="$val">);
            return "$label$input";
        }
    }
    return _man_html_cbox( $mode, "", "o$cmd_opt", $tag_nam, $tag_val, $cmd_opt );
}

sub _man_name_ankor {
    my $n = shift;
    $n =~ s/,//g;

    return $n;
}

sub _man_html_ankor {
    my $n = shift;
    my $a = '';
    return qq(<a name="a$n"></a>) if ( $n !~ m/^[-\+]+/ );
    foreach my $n ( split( /[\s,]+/, $n ) ) {
        $a .= sprintf( "<a name='a%s'></a>", _man_name_ankor($n) );
    }
    return $a;
}

sub _man_html_go {
    my $key = shift;
    return "" if ( $key ne 'cgi' );
    my $top = qq(        <a class="b" href="#aFORM" title="return to top">^</a>\n);
    my $run = qq(        <input type="submit" value="start" title="execute o-saft.pl with selected commands and options"/>\n);
    return "$top$run";
}

sub _man_html_cmds {
    my $key  = shift;
    my $txt  = "";
    my $cmds = _man_cmd_from_source();

    _man_dbx("_man_html_cmds($key) ...");
    foreach my $cmd ( split( /[\r\n]/, $cmds ) ) {
        next if ( $cmd =~ m/^\s*$/ );
        $cmd =~ s/^\s*//;
        if ( $cmd =~ m/^[+]/ ) {
            my $desc = "";
            ( $cmd, $desc ) = split( /\s+/, $cmd, 2 );
            $txt .= sprintf( "<b>%s </b> %s<br />\n", _man_html_cbox( $key, "", "c$cmd", "--cmd", $cmd, $cmd ), $desc );
        }
        else {
            $txt .= _man_html_go($key) . "\n";
            $txt .= sprintf( "%s\n<h3>%s</h3>\n", _man_html_ankor($cmd), $cmd );
        }
    }
    return $txt;
}

sub _man_html {

    my $key = shift;
    my $anf = shift;
    my $end = shift;
    my $txt;
    my @head;
    my $skip = 0;
    my $c    = 0;
    my $h    = 0;
    my $a    = "";
    my $p    = "";
    _man_dbx("_man_html($key, $anf, $end) ...");

    while ( $_ = shift @help ) {
        last if /^TODO/;
        $h = 1 if /^=head1 $anf/;
        $h = 0 if /^=head1 $end/;
        next if ( 0 == $h );
        if ( 0 < $skip ) { $skip--; next; }

        m!<<\s*undef! or s!<<!&lt;&lt;!g;
        m/^=head1 (.*)/ && do {
            push( @head, $1 );
            $txt .= sprintf( "$p\n<h1>%s %s </h1>\n", _man_html_ankor($1), $1 );
            $p = "";
            next;
        };
        m/^=head2 (.*)/ && do {
            my $x = $1;
            if ( $x =~ m/Discrete commands to test/ ) {
                $txt .= _man_html_cmds($key);
            }
            else {
                $txt .= _man_html_go($key);
                $txt .= _man_html_ankor($x) . "\n";
                $txt .= sprintf( "<h3>%s %s </h3> <p>\n", _man_html_chck( $key, $x ), $x );
            }
            next;
        };
        m/^=head3 (.*)/ && do {
            $a = $1;
            if ( 'cgi' eq $key ) {
                $txt .= _man_help_button( $a, "b r", "open window with special help" ) if ( $a =~ m/--help/ );
            }
            $txt .= _man_html_ankor($a) . "\n";
            $txt .= sprintf( "<h4>%s </h4> <p>\n", _man_html_chck( $key, $a ) );
            next;
        };
        m/Discrete commands,/ && do { $skip = 2; next; };

        m/(--help=[A-Za-z0-9_.-]+)/ && do {
            if ( 'cgi' eq $key ) {
                $txt .= _man_help_button( $1, "b r", "open window with special help" );
            }
        };
        m/^\s*S&([^&]*)&/ && do {
            my $v = $1;
            $v =~ s!<<!&lt;&lt;!g;
            $txt .= qq(<div class="c" >$v</div>\n);
            next;
        };
        s!"([^"]*)"!<cite>$1</cite>!g;
        s!'([^']*)'!<span class="c" >$1</span>!g;

        m![IX]&(?:[^&]*)&! && do {
            s/\s+&/&/g;
        };
        s!I&([^&]*)&!<a href="#a$1">$1</a>!g;
        s!X&([^&]*)&!<a href="#a$1">$1</a>!g;
        s!L&([^&]*)&!<i>$1</i>!g;

        s!^\s+($mytool .*)!<div class="c" >$1</div>!;

        s!(^=item +\*\*? )(.+)[|-]( .*)!$1<span class="d">$2</span>&ndash;$3!g;
        m/^=item +\* (.*)/   && do { $txt .= qq(<li>$1</li>\n);             next; };
        m/^=item +\*\* (.*)/ && do { $txt .= qq(<li class="l2">$1 </li>\n); next; };
        s/^(?:=[^ ]+ )//;
        s!<<!&lt;&lt;!g;

        m/^\s*$/ && do {
            $a = "id='h$a'" if ( '' ne $a );
            $txt .= "$p<p $a>";
            $p = "</p>";
            $a = '';
        };
        s!(^ {12}.*)!<li class="n">$1</li>!;
        $txt .= $_;
    }
    $txt .= "$p";
    my $toc;
    $toc .= sprintf( "  <a href=\"#a%s\">%s</a>\n", $_, $_ ) foreach @head;
    $html{'body_aside'} =~ s/__HTML_aside__/$toc/g;
    $txt .= $html{'body_aside'};
    return $txt;
}

sub _man_head {
    my @args = @_;
    my $len1 = shift @args;
    _man_dbx("_man_head(..) ...");
    my $len0 = $len1 - 1;
    return "" if ( 1 > $cfg_header );
    return sprintf( "=%${len0}s | %s\n", @args ) . sprintf( "=%s+%s\n", '-' x $len1, '-' x 60 );
}

sub _man_foot {
    my $len1 = shift;
    return "" if ( 1 > $cfg_header );
    return sprintf( "=%s+%s\n", '-' x $len1, '-' x 60 );
}

sub _man_opt {
    my ( $key, $sep, $val ) = @_;
    my $len = 16;
    $len = 1 if ( "=" eq $sep );
    my $txt = sprintf( "%${len}s%s%s\n", $key, $sep, $val );
    return _man_squeeze( ( 16 + length($sep) ), $txt );
}

sub _man_cfg {
    my ( $typ, $key, $sep, $txt ) = @_;
    $txt = '"' . $txt . '"' if ( $typ =~ m/^cfg(?!_cmd)/ );
    $key = "--$typ=$key"    if ( $typ =~ m/^cfg/ );
    return _man_opt( $key, $sep, $txt );
}

sub _man_txt {
    my ( $typ, $key, $sep, $txt ) = @_;
    $txt =~ s/(\n)/\\n/g;
    $txt =~ s/(\r)/\\r/g;
    $txt =~ s/(\t)/\\t/g;
    return _man_cfg( $typ, $key, $sep, $txt );
}

sub _man_pod_item {
    my $line = shift;
    return "=over\n\n$line\n=back\n";
}

sub _man_doc_opt {
    my ( $typ, $sep, $format ) = @_;
    my $url = "";
    my @txt = _get_file($typ);
    my $opt;
    foreach my $line (@txt) {
        chomp $line;
        next if ( $line =~ m/^\s*$/ );
        next if ( $line =~ m/^\s*#/ );
        my ( $key, $val ) = split( "\t", $line );
        $key =~ s/\s*$//;
        if ( 'rfc' eq $typ ) {
            $url = $val if ( $key eq "url" );
            $val = $val . "\n\t\t\t$url/html/rfc$key";
            $key = "RFC $key";
        }
        $opt .= _man_opt( $key, $sep, $val )      if ( 'opt' eq $format );
        $opt .= _man_pod_item("$key $sep $val\n") if ( 'POD' eq $format );
    }
    return $opt;
}

sub _man_doc_pod {
    my ( $typ, $sep ) = @_;
    my @txt = _get_file($typ);
    my $help = "@txt";
    $help =~ s/\n/\n#/g;
    return << "EoHelp";
# begin $typ

# =head1 $typ

$help
# end $typ

EoHelp
}

sub _man_pod_head {
    my $txt = <<'EoHelp';
#!/usr/bin/perldoc
#?
# Generated by o-saft.pl .
# Unfortunately the format in  @help is incomplete,  for example proper  =over
# and corresponding =back  paragraph is missing. It is mandatory around  =item
# paragraphs. However, to avoid tools complaining about that,  =over and =back
# are added to each  =item  to avoid error messages in the viewer tools.
# Hence the additional identations for text following the =item are missing.
# Tested viewers: podviewer, perldoc, pod2usage, tkpod

EoHelp
    $txt .= "=pod\n\n";
    $txt .= "=encoding utf8\n\n";
    return $txt;
}

sub _man_pod_text {
    my $code  = 0;
    my $empty = 0;
    my $pod;
    while ( $_ = shift @help ) {
        last if m/^(?:=head[1] )?END\s+#/;
        m/^$/ && do {
            if ( 0 == $empty ) { $pod .= $_; $empty++; }
            next;
        };
        s/^(\s*(?:o-saft\.|checkAll|yeast\.).*)/S&$1&/;
        s/^ {1,13}//;
        s/^S&\s*([^&]*)&/\t$1/ && do {
            $pod .= "\n" if ( 0 == ( $empty + $code ) );
            $pod .= $_;
            $empty = 0;
            $code++;
            next;
        };
        $code = 0;
        s:['`]([^']*)':C<$1>:g;
        s:(^|\s)X&([^&]*)&:$1L</$2>:g;
        s:(^|\s)L&([^&]*)&:$1L<$2|$2>:g;

        s:(^|\s)I&([^&]*)&:$1I<$2>:g;
        s/^([A-Z., -]+)$/B<$1>/;
        s/^(=item)\s+(.*)/$1 $2/;
        my $line = $_;
        m/^=/ && do {

            $pod .= "\n"                  if ( 0 == $empty );
            $pod .= "$line"               if $line =~ m/^=[hovbefpc].*/;
            $pod .= _man_pod_item "$line" if $line =~ m/^=item/;
            $pod .= "\n";
            $empty = 1;
            next;
        };
        $pod .= "$line";
        $empty = 0;
    }
    return $pod;
}

sub _man_pod_foot {
    my $pod = <<'EoHelp';
Generated with:

        o-saft.pl --no-warnings --no-header --help=gen-pod > o-saft.pod

EoHelp
    $pod .= "=cut\n\n";
    $pod .= _man_doc_pod( 'abbr', "-" );
    $pod .= _man_doc_pod( 'rfc',  "-" );
    $pod .= $_voodoo;
    return $pod;
}

sub _man_pod_file {
    my $pod = "#!/usr/bin/perldoc\n#\n# Generated by" . __FILE__ . "\n\n";
    $pod .= "=pod\n\n";
    $pod .= "=encoding utf8\n\n";
    return $pod . _man_pod_text();
}

sub _man_wiki_head {
    return <<'EoHelp';
==O-Saft==
This is O-Saft's documentation as you get with:
 o-saft.pl --help
<small>On Windows following must be used:
 o-saft.pl --help --v
</small>

__TOC__ <!-- autonumbering is ugly here, but can only be switched of by changing MediaWiki:Common.css -->
<!-- position left is no good as the list is too big and then overlaps some texts
{|align=right
 |<div>__TOC__</div>
 |}
-->

[[Category:OWASP Project]]  [[Category:OWASP_Builders]]  [[Category:OWASP_Defenders]]  [[Category:OWASP_Tool]]  [[Category:SSL]]  [[Category:Test]]
----
EoHelp
}

sub _man_wiki_text {
    my $pod;
    my $mode = shift;
    while ( $_ = shift @help ) {
        last if /^=head1 TODO/;
        s/^=head1 (.*)/====$1====/;
        s/^=head2 (.*)/=====$1=====/;
        s/^=head3 (.*)/======$1======/;
        s/^=item (\*\* .*)/$1/;
        s/^=item (\* .*)/$1/;
        s/^=[^= ]+ *//;
        m/^=/ && do { $pod .= $_; next; };
        s:['`]([^']*)':<code>$1</code>:g;
        s/^S&([^&]*)&/  $1/ && do { $pod .= $_; next; };
        s/X&([^&]*)&/[[#$1|$1]]/g;
        s/L&([^&]*)&/\'\'$1\'\'/g;
        s/I&([^&]*)&/\'\'$1\'\'/g;
        s/^ +//;

        if ( 'colon' eq $mode ) {
            s/^([^=].*)/:$1/;
        }
        else {
            s/^([^=*].*)/:$1/;
        }
        s/^:?\s*($mytool)/  $1/;
        s/^:\s+$/\n/;
        $pod .= $_;
    }
    return $pod;
}

sub _man_wiki_foot {
    return <<'EoHelp';
----
<small>
Content of this wiki page generated with:
 o-saft.pl --no-warning --no-header --help=gen-wiki
</small>

EoHelp
}

sub _man_cmd_from_cfg {
    _man_dbx("_man_cmd_from_cfg() ...");
    my $txt = "\n                  Commands defined internal as summary commands\n";
    foreach my $key ( sort( keys %cfg ) ) {
        next if ( $key !~ m/^cmd-(.*)/ );
        $key = $1;
        my $len = "%-17s";
        $len = "%s " if ( length($key) > 16 );
        $txt .= sprintf( "+$len%s\n", $1, "(see $0 --help=cmds)" );
    }
    return $txt;
}

sub _man_cmd_from_source {
    _man_dbx("_man_cmd_from_source() ...");
    my $txt  = "";
    my $skip = 1;
    my $fh   = undef;
    _man_dbx("_man_cmd_from_source: reading lib/OData.pm");
    if ( open( $fh, '<:encoding(UTF-8)', _get_filename("lib/OData.pm") ) ) {
        while (<$fh>) {
            if (m/^(?:my|our)\s+%(?:check_(?:[a-z0-9_]+)|data)\s*=\s*\(\s*##*\s*(.*)/) {
                $skip = 0;
                $txt .= "\n                  Commands to show results of checked $1\n";
                next;
            }
            if (m/^\s*\)\s*;/) { $skip = 1; next; }
            next if ( 1 == $skip );
            next if (m/^\s*'(?:SSLv2|SSLv3|D?TLSv1|TLSv11|TLSv12|TLSv13)-/);
            if (m/^\s+'([^']*)'.*"([^"]*)"/) {
                my $key = $1;
                my $val = $2;
                my $len = "%-17s";
                $len = "%s " if ( length($key) > 16 );
                my $t = "\t";
                $txt .= sprintf( "+$len%s\n", $1, $2 );
            }
        }
        close($fh);
    }
    else {
        $txt .= sprintf( "%s cannot read '%s'; %s\n", $OText::STR{ERROR}, _get_filename("o-saft.pl"), $! );
    }
    return $txt;
}

sub _man_cmd_from_rcfile {
    my $txt = "\n                  Commands locally defined in $cfg{'RC-FILE'}\n";
    my $val = "<<description missing>>";
    my $fh  = undef;
    _man_dbx("_man_cmd_from_rcfile: reading $cfg{'RC-FILE'}");
    if ( open( $fh, '<:encoding(UTF-8)', $cfg{'RC-FILE'} ) ) {
        while (<$fh>) {
            if (m/^##[?]\s+([a-zA-Z].*)/) {
                $val = $1;
                next;
            }
            if (m/^--cfg_cmd=([^=]*)=/) {
                $txt .= sprintf( "+%-17s%s\n", $1, $val );
                $val = "<<description missing>>";
            }
        }
        close($fh);
    }
    else {
        $txt .= sprintf( "%s cannot read '%s'; %s\n", $OText::STR{ERROR}, $cfg{'RC-FILE'}, $! );
    }
    return $txt;
}

sub _man_ciphers_get {
    _man_dbx("_man_ciphers_get() ..");
    my $ciphers = "";
    foreach my $key ( sort( keys %ciphers ) ) {
        my $name = Ciphers::get_name($key);
        next if not $name;
        next if $name =~ m/^\s*$/;
        my $sec   = Ciphers::get_sec($key);
        my $hex   = Ciphers::key2text($key);
        my $mac   = Ciphers::get_mac($key);
        my @alias = Ciphers::get_names($key);
        my @_keep = grep { $alias[$_] ne $name } 0 .. $#alias;
        @alias = @alias[@_keep];
        my $pfs  = Ciphers::get_pfs($key);
        my $rfc  = Ciphers::get_rfc($key);
        my $rfcs = "";

        foreach my $key ( split( /,/, $rfc ) ) {
            my $num = $key;
            $num =~ s/[^0-9]//g;
            if ( "" eq $num ) {
                $rfcs .= $key;
            }
            else {
                $rfcs .= "https://www.rfc-editor.org/rfc/rfc$num";
            }
            $rfcs .= " , ";
        }
        $rfcs =~ s/ , $//;
        $ciphers .=
            "\n$hex\t$sec\t$name"
          . "\nname\t"
          . $name
          . "\nnames\t"
          . join( ', ', @alias )
          . "\nconst\t"
          . join( ', ', Ciphers::get_consts($key) )
          . "\nopenssl\t"
          . Ciphers::get_openssl($key)
          . "\nssl\t"
          . Ciphers::get_ssl($key)
          . "\nkeyx\t"
          . Ciphers::get_keyx($key)
          . "\nauth\t"
          . Ciphers::get_auth($key)
          . "\nenc\t"
          . Ciphers::get_enc($key)
          . "\nbits\t"
          . Ciphers::get_bits($key)
          . "\nenc_size\t"
          . Ciphers::get_encsize($key)
          . "\nmac\t"
          . $mac
          . "\nmac_size\t" . ''
          . "\npfs\t"
          . $pfs
          . "\nrfc\t"
          . $rfcs
          . "\nnotes\t"
          . Ciphers::get_notes($key) . "\n";
    }
    return $ciphers;
}

sub _man_ciphers_html_dl {
    my $dl = shift;
    $dl =~ s/\n$//;
    return << "EoHTML";
    <div>
      <dl>
$dl
      </dl>
    </div>
EoHTML
}

sub _man_ciphers_html_li {
    my ( $hex, $sec, $name, $dl ) = @_;
    $name = "" if not defined $name;
    $dl =~ s/\n$//;
    return << "EoHTML";

  <details title="show details">
    <summary> <span>$hex</span> <span sec="$sec">$sec</span> $name </summary>
$dl
  </details>
EoHTML
}

sub _man_ciphers_html_ul {
    my $ciphers = shift;
    my $ul      = '';
    my ( $hex, $sec, $name, $dl );
    $dl = "";
    foreach my $line ( split( /\n/, $ciphers ) ) {
        chomp($line);
        next if $line =~ m/^\s*$/;
        $line =~ s/^\s*//;
        ( $hex, $sec, $name ) = split( /\t/, $line );
        if ( $line =~ m/^0x/ ) {
            if ( "" ne $dl ) {
                $ul .= _man_ciphers_html_li( $hex, $sec, $name, _man_ciphers_html_dl($dl) );
                $dl = "";
            }
            ( $hex, $sec, $name ) = split( /\t/, $line );
            next;
        }
        my ( $key, $val ) = split( /\t/, $line );
        my $txt = $key;
        $txt =~ s/$key/$ciphers_desc{$key}/;
        $sec = "";
        $sec = "sec='$val'" if ( "openssl" eq $key );
        $sec = "sec='$val'" if ( "sec" eq $key );
        $dl .= "      <dt>${txt}:</dt><dd $sec typ='$val' ><t> </t></dd><br />\n";
    }
    $ul .= _man_ciphers_html_li( $hex, $sec, $name, _man_ciphers_html_dl($dl) ) if ( "" ne $dl );
    return "$ul\n";
}

sub _man_ciphers_html_tb {
    my $ciphers = shift;
    my $tab     = '  <table><thead>';
    $tab .= "\n    <tr>\n";
    $tab .= "      <th rowspan=2>$ciphers_desc{'hex'}</th>\n";
    $tab .= "      <th rowspan=2>$ciphers_desc{'sec'}</th>\n";
    $tab .= "      <th colspan=3>Names</th>\n";
    $tab .= "      <th rowspan=2>$ciphers_desc{'openssl'}</th>\n";
    $tab .= "      <th rowspan=2>$ciphers_desc{'ssl'}</th>\n";
    $tab .= "      <th rowspan=2>$ciphers_desc{'keyx'}</th>\n";
    $tab .= "      <th rowspan=2>Authen-tication</th>\n";
    $tab .= "      <th colspan=3>Encryption</th>\n";
    $tab .= "      <th colspan=1>MAC</th>\n";
    $tab .= "      <th rowspan=2>$ciphers_desc{'pfs'}</th>\n";
    $tab .= "      <th rowspan=2>RFC(s)&#xa0;URL</th>\n";
    $tab .= "      <th rowspan=2>$ciphers_desc{'notes'}</th>\n";
    $tab .= "    </tr>\n";
    $tab .= "\n    <tr>\n";

    foreach my $key (qw(suite names const enc bits enc_size mac)) {
        my $txt = $ciphers_desc{$key};
        $txt =~ s|^Encryption ||;
        $txt =~ s|MAC\s*/\s*HASH||i;
        $tab .= "      <th>$txt</th>\n";
    }
    $tab .= "    </tr></thead><tbody>\n";
    my ( $hex, $sec, $name, $td );
    $td = "";
    foreach my $line ( split( /\n/, $ciphers ) ) {
        chomp($line);
        next if $line =~ m/^\s*$/;
        next if $line =~ m/^mac_/;
        next if $line =~ m/^name\s/;
        $line =~ s/^\s*//;
        if ( $line =~ m/^0x/ ) {
            if ( "" ne $td ) {
                $tab .= "    <tr>\n$td    </tr>\n";
                $td = "";
            }
            ( $hex, $sec, $name ) = split( /\t/, $line );
            $td .= "        <td>$hex</td>\n";
            $td .= "        <td><span sec='$sec'>$sec</span></td>\n";
            $td .= "        <td>$name</td>\n";
            next;
        }
        my ( $key, $val ) = split( /\t/, $line );
        $sec = "";
        $sec = "sec='$val'" if ( "openssl" eq $key );
        $sec = "sec='$val'" if ( "sec" eq $key );
        $td .= "        <td typ='$val' $sec><t> </t></td>\n";
    }
    $tab .= "    <tr>\n$td    </tr>\n" if ( "" ne $td );
    return "$tab\n  </tbody></table>\n";
}

sub man_docs_write {
    _man_dbx("man_docs_write() ...");
    if ( $ich =~ m/^OMan/ ) {
        OCfg::warn( "094:", "'$parent' used as program name in generated files" );
        OCfg::hint("documentation files should be generated using '$cfg{files}{SELF} --help=gen-docs'");
    }
    my $fh = undef;
    foreach my $mode ( sort keys %{ $cfg{'files'} } ) {
        next if $mode !~ m/^--help/;
        next if $mode =~ m/^--help=warnings/;
        my $doc = "$cfg{'files'}{$mode}";
        _man_dbx("man_docs_write: writing $doc ; mode=$mode");
        open( $fh, '>:encoding(UTF-8)', $doc ) or do {
            OCfg::warn( "093:", "help file '$doc' cannot be opened: $! ; ignored" );
            next;
        };
        print $fh man_alias()         if ( $mode =~ /alias$/ );
        print $fh man_commands()      if ( $mode =~ /commands?$/ );
        print $fh man_options()       if ( $mode =~ /opts$/ );
        print $fh man_ciphers('text') if ( $mode =~ /ciphers.?text$/ );
        print $fh man_help('NAME')    if ( $mode =~ /help$/ );
        print $fh man_table('check')  if ( $mode =~ /checks$/ );
        print $fh man_table('rfc')    if ( $mode =~ /rfc$/ );
        print $fh man_table('regex')  if ( $mode =~ /regex$/ );
        print $fh man_table('abbr')   if ( $mode =~ /glossary?$/ );
        print $fh man_table('data')   if ( $mode =~ /data$/ );
        close($fh);
    }
    exit(0);
    return;
}

sub man_help_brief {
    _man_dbx("man_help_brief() ...");
    my %opts;
    my $skip = 1;
    my $idx  = 0;
    my $key  = "";
    foreach my $line (@help) {

        $skip = 1 if ( $line =~ m/^=head2\s+Options for / );
        $skip = 0 if ( $line =~ m/^=head2\s+Options for help/ );
        next if ( $line =~ m/^=head2\s+Options for help/ );
        next if ( 1 == $skip );
        next if ( $line =~ m/^\s*$/ );
        chomp $line;
        if ( $line =~ m/^=head3\s+--h/ ) {
            $idx++;
            $key = $line;
            $key =~ s/^=head3\s+//;
            $opts{$idx}->{'opt'} = $key;
            next;
        }
        $line =~ s/^\s*//;
        $line =~ s![ISX]&([^&]*)&!$1!g;
        $line = sprintf( "\n%17s %s", " ", $line ) if ( defined $opts{$idx}->{'txt'} );
        $opts{$idx}->{'txt'} .= $line;
    }
    my $pod = "\n" . _man_head( 15, "Option", "Description" );
    foreach my $key ( sort { $a <=> $b } keys %opts ) {
        $pod .= sprintf( "%-17s %s\n", $opts{$key}->{'opt'}, $opts{$key}->{'txt'} || "" );
    }
    $pod .= _man_foot(15);
    $pod .= "\n" . _man_head( 15, "Command", "Description" );
    $pod .= _man_squeeze( 18, $_cmd_brief );
    $pod .= _man_foot(15);
    my $opt = "";
    $opt = " --header" if ( 0 < $cfg_header );
    $pod .= qq(\nFor more options  see: $cfg{me}$opt --help=opt);
    $pod .= qq(\nFor more commands see: $cfg{me}$opt --help=commands\n\n);
    return $pod;
}

sub man_commands {
    _man_dbx("man_commands($parent) ...");
    my $txt = "\n" . _man_head( 15, "Command", "Description" );
    $txt .= _man_squeeze( 18, $_commands );
    $txt .= _man_squeeze( 18, _man_cmd_from_source() );
    $txt .= _man_squeeze( 18, _man_cmd_from_cfg() );
    $txt .= _man_squeeze( 18, _man_cmd_from_rcfile() );
    $txt .= _man_foot(15) . "\n";
    return $txt;
}

sub man_warnings {
    _man_dbx("man_warnings($parent) ...");
    my $pod = "";
    my $txt = "";
    my $fh  = undef;
    local $/ = undef;
    my $doc = "$cfg{'dirs'}->{'doc'}/o-saft.pl.--help=warnings";
    _man_dbx("man_warnings: reading $doc");
    if ( 0 < ( grep { /^--v/ } @ARGV ) ) {
        printf( "%s%s\n", $OText::STR{'INFO'} || '**INFO: ', "  man_warnings: reading $doc" );
    }
    if ( not open( $fh, '<:encoding(UTF-8)', $doc ) ) {
        OCfg::warn( "091:", "help file '$doc' cannot be opened: $! ; ignored" );
        OCfg::hint( $cfg{'hints'}->{'help=warnings'} );
        return $pod;
    }
    $txt = <$fh>;
    close($fh);
    $pod .= <<"EoHelp";

=== Warning and error messages ===

= Messages numbers and texts used in $cfg{'me'} and its own modules.
= Note that message texts may contain variables, like '\$key', which are
=      replaced with propper texts at runtime.
=
= Each warning has a unique number. The numbers are grouped as follows:
=
=     0xx     startup check, options, arguments
=     1xx     check (runtime) functionality
=     2xx     loop targets (hosts)
=     3xx     connect functions
=     4xx     cipher check functions
=     5xx     inernal check functions
=     6xx     check functions
=     8xx     print functions

# TODO: some missing, i.e. 002: 003: 004:

EoHelp
    $pod .= _man_head( 15, "Error/Warning", "Message text" );
    $pod .= $txt;
    $pod .= _man_foot(15);
    return $pod;
}

sub man_opt_help {
    _man_dbx("man_opt_help() ..");
    my $txt = "";
    foreach (@help) { $txt .= $_ if ( m/Options for help and documentation/ .. m/Options for all commands/ ); }
    $txt =~ s/^=head.//msg;
    $txt =~ s/Options for all commands.*.//msg;
    $txt = _man_squeeze( undef, $txt );
    return $txt;
}

sub man_ciphers_html {
    my $txt = shift;
    _man_dbx("man_ciphers_html() ..");
    my $cnt = scalar( keys %ciphers );
    my $htm =
        $html{'doctype'}
      . '<html><head>'
      . $html{'meta'}
      . '<style>'
      . $html{'style_root'}
      . $html{'style_button'}
      . $html{'style_ciphers'}
      . '</style></head>'
      . << "EoHTML";

<body>
  <h2><span id="txt" >$html{'title'}</span>
  <a class="b" title="Toggle Layout: table or list" href="/cgi-bin/o-saft.cgi?--cgi&--header&--content-type=html&--help=ciphers-list">table <> list</a>
  </h2>
  <h1> $cnt Cipher Suites</h1>
EoHTML

    $htm .= _man_ciphers_html_tb($txt);
    $htm .= '</body></html>';
    return $htm;
}

sub man_ciphers_list {
    my $txt = shift;
    _man_dbx("man_ciphers_html() ..");
    my $cnt  = scalar( keys %ciphers );
    my $head = $html{'meta'};
    my $htm =
        $html{'doctype'}
      . '<html><head>'
      . $html{'meta'}
      . '<style>'
      . $html{'style_root'}
      . $html{'style_button'}
      . $html{'style_ciphers'}
      . '</style></head>'
      . << "EoHTML";

<body>
  <h2><span id="txt" >$html{'title'}</span>
  <a class="b" title="Toggle Layout: table or list" href="/cgi-bin/o-saft.cgi?--cgi&--header&--content-type=html&--help=ciphers-html">table <> list</a>
  </h2>
  <h1> $cnt Cipher Suites</h1>
EoHTML

    $htm .= _man_ciphers_html_ul($txt);
    $htm .= '</body></html>';
    return $htm;
}

sub man_ciphers_text {
    my $txt  = shift;
    my $keys = "";
    _man_dbx("man_ciphers_text() ..");
    if ( 0 < $_trace ) {
        foreach my $key ( keys %ciphers_desc ) {
            next if "additional_notes" eq $key;
            $keys .= "#\t$key\t$ciphers_desc{$key}\n";
        }
    }
    foreach my $key ( keys %ciphers_desc ) {
        $txt =~ s/\n$key\s/\n\t$ciphers_desc{$key}\t/g;
    }
    my $note = $ciphers_desc{'additional_notes'};
    $note =~ s/\n/\n= /g;

    return "$keys$txt$note\n";
}

sub man_ciphers {
    my $typ = shift;
    _man_dbx("man_ciphers($typ) ..");
    my $txt = _man_ciphers_get();
    return man_ciphers_html($txt) if ( 'html' eq $typ );
    return man_ciphers_list($txt) if ( 'list' eq $typ );
    return man_ciphers_text($txt) if ( 'text' eq $typ );
    return "";
}

sub man_table {

    my $typ = shift;
    $typ =~ s/^cipher(pattern|range)/$1/;
    my $pod = "";
    _man_dbx("man_table($typ) ..");
    my %types = (
        'regex'   => [ "key",            " - ",  "Regular Expressions used internally" ],
        'ourstr'  => [ "key",            " - ",  "Regular Expressions to match own output" ],
        'abbr'    => [ "Abbrevation",    " - ",  "Description" ],
        'intern'  => [ "Command",        "    ", " List of commands" ],
        'compl'   => [ "Compliance",     " - ",  "Brief description of performed checks" ],
        'range'   => [ "Range name",     " - ",  "hex values in this range" ],
        'pattern' => [ "Pattern name",   " - ",  "Pattern description\tused pattern" ],
        'rfc'     => [ "Number",         " - ",  "RFC Title and URL" ],
        'links'   => [ "Title",          " - ",  "URL" ],
        'check'   => [ "+command / key", " - ",  "Description and text for label" ],
        'data'    => [ "+command / key", " - ",  "Description and text for label" ],
        'info'    => [ "+command / key", " - ",  "Description and text for label" ],
        'hint'    => [ "key",            " - ",  "Hint text" ],
        'text'    => [ "key",            " - ",  " text" ],
        'cmds'    => [ "internal key",   " - ",  "List of commands" ],
    );
    my $txt = "";
    my $sep = "\t";
    if ( defined $types{$typ} ) {
        $sep = $types{$typ}->[1];
    }
    else {
        if ( $typ =~ m/(?:^cfg[_-]|[_-]cfg$)/ ) {
            $sep = "=" if ( $typ =~ m/(?:^cfg[_-]|[_-]cfg$)/ );
        }
        else {
            print STDERR "**WARNING: 510: unknown table type '$typ'; using 'text' instead.\n";
            return $pod;
        }
    }
    _man_dbx("man_table($typ) ...");
    if ( $typ !~ m/^cfg/ ) {
        $pod .= _man_head( 16, $types{$typ}->[0], $types{$typ}->[2] );
    }

  TABLE: {
        if ( $typ =~ m/(abbr|links?|rfc)/ ) {
            $pod .= _man_doc_opt( $typ, $sep, 'opt' );
            last;
        }

        if ( $typ eq 'compl' ) {
            $pod .= _man_opt( $_, $sep, $cfg{'compliance'}->{$_} ) foreach ( sort keys %{ $cfg{'compliance'} } );
            last;
        }

        if ( $typ eq 'intern' ) {
            foreach my $key ( sort keys %cfg ) {
                next if ( $key !~ m/^commands_(?:.*)/ );
                $pod .= _man_opt( $key, $sep, "+" . join( ' +', @{ $cfg{$key} } ) );
            }
            foreach my $key ( sort keys %cfg ) {
                next if ( $key !~ m/^cmd-(.*)/ );
                $pod .= _man_opt( "cmd-" . $1, $sep, "+" . join( ' +', @{ $cfg{$key} } ) );
            }
            last;
        }

        if ( $typ =~ m/(hint|ourstr|pattern|range|regex)/ ) {
            my $list = $1;
            $list =~ s/^cfg[._-]?//;
            $list =~ s/[._-]?cfg$//;
            $list = 'hints'          if ( $list =~ m/hint/ );
            $list = 'cipherpatterns' if ( $list =~ m/pattern/ );
            $list = 'cipherranges'   if ( $list =~ m/range/ );
            foreach my $key ( sort keys %{ $cfg{$list} } ) {
                $txt = $cfg{$list}->{$key} || "";
                if ( 'ARRAY' eq ref( $cfg{$list}->{$key} ) ) {
                    $txt = join( "\t", @{ $cfg{$list}->{$key} } );
                }
                if ( 'range' eq $typ ) {
                    $txt =~ s/     */                   /g;
                }
                $pod .= _man_cfg( $typ, $key, $sep, $txt );
            }
            last;
        }
        if ( $typ =~ m/cmd/ ) {
            foreach my $key ( sort keys %cfg ) {
                next if ( $key !~ m/^cmd-/ );
                $txt = $cfg{$key};
                if ( 'ARRAY' eq ref( $cfg{$key} ) ) {
                    $txt = join( " ", @{ $cfg{$key} } );
                }
                $key =~ s/^cmd.// if ( $typ =~ m/cfg/ );
                $pod .= _man_cfg( $typ, $key, $sep, $txt );
            }
            last;
        }
        if ( $typ =~ m/check/ ) {
            foreach my $key ( sort keys %main::checks ) {
                $pod .= _man_cfg( $typ, $key, $sep, $main::checks{$key}->{txt} );
            }
            last;
        }
        if ( $typ =~ m/(?:data|info)/ ) {
            foreach my $key ( sort keys %::data ) {
                $pod .= _man_cfg( $typ, $key, $sep, $main::data{$key}->{txt} );
            }
            last;
        }
        if ( $typ =~ m/text/ ) {
            foreach my $key ( sort keys %::text ) {
                if ( '' eq ref( $main::text{$key} ) ) {
                    $pod .= _man_txt( $typ, $key, $sep, $main::text{$key} );
                }
                if ( 'HASH' eq ref( $main::text{$key} ) ) {
                }
            }
            last;
        }
    }
    if ( $typ =~ m/range/ ) {
        my %sizes = (
            'SSLv2_full' => 0xffffff,
            'full'       => 0xffffff,
            'safe'       => 0x2fffff,
            'long'       => 0xffff,
            'huge'       => 0xffff,
        );
        foreach my $key ( sort( keys( %{ $cfg{'cipherranges'} } ) ) ) {
            next if ( $key =~ m/(full|huge|long|safe)/i );
            my @list = eval( $cfg{'cipherranges'}->{$key} || "" );
            $sizes{$key} = scalar(@list);
        }
        $pod .= _man_foot(16);
        $pod .= <<"EoHelp";
= Number of ciphers per range =
EoHelp
        foreach my $key ( sort( keys(%sizes) ) ) {
            $pod .= sprintf( "%16s - %s\n", $key, $sizes{$key} );
        }
    }
    if ( $typ !~ m/cfg/ ) {
        $pod .= _man_foot(16);
    }
    else {
        $pod .= <<"EoHelp" if ( ( $cfg{'out'}->{'warning'} + $cfg{'out'}->{'hint'} ) > 1 );
= Format is:  KEY=TEXT ; NL, CR and TAB are printed as \\n, \\r and \\t
= (Don't be confused about multiple  =  as they are part of  TEXT.)
= The string  @@  inside texts is used as placeholder.
= NOTE: " are not escaped!

EoHelp
    }
    return $pod;
}

sub man_alias {
    _man_dbx("man_alias() ..");
    my $pod = "\n" . _man_head( 27, "Alias (regex)         ", "command or option   # used by ..." );
    my $txt = "";
    my $fh  = undef;
    my $src = _get_filename($parent);
    _man_dbx("man_alias: reading $src");
    if ( open( $fh, '<:encoding(UTF-8)', $src ) ) {
        while (<$fh>) {
            next if ( not m(# alias:) );
            next if ( not m|^\s*#?if[^/']*.([^/']+).[^/']+.([^/']+).[^#]*#\s*alias:\s*(.*)?| );
            my $commt = $3;
            my $alias = $2;
            my $regex = $1;
            $regex =~ s/^\^//;
            $regex =~ s/^\\//;
            $regex =~ s/\$$//;
            $regex =~ s/\(\?:/(/g;
            $regex =~ s/\[\+\]/+/g;
            $regex =~ s/_\?/-/g;

            if ( $alias !~ m/^[+-]/ ) {
                $alias = $commt if ( $commt =~ m/^[+-]/ );
            }
            if ( 29 > length($regex) ) {
                $txt = sprintf( "%-29s%-21s# %s\n", $regex, $alias, $commt );
            }
            else {
                $txt = sprintf( "%s\n", $regex );
                $txt .= sprintf( "%-29s%-21s# %s\n", "", $alias, $commt );
            }
            $pod .= _man_squeeze( 29, $txt );
        }
        close($fh);
    }
    $pod .= _man_foot(27);
    $pod .= <<'EoHelp';
= Note for names in  Alias  column:
=   For option names  - or _ characters are not shown, they are stripped anyway.
=   For command names - or _ characters are also possible, but only - is shown.

EoHelp
    return $pod;
}

sub man_options {
    _man_dbx("man_options() ..");
    my @txt = grep { /^=head. (General|Option|--)/ } @help;
    foreach my $line (@txt) { $line =~ s/^=head. *// }
    my ($end) = grep { $txt[$_] =~ /^Options vs./ } 0 .. $#txt;

    return join( '', "OPTIONS\n", splice( @txt, 0, $end ) );
}

sub man_toc {
    my $typ = lc(shift) || "";
    my $toc;
    _man_dbx("man_toc() ..");
    foreach my $txt ( grep { /^=head. / } @help ) {
        next if ( $txt !~ m/^=head/ );
        next if ( $txt =~ m/^=head. *END/ );
        if ( $typ =~ m/cfg/ ) {
            $txt =~ s/^=head1 *(.*)/{ $toc .= "--help=$1\n"}/e;
        }
        else {
            $txt =~ s/^=head([12]) *(.*)/{ $toc .= "  " x $1 . $2 . "\n"}/e;
        }
    }
    return $toc;
}

sub man_pod {
    _man_dbx("man_pod() ...");
    return _man_pod_head() . _man_pod_text() . _man_pod_foot();
}

sub man_man {
    _man_dbx("man_man() ...");
    my $pod = "o-saft.pod";
    $pod = "$cfg{'dirs'}->{'doc'}/o-saft.pod"    if ( !-e $pod );
    $pod = "../$cfg{'dirs'}->{'doc'}/o-saft.pod" if ( !-e $pod );
    exec("pod2man --name=o-saft.pl --center='OWASP - SSL advanced forensic tool' --utf8 $pod");
}

sub man_html {
    _man_dbx("man_html() ...");
    return _man_http_head() . _man_html_head() . _man_html( 'html', 'NAME', 'TODO' ) . _man_html_foot();
}

sub man_cgi {
    _man_dbx("man_cgi() ...");
    return _man_http_head() . _man_html_head() . _man_html_form() . $html{'warning_box'} . _man_html_foot();
}

sub man_wiki {
    my $mode = shift;
    _man_dbx("man_wiki($mode) ...");
    return _man_wiki_head() . _man_wiki_text($mode) . _man_wiki_foot();
}

sub man_help {
    my $label = lc(shift) || "";
    my $anf   = uc($label);
    my $end   = "[A-Z]";
    _man_dbx("man_help($anf, $end) ...");
    if ( 1 < ( grep { /^--v/ } @ARGV ) ) {
        return ODoc::get_egg("help.txt");
    }
    my @helptext = ODoc::get_custom( "help.txt", $parent, $version );
    my $txt      = join( '', @helptext );
    if ( $label =~ m/^name/i ) { $end = "TODO"; }
    $txt =~ s/.*?\n$anf//ms;
    $txt =~ s/\n$end.*//ms;
    $txt = "\n$anf" . $txt;
    $txt = _man_squeeze( undef, $txt );

    if ( $label =~ m/^todo/i ) {
        $txt .= "\n  NOT YET IMPLEMENTED\n";
        foreach my $label ( sort keys %OData::checks ) {
            next if ( 0 >= grep( { lc($label) eq lc($_) } \@{ $cfg{'commands_notyet'} } ) );
            $txt .= "        $label\t- " . $OData::checks{$label}->{txt} . "\n";
        }
    }
    return $txt;
}

sub man_src_grep {
    my $hlp = shift;
    my $key = shift;
    my $pod = "\n";
    $pod .= _man_head( 14, "Option    ", "Description where program terminates" );
    _man_dbx("man_src_grep($hlp) ...");
    my $fh  = undef;
    my $src = _get_filename($parent);
    _man_dbx("man_src_grep: reading $src");

    if ( open( $fh, '<:encoding(UTF-8)', $src ) ) {
        while (<$fh>) {
            next if (m(^\s*#));
            next if (m(# alias));
            next if ( not m($hlp) );
            my $opt     = $_;
            my $comment = $_;
            if ( $key =~ m/exit=/ ) {
                $opt     =~ s/^[^"]*"\s*/$key/;
                $opt     =~ s/\s+.*//s;
                $comment =~ s/^[^-]*//;
                $comment =~ s/".*$//s;
                $pod .= sprintf( "%-15s%s\n", $opt, $comment );
            }
        }
        close($fh);
    }
    $pod .= _man_foot(14);
    return $pod;
}

sub man_printhelp {

    my $hlp = shift;
    my $txt;
    _man_dbx("man_printhelp($hlp) ...");
    man_docs_write() if ( $hlp =~ m/^gen[_.=-]?docs$/ );
    return           if ( $hlp =~ m/^gen[_.=-]?docs$/ );
    _man_use_tty();
    _man_html_init();

    $txt = man_help('NAME')           if ( $hlp =~ /^$/ );
    $txt = man_help('TODO')           if ( $hlp =~ /^todo$/i );
    $txt = man_help('KNOWN PROBLEMS') if ( $hlp =~ /^(err(?:or)?|problem)s?$/i );
    $txt = man_help('KNOWN PROBLEMS') if ( $hlp =~ /^faq/i );
    $txt .= man_help('LIMITATIONS') if ( $hlp =~ /^faq/i );
    print man_help($hlp)            if ( $hlp =~ /^(?:CHECKS?|CUSTOM)$/ );
    return                          if ( $hlp =~ /^(?:CHECKS?|CUSTOM)$/ );

    $hlp = lc($hlp);
    $txt = man_toc($1)         if ( $hlp =~ /^((?:toc|contents?)(?:.cfg)?)$/ );
    $txt = man_html()          if ( $hlp =~ /^(gen-)?html$/ );
    $txt = man_wiki('colon')   if ( $hlp =~ /^(gen-)?wiki$/ );
    $txt = man_pod()           if ( $hlp =~ /^(gen-)?pod$/ );
    $txt = man_man()           if ( $hlp =~ /^(gen-)?man$/ );
    $txt = man_man()           if ( $hlp =~ /^(gen-)?[nt]roff$/ );
    $txt = man_cgi()           if ( $hlp =~ /^(gen-)?cgi$/ );
    $txt = man_ciphers('text') if ( $hlp =~ /^(gen-)?-?ciphers$/ );
    $txt = man_ciphers('text') if ( $hlp =~ /^(gen-)?-?ciphers.?text$/ );
    $txt = man_ciphers('list') if ( $hlp =~ /^(gen-)?-?ciphers.?list$/ );
    $txt = man_ciphers('html') if ( $hlp =~ /^(gen-)?-?ciphers.?html$/ );
    $txt = man_alias()         if ( $hlp =~ /^alias(es)?$/ );
    $txt = man_commands()      if ( $hlp =~ /^commands?$/ );
    $txt = man_options()       if ( $hlp =~ /^opts?$/ );
    $txt = man_warnings()      if ( $hlp =~ /^warnings?$/ );
    $txt = man_opt_help()      if ( $hlp =~ /^help$/ );
    $txt = man_help_brief()    if ( $hlp =~ /^help[_.-]brief$/ );
    $txt = man_table('rfc')    if ( $hlp =~ /^(gen-)?rfcs?$/ );
    $txt = man_table('links')  if ( $hlp =~ /^(gen-)?links?$/ );
    $txt = man_table('abbr')   if ( $hlp =~ /^(gen-)?(abbr|abk|glossary?)$/ );
    $txt = man_table('compl')  if ( $hlp =~ /^compliance$/ );
    $txt = man_table($1)       if ( $hlp =~ /^(compl|hint|intern|pattern|range|regex)s?$/ );
    $txt = man_table($1)       if ( $hlp =~ /^(cipher[_.-]?(?:pattern|range|regex|ourstr)?)s?$/ );

    if ( $hlp eq "tools" ) {
        my @txt = ODoc::get_custom( "tools.txt", $parent, $version );
        $txt = join( "", @txt );
    }
    if ( $hlp =~ m/^(coding|Program.?Code)$/i ) {
        my @txt = ODoc::get_custom( "coding.txt", $parent, $version );
        $txt = join( "", @txt );
    }
    if ( $hlp =~ m/^(devel|developer|development)$/i ) {
        $txt = join( "", ODoc::get_custom( "devel.txt", $parent, $version ) );
    }
    $txt = man_src_grep( qr/\s*_trace_(exit|info|next)\(/, "--exit=" ) if ( $hlp =~ /^exit$/ );
    $txt = man_table($1)            if ( $hlp =~ /^(cmds|check|data|info|ourstr|text)s?$/ );
    $txt = man_table( 'cfg_' . $1 ) if ( $hlp =~ /^cfg[_.-]?(cmd|check|data|info|hint|text|range|regex|ourstr)s?$/ );
    if ( $hlp eq "cmd" ) {
        $txt = "+" . join( ' +', @{ $cfg{'commands'} }, @{ $cfg{'commands_usr'} } );
    }
    if ( $hlp eq "legacy" ) {
        $txt = "# $parent legacy values:\t" . join( ' ', @{ $cfg{'legacys'} } );
    }
    if ( not $txt ) {
        _man_dbx( "man_printhelp: " . uc($hlp) );
        $txt = man_help( uc($hlp) ) if ( $hlp !~ m/^[+-]-?/ );
    }
    $txt =~ s/\n\n*$/\n/ if $txt;
    print $txt || "";
    return;
}

sub _main {
    push( @ARGV, "--help" ) if 0 > $#ARGV;
    my $file;
    my %usage = (
        '# commands to show help text in various formats' => {
            '$format' => 'see ' . ( caller(0) )[1] . ' (section METHODS)',
        },
    );
    while ( my $arg = shift @ARGV ) {
        if ( $arg =~ m/--?h(?:elp)?$/x ) { OText::print_pod( $0, __FILE__, $SID_oman ); exit 0; }
        if ( $arg eq '--usage' )         { OText::usage_show( "", \%usage );            exit 0; }
        if ( $arg =~ m/^--(?:v|trace.?CMD)/i ) { $_trace++; next; }

        if ( $arg =~ /^version$/ )         { print "$SID_oman\n"; next; }
        if ( $arg =~ /^[-+]?V(ERSION)?$/ ) { print "$VERSION\n";  next; }
        if ( $arg =~ /--pod=(.*)$/ ) {
            $file = $1;
            next if ( not -e $file );
            @help = ODoc::get_markup( $file, __FILE__, $VERSION );
            print _man_pod_file();
            next;
        }
        if ( $arg =~ /--file=(.*)$/ ) {
            $file = $1;
            @help = ODoc::get_markup( $file, __FILE__, $VERSION ) if ( -e $file );
            next;
        }
        $arg = "gen-docs" if ( $arg =~ m/--(?:help=)?gen[_.=-]?docs/x );
        $arg =~ s/--help[_.=-]?//;
        $arg =~ s/--test[_.=-]?//;
        $arg =~ s/\+test[_.=-]?//;
        next if ( $arg =~ m/^[+-]-?/ );
        man_printhelp($arg);
    }
    exit 0;
}

sub done { }



package OTrace;
no warnings 'redefine';

no warnings 'once';

my $SID_trace = "@(#) OTrace.pm 3.43 24/09/06 21:24:04";
our $VERSION = "24.09.24";

our $prefix_trace   = "#" . __PACKAGE__ . ":";
our $prefix_verbose = "#" . __PACKAGE__ . ":";

my $trace   = 0;
my $verbose = 0;

BEGIN {

    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##x;
    if ( exists $ENV{'PWD'} and not( grep { /^$ENV{'PWD'}$/ } @INC ) ) {
        unshift( @INC, $ENV{'PWD'} );
    }
    unshift( @INC, $_path ) if not( grep { /^$_path$/ } @INC );
    unshift( @INC, "lib" )  if not( grep { /^lib$/ } @INC );
    if ( not exists &_is_cfg_intern ) {

        sub _is_member {
            my ( $is, $ref ) = @_;
            return grep( { lc($is) eq lc($_) } @{$ref} );
        }
        sub _is_cfg_intern { return _is_member( shift, \@{ $cfg{'commands_int'} } ); }
    }
    our @EXPORT_OK = qw(
      trace_
      trace
      trace0
      trace1
      trace2
      trace3
      time_get
      arg_show
      args_show
      init_show
      exit_show
      ciphers_show
      target_show
      test_show
      done
    );
}

sub time_get {
    my $now = 0;
    return "" if ( 0 >= $cfg{'out'}->{'traceTIME'} );
    if ( defined $time0 ) {
        $now = time();
        $now -= $time0 if ( 0 >= $cfg{'out'}->{'time_absolut'} );
        $now = 0       if ( 0 > $now );
    }
    $now -= 3600;
    return sprintf( " %02s:%02s:%02s", ( localtime($now) )[ 2, 1, 0 ] );
}

sub __trace { my @txt = @_; return sprintf( "%s%s %s", $prefix_trace, time_get(), "@txt" ); }
sub trace_  { my @txt = @_; printf( "%s",   "@txt" )             if ( 0 < $cfg{'trace'} ); return; }
sub trace   { my @txt = @_; printf( "%s\n", __trace( $txt[0] ) ) if ( 0 < $cfg{'trace'} ); return; }
sub trace0  { my @txt = @_; printf( "%s\n", __trace("") )        if ( 0 < $cfg{'trace'} ); return; }
sub trace1  { my @txt = @_; printf( "%s\n", __trace(@txt) )      if ( 1 < $cfg{'trace'} ); return; }
sub trace2  { my @txt = @_; printf( "%s\n", __trace(@txt) )      if ( 2 < $cfg{'trace'} ); return; }
sub trace3  { my @txt = @_; printf( "%s\n", __trace(@txt) )      if ( 3 < $cfg{'trace'} ); return; }
sub arg_show { my @txt = @_; printf( "%s\n", __trace( " ARG: ", @txt ) ) if $cfg{'out'}->{'traceARG'}; return; }

sub __LINE  { return "#----------------------------------------------------"; }
sub __undef { my $v = shift; $v = $OText::STR{'UNDEF'} if not defined $v; return $v; }
sub ___ARR { return join( " ", "[", sort(@_), "]" ); }

sub _q_ARR {
    return ___ARR( map { "`" . $_ . "'" } @_ );
}
sub __TEXT { return $prefix_verbose . "@_"; }
sub ___K_V { my ( $k, $v ) = @_;                         return sprintf( "%s%21s= %s", $prefix_verbose, $k, __undef($v) ); }
sub _p_k_v { printf( "%s\n", ___K_V(@_) );               return; }
sub _ptext { printf( "%s\n", __TEXT(@_) );               return; }
sub _pline { printf( "%s\n", __TEXT( __LINE(), "@_" ) ); return; }
sub _pnull { _ptext("value $OText::STR{'UNDEF'} means that internal variable is not defined @_"); return; }

sub __trac {
    my $ref  = shift;
    my $key  = shift;
    my $data = "";
    if ( not defined $ref->{$key} ) {
        return ___K_V( $key, "$OText::STR{'UNDEF'}" );
    }
  SWITCH: for ( ref( $ref->{$key} ) ) {
        /^$/     && do { $data .= ___K_V( $key, $ref->{$key} ); last SWITCH; };
        /CODE/   && do { $data .= ___K_V( $key, "<<code>>" );   last SWITCH; };
        /SCALAR/ && do { $data .= ___K_V( $key, $ref->{$key} ); last SWITCH; };
        /Regexp/ && do { $data .= ___K_V( $key, $ref->{$key} ); last SWITCH; };
        /ARRAY/ && do { $data .= ___K_V( $key, ___ARR( @{ $ref->{$key} } ) ); last SWITCH; };
        /HASH/  && do {
            last SWITCH if ( 2 >= $ref->{'trace'} );
            $data .= __TEXT("# - - - - HASH: $key= {\n");
            foreach my $k ( sort( keys %{ $ref->{$key} } ) ) {
                my $val = "";
                if ( defined ${ $ref->{$key} }{$k} ) {
                    if ( 'ARRAY' eq ref( ${ $ref->{$key} }{$k} ) ) {
                        $val = ___ARR( @{ $ref->{$key}{$k} } );
                    }
                    else {
                        $val = join( "-", ${ $ref->{$key} }{$k} );
                    }
                }
                $data .= ___K_V( "    $k", $val . "\n" );
            }
            $data .= __TEXT("# - - - - HASH: $key }");
            last SWITCH;
        };
        $data .= __TEXT( $OText::STR{WARN} . " user defined type '$_' skipped" );
    }

    return $data;
}

sub _ptype { my $d = __trac(@_); printf( "%s\n", $d ) if ( $d !~ m/^\s*$/ ); return; }

sub __data       { return ( _is_member( shift, \@{ $cfg{'commands'} } ) > 0 ) ? "*" : "?"; }
sub __data_title { return sprintf( "=%19s %s %s %s %s %s %s %s", @_ ); }
sub __data_head  { return __data_title( "key", "command", " %data ", "%checks", "cmd-ch.", " short ", "intern ", "" ); }
sub __data_line  { return sprintf( "=%19s+%s+%s+%s+%s+%s+%s+%s", "-" x 19, "-" x 7, "-" x 7, "-" x 7, "-" x 7, "-" x 7, "-" x 7, "-" x 7 ); }
sub __data_data  { return sprintf( "%20s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", @_ ); }

sub __prot_option {
    my $data;
    foreach my $key ( sort( keys %{ $cfg{'openssl_option_map'} } ) ) {
        $data .= __trac( \%{ $cfg{'openssl_option_map'} }, $key ) . "\n";
    }
    chomp $data;
    return $data;
}

sub __prot_version {
    my $data;
    foreach my $key ( sort( keys %{ $cfg{'openssl_version_map'} } ) ) {
        $data .=
          __TEXT( sprintf( "%21s= ", $key ) . sprintf( "0x%04X 0x%08x", ${ $cfg{'openssl_version_map'} }{$key}, ${ $cfg{'openssl_version_map'} }{$key} ) )
          . "\n";
    }
    chomp $data;
    return $data;
}

sub _test_help {
    local $\ = "\n";
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== commands for internal testing ===
=
= Print list of commands for internal testing/information.
=
=   command         prints this information
=  ----------------+----------------------------------------------
=   +tests          this text
=   +test-init      data structure  %cfg after initialisation
=   +test-avail     overview of all available commands and checks
=   +test-maps      internal data strucures '%cfg{openssl}', '%cfg{ssleay}'
=   +test-prot      internal data according protocols
=   +test-vars      internal data structures using Data::Dumper
=   +test-regex     results for applying various texts to regex
=   +test-memory    overview of variables' memory usage
=   +test-methods   available methods for openssl in Net::SSLeay
=   +test-sclient   available options for 'openssl s_client' from Net::SSLeay
=   +test-sslmap    constants for SSL protocols from Net::SSLeay
=   +test-ssleay    information about Net::SSLeay capabilities
=   +test-ciphers-* various ciphers listings; available with o-saft.pl only
=  ----------------+----------------------------------------------
=
EoT
    return $data;
}

sub _test_avail {
    local $\ = "\n";
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal data structures: overview of commands, %data and %checks ===
=
= Print a simple overview of all available commands for  +info  and  +check .
= The purpose is to show if a proper key is defined in  %data and %checks  for
= each command from  %cfg{'commands'}  and vice versa.
=
=   column      description
=  ------------+--------------------------------------------------
=   key         key in %cfg{'commands'}
=   command     key (see above) available as command: +key
=   %data       command returns %data  (part of +info)
=   %checks     command returns %check (part of +check)
=   cmd-ch.     command listed in ...
=   short       description of command available as short text
=   intern      internal command only, not avaialable as +key
=  ------------+--------------------------------------------------
=
EoT

    my $old;
    my @yeast = ();
    my $cmd   = " ";
    print __data_head();
    print __data_line();
    $old = "";
    foreach my $key ( sort { uc($a) cmp uc($b) } @{ $cfg{'commands'} }, keys %data, keys %OData::shorttexts, keys %checks )
    {
        next if ( $key eq $old );
        $old = $key;
        if ( ( not defined $checks{$key} ) and ( not defined $data{$key} ) ) {
            push( @yeast, $key );
            next;
        }
        $cmd = "+" if ( 0 < _is_member( $key, \@{ $cfg{'commands'} } ) );
        $cmd = "-" if ( $key =~ /$cfg{'regex'}->{'SSLprot'}/i );
        print __data_data(
            $key, $cmd,
            ( defined $data{$key} )                             ? __data($key) : " ",
            ( defined $checks{$key} )                           ? "*"          : " ",
            ( _is_member( $key, \@{ $dbx{'cmd-check'} } ) > 0 ) ? "*"          : "!",
            ( defined $OData::shorttexts{$key} )                ? "*"          : " ",
            ( _is_cfg_intern($key) )                            ? "I"          : " ",
            "",
        );
    }
    print __data_line();
    print __data_head();
    print <<'EoT';
=
=   +  command (key) present
=   I  command is an internal command or alias (ok in column 'intern')
=   -  command (key) used internal for checks only (ok in column 'command')
=   *  key present
=      key not present
=   ?  key in %data present but missing in $cfg{commands}
=   !  key in %cfg{cmd-check} present but missing in redefined %cfg{cmd-check}
=
= Some commands (keys) in column  cmd-ch.  marked  !  are not considered an
= error 'cause they are ancient checks like hastls10_old, or special checks
= like extensions, or are just for documentation like cps_valid.
=
= A short text should be available for  each command and for all data keys,
# except for internal commands (columns intern) and following:
=      cn_nosni, ext_*, valid_*
=
= Internal or summary commands:
EoT
    print "=      " . join( " ", @yeast ) . "\n";
    return;
}

sub _test_init {
    local $\                     = "\n";
    local $Data::Dumper::Deparse = 1;
    my $line = "#--------------------+-------------------------------------------";
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal data structures: initialisation of %cfg, %data and %checks ===
=
= Print initialised data structures  %data and %checks  after all command-line
= options have been applied.
=
EoT
    _pline("%cfg {");
    _ptext("#                key | value");
    _ptext($line);
    _p_k_v( "ARGV", ___ARR( @{ $cfg{'ARGV'} } ) );
    _pline("%cfg{use} {");

    foreach my $key ( sort( keys %{ $cfg{'use'} } ) ) {
        _p_k_v( $key, $cfg{'use'}{$key} );
    }
    _pline("%cfg{use} }");
    _ptext($line);
    _pline("%cfg }");
    _pline("%data {");
    _ptext("#                key | value (function code)");
    _ptext($line);
    foreach my $key ( sort( keys %data ) ) {
        my $code = Dumper( $data{$key}->{val} );
        $code =~ s/^\$VAR.*//;
        $code =~ s/(?:}[;,])?\s*$//g;
        $code =~ s/use\s*(?:strict|warnings);//g;
        $code =~ s/package\s*.*;//g;
        $code =~ s/BEGIN\s*.*//g;
        $code =~ s/return\s*//g;
        $code =~ s/\n//g;
        $code =~ s/^\s*//g;
        _p_k_v( $key, $code );
    }
    _ptext($line);
    _pline("%data }");
    _pline("%checks {");
    _ptext("#                key | value");
    _ptext($line);
    foreach my $key ( sort( keys %checks ) ) {
        _p_k_v( $key, $checks{$key}->{val} );
    }
    _ptext($line);
    _pline("%checks }");
    return;
}

sub _test_maps {
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal data structures: %cfg{openssl}, %cfg{ssleay} ===
=
= Print internal mappings for openssl functionality (mainly options).
=
EoT
    local $\ = "\n";
    my $data = SSLinfo::test_sslmap();
    $data =~ s/^../#$cfg{'me'}:/smg;
    print $data;
    _pline("%cfg{openssl_option_map} {");
    print __prot_option();
    _pline("%cfg{openssl_version_map} {");
    print __prot_version();
    return;
}

sub _test_prot {
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal data structure according protocols ===
=
= Print information about SSL/TLS protocols in various internal variables.
=
EoT
    local $\ = "\n";
    my $ssl = $cfg{'regex'}->{'SSLprot'};
    _pnull("\n");
    _pline("%cfg {");
    foreach my $key ( sort( keys %cfg ) ) {
        _ptype( \%cfg, $key ) if ( $key =~ m/$ssl/ );
    }
    _pline("}");
    _pline("%cfg{openssl_option_map} {");
    print __prot_option();
    _pline("}");
    _pline("%cfg{openssl_version_map} {");
    print __prot_version();
    _pline("}");
    _pline("%checks {");

    foreach my $key ( sort( keys %checks ) ) {
        _ptext( sprintf( "%14s= ", $key ) . $hecks{$key}->{txt} ) if ( $key =~ m/$ssl/ );
    }
    _pline("}");
    _pline("%shorttexts {");
    foreach my $key ( sort( keys %OData::shorttexts ) ) {
        _ptext( sprintf( "%14s= ", $key ) . $OData::shorttexts{$key} ) if ( $key =~ m/$ssl/ );
    }
    _pline("}");
    return;
}

sub _test_regex {
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal data structure %cfg{regex} ===
=
= Print information internal data structure %cfg{regex}.
=
EoT
    local $\ = "\n";
    _pline("%cfg{regex} {");
    foreach my $key ( sort( keys %{ $cfg{'regex'} } ) ) {
        _p_k_v( $key, $cfg{'regex'}->{$key} );
    }
    _pline("%cfg{regex} }");
    return;
}

sub _test_methods {
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal list of methods to call openssl ===
=
= Print available methods in Net::SSLeay.
=
EoT
    my $list = SSLinfo::test_methods();
    $list =~ s/ /\n  /g;
    print "  $list\n";
    return;
}

sub _test_openssl {
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal data structure %_OpenSSL_opt ===
=
= Print internal data structure from SSLinfo.
=
EoT
    my $list = SSLinfo::test_openssl();
    print "$list\n";
    return;
}

sub _test_sclient {
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal list of openssl s_client options ===
=
= Print available options for 'openssl s_client' from Net::SSLeay.
=
EoT
    my $list = SSLinfo::test_sclient();
    $list =~ s/ /\n  /g;
    print "  $list\n";
    return;
}

sub _test_sslmap {
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';

=== internal list of constants for SSL protocols ===
=
= Print available constants for SSL protocols in Net::SSLeay.
=
EoT
    print SSLinfo::test_sslmap() . "\n";
    return;
}

sub _test_ssleay {
    printf( "#%s:\n", ( caller(0) )[3] );
    print <<'EoT';
 
=== internal data of Net::SSLeay ===
=
= Print information about Net::SSLeay capabilities.
=
EoT
    print SSLinfo::test_ssleay();
    return;
}

sub _test_memory {
    printf( "#%s:\n", ( caller(0) )[3] );
    require Devel::Size;
    my %types = (
        'ARRAY'   => '@',
        'CODE'    => '{',
        'FORMAT'  => '#',
        'GLOB'    => '*',
        'HASH'    => '%',
        'IO'      => '&',
        'LVALUE'  => '=',
        'REF'     => '\\',
        'REGEXP'  => '/',
        'SCALAR'  => '$',
        'VSTRING' => '"',
    );
    print <<'EoT';

=== memory usage of internal variables ===
=
= Print memory usage of internal variables.
=
EoT
    my $line = "=------+----------------";
    print "= Bytes variable\n$line";
    foreach my $k ( sort( keys %cfg ) ) {
        printf( "%6s\t%s\n", Devel::Size::total_size( \$cfg{$k} ), "%cfg{$k}" );
    }
    foreach my $k ( sort( keys %data ) ) {
        printf( "%6s\t%s\n", Devel::Size::total_size( \$data{$k} ), "%data{$k}" );
    }
    foreach my $k ( sort( keys %checks ) ) {
        printf( "%6s\t%s\n", Devel::Size::total_size( \$checks{$k} ), "%checks{$k}" );
    }
    foreach my $k ( sort( keys %dbx ) ) {
        printf( "%6s\t%s\n", Devel::Size::total_size( \$dbx{$k} ), "%dbx{$k}" );
    }
    print "$line\n";
    my $bytes = 0;
    foreach my $v ( sort( keys %main:: ) ) {
        next if ( not grep { /^(cfg|check|cipher|cmd|data|dbx|info|osaft|short|text)/ } $v );
        next if ( grep { /^check(cipher|http)/ } $v );
        next if ( "*{$main::{$v}}" !~ m/\*main::/ );
        my $size = Devel::Size::total_size( \$main::{$v} );
        $bytes += $size;
        printf( "%7s\t%s\n", $size, "%$v" );
    }
    print "$line\n";
    printf( "%7s\t(%2.2f MB) total\n", $bytes, $bytes / 1024 / 1024 );
    return;
}

sub __dump_var {
    my $type = shift;
    my $var  = shift;
    my $name = "$type$var";
    _pline("$name {");
    $var = $::{$var};
    printf( "%s = %s\n", $name, Dumper($$var) )    if ( '$' eq $type );
    printf( "%s = %s\n", $name, Dumper( \%$var ) ) if ( '%' eq $type );
    printf( "%s = %s\n", $name, Dumper( \@$var ) ) if ( '@' eq $type );
    _pline("$name }");
    return;
}

sub _test_vars {
    printf( "#%s:\n", ( caller(0) )[3] );
    local $\ = "\n";
    local $Data::Dumper::Deparse = 1;

    local $Data::Dumper::Sparseseen = 1;
    local $Data::Dumper::Purity     = 0;
    local $Data::Dumper::Sortkeys   = 1;
    local $Data::Dumper::Quotekeys  = 1;
    local $Data::Dumper::Indent     = 1;
    local $Data::Dumper::Pair       = "\t=> ";
    local $Data::Dumper::Terse = 1;

    print <<'EoT';

=== internal data structures: %ciphers %prot %cfg %data %info %checks ===
=
= Print initialised internal data structures using Perl's Data::Dumper.
=
EoT
    __dump_var( '$', 'cipher_results' );
    __dump_var( '%', 'ciphers' );
    __dump_var( '%', 'ciphers_desc' );
    __dump_var( '%', 'prot' );
    __dump_var( '%', 'cfg' );
    __dump_var( '%', 'data' );
    __dump_var( '%', 'info' );
    __dump_var( '%', 'checks' );
    return;
}

sub ciphers_show {
    return if ( 0 >= $cfg{'trace'} );
    my $need = shift;
    _pline("ciphers {");
    my $_cnt    = scalar @{ $cfg{'ciphers'} };
    my $ciphers = "@{$cfg{'ciphers'}}";
    _p_k_v( "_need_cipher", $need );
    if ( 0 < $need ) {
        my @range;
        if ( $cfg{'cipherrange'} =~ m/(full|huge|long|safe|rfc|intern)/i ) {
            $_cnt  = 0xffffff;
            $_cnt  = 0x2fffff if ( $cfg{'cipherrange'} =~ m/safe/i );
            $_cnt  = 0xffff   if ( $cfg{'cipherrange'} =~ m/long/i );
            $_cnt  = 0xffff   if ( $cfg{'cipherrange'} =~ m/huge/i );
            $_cnt  = 2051     if ( $cfg{'cipherrange'} =~ m/rfc/i );
            $_cnt  = 2640     if ( $cfg{'cipherrange'} =~ m/intern/i );
            @range = "<<huge list not printed>>";
        }
        else {
            @range = OCfg::get_ciphers_range( 'TLSv13', $cfg{'cipherrange'} );
            $_cnt = scalar @range;
        }
        $_cnt = sprintf( "%5s", $_cnt );
        foreach my $key (qw(starttls ciphermode cipherpattern cipherrange)) {
            _p_k_v( $key, $cfg{$key} );
        }
        foreach my $txt ( split( /\n/, $cfg{'cipherranges'}->{ $cfg{'cipherrange'} } ) ) {
            next if $txt =~ m/^\s*$/;
            $txt =~ s/^\s*/                /;
            _ptext($txt);
        }
        _p_k_v( "$_cnt ciphers", "@range" );
        _p_k_v( "cipher_dh",     $cfg{'cipher_dh'} );
        _p_k_v( "cipher_md5",    $cfg{'cipher_md5'} );
        _p_k_v( "cipher_ecdh",   $cfg{'cipher_ecdh'} );
        _p_k_v( "cipher_npns",   ___ARR( @{ $cfg{'cipher_npns'} } ) );
        _p_k_v( "cipher_alpns",  ___ARR( @{ $cfg{'cipher_alpns'} } ) );
    }
    _pline("ciphers }");
    return;
}

sub target_show {
    my @targets = @_;
    return if ( 0 >= $cfg{'trace'} );
    my $data = "";
    if ( 2 > $cfg{'trace'} ) {
        foreach my $target (@targets) {
            next if ( 0 == @{$target}[0] );
            $data .= sprintf( "%s:%s%s ", @{$target}[ 2 .. 3, 6 ] );
        }
        _p_k_v( "targets", "[ $data]" );
    }
    else {
        $data = "# - - - -ARRAY: targets= [\n";
        $data .=
          __TEXT( sprintf( " #  Index %6s %24s : %5s %10s %5s %-16s %s\n", "Prot.", "Hostname or IP", "Port", "Auth", "Proxy", "Path", "Orig. Parameter" ) );
        foreach my $target (@targets) {
            $data .= __TEXT( sprintf( "    [%3s] %6s %24s : %5s %10s %5s %-16s %s\n", @{$target}[ 0, 1 .. 7 ] ) );
        }
        $data .= __TEXT("# - - - -ARRAY: targets ]\n");
        _ptext($data);
    }
    return;
}

sub init_show {

    return if ( 0 >= $cfg{'trace'} );
    local $\ = "\n";
    my $arg = " (does not exist)";
    if ( -f $cfg{'RC-FILE'} ) { $arg = " (exists)"; }
    _ptext("!!Hint: use --trace=2  to see SSLinfo variables") if ( 2 > $cfg{'trace'} );
    _ptext("!!Hint: use --trace=2  to see external commands") if ( 2 > $cfg{'trace'} );
    _ptext("!!Hint: use --trace=3  to see full %cfg")         if ( 3 > $cfg{'trace'} );
    _pnull();
    _ptext("#") if ( 3 > $cfg{'trace'} );
    _pline("%ENV {");
    _p_k_v( "PATH",           __undef( $ENV{'PATH'} ) );
    _p_k_v( "PERL5LIB",       __undef( $ENV{'PERL5LIB'} ) );
    _p_k_v( "PERLDB_OPTS",    __undef( $ENV{'PERLDB_OPTS'} ) );
    _p_k_v( "PERL_HASH_SEED", __undef( $ENV{'PERL_HASH_SEED'} ) );
    _p_k_v( "OSAFT_CONFIG",   __undef( $ENV{'OSAFT_CONFIG'} ) );
    _p_k_v( "OSAFT_OPTIONS",  __undef( $ENV{'OSAFT_OPTIONS'} ) );
    _p_k_v( "OSAFT_MAKE",     __undef( $ENV{'OSAFT_MAKE'} ) );
    _pline("%ENV }");
    _pline("");
    _p_k_v( "$0", main::_VERSION() );

    _p_k_v( "OCfg",     __undef($OCfg::VERSION) );
    _p_k_v( "SSLhello", __undef($SSLhello::VERSION) );
    _p_k_v( "SSLinfo",  __undef($SSLinfo::VERSION) );
    _p_k_v( "RC-FILE",      $cfg{'RC-FILE'} . $arg );
    _p_k_v( "--rc",         ( ( grep { /(?:--rc)$/i } @ARGV ) > 0 )     ? 1 : 0 );
    _p_k_v( "--no-rc",      ( ( grep { /(?:--no.?rc)$/i } @ARGV ) > 0 ) ? 1 : 0 );
    _p_k_v( "verbose",      $cfg{'verbose'} );
    _p_k_v( "trace",        "$cfg{'trace'}, traceARG=$cfg{'out'}->{'traceARG'}, traceKEY=$cfg{'out'}->{'traceKEY'}, traceTIME=$cfg{'out'}->{'traceTIME'}" );
    _p_k_v( "time_absolut", $cfg{'out'}->{'time_absolut'} );
    _p_k_v( "dbx{files}",   ___ARR( @{ $dbx{'files'} } ) );

    if ( 1 < $cfg{'trace'} ) {
        _pline("SSLinfo {");
        _p_k_v( "::trace",         $SSLinfo::trace );
        _p_k_v( "::linux_debug",   $SSLinfo::linux_debug );
        _p_k_v( "::slowly",        $SSLinfo::slowly );
        _p_k_v( "::timeout",       $SSLinfo::timeout );
        _p_k_v( "::use_openssl",   $SSLinfo::use_openssl );
        _p_k_v( "::use_sclient",   $SSLinfo::use_sclient );
        _p_k_v( "::use_extdebug",  $SSLinfo::use_extdebug );
        _p_k_v( "::use_nextprot",  $SSLinfo::use_nextprot );
        _p_k_v( "::use_reconnect", $SSLinfo::use_reconnect );
        _p_k_v( "::use_SNI",       $SSLinfo::use_SNI );
        _p_k_v( "::use_http",      $SSLinfo::use_http );
        _p_k_v( "::no_cert",       $SSLinfo::no_cert );
        _p_k_v( "::no_cert_txt",   $SSLinfo::no_cert_txt );
        _p_k_v( "::protos_alpn",   $SSLinfo::protos_alpn );
        _p_k_v( "::protos_npn",    $SSLinfo::protos_npn );
        _p_k_v( "::sclient_opt",   $SSLinfo::sclient_opt );
        _p_k_v( "::ignore_case",   $SSLinfo::ignore_case );
        _p_k_v( "::timeout_sec",   $SSLinfo::timeout_sec );
        _pline("SSLinfo }");
    }

    _pline("%cmd {");
    foreach my $key ( sort( keys %::cmd ) ) { _ptype( \%::cmd, $key ); }
    _p_k_v( "# extopenssl", "1 # use openssl to check ciphers" );
    _pline("%cmd }");

    if ( 1 < $cfg{'trace'} ) {
        _pline("complete %cfg {");
        foreach my $key ( sort( keys %cfg ) ) {
            if ( $key =~ m/(hints|openssl|ssleay|sslerror|sslhello|regex|^out|^use)$/ ) {

                _ptext("# - - - - HASH: $key= {");
                if ( $key =~ m/openssl/ ) {
                    _p_k_v( "# option not available", "[ 0 ...]" );
                    _p_k_v( "# option available   ",  "[ 1 text] # text describes purpose of option" );
                }
                foreach my $k ( sort( keys %{ $cfg{$key} } ) ) {
                    if ( $key =~ m/openssl/ ) {
                        _p_k_v( $k, ___ARR( @{ $cfg{$key}{$k} } ) );
                    }
                    else {
                        _ptype( $cfg{$key}, $k );
                    }
                }
                _ptext("# - - - - HASH: $key }");
            }
            else {
                if ( $key =~ m/targets/ ) {
                    target_show( @{ $cfg{'targets'} } );
                }
                else {
                    if ( "time0" eq $key and defined $ENV{'OSAFT_MAKE'} ) {
                        my $t0 = $cfg{'time0'};
                        $cfg{'time0'} = $OText::STR{MAKEVAL};
                        _ptype( \%cfg, $key );
                        $cfg{'time0'} = $t0;
                    }
                    else {
                        if ( "RC-ARGV" eq $key ) {

                            print( ___K_V( $key, _q_ARR( @{ $cfg{'RC-ARGV'} } ) ) );
                        }
                        else {
                            _ptype( \%cfg, $key );
                        }
                    }
                }
            }
        }
        _pline("%cfg }");
        return;
    }
    my $sni_name = __undef( $cfg{'sni_name'} );
    my $port     = __undef( $cfg{'port'} );
    _pline("user-friendly cfg {");
    foreach my $key (qw(ca_depth ca_path ca_file)) {
        _p_k_v( $key, $cfg{$key} );
    }
    _p_k_v( "default port", "$port (last specified)" );
    target_show( @{ $cfg{'targets'} } );
    _ptext( "              use_SNI=", $SSLinfo::use_SNI . ", force-sni=$cfg{'use'}->{'forcesni'}, sni_name=$sni_name" );
    _p_k_v( "use->http",     $cfg{'use'}->{'http'} );
    _p_k_v( "use->https",    $cfg{'use'}->{'https'} );
    _p_k_v( "out->hostname", $cfg{'out'}->{'hostname'} );
    _p_k_v( "out->header",   $cfg{'out'}->{'header'} );

    foreach my $key (qw(format legacy cipherrange slow_server_delay starttls starttls_delay)) {
        _p_k_v( $key, $cfg{$key} );
    }
    foreach my $key (qw(starttls_phase starttls_error cipher)) {
        _p_k_v( $key, ___ARR( @{ $cfg{$key} } ) );
    }
    _p_k_v( "SSL version",    ___ARR( @{ $cfg{'version'} } ) );
    _p_k_v( "SSL versions",   ___ARR( map { $_ . "=" . $cfg{$_} } sort( @{ $cfg{versions} } ) ) );
    _p_k_v( "special SSLv2",  "null-sslv2=$cfg{'use'}->{'nullssl2'}, ssl-lazy=$cfg{'use'}->{'ssl_lazy'}" );
    _p_k_v( "ignore output",  ___ARR( @{ $cfg{'ignore-out'} } ) );
    _p_k_v( "user commands",  ___ARR( @{ $cfg{'commands_usr'} } ) );
    _p_k_v( "given commands", ___ARR( @{ $cfg{'done'}->{'arg_cmds'} } ) );
    _p_k_v( "commands",       ___ARR( @{ $cfg{'do'} } ) );
    _pline("user-friendly cfg }");
    _ptext("(more information with: --trace=2  or  --trace=3 )") if ( 1 > $cfg{'trace'} );
    return;
}

sub exit_show {
    return if ( 0 >= $cfg{'trace'} );
    _p_k_v( "cfg'exitcode'", $cfg{'use'}->{'exitcode'} );
    _p_k_v( "exit status",   ( ( $cfg{'use'}->{'exitcode'} == 0 ) ? 0 : $checks{'cnt_checks_no'}->{val} ) );
    _ptext("internal administration ..");
    _pline('@cfg{done} {');
    foreach my $key ( sort( keys %{ $cfg{'done'} } ) ) {
        _ptype( \%{ $cfg{'done'} }, $key );
    }
    _pline('@cfg{done} }');
    return;
}

sub args_show {
    return if ( 0 >= $cfg{'out'}->{'traceARG'} );
    _pline("ARGV {");
    arg_show("# summary of all arguments and options from command-line");
    arg_show( "       called program ARG0= " . $cfg{'ARG0'} );
    arg_show( "     passed arguments ARGV= " . ___ARR( @{ $cfg{'ARGV'} } ) );
    arg_show( "                   RC-FILE= " . $cfg{'RC-FILE'} );
    arg_show("      from RC-FILE RC-ARGV= ($#{$cfg{'RC-ARGV'}} more args ...)");
    if ( 2 > $cfg{'trace'} ) {
        arg_show("      !!Hint:  use --trace=2 to get the list of all RC-ARGV");
        arg_show("      !!Hint:  use --trace=3 to see the processed RC-ARGV");
    }
    arg_show( "      from RC-FILE RC-ARGV= " . _q_ARR( @{ $cfg{'RC-ARGV'} } ) ) if ( 1 < $cfg{'trace'} );
    my $txt = "[ ";
    foreach my $target ( @{ $cfg{'targets'} } ) {
        next if ( 0 == @{$target}[0] );
        $txt .= sprintf( "%s:%s ", @{$target}[ 2 .. 3 ] );
    }
    $txt .= "]";
    arg_show( "         collected targets= " . $txt );
    if ( 2 < $cfg{'trace'} ) {
        arg_show(" #--v { processed files, arguments and options");
        arg_show( "    read files and modules= " . ___ARR( @{ $dbx{file} } ) );
        arg_show( "processed  exec  arguments= " . ___ARR( @{ $dbx{exe} } ) );
        arg_show( "processed normal arguments= " . ___ARR( @{ $dbx{argv} } ) );
        arg_show( "processed config arguments= " . _q_ARR( @{ $dbx{cfg} } ) );
        arg_show(" #--v }");
    }
    _pline("ARGV }");
    return;
}

sub rcfile_show {
    return if ( 0 >= $cfg{'trace'} );
    _pline("RC-FILE {");
    _pline("RC-FILE }");
    return;
}

sub test_show {
    my $arg = shift;
    _ptext($arg);
    if ( $arg =~ /^\+test.?ciphers?.?list$/ ) {

        push( @{ $cfg{'do'} },      'cipher' );
        push( @{ $cfg{'version'} }, 'TLSv1' ) if ( 0 > $#{ $cfg{'version'} } );
        $cfg{'trace'} = 1;
        ciphers_show(1);
        return;
    }
    OCfg::test_cipher_regex() if ( $arg =~ m/cipher.?regex/ );
    Ciphers::show($arg)       if ( $arg =~ /^\+test.?cipher/ );
    _test_help()              if ( '+test' eq $arg );
    _test_help()              if ( '+tests' eq $arg );
    _test_help()              if ( '+testtest' eq $arg );
    _test_sclient()           if ( '+testsclient' eq $arg );
    _test_ssleay()            if ( '+testssleay' eq $arg );
    _test_sslmap()            if ( '+testsslmap' eq $arg );
    _test_openssl()           if ( '+testopenssl' eq $arg );
    _test_methods()           if ( '+testmethods' eq $arg );
    _test_memory()            if ( '+testmemory' eq $arg );
    _test_regex()             if ( '+testregex' eq $arg );
    $arg =~ s/^[+-]-?tests?[._-]?//;
    _test_avail() if ( $arg =~ m/^avail(?:able)?$/ );
    _test_init()  if ( 'init' eq $arg );
    _test_maps()  if ( 'maps' eq $arg );
    _test_prot()  if ( 'prot' eq $arg );
    _test_vars()  if ( 'vars' eq $arg );
    return;
}

sub _main {
    my $arg = shift || "--help";
    $cfg{'time0'} = $OText::STR{MAKEVAL} if ( defined $ENV{'OSAFT_MAKE'} );
    my %usage = ( '# commands to print data' => { '+test-regex' => 'show %cfg{regex}' }, );
    if ( $arg =~ m/--?h(elp)?$/x ) { OText::print_pod( $0, __FILE__, $SID_trace ); exit 0; }
    if ( $arg eq '--usage' )       { OText::usage_show( "", \%usage );             exit 0; }
    if ( $arg =~ m/^--?trace/ )         { $trace++; }
    if ( $arg eq 'version' )            { print "$SID_trace\n"; exit 0; }
    if ( $arg =~ m/^[-+]?V(ERSION)?$/ ) { print "$VERSION\n";   exit 0; }
    if ( $arg =~ m/\+test.?regex$/ )    { _test_regex();        exit 0; }
    if ( $arg =~ m/\+tests?$/ )         { _test_help();         exit 0; }

    if ( $arg =~ m/[-+]-?test[_.-]?(.*)/ ) {
        $arg = "+test-$1";
        printf("#$0: direct testing not yet possible, please try:\n   o-saft.pl $arg\n");
    }
    exit 0;
}

sub done { }


package OUsr;

my $SID_ousr = "@(#) OUsr.pm 3.18 24/07/27 14:42:17";
our $VERSION = "24.06.24";

no warnings 'redefine';

{
    our $trace          = 0;
    our $verbose        = 0;
    our $prefix_trace   = "#" . __PACKAGE__ . ":";
    our $prefix_verbose = "#" . __PACKAGE__ . ":";
}

BEGIN {

    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##x;
    if ( exists $ENV{'PWD'} and not( grep { /^$ENV{'PWD'}$/ } @INC ) ) {
        unshift( @INC, $ENV{'PWD'} );
    }
    unshift( @INC, $_path ) if not( grep { /^$_path$/ } @INC );
    unshift( @INC, "lib" )  if not( grep { /^lib$/ } @INC );
    unshift( @INC, "." )    if not( grep { /^\.$/ } @INC );
    our @EXPORT_OK = qw(
      pre_init
      pre_file
      pre_args
      pre_exec
      pre_cipher
      pre_main
      pre_host
      pre_info
      pre_open
      pre_cmds
      pre_data
      pre_print
      pre_next
      pre_exit
      version
    );
}

if ( exists $INC{'lib/OTrace.pm'} ) {
    *trace = \&OTrace::trace;
}
else {

    sub trace {
        my @txt = @_;
        return if not return ( grep { /--(?:trace(?:=\d*)?$)/ } @ARGV );
        printf( "#%s: %s\n", __PACKAGE__, "@txt" );
        return;
    }
}


sub version { return $VERSION; }

sub pre_init {
    trace("pre_init ...");
    return;
}

sub pre_file {
    trace("pre_file ...");
    return;
}

sub pre_args {
    trace("pre_args ...");
    return;
}

sub pre_exec {
    trace("pre_exec ...");
    return;
}

sub pre_cipher {
    trace("pre_cipher ...");
    return;
}

sub pre_main {
    trace("pre_main ...");
    return;
}

sub pre_host {
    trace("pre_host ...");
    return;
}

sub pre_info {
    trace("pre_info ...");
    return;
}

sub pre_open {
    trace("pre_open ...");
    return;
}

sub pre_cmds {
    trace("pre_cmds ...");
    return;
}

sub pre_data {
    trace("pre_data ...");
    return;
}

sub pre_print {
    trace("pre_print ...");
    return;
}

sub pre_next {
    trace("pre_next ...");
    return;
}

sub pre_exit {
    trace("pre_exit ...");
    return;
}

sub _main {
    my $arg = shift || "--help";
    OText::print_pod( $0, __FILE__, $SID_ousr ) if ( $arg =~ m/--?h(elp)?$/x );
    print "$SID_ousr\n" if ( $arg =~ /^version$/ );
    print "$VERSION\n"  if ( $arg =~ /^[-+,]?V(ERSION)?$/ );
    exit 0;
}

sub done { }


package main;

foreach my $file (
    qw(
    lib/OText.pm
    lib/OCfg.pm
    lib/OData.pm
    lib/Ciphers.pm
    lib/error_handler.pm
    lib/SSLhello.pm
    lib/SSLinfo.pm
    lib/ODoc.pm
    lib/OMan.pm
    lib/OTrace.pm
    lib/OUsr.pm
    )
  )
{
    push( @{ $OCfg::dbx{'files'} }, $file );
}

sub _tprint { my @txt = @_; printf( "#%s: %s\n", $cfg{'me'}, join( " ", @txt ) ); return; }

sub _warn_nosni {
    my $err = shift;
    my $ssl = shift;
    my $sni = shift;
    return if ( $sni < 1 );
    return if ( $ssl !~ m/^SSLv[23]/ );
    OCfg::warn("$err $ssl does not support SNI; cipher checks are done without SNI");
    return;
}

sub _vprint2 {
    my @txt = @_;
    return if ( 1 >= _is_cfg_verbose() );
    _vprint(@txt);
    return;
}

sub _vprint_read {
    my ( $fil, @txt ) = @_;
    return if ( 0 < _is_argv('(?:--no.?header|--cgi)') );
    return if ( 0 >= _is_argv('(?:--v$|--trace|--warn)') );
    if ( 0 >= _is_argv('(?:--trace[_.-]?(?:ARG|CMD|TIME|ME)$)') ) {
        return if ( 0 < _is_argv('(?:--trace[_.-]?CLI|KEY$)') );
    }
    _tprint( "read", $fil, "(@txt)" ) if _is_argv('(?:--trace)');
    _vprint( "read", $fil, "(@txt)" );
    return;
}

sub _vprint_me {
    my ( $s, $m, $h, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime();
    _vprint( $cfg{'me'}, _VERSION() );
    _vprint( $cfg{'me'}, @{ $cfg{'ARGV'} } );
    if ( defined $ENV{'OSAFT_MAKE'} ) {
        _vprint("$cfg{'me'}: dd.mm.yyyy HH:MM:SS $make_text");
    }
    else {
        _vprint( sprintf( "%s: %02s.%02s.%s %02s:%02s:%02s", $cfg{'me'}, $mday, ( $mon + 1 ), ( $year + 1900 ), $h, $m, $s ) );
    }
    return;
}

sub _load_file {
    my $fil = shift;
    my $txt = shift;
    my $err = "";
    return $err if ( grep { /$fil/ } @{ $dbx{'files'} } );

    eval { require $fil; } or OCfg::warn("101: 'require $fil' failed");
    $err = $@;
    chomp $err;
    if ( "" eq $err ) {
        $fil = $INC{$fil};
        $txt = "$txt done";
    }
    else {
        $txt = "$txt failed";
    }
    push( @{ $dbx{'files'} }, $fil );
    _vprint_read( $fil, $txt );
    return $err;
}

my $arg  = "";
my @argv = ();

my $host    = "";
my $port    = "";
my $legacy  = "";
my $verbose = 0;

my $help    = "";
my $test    = "";
my $info    = 0;
my $check   = 0;
my $quick   = 0;
my $cmdsni  = 0;
my $sniname = undef;

our %cmd = (
    'timeout'    => "timeout",
    'openssl'    => "openssl",
    'openssl3'   => "openssl",
    'libs'       => [],
    'path'       => [],
    'extopenssl' => 1,
    'extsclient' => 1,
    'envlibvar'  => "LD_LIBRARY_PATH",
    'envlibvar3' => "LD_LIBRARY_PATH",
    'call'       => [],

    'version' => "",
);

$cfg{'time0'} = $time0;
OCfg::set_user_agent("$cfg{'me'}/3.164");
OCfg::set_user_agent("$cfg{'me'}/$OText::STR{'MAKEVAL'}") if ( defined $ENV{'OSAFT_MAKE'} );

%{ $cfg{'done'} } = (
    'targets'    => 0,
    'dbxfile'    => 0,
    'rc_file'    => 0,
    'init_all'   => 0,
    'ssl_failed' => 0,
    'ssl_errors' => 0,
    'arg_cmds'   => [],

    'default_get'     => 0,
    'ciphers_all'     => 0,
    'ciphers_get'     => 0,
    'checkciphers'    => 0,
    'checkpreferred'  => 0,
    'check02102'      => 0,
    'check03116'      => 0,
    'check2818'       => 0,
    'check6125'       => 0,
    'check7525'       => 0,
    'checkdates'      => 0,
    'checksizes'      => 0,
    'checkbleed'      => 0,
    'checkcert'       => 0,
    'checkprot'       => 0,
    'checkdest'       => 0,
    'checkhttp'       => 0,
    'checksstp'       => 0,
    'checksni'        => 0,
    'checkssl'        => 0,
    'checkalpn'       => 0,
    'checkdv'         => 0,
    'checkev'         => 0,
    'check_dh'        => 0,
    'check_url'       => 0,
    'check_certchars' => 0,
);

our %text = (
    'separator' => ":",

    'undef'             => "<<undefined>>",
    'response'          => "<<response>>",
    'protocol'          => "<<protocol probably supported, but no ciphers accepted>>",
    'need_cipher'       => "<<check possible in conjunction with +cipher only>>",
    'na'                => "<<N/A>>",
    'na_SSLv2'          => "<<N/A as SSLv2 has no server selected cipher>>",
    'na_STS'            => "<<N/A as STS not set>>",
    'na_sni'            => "<<N/A as --no-sni in use>>",
    'na_dns'            => "<<N/A as --no-dns in use>>",
    'na_cert'           => "<<N/A as --no-cert in use>>",
    'na_http'           => "<<N/A as --no-http in use>>",
    'na_tlsextdebug'    => "<<N/A as --no-tlsextdebug in use>>",
    'na_nextprotoneg'   => "<<N/A as --no-nextprotoneg in use>>",
    'na_reconnect'      => "<<N/A as --no_reconnect in use>>",
    'na_openssl'        => "<<N/A as --no-openssl in use>>",
    'disabled'          => "<<N/A as @@ in use>>",
    'disabled_protocol' => "<<N/A as protocol disabled or NOT YET implemented>>",
    'disabled_test'     => "tests with/for @@ disabled",
    'miss_cipher'       => "<<N/A as no ciphers found>>",
    'miss_protocol'     => "<<N/A as no protocol found>>",
    'miss_RSA'          => " <<missing ECDHE-RSA-* cipher>>",
    'miss_ECDSA'        => " <<missing ECDHE-ECDSA-* cipher>>",
    'missing'           => " <<missing @@>>",
    'enabled_extension' => " <<@@ extension enabled>>",
    'unexpected'        => " <<unexpected @@>>",
    'insecure'          => " <<insecure @@>>",
    'invalid'           => " <<invalid @@>>",
    'bit256'            => " <<keysize @@ < 256>>",
    'bit512'            => " <<keysize @@ < 512>>",
    'bit2048'           => " <<keysize @@ < 2048>>",
    'bit4096'           => " <<keysize @@ < 4096>>",
    'EV_large'          => " <<too large @@>>",
    'EV_subject_CN'     => " <<missmatch: subject CN= and commonName>>",
    'EV_subject_host'   => " <<missmatch: subject CN= and given hostname>>",
    'no_reneg'          => " <<secure renegotiation not supported>>",
    'cert_dates'        => " <<invalid certificate date>>",
    'cert_valid'        => " <<certificate validity to large @@>>",
    'cert_chars'        => " <<invalid charcters in @@>>",
    'wildcards'         => " <<uses wildcards:@@>>",
    'gethost'           => " <<gethostbyaddr() failed>>",
    'out_target'        => "\n==== Target: @@ ====\n",
    'out_ciphers'       => "\n=== Ciphers: Checking @@ ===",
    'out_infos'         => "\n=== Information ===",
    'out_scoring'       => "\n=== Scoring Results EXPERIMENTAL ===",
    'out_checks'        => "\n=== Performed Checks ===",
    'out_list'          => "=== List @@ Ciphers ===",
    'out_summary'       => "=== Ciphers: Summary @@ ===",
    'host_name'  => "Given hostname",
    'host_IP'    => "IP for given hostname",
    'host_rhost' => "Reverse resolved hostname",
    'host_DNS'   => "DNS entries for given hostname",
    'cipher'     => "Cipher",
    'support'    => "supported",
    'security'   => "Security",
    'dh_param'   => "DH Parameters",
    'desc'       => "Description",
    'desc_check' => "Check Result (yes is considered good)",
    'desc_info'  => "Value",
    'desc_score' => "Score (max value 100)",
    'anon_text'  => "<<anonymised>>",

    'legacy' => {

        'compact' => { 'not' => '-',   'yes' => "yes", 'no' => "no" },
        'simple'  => { 'not' => '-?-', 'yes' => "yes", 'no' => "no" },
        'full'    => { 'not' => '-?-', 'yes' => "Yes", 'no' => "No" },
        'key'     => { 'not' => '-?-', 'yes' => "yes", 'no' => "no" },
        'owasp'   => { 'not' => '-?-', 'yes' => "",    'no' => "" },
        'sslaudit'      => { 'not' => '-?-', 'yes' => "successfull",        'no' => "unsuccessfull" },
        'sslcipher'     => { 'not' => '-?-', 'yes' => "ENABLED",            'no' => "DISABLED" },
        'ssldiagnos'    => { 'not' => '-?-', 'yes' => "CONNECT_OK CERT_OK", 'no' => "FAILED" },
        'sslscan'       => { 'not' => '-?-', 'yes' => "Accepted",           'no' => "Rejected" },
        'ssltest'       => { 'not' => '-?-', 'yes' => "Enabled",            'no' => "Disabled" },
        'ssltest-g'     => { 'not' => '-?-', 'yes' => "Enabled",            'no' => "Disabled" },
        'sslyze'        => { 'not' => '-?-', 'yes' => "%s",                 'no' => "SSL Alert" },
        'testsslserver' => { 'not' => '-?-', 'yes' => "",                   'no' => "" },
        'thcsslcheck'   => { 'not' => '-?-', 'yes' => "supported",          'no' => "unsupported" },
    },

    'hints' => {

    },

    'mnemonic' => {
        'example'     => "TLS_DHE_DSS_WITH_3DES-EDE-CBC_SHA",
        'description' => "TLS Version _ key establishment algorithm _ digital signature algorithm _ WITH _ confidentility algorithm _ hash function",
        'explain'     => "TLS Version1 _ Ephemeral DH key agreement _ DSS which implies DSA _ WITH _ 3DES encryption in CBC mode _ SHA for HMAC"
    },

    'firefox' => {
        'browser.cache.disk_cache_ssl'                                                  => "En-/Disable caching of SSL pages",
        'security.enable_tls_session_tickets'                                           => "En-/Disable Session Ticket extension",
        'security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref' => "",
        'security.ssl.renego_unrestricted_hosts'                                        => '??',
        'security.ssl.require_safe_negotiation'                                         => "",
        'security.ssl.treat_unsafe_negotiation_as_broken'                               => "",
        'security.ssl.warn_missing_rfc5746'                                             => "",
        'pfs.datasource.url'                                                            => '??',
        'browser.identity.ssl_domain_display'                                           => "coloured non EV-SSL Certificates",
    },
    'IE' => {
        'HKLM\\...' => "sequence of ciphers",
    },

);

my $cgi = 0;
$cgi = 1 if _is_argv('(?:--cgi-?(?:exec|trace$))');

_trace_info("RCFILE0 - RC-FILE start");
if ( 0 < _is_argv('(?:--rc)') ) {
    $cfg{'RC-FILE'} = $0;
    $cfg{'RC-FILE'} =~ s#($cfg{'me'})$#.$1#;
}
if ( defined $ENV{'OSAFT_CONFIG'} ) {
    _trace_info("CONFIG  - OSAFT_CONFIG $ENV{'OSAFT_CONFIG'}");
    if ( -e $ENV{'OSAFT_CONFIG'} ) {
        $cfg{'RC-FILE'} = $ENV{'OSAFT_CONFIG'};
    }
    else {
        $cfg{'RC-FILE'} = "";
        OCfg::warn("038: OSAFT_CONFIG '$ENV{'OSAFT_CONFIG'}' does not exist; no RC-FILE read");
    }
}
if ( 0 < _is_argv('(?:--rc=)') ) {
    $cfg{'RC-FILE'} = ( grep { /--rc=.*/ } @ARGV )[0];
    $cfg{'RC-FILE'} =~ s#--rc=##;

}
_tprint("RC-FILE: $cfg{'RC-FILE'}") if _is_trace();
my @rc_argv = "";
if ( 0 >= _is_argv('(?:--no.?rc)') ) {

    OCfg::hint("use  --trace  to see complete settings") if _is_argv('(?:--v(?:=[0-9]+)?)');
    if ( open( my $rc, '<:encoding(UTF-8)', "$cfg{'RC-FILE'}" ) ) {
        push( @{ $dbx{'files'} }, $cfg{'RC-FILE'} );
        _vprint_read( "$cfg{'RC-FILE'}", "RC-FILE done" );
        @rc_argv = grep { !/^\s*[#=][^\r\n]*/ } <$rc>;
        @rc_argv = grep { s/[\r\n]// } @rc_argv;
        @rc_argv = grep { s/\s*([+,-]-?)/$1/ } @rc_argv;
        close($rc);
        OCfg::warn("052: option with trailing spaces '$_'") foreach ( grep { m/\s+$/ } @rc_argv );
        push( @argv, @rc_argv );

        my @cfgs;
        if ( _is_trace() ) {
            _tprint("$cfg{'RC-FILE'}");
            _tprint("#------------------------------------------------- RC-FILE {");
        }
        my @tmp_argv = @rc_argv;
        foreach my $val (@tmp_argv) {
            if ( $val !~ m/^\s*([+,-]-?)/ ) {
                OCfg::warn("040: invalid argument in RC-FILE '$val'; setting ignored");
                @argv = grep { !/$val/ } @argv;

                next;
            }
            $val =~ s/(--cfg[^=]*=[^=]*).*/$1/ if ( 0 >= _is_argv('(?:--trace)') );
            _tprint("     $val")               if ( _is_trace() );
            if ( $val =~ m/--cfg[^=]*=[^=]*/ ) {
                $val =~ s/--cfg[^=]*=([^=]*).*/+$1/;
                push( @cfgs, $val );
            }
        }
        if ( _is_trace() ) {
            _tprint("added/modified= @cfgs");
            _tprint("#------------------------------------------------- RC-FILE }");
        }
    }
    else {
        _vprint_read( "$cfg{'RC-FILE'}", "RC-FILE: $!" ) if _is_trace();
    }
}
@{ $cfg{'RC-ARGV'} } = @rc_argv;
$cfg{'done'}->{'rc_file'}++ if ( 0 < $#rc_argv );

_trace_info("RCFILE9 - RC-FILE end");

if ( defined $ENV{'OSAFT_OPTIONS'} ) {
    _trace_info("OPTIONS - OSAFT_OPTIONS $ENV{'OSAFT_OPTIONS'}");
    push( @argv, split( " ", $ENV{'OSAFT_OPTIONS'} ) );
}

push( @argv, @ARGV );
push( @ARGV, "--no-header" ) if ( ( grep { /--no-?header/ } @argv ) );

my $err = "";
my @dbx = grep { /--(?:trace|v$|exitcode.?v$|tests?)/ } @argv;
push( @dbx, grep { /^[+,](?:tests?)/ } @argv );
if ( ( $#dbx >= 0 ) and ( grep { /--cgi=?/ } @argv ) <= 0 ) {
    $arg = "lib/OTrace.pm";
    $arg = $dbx[0] if ( $dbx[0] =~ m#/# );
    $arg =~ s#[^=]+=##;
    $err = _load_file( $arg, "trace module" );
    if ( $err ne "" ) {
        die $OText::STR{ERROR}, "012: $err\n" unless ( -e $arg );
    }
}
else {
    sub trace      { }
    sub trace_     { }
    sub trace1     { }
    sub trace2     { }
    sub trace_arg  { }
    sub trace_args { }
    sub trace_init { }
    sub trace_exit { }
}
if ( exists $INC{'lib/OTrace.pm'} ) {
    no warnings 'redefine';
    no warnings 'once';
    *trace      = \&OTrace::trace;
    *trace_     = \&OTrace::trace_;
    *trace1     = \&OTrace::trace1;
    *trace2     = \&OTrace::trace2;
    *trace_arg  = \&OTrace::arg_show;
    *trace_args = \&OTrace::args_show;
    *trace_init = \&OTrace::init_show;
    *trace_exit = \&OTrace::exit_show;
    $OTrace::trace          = $cfg{'trace'};
    $OTrace::prefix_trace   = $cfg{'prefix_trace'};
    $OTrace::prefix_verbose = $cfg{'prefix_verbose'};
}

if ( ( grep { /--(?:use?r)/ } @argv ) > 0 ) {
    $err = _load_file( "lib/OUsr.pm", "user module" );
    if ( $err ne "" ) {
        no warnings 'redefine';
        sub OUsr::version    { return ""; }
        sub OUsr::pre_init   { }
        sub OUsr::pre_file   { }
        sub OUsr::pre_args   { }
        sub OUsr::pre_exec   { }
        sub OUsr::pre_cipher { }
        sub OUsr::pre_main   { }
        sub OUsr::pre_host   { }
        sub OUsr::pre_info   { }
        sub OUsr::pre_open   { }
        sub OUsr::pre_cmds   { }
        sub OUsr::pre_data   { }
        sub OUsr::pre_print  { }
        sub OUsr::pre_next   { }
        sub OUsr::pre_exit   { }

    }
}

OUsr::pre_init();

my %scores = (

    'check_conn' => { 'val' => 100, 'txt' => "SSL connection checks" },
    'check_ciph' => { 'val' => 100, 'txt' => "Ciphers checks" },
    'check_cert' => { 'val' => 100, 'txt' => "Certificate checks" },
    'check_dest' => { 'val' => 100, 'txt' => "Target checks" },
    'check_http' => { 'val' => 100, 'txt' => "HTTP(S) checks" },
    'check_size' => { 'val' => 100, 'txt' => "Certificate sizes checks" },
    'checks'     => { 'val' => 100, 'txt' => "Total scoring" },
);

my %score_ssllabs = (
    'check_prot' => { 'val' => 0, 'score' => 0.3, 'txt' => "Protocol support" },
    'check_keyx' => { 'val' => 0, 'score' => 0.3, 'txt' => "Key exchange support" },
    'check_ciph' => { 'val' => 0, 'score' => 0.4, 'txt' => "Cipher strength support" },

    'A' => { 'val' => 0, 'score' => 80, 'txt' => "A" },
    'B' => { 'val' => 0, 'score' => 65, 'txt' => "B" },
    'C' => { 'val' => 0, 'score' => 50, 'txt' => "C" },
    'D' => { 'val' => 0, 'score' => 35, 'txt' => "D" },
    'E' => { 'val' => 0, 'score' => 20, 'txt' => "E" },
    'F' => { 'val' => 0, 'score' => 20, 'txt' => "F" },

    'SSLv2'   => { 'val' => 0, 'score' => 20,  'txt' => "SSL 2.0" },
    'SSLv3'   => { 'val' => 0, 'score' => 80,  'txt' => "SSL 3.0" },
    'TLSv1'   => { 'val' => 0, 'score' => 90,  'txt' => "TLS 1.0" },
    'TLSv11'  => { 'val' => 0, 'score' => 95,  'txt' => "TLS 1.1" },
    'TLSv12'  => { 'val' => 0, 'score' => 100, 'txt' => "TLS 1.2" },
    'TLSv13'  => { 'val' => 0, 'score' => 100, 'txt' => "TLS 1.3" },
    'DTLSv09' => { 'val' => 0, 'score' => 80,  'txt' => "DTLS 0.9" },
    'DTLSv1'  => { 'val' => 0, 'score' => 100, 'txt' => "DTLS 1.0" },
    'DTLSv11' => { 'val' => 0, 'score' => 100, 'txt' => "DTLS 1.1" },
    'DTLSv12' => { 'val' => 0, 'score' => 100, 'txt' => "DTLS 1.2" },
    'DTLSv13' => { 'val' => 0, 'score' => 100, 'txt' => "DTLS 1.3" },

    'key_debian' => { 'val' => 0, 'score' => 0,   'txt' => "Weak key (Debian OpenSSL flaw)" },
    'key_anonx'  => { 'val' => 0, 'score' => 0,   'txt' => "Anonymous key exchange (no authentication)" },
    'key_512'    => { 'val' => 0, 'score' => 20,  'txt' => "Key length < 512 bits" },
    'key_export' => { 'val' => 0, 'score' => 40,  'txt' => "Exportable key exchange (limited to 512 bits)" },
    'key_1024'   => { 'val' => 0, 'score' => 40,  'txt' => "Key length < 1024 bits (e.g., 512)" },
    'key_2048'   => { 'val' => 0, 'score' => 80,  'txt' => "Key length < 2048 bits (e.g., 1024)" },
    'key_4096'   => { 'val' => 0, 'score' => 90,  'txt' => "Key length < 4096 bits (e.g., 2048)" },
    'key_good'   => { 'val' => 0, 'score' => 100, 'txt' => "Key length >= 4096 bits (e.g., 4096)" },

    'ciph_0'   => { 'val' => 0, 'score' => 0, 'txt' => "0 bits (no encryption)" },
    'ciph_128' => { 'val' => 0, 'score' => 0, 'txt' => "< 128 bits (e.g., 40, 56)" },
    'ciph_256' => { 'val' => 0, 'score' => 0, 'txt' => "< 256 bits (e.g., 128, 168)" },
    'ciph_512' => { 'val' => 0, 'score' => 0, 'txt' => ">= 256 bits (e.g., 256)" },

);

my %score_howsmyssl = (
    'good'       => { 'txt' => "Good" },
    'probably'   => { 'txt' => "Probably Okay" },
    'improvable' => { 'txt' => "Improvable" },
    'bad' => { 'txt' => "Bad" },
);

my %info_gnutls = (

    'I' => "<72      <1008      <160      INSECURE    Considered to be insecure",
    'W' => "72        1008       160      WEAK        Short term protection against small organizations",
    'L' => "80        1248       160      LOW         Very short term protection against agencies",
    'l' => "96        1776       192      LEGACY      Legacy standard level",
    'M' => "112       2432       224      NORMAL      Medium-term protection",
    'H' => "128       3248       256      HIGH        Long term protection",
    'S' => "256       15424      512      ULTRA       Foreseeable future",
);

my $old   = "";
my $regex = join( "|", @{ $cfg{'versions'} } );
foreach my $key ( sort { uc($a) cmp uc($b) } keys %data, keys %checks, @{ $cfg{'commands_int'} } ) {
    next if ( $key eq $old );
    $old = $key;
    push( @{ $cfg{'commands'} },       $key ) if ( $key !~ m/^(?:$regex)/ );
    push( @{ $cfg{'cmd-hsts'} },       $key ) if ( $key =~ m/$cfg{'regex'}->{'cmd-hsts'}/i );
    push( @{ $cfg{'cmd-http'} },       $key ) if ( $key =~ m/$cfg{'regex'}->{'cmd-http'}/i );
    push( @{ $cfg{'cmd-sizes'} },      $key ) if ( $key =~ m/$cfg{'regex'}->{'cmd-sizes'}/ );
    push( @{ $cfg{'need-checkhttp'} }, $key ) if ( $key =~ m/$cfg{'regex'}->{'cmd-hsts'}/ );
    push( @{ $cfg{'need-checkhttp'} }, $key ) if ( $key =~ m/$cfg{'regex'}->{'cmd-http'}/ );
}

push( @{ $cfg{'cmd-check'} },   $_ ) foreach ( keys %checks );
push( @{ $cfg{'cmd-info--v'} }, 'dump' );
foreach my $key ( keys %data ) {
    push( @{ $cfg{'cmd-info--v'} }, $key );
    next if ( _is_cfg_intern($key) );
    next if ( $key =~ m/^(ciphers)/ and $verbose == 0 );
    next if ( $key =~ m/^modulus$/  and $verbose == 0 );
    push( @{ $cfg{'cmd-info'} }, $key );
}
push( @{ $cfg{'cmd-info--v'} }, 'info--v' );

foreach my $key (qw(commands commands_cmd commands_usr commands_int cmd-info--v)) {
    @{ $cfg{$key} } = sort( @{ $cfg{$key} } );
}
if ( 0 < _is_argv('(?:--no.?rc)') ) {
    foreach my $key (qw(do cmd-check cmd-info cmd-quick cmd-vulns)) {
        @{ $cfg{$key} } = sort( @{ $cfg{$key} } );
    }
}

_trace_info("INIT    - RC-FILE merged");

$cmd{'extopenssl'} = 0 if ( $^O =~ m/MSWin32/ );

$cmd{'openssl'} = $cfg{'openssl_env'} if ( defined $cfg{'openssl_env'} );
if ( defined $ENV{'LIBPATH'} ) {
    OCfg::hint("LIBPATH environment variable found, consider using '--envlibvar=LIBPATH'");
}

_trace_info("INIT9   - initialisation end");
OUsr::pre_file();

sub __subst { my ( $is, $txt ) = @_; $is =~ s/@@/$txt/; return $is; }
sub _get_text { my ( $is, $txt ) = @_; return __subst( $text{$is}, $txt ); }
sub _get_yes_no { my $val = shift || ""; return ( $val eq "" ) ? 'yes' : 'no (' . $val . ')'; }

sub _get_base2 {
    my $value = shift;
    $value = 1 if ( $value !~ /^[0-9]+$/ );
    return 0 if ( $value == 0 );
    $value = log($value);
    return ( $value + ( $value / 100 * 44 ) );
}

sub _hex_like_openssl {
    my $c = shift;
    $c =~ s/0x(..)(..)(..)(..)/0x$2,0x$3,0x$4 - /;
    $c =~ s/^0x00,// if ( $c ne "0x00,0x00,0x00" );
    return sprintf( "%22s", $c );
}

sub __need_this {
    my $key = shift;
    my $is  = join( "|", @{ $cfg{'do'} } );
    $is =~ s/\+/\\+/g;
    return grep { /^($is)$/ } @{ $cfg{$key} };
}

sub _need_netinfo {
    my $need_cipher = join( "|", @{ $cfg{'need-cipher'} } );
    return grep { not /^(?:$need_cipher)$/ } @{ $cfg{'do'} };
}
sub _need_cipher     { return __need_this('need-cipher'); }
sub _need_default    { return __need_this('need-default'); }
sub _need_checkssl   { return __need_this('need-checkssl'); }
sub _need_checkalpn  { return __need_this('need-checkalpn'); }
sub _need_checkbleed { return __need_this('need-checkbleed'); }
sub _need_checkchr   { return __need_this('need-checkchr'); }
sub _need_checkdest  { return __need_this('need-checkdest'); }
sub _need_check_dh   { return __need_this('need-check_dh'); }
sub _need_checkhttp  { return __need_this('need-checkhttp'); }
sub _need_checkprot  { return __need_this('need-checkprot'); }
sub _is_do_cmdvulns { return __need_this('cmd-vulns'); }

sub _is_hashkey {
    my ( $is, $ref ) = @_;
    return grep( { lc($is) eq lc($_) } keys %{$ref} );
}

sub _is_member {
    my ( $is, $ref ) = @_;
    return grep( { lc($is) eq lc($_) } @{$ref} );
}
sub _is_cfg_do      { my $is = shift; return _is_member( $is, \@{ $cfg{'do'} } ); }
sub _is_cfg_intern  { my $is = shift; return _is_member( $is, \@{ $cfg{'commands_int'} } ); }
sub _is_cfg_hexdata { my $is = shift; return _is_member( $is, \@{ $cfg{'data_hex'} } ); }
sub _is_cfg_call    { my $is = shift; return _is_member( $is, \@{ $cmd{'call'} } ); }
sub _is_cfg     { my $is = shift; return $cfg{$is}; }
sub _is_cfg_ssl { my $is = shift; return $cfg{$is}; }
sub _is_cfg_out { my $is = shift; return $cfg{'out'}->{$is}; }
sub _is_cfg_tty { my $is = shift; return $cfg{'tty'}->{$is}; }
sub _is_cfg_use { my $is = shift; return $cfg{'use'}->{$is}; }
sub _is_cfg_trace      { return $cfg{'trace'}; }
sub _is_cfg_verbose    { return $cfg{'verbose'}; }
sub _is_cfg_ciphermode { my $is = shift; return ( $cfg{'ciphermode'} =~ $is ); }
sub _is_cfg_legacy { my $is = shift; return ( $cfg{'legacy'} =~ $is ); }

sub _set_cfg_out { my ( $is, $val ) = @_; $cfg{'out'}->{$is} = $val; return; }
sub _set_cfg_tty { my ( $is, $val ) = @_; $cfg{'tty'}->{$is} = $val; return; }
sub _set_cfg_use { my ( $is, $val ) = @_; $cfg{'use'}->{$is} = $val; return; }

sub _set_cfg_list {
    my ( $is, $val ) = @_;
    $cfg{$is} = [""] if ( $val =~ /^[,:][,:]$/ );
    if ( $val =~ /^[,:]$/ ) {
        $cfg{$is} = [];
    }
    else {
        push( @{ $cfg{$is} }, split( /,/, $val ) );
    }
    return;
}

sub _set_cfg;

sub _set_cfg_from_file {
    my $typ = shift;
    my $fil = shift;
    trace("_set_cfg_from_file($typ, $fil) {");
    my $line = "";
    my $fh;
    if ( open( $fh, '<:encoding(UTF-8)', $fil ) ) {
        push( @{ $dbx{'files'} }, $fil );
        _vprint_read( "$fil", "USER-FILE configuration file" ) if ( _is_cfg_out('header') );
        while ( $line = <$fh> ) {
            chomp $line;
            $line =~ s/\s*#.*$// if ( $typ !~ m/^CFG-text/i );
            next if ( $line =~ m/^\s*=/ );
            next if ( $line =~ m/^\s*$/ );
            trace("_set_cfg_from_file: set $line ");
            _set_cfg( $typ, $line );
        }
        close($fh);
        goto FIN;
    }
    OCfg::warn("070: configuration file '$fil' cannot be opened: $! ; file ignored");
  FIN:
    trace("_set_cfg_from_file() }");
    return;
}

sub _set_cfg {
    my $typ = shift;
    my $arg = shift;
    my ( $key, $val );
    trace("_set_cfg($typ, ) {");
    if ( $typ !~ m/^CFG-$cfg{'regex'}->{'cmd-cfg'}/ ) {
        OCfg::warn("071: configuration key unknown '$typ'; setting ignored");
        goto FIN;
    }
    if ( ( $arg =~ m|^[a-zA-Z0-9,._+#()\/-]+| ) and ( -f "$arg" ) ) {

        if ( $cgi == 1 ) {

            OCfg::warn("072: configuration files are not read in CGI mode; ignored");
            return;
        }
        _set_cfg_from_file( $typ, $arg );
        goto FIN;
    }

    ( $key, $val ) = split( /=/, $arg, 2 );
    $key =~ s/[^a-zA-Z0-9_?=+-]*//g;
    $val = "" if not defined $val;
    $val =~ s/^[+]//;
    $val =~ s/ [+]/ /g;

    if ( $typ eq 'CFG-cmd' ) {
        $typ = 'cmd-' . $key;
        trace("_set_cfg: cfg{$typ}, KEY=$key, CMD=$val");
        @{ $cfg{$typ} } = ();
        push( @{ $cfg{$typ} }, split( /\s+/, $val ) );
        foreach my $key ( @{ $cfg{$typ} } ) {
            next if ( _is_hashkey( $key, \%checks ) );
            next if ( _is_hashkey( $key, \%data ) );
            next if ( _is_member( $key, \@{ $cfg{'need-NL'} } ) );
            next if ( _is_cfg_intern($key) );
            if ( $key eq 'protocols' ) {
                push( @{ $cfg{$typ} }, 'next_protocols' );
                next;
            }
            if ( $key eq 'default' ) {
                push( @{ $cfg{$typ} }, 'cipher_selected' );
                OCfg::warn("073: configuration: please use '+cipher-selected' instead of '+$key'; setting ignored");
                next;
            }
            OCfg::warn("074: configuration: unknown command '+$key' for '$typ'; setting ignored");
        }
        if ( ( _is_member( $key, \@{ $cfg{'commands'} } ) + _is_member( $key, \@{ $cfg{'commands_cmd'} } ) + _is_member( $key, \@{ $cfg{'commands_int'} } ) ) <
            1 )
        {
            if ( not _is_member( "cmd-$key", \@{ $cfg{'commands_cmd'} } ) ) {
                if ( $key =~ m/^([a-z0-9_.-]+)$/ ) {
                    push( @{ $cfg{'commands_usr'} }, $key );
                    OCfg::warn("046: command '+$key' specified by user") if _is_v_trace();
                }
            }
        }
    }

    if ( $typ eq 'CFG-score' ) {
        trace("_set_cfg: KEY=$key, SCORE=$val");
        if ( $val !~ m/^(?:\d\d?|100)$/ ) {
            OCfg::warn("076: configuration: invalid score value '$val'; setting ignored");
            goto FIN;
        }
        $checks{$key}->{score} = $val if ( $checks{$key} );
    }

    $val =~ s/(\\n)/\n/g;
    $val =~ s/(\\r)/\r/g;
    $val =~ s/(\\t)/\t/g;
    trace("_set_cfg: KEY=$key, TYP=$typ, LABEL=$val");
    $checks{$key}->{txt}  = $val if ( $typ =~ /^CFG-check/ );
    $data{$key}->{txt}    = $val if ( $typ =~ /^CFG-data/ );
    $data{$key}->{txt}    = $val if ( $typ =~ /^CFG-info/ );
    $cfg{'hints'}->{$key} = $val if ( $typ =~ /^CFG-hint/ );
    $text{$key}           = $val if ( $typ =~ /^CFG-text/ );
    $scores{$key}->{txt}  = $val if ( $typ =~ /^CFG-scores/ );
    $scores{$key}->{txt}  = $val if ( $key =~ m/^check_/ );

  FIN:
    trace("_set_cfg() }");
    return;
}

sub _set_cfg_init {
    my ( $typ, $arg ) = @_;
    my ( $key, $val ) = split( /=/, $arg, 2 );
    OCfg::warn("075: TESTING only: setting configuration: 'cfg{$key}=$val';");
  SWITCH: for ( ref( $cfg{$key} ) ) {
        /^$/     && do { $cfg{$key}      = $val; };
        /SCALAR/ && do { $cfg{$key}      = $val; };
        /ARRAY/  && do { @{ $cfg{$key} } = ($val); };
        /HASH/   && do { %{ $cfg{$key} } = $val; };
        /CODE/   && do { OCfg::warn("999: cannot set CODE"); };
    }
    return;
}

sub _is_cipher_key { return Ciphers::is_valid_key(shift); }
sub _get_cipher_sec { return Ciphers::get_sec( Ciphers::get_key(shift) ); }

sub _set_cipher_sec {
    my ( $typ, $arg ) = @_;
    my ( $key, $val ) = split( /=/, $arg, 2 );
    $key = Ciphers::get_key($key) if ( not _is_cipher_key($key) );
    return if not $key;
    Ciphers::set_sec( $key, $val );
    return;
}

sub __is_number {
    my $val = shift;
    return 0 if not defined $val;
    return 0 if $val eq '';
    return ( $val ^ $val ) ? 0 : 1;
}

sub _load_modules {
    trace("_load_modules() {");
    my $_err = "";
    if ( 1 > 0 ) {
        $_err = _load_file( "IO/Socket/SSL.pm", "IO-SSL module" );
        warn $OText::STR{ERROR}, "005: $_err" if ( "" ne $_err );
    }
    if ( 0 < $cfg{'need_netdns'} ) {
        $_err = _load_file( "Net/DNS.pm", "Net::DNS module'" );
        if ( "" ne $_err ) {
            warn $OText::STR{ERROR}, "007: $_err";
            OCfg::warn("111: option '--mx disabled");
            $cfg{'use'}->{'mx'} = 0;
        }
    }
    if ( 0 < $cfg{'need_timelocal'} ) {
        $_err = _load_file( "Time/Local.pm", "Time::Local module" );
        if ( "" ne $_err ) {
            warn $OText::STR{ERROR}, "008: $_err";
            OCfg::warn("112: value for '+sts_expired' not applicable");
        }
    }
    $_err = _load_file( "Encode.pm", "Encode module" );
    if ( "" ne $_err ) {
        warn $OText::STR{ERROR}, "008: $_err";
    }

    $_err = _load_file( "lib/SSLhello.pm", "SSLhello module" );
    if ( "" ne $_err ) {
        die $OText::STR{ERROR}, "010: $_err\n" if ( not _is_cfg_do('version') );
        warn $OText::STR{ERROR}, "010: $_err";
    }
    if ( $cfg{'starttls'} ) {
        $cfg{'use'}->{'http'} = 0;

    }
    goto FIN if ( 1 > $cfg{'need_netinfo'} );
    $_err = _load_file( "lib/SSLinfo.pm", "SSLinfo module" );
    if ( "" ne $_err ) {
        die $OText::STR{ERROR}, "011: $_err\n" if ( not _is_cfg_do('version') );
        warn $OText::STR{ERROR}, "011: $_err";
    }
  FIN:
    trace("_load_modules() }");
    return;
}

sub _check_modules {
    trace("_check_modules() {");
    my %expected_versions = (
        'IO::Socket::INET' => "1.31",
        'IO::Socket::SSL'  => "1.37",
        'Net::SSLeay'      => "1.49",
        'Net::DNS'         => "0.65",
        'Time::Local'      => "1.23",
    );
    my $have_version = 1;
    eval { require version; } or $have_version = 0;
    if ( __is_number($version::VERSION) ) {
        $have_version = 0 if ( $version::VERSION < 0.77 );
    }
    else {
        $have_version     = 0;
        $version::VERSION = "";
    }
    if ( $have_version == 0 ) {
        warn $OText::STR{WARN}, "120: ancient perl has no 'version' module; version checks may not be accurate;";
    }
    trace( sprintf( "# %s+%s+%s",    "-" x 24,      "-" x 7,   "-" x 15 ) );
    trace( sprintf( "# %-24s %s %s", "module name", "VERSION", "> expected versions" ) );
    trace( sprintf( "# %s+%s+%s",    "-" x 24,      "-" x 7,   "-" x 15 ) );
    foreach my $mod ( keys %expected_versions ) {
        next if ( ( $cfg{'need_netdns'} == 0 )    and ( $mod eq "Net::DNS" ) );
        next if ( ( $cfg{'need_timelocal'} == 0 ) and ( $mod eq "Time::Local" ) );
        no strict 'refs';

        my $expect = $expected_versions{$mod};
        my $v      = $mod . "::VERSION";
        my $ok     = "yes";
        eval { $v = $$v; } or $v = 0;
        if ( $have_version == 1 ) {

            $v      = version->parse("v$v");
            $expect = version->parse("v$expect");
        }
        if ( $v < $expect ) {
            $ok = "no";
            $ok = "missing" if ( $v == 0 );
            warn $OText::STR{WARN}, "121: ancient $mod $v < $expect detected;";
        }
        trace( sprintf( "# %-24s %-7s > %s\t%s", $mod, $v, $expect, $ok ) );
    }
    trace( sprintf( "# %s+%s+%s", "-" x 24, "-" x 7, "-" x 15 ) );
    trace("_check_modules() }");
    return;
}

sub _enable_functions {
    my $version_openssl  = shift;
    my $version_ssleay   = shift;
    my $version_iosocket = shift;
    trace("_enable_functions($version_openssl, $version_ssleay, $version_iosocket) {");
    my $txo          = sprintf( "ancient openssl 0x%x", $version_openssl );
    my $txi          = "ancient IO::Socket::SSL $version_iosocket";
    my $txs          = "ancient Net::SSLeay $version_ssleay";
    my $txt          = "improper Net::SSLeay $version_ssleay";
    my $txt_buggysni = "does not support SNI or is known to be buggy; SNI disabled;";

    if ( $cfg{'ssleay'}->{'openssl'} == 0 ) {
        warn $OText::STR{WARN}, "122: $txs; cannot detect OpenSSL version";
    }
    if ( $cfg{'ssleay'}->{'iosocket'} == 0 ) {
        warn $OText::STR{WARN}, "123: ancient or unknown version of IO::Socket detected";
    }

    if ( $cfg{'ssleay'}->{'can_sni'} == 0 ) {
        if ( ( _is_cfg_use('sni') ) and ( not _is_cfg_ciphermode('openssl') ) ) {
            $cfg{'use'}->{'sni'} = 0;
            if ( $version_iosocket < 1.90 ) {
                warn $OText::STR{WARN}, "124: $txi < 1.90; $txt_buggysni";
            }
            if ( $version_openssl < 0x01000000 ) {
                warn $OText::STR{WARN}, "125: $txo < 1.0.0; $txt_buggysni";
            }
            OCfg::hint("use '--ciphermode=openssl' to disable this check") if ( _is_cfg_out('hint_check') );
        }
    }
    trace(" cfg{use}->{sni}= $cfg{'use'}->{'sni'}");

    if ( ( $cfg{'ssleay'}->{'set_alpn'} == 0 ) or ( $cfg{'ssleay'}->{'get_alpn'} == 0 ) ) {
        if ( _is_cfg_use('alpn') ) {
            $cfg{'use'}->{'alpn'} = 0;
            warn $OText::STR{WARN}, "126: $txt; tests with/for ALPN disabled";
            if ( $version_openssl < 0x10002000 ) {
                warn $OText::STR{WARN}, "128: $txo < 1.0.2" if ( $cfg{'verbose'} > 1 );
            }
            OCfg::hint("126: use '--no-alpn' to disable this check") if ( _is_cfg_out('hint_check') );
        }
    }
    trace(" cfg{use}->{alpn}= $cfg{'use'}->{'alpn'}");

    if ( $cfg{'ssleay'}->{'set_npn'} == 0 ) {
        if ( _is_cfg_use('npn') ) {
            $cfg{'use'}->{'npn'} = 0;
            warn $OText::STR{WARN}, "129: $txt; tests with/for NPN disabled";
            if ( $version_openssl < 0x10001000 ) {
                warn $OText::STR{WARN}, "132: $txo < 1.0.1" if ( $cfg{'verbose'} > 1 );
            }
            OCfg::hint("129: use '--no-npn' to disable this check") if ( _is_cfg_out('hint_check') );
        }
    }
    trace(" cfg{use}->{npn}= $cfg{'use'}->{'npn'}");

    if ( $cfg{'ssleay'}->{'can_ocsp'} == 0 ) {
        warn $OText::STR{WARN}, "133: $txt; tests for OCSP disabled";
    }

    if ( $cfg{'ssleay'}->{'can_ecdh'} == 0 ) {
        warn $OText::STR{WARN}, "134: $txt; setting curves disabled";
    }
    trace("_enable_functions() }");
    return;
}

sub _check_functions {

    trace("_check_functions() {");
    my $txt              = "";
    my $tmp              = "";
    my $version_openssl  = 0;
    my $version_ssleay   = -1;
    my $version_iosocket = -1;
    my $text_ssleay      = "Net::SSLeay\t$version_ssleay supports";

    if ( not defined $Net::SSLeay::VERSION ) {
        if ( $cmd{'extopenssl'} == 0 ) {
            die $OText::STR{ERROR}, "014: Net::SSLeay not found, useless use of SSL advanced forensic tool\n";
        }
    }
    else {
        $version_ssleay = $Net::SSLeay::VERSION;
        $text_ssleay    = "Net::SSLeay\t$version_ssleay supports";
    }
    if ( not exists &Net::SSLeay::OPENSSL_VERSION_NUMBER ) {
        $cfg{'ssleay'}->{'openssl'} = 0;
    }
    else {
        $version_openssl = Net::SSLeay::OPENSSL_VERSION_NUMBER();
    }
    if ( not defined $IO::Socket::SSL::VERSION ) {
        $cfg{'ssleay'}->{'iosocket'} = 0;
    }
    else {
        $version_iosocket = $IO::Socket::SSL::VERSION;
    }

    trace(" check for proper SNI support ...");
    if ( $version_iosocket < 1.90 ) {
        $cfg{'ssleay'}->{'can_sni'} = 0;
    }
    else {
        trace("IO::Socket::SSL\t$version_iosocket OK\tyes");
    }
    if ( $version_openssl < 0x01000000 ) {
        $cfg{'ssleay'}->{'can_sni'} = 0;
    }
    else {
        trace("$text_ssleay OpenSSL version\tyes");
    }

    trace(" check if Net::SSLeay is usable ...");
    if ( $version_ssleay < 1.49 ) {
        warn $OText::STR{WARN}, "135: Net::SSLeay $version_ssleay < 1.49; may throw warnings and/or results may be missing;";
    }
    else {
        trace("$text_ssleay (OK)\tyes");
    }

    trace(" check if Net::SSLeay support SSLv2 ...");
    if ( $version_ssleay < 1.86 ) {
        if ( _is_cfg_ciphermode('ssleay') ) {
            warn $OText::STR{WARN}, "136: Net::SSLeay $version_ssleay < 1.86; does not support SSLv2;";
        }
    }
    else {
        trace("$text_ssleay (OK)\tyes");
    }

    trace(" check if Net::SSLeay support TLSv13 ...");
    if ( $version_ssleay < 1.92 ) {
        if ( _is_cfg_ciphermode('socket') ) {
            warn $OText::STR{WARN}, "137: Net::SSLeay $version_ssleay < 1.92; does not support TLSv13;";
        }
    }
    else {
        trace("$text_ssleay (OK)\tyes");
    }

    trace(" check for NPN and ALPN support ...");
    if ( ( $version_ssleay < 1.56 ) or ( $version_openssl < 0x10002000 ) ) {
        $cfg{'ssleay'}->{'set_alpn'} = 0;
        $cfg{'ssleay'}->{'get_alpn'} = 0;
    }
    else {
        trace("$text_ssleay ALPN\tyes");
    }
    if ( ( $version_ssleay < 1.46 ) or ( $version_openssl < 0x10001000 ) ) {
        $cfg{'ssleay'}->{'set_npn'} = 0;
    }
    else {
        trace("$text_ssleay  NPN\tyes");
    }

    if ( not exists &Net::SSLeay::CTX_set_alpn_protos ) {
        $cfg{'ssleay'}->{'set_alpn'} = 0;
    }
    else {
        trace("$text_ssleay set ALPN\tyes");
    }

    if ( not exists &Net::SSLeay::P_alpn_selected ) {
        $cfg{'ssleay'}->{'get_alpn'} = 0;
    }
    else {
        trace("$text_ssleay get ALPN\tyes");
    }

    if ( not exists &Net::SSLeay::CTX_set_next_proto_select_cb ) {
        $cfg{'ssleay'}->{'set_npn'} = 0;
    }
    else {
        trace("$text_ssleay set  NPN\tyes");
    }

    if ( not exists &Net::SSLeay::P_next_proto_negotiated ) {
        $cfg{'ssleay'}->{'get_npn'} = 0;
    }
    else {
        trace("$text_ssleay get  NPN\tyes");
    }

    if ( not exists &Net::SSLeay::OCSP_cert2ids ) {
        $cfg{'ssleay'}->{'can_ocsp'} = 0;
    }
    else {
        trace("$text_ssleay OSCP\tyes");
    }

    if ( not exists &Net::SSLeay::CTX_set_tmp_ecdh ) {
        $cfg{'ssleay'}->{'can_ecdh'} = 0;
    }
    else {
        trace("$text_ssleay Curves\tyes");
    }

    $cfg{'ssleay'}->{'can_npn'} = $cfg{'ssleay'}->{'get_npn'};
    _enable_functions( $version_openssl, $version_ssleay, $version_iosocket );
    trace("_check_functions() }");
    return;
}

sub _check_ssl_methods {
    trace("_check_ssl_methods() {");
    my $typ;
    my @list;
    if ( _is_cfg_ciphermode('openssl|socket') ) {
        @list = SSLinfo::ssleay_methods();
    }
    trace(" SSLeay methods= [ @list ]");
    foreach my $ssl ( @{ $cfg{'versions'} } ) {
        next if ( $cfg{$ssl} == 0 );
        if ( _is_cfg_ciphermode('intern|dump') ) {
            push( @{ $cfg{'version'} }, $ssl );
            next;
        }
        $cfg{$ssl} = 0;
        if ( $ssl !~ /$cfg{'regex'}->{'SSLprot'}/ ) {
            OCfg::warn("141: SSL version '$ssl': not supported; not checked");
            next;
        }
        if ( _is_cfg_use('ssl_lazy') or _is_cfg_ciphermode('openssl|socket') ) {
            push( @{ $cfg{'version'} }, $ssl );
            $cfg{$ssl} = 1;
            next;
        }
        next if ( not _is_cfg_do('cipher') );
        $typ = 0;
        $typ++ if ( ( $ssl eq 'SSLv2' )   and ( grep { /^SSLv2_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'SSLv3' )   and ( grep { /^SSLv3_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'TLSv1' )   and ( grep { /^TLSv1_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'TLSv11' )  and ( grep { /^TLSv1_1_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'TLSv12' )  and ( grep { /^TLSv1_2_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'TLSv13' )  and ( grep { /^TLSv1_3_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'DTLSv1' )  and ( grep { /^DTLSv1_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'DTLSv11' ) and ( grep { /^DTLSv1_1_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'DTLSv12' ) and ( grep { /^DTLSv1_2_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'DTLSv13' ) and ( grep { /^DTLSv1_3_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'SSLv2' )   and ( grep { /^SSLv23_method$/ } @list ) );
        $typ++ if ( ( $ssl eq 'SSLv3' )   and ( grep { /^SSLv23_method$/ } @list ) );

        if ( $typ > 0 ) {
            push( @{ $cfg{'version'} }, $ssl );
            $cfg{$ssl} = 1;
        }
        else {
            OCfg::warn("143: SSL version '$ssl': not supported by Net::SSLeay; not checked");
            OCfg::hint("143: consider using '--ciphermode=intern' instead") if not _is_cfg_ciphermode('intern');
        }
    }

    if ( not _is_cfg_do('version') ) {
        trace(" supported SSL versions= [ @{$cfg{'versions'}} ]");
        trace("   checked SSL versions= [ @{$cfg{'version'}} ]");
    }
    trace("_check_ssl_methods() }");
    return;
}

sub _enable_sclient {
    my $opt = shift;
    trace("_enable_sclient() {");
    my $txt = $cfg{'openssl'}->{$opt}[1] || $OText::STR{UNDEF};
    my $val = $cfg{'openssl'}->{$opt}[0];
    if ( $val == 0 ) {
        if ( $opt =~ m/^-(?:alpn|npn|curves)$/ ) {
            if ( _is_cfg_ciphermode('openssl') ) {
                OCfg::warn("144: openssl $cmd{'version'}: 's_client' does not support '$opt'; $txt") if ( $txt ne "" );
            }
        }
        if ( $opt =~ m/^-(?:no.?)?(?:dtls|tls|ssl)/ ) {
            if ( _is_cfg_ciphermode('openssl|socket') ) {
                OCfg::warn("145: openssl $cmd{'version'}: 's_client' does not support '$opt'; $txt") if ( $txt ne "" );
            }
        }
        if ( $opt eq '-tlsextdebug' ) {
            OCfg::warn(
"146: openssl $cmd{'version'}: '-tlsextdebug' not supported; results for following commands may be wrong: +heartbeat, +heartbleed, +session_ticket, +session_lifetime"
            );
        }
        $cfg{'use'}->{'reconnect'} = $val  if ( $opt eq '-reconnect' );
        $cfg{'use'}->{'extdebug'}  = $val  if ( $opt eq '-tlsextdebug' );
        $cfg{'use'}->{'alpn'}      = $val  if ( $opt eq '-alpn' );
        $cfg{'use'}->{'npn'}       = $val  if ( $opt eq '-npn' );
        $cfg{'sni'}                = $val  if ( $opt eq '-servername' );
        $cfg{'ca_file'}            = undef if ( $opt =~ /^-CAfile/i );
        $cfg{'ca_path'}            = undef if ( $opt =~ /^-CApath/i );

        if ( _is_cfg_use('npn') and ( $cmd{'version'} gt "2.0" ) ) {
            $cfg{'use'}->{'npn'} = 0;
            OCfg::warn("150: openssl $cmd{'version'}: cannot supply '-nextprotoneg' with TLSv1.3, hence globally disabled");
            OCfg::hint("150: $cfg{'hints'}->{'150'}") if _is_cfg_out('hint_cipher');
        }
    }
    trace("_enable_sclient() }");
    return;
}

sub _check_protocol {
    my $opt = shift;
    my $ssl;
    return if ( $opt !~ m/^-(?:no.?)?(?:dtls|tls|ssl)/ );
    my $val = SSLinfo::s_client_opt_get($opt);
    $val = 0 if ( '<<openssl>>' eq $val );
    $cfg{'openssl'}->{$opt}[0] = $val;
    if ( grep { $ssl = $_ if $opt eq ( $prot{$_}{'opt'} || "" ); } keys %prot ) {
        $cfg{$ssl} = $val if ( 0 < $cfg{$ssl} );
    }
    return;
}

sub _reset_openssl {
    $cmd{'openssl'}    = "";
    $cmd{'extopenssl'} = 0;
    $cmd{'extsclient'} = 0;
    return;
}

sub _check_openssl {
    return if ( $cmd{'openssl'} eq "" );
    trace("_check_openssl() {");
    $SSLinfo::openssl = $cmd{'openssl'};
    $SSLinfo::trace   = $cfg{'trace'};
    if ( not defined &SSLinfo::s_client_check ) {
        trace("_check_openssl: SSLinfo.pm not loaded, no checks performed");
        return;
    }
    if ( not defined SSLinfo::s_client_check() ) {
        OCfg::warn("147: '$cmd{'openssl'}' not available; all openssl functionality disabled");
        OCfg::hint("147: consider using '--openssl=/path/to/openssl'");
        _reset_openssl();
    }
    foreach my $opt ( sort( SSLinfo::s_client_get_optionlist() ) ) {
        my $val = SSLinfo::s_client_opt_get($opt);
        $val = 0 if ( '<<openssl>>' eq $val );
        $cfg{'openssl'}->{$opt}[0] = $val;
        next if ( $cfg{'openssl'}->{$opt}[1] eq "<<NOT YET USED>>" );
        _enable_sclient($opt);
        _check_protocol($opt) if ( _is_cfg_ciphermode('openssl') );
    }
    if ( $cmd{'version'} lt "1.0.2" ) {
        OCfg::warn("142: ancient openssl $cmd{'version'}: using '-msg' option to get DH parameters");
        $cfg{'openssl_msg'} = '-msg' if ( 1 == $cfg{'openssl'}->{'-msg'}[0] );
    }
    if ( $cmd{'version'} gt "2.0" ) {
        if ( _is_cfg_ciphermode('openssl|socket') ) {
            if ( _is_cfg_out('hint_cipher') ) {
                OCfg::hint("142: $cfg{'hints'}->{'openssl3'}");
                OCfg::hint("142: $cfg{'hints'}->{'openssl3c'}");
            }
        }
    }
    if ( $cmd{'version'} gt "2.0" ) {
    }
    trace("_check_openssl() }");
    return;
}

sub _init_opensslexe {
    trace("_init_opensslexe() {");
    my $exe = "";
    foreach my $p ( "", split( /:/, $ENV{'PATH'} ) ) {

        $exe = "$p/$cmd{'openssl'}";
        last if ( -e $exe );
        $exe = "";
    }
    $exe =~ s#//#/#g;
    if ( $exe eq "" or $exe eq "/" ) {
        $exe = "";
        OCfg::warn("149: no executable for '$cmd{'openssl'}' found; all openssl functionality disabled");
        OCfg::hint("149: consider using '--openssl=/path/to/openssl'");
        _reset_openssl();
    }
    trace("_init_opensslexe()\t= $exe }");
    return $exe;
}

sub _init_openssldir {
    return "" if ( $cmd{'openssl'} eq "" );
    my $dir = qx("$cmd{'openssl'}" version -d);

    chomp $dir;
    my $status = $?;
    my $error  = $!;
    my $capath = "";
    trace("_init_openssldir() { dir: $dir");
    if ( ( $error ne "" ) && ( $status != 0 ) ) {

        print $OText::STR{WARN}, "002: perl returned error: '$error'\n";
        if ( $error =~ m/allocate memory/ ) {
            print $OText::STR{WARN}, "003: using external programs disabled.\n";
            print $OText::STR{WARN}, "003: data provided by external openssl may be shown as:  <<openssl>>\n";
        }
        _reset_openssl();
        $status = 0;
    }
    else {
        my $openssldir = $dir;
        $dir =~ s#[^"]*"([^"]*)"#$1#;
        $capath = $dir;
        unshift( @{ $cfg{'ca_paths'} }, $dir );
        if ( -e "$dir/certs" ) {
            $capath = "$dir/certs";
        }
        else {
            OCfg::warn("148: 'openssl version -d' returned: '$openssldir' which does not contain certs/ ; ignored.");
        }
    }
    if ( $status != 0 ) {
        $cmd{'openssl'} = "";
        print $OText::STR{WARN}, "004: perl returned status: '$status' ('" . ( $status >> 8 ) . "')\n";
    }
    trace("_init_openssldir()\t= $capath }");
    return $capath;
}

sub _init_openssl_ca {
    my $ca_path = shift;
    return $ca_path if ( not defined $ca_path or $ca_path eq "" );
    trace("_init_openssl_ca($ca_path) {");
    my $ca = undef;
    foreach my $f ( @{ $cfg{'ca_files'} } ) {
        $ca = "$cfg{'ca_path'}/$f";
        goto FIN if -e "$ca";
    }
    $ca = undef;
    OCfg::warn("058: given path '$ca_path' does not contain a CA file");
    foreach my $p ( @{ $cfg{'ca_paths'} } ) {
        foreach my $f ( @{ $cfg{'ca_files'} } ) {
            $ca = "$p/$f";
            if ( -e "$ca" ) {
                OCfg::warn("059: found PEM file for CA; using '--ca-path=$p'");
                goto FIN;
            }
        }
    }
  FIN:
    trace("_init_openssl_ca()\t= $ca }");
    return $ca;
}

sub _init_openssl {

    trace("_init_openssl() {");
    $cmd{'openssl'} = _init_opensslexe();
    $cmd{'version'} = OCfg::get_openssl_version( $cmd{'openssl'} );

    if ( not defined $cfg{'ca_path'} ) {
        $cfg{'ca_path'} = _init_openssldir();
    }

    $cfg{'ca_file'} = _init_openssl_ca( $cfg{'ca_path'} );
    if ( not defined $cfg{'ca_file'} or $cfg{'ca_path'} eq "" ) {
        $cfg{'ca_file'} = "$cfg{'ca_paths'}[0]/$cfg{'ca_files'}[0]";
        OCfg::warn("060: no PEM file for CA found; using '--ca-file=$cfg{'ca_file'}'");
        OCfg::warn("060: if default file does not exist, some certificate checks may fail");
        OCfg::hint("060: use '--ca-file=/full/path/$cfg{'ca_files'}[0]'");
    }
    trace("_init_openssl() }");
    return;
}

sub _init_checks_score {
    $checks{$_}->{score} = 10 foreach ( keys %checks );
    $checks{'sts_maxage0d'}->{score} = 0;
    $checks{'sts_maxage1d'}->{score} = 10;
    $checks{'sts_maxage1m'}->{score} = 20;
    $checks{'sts_maxage1y'}->{score} = 70;
    $checks{'sts_maxagexy'}->{score} = 100;
    $checks{'sts_maxage18'}->{score} = 100;
    foreach ( keys %checks ) {
        $checks{$_}->{score} = 90 if (m/WEAK/i);
        $checks{$_}->{score} = 30 if (m/LOW/i);
        $checks{$_}->{score} = 10 if (m/MEDIUM/i);
    }
    return;
}

sub _init_checks_val {
    trace("_init_checks_val() {");
    my $notxt = "";
    if ( not _is_cfg_use('dns') ) {
        $checks{'reversehost'}->{val} = $text{'na_dns'};
    }
    if ( not _is_cfg_use('http') ) {
        $checks{'crl_valid'}->{val}  = _get_text( 'disabled', "--no-http" );
        $checks{'ocsp_valid'}->{val} = _get_text( 'disabled', "--no-http" );
        foreach my $key ( keys %checks ) {
            $checks{$key}->{val} = $text{'na_http'} if ( _is_member( $key, \@{ $cfg{'cmd-http'} } ) );
        }
    }
    if ( not _is_cfg_use('cert') ) {
        $cfg{'no_cert_txt'} = $notxt if ( "" eq $cfg{'no_cert_txt'} );
        foreach my $key ( keys %OData::check_cert ) {
            $checks{$key}->{val} = $text{'na_cert'} if ( _is_hashkey( $key, \%OData::check_cert ) );
        }
        foreach my $key (qw(hostname certfqdn tr_02102+ tr_02102- tr_03116+ tr_03116- rfc_6125_names rfc_2818_names)) {
            $checks{$key}->{val} = $text{'na_cert'};
        }
    }
    if ( not _is_cfg_ssl('SSLv2') ) {
        $notxt                     = _get_text( 'disabled', "--no-SSLv2" );
        $checks{'hassslv2'}->{val} = $notxt;
        $checks{'drown'}->{val}    = $notxt;
    }
    if ( not _is_cfg_ssl('SSLv3') ) {
        $notxt                     = _get_text( 'disabled', "--no-SSLv3" );
        $checks{'hassslv3'}->{val} = $notxt;
        $checks{'poodle'}->{val}   = $notxt;
    }
    $checks{'hastls10'}->{val}  = _get_text( 'disabled', "--no-TLSv1" )  if ( 1 > $cfg{'TLSv1'} );
    $checks{'hastls11'}->{val}  = _get_text( 'disabled', "--no-TLSv11" ) if ( 1 > $cfg{'TLSv11'} );
    $checks{'hastls12'}->{val}  = _get_text( 'disabled', "--no-TLSv12" ) if ( 1 > $cfg{'TLSv12'} );
    $checks{'hastls13'}->{val}  = _get_text( 'disabled', "--no-TLSv13" ) if ( 1 > $cfg{'TLSv13'} );
    $checks{'hasalpn'}->{val}   = _get_text( 'disabled', "--no-alpn" )   if ( not _is_cfg_use('alpn') );
    $checks{'hasnpn'}->{val}    = _get_text( 'disabled', "--no-npn" )    if ( not _is_cfg_use('npn') );
    $checks{'sni'}->{val}       = $text{'na_sni'}         if ( not _is_cfg_use('sni') );
    $checks{'certfqdn'}->{val}  = $text{'na_sni'}         if ( not _is_cfg_use('sni') );
    $checks{'heartbeat'}->{val} = $text{'na_tlsextdebug'} if ( not _is_cfg_use('extdebug') );

    if ( 1 > $cmd{'extopenssl'} ) {
        foreach my $key (qw(sernumber len_sigdump len_publickey modulus_exp_1 modulus_exp_65537 modulus_exp_oldssl modulus_size_oldssl)) {
            $checks{$key}->{val} = $text{'na_openssl'};
        }
    }
    trace("_init_checks_val() }");
    return;
}

sub _init_all {
    _tprint("_init_all() {") if _is_trace();
    $cfg{'done'}->{'init_all'}++;
    _init_checks_score();
    $cfg{'hints'}->{$_} = $text{'hints'}->{$_} foreach ( keys %{ $text{'hints'} } );
    _tprint("_init_all() }") if _is_trace();
    return;
}

sub _resetchecks {
    foreach ( keys %{ $cfg{'done'} } ) {
        next if ( !m/^check/ );
        $cfg{'done'}->{$_} = 0;
    }
    $cfg{'done'}->{'ciphers_all'} = 0;
    $cfg{'done'}->{'ciphers_get'} = 0;
    _init_checks_val();
    return;
}

sub _getscore {
    my $key     = shift;
    my $value   = shift || "";
    my $hashref = shift;
    my %hash    = %$hashref;
    return 0 if ( $value eq "" );
    my $score = $hash{$key}->{score} || 0;
    trace("_getscore($key, '$value')\t= $score");
    return $score;
}

sub _is_tls12only {
    my ( $host, $port ) = @_;
    my @ret;
    foreach my $ssl (qw(SSLv2 SSLv3 TLSv1 TLSv11)) {
        if ( $prot{$ssl}->{'cnt'} > 0 ) {
            push( @ret, $ssl );
        }
        if ( $cfg{$ssl} == 0 ) {
            push( @ret, _get_text( 'disabled', "--no-$ssl" ) );
        }
    }
    return join( " ", @ret );
}

sub _is_ssl_error {
    my ( $anf, $end, $txt ) = @_;
    return 0 if ( ( $end - $anf ) <= $cfg{'sslerror'}->{'timeout'} );
    $cfg{'done'}->{'ssl_errors'}++;
    $cfg{'done'}->{'ssl_failed'}++;
    return 0 if ( not _is_cfg_use('ssl_error') );
    if ( $cfg{'done'}->{'ssl_errors'} > $cfg{'sslerror'}->{'total'} ) {
        OCfg::warn("301: $txt after $cfg{'sslerror'}->{'total'} total errors");
        OCfg::hint("301: use '--no-ssl-error' or '--ssl-error-max=' to continue connecting");
        return 1;
    }
    if ( $cfg{'done'}->{'ssl_failed'} > $cfg{'sslerror'}->{'max'} ) {
        OCfg::warn("302: $txt after $cfg{'sslerror'}->{'max'} max errors");
        OCfg::hint("302: use '--no-ssl-error' or '--ssl-error-max=' to continue connecting");
        return 1;
    }
    return 0;
}

sub __readframe {
    my $cl  = shift;
    my $len = 5;
    my $buf = '';
    vec( my $rin = '', fileno($cl), 1 ) = 1;
    while ( length($buf) < $len ) {
        select( my $rout = $rin, undef, undef, $cfg{'timeout'} ) or return;
        sysread( $cl, $buf, $len - length($buf), length($buf) )  or return;
        $len = unpack( "x3n", $buf ) + 5 if length($buf) == 5;
    }
    ( my $type, my $ver, $buf ) = unpack( "Cnn/a*", $buf );
    my @msg;
    if ( $type == 22 ) {
        while ( length($buf) >= 4 ) {
            my $ht;
            ( $ht, $len ) = unpack( "Ca3", substr( $buf, 0, 4, '' ) );
            $len = unpack( "N", "\0$len" );
            push @msg, [ $ht, substr( $buf, 0, $len, '' ) ];
            _vprint2( sprintf( "  ...ssl received type=%d ver=0x%x ht=0x%x size=%d", $type, $ver, $ht, length( $msg[-1][1] ) ) );
        }
    }
    else {
        @msg = $buf;
        _vprint2( sprintf( "  ...ssl received type=%d ver=%x size=%d", $type, $ver, length($buf) ) );
    }
    return ( $type, $ver, @msg );
}

sub _is_vuln_bleed {
    my ( $host, $port ) = @_;
    my $heartbeats = 1;
    trace("_is_vuln_bleed($host, $port) {");
    my $cl  = undef;
    my $ret = "";
    my ( $type, $ver, $buf, @msg ) = ( "", "", "", () );
    local $\ = undef;

    unless ( ( $cfg{'starttls'} ) || ( ( $cfg{'proxyhost'} ) && ( $cfg{'proxyport'} ) ) ) {
        $cl = IO::Socket::INET->new( PeerAddr => "$host:$port", Timeout => $cfg{'timeout'} ) or do {
            OCfg::warn("321: _is_vuln_bleed: failed to connect: '$!'");
            trace("_is_vuln_bleed: fatal exit in IO::Socket::INET->new");
            return "failed to connect";
        };
    }
    else {
        trace("_is_vuln_bleed: using 'SSLhello'");
        $cl = SSLhello::openTcpSSLconnection( $host, $port );
        if ( ( not defined $cl ) || ($@) ) {
            local $@ = " Did not get a valid SSL-Socket from Function openTcpSSLconnection -> Fatal Exit of openTcpSSLconnection" unless ($@);
            OCfg::warn("322: _is_vuln_bleed (with openTcpSSLconnection): $@\n");
            trace("_is_vuln_bleed: fatal exit in _doCheckSSLciphers");
            return ("failed to connect");
        }
    }

    print $cl pack(
        "H*",
        join(
            '', qw(
              16 03 02 00  dc 01 00 00 d8 03 02 53
              43 5b 90 9d 9b 72 0b bc  0c bc 2b 92 a8 48 97 cf
              bd 39 04 cc 16 0a 85 03  90 9f 77 04 33 d4 de 00
              00 66 c0 14 c0 0a c0 22  c0 21 00 39 00 38 00 88
              00 87 c0 0f c0 05 00 35  00 84 c0 12 c0 08 c0 1c
              c0 1b 00 16 00 13 c0 0d  c0 03 00 0a c0 13 c0 09
              c0 1f c0 1e 00 33 00 32  00 9a 00 99 00 45 00 44
              c0 0e c0 04 00 2f 00 96  00 41 c0 11 c0 07 c0 0c
              c0 02 00 05 00 04 00 15  00 12 00 09 00 14 00 11
              00 08 00 06 00 03 00 ff  01 00 00 49 00 0b 00 04
              03 00 01 02 00 0a 00 34  00 32 00 0e 00 0d 00 19
              00 0b 00 0c 00 18 00 09  00 0a 00 16 00 17 00 08
              00 06 00 07 00 14 00 15  00 04 00 05 00 12 00 13
              00 01 00 02 00 03 00 0f  00 10 00 11 00 23 00 00
              00 0f 00 01 01
            )
        )
    );
    while (1) {
        ( $type, $ver, @msg ) = __readframe($cl) or do {
            OCfg::warn("323: heartbleed: no reply: '$!'");
            OCfg::hint("323: server does not respond, this does not indicate that it is not vulnerable!");
            return "no reply";
        };
        last if $type == 22 and grep { $_->[0] == 0x0e } @msg;
    }
    for ( 1 .. $heartbeats ) {
        _vprint2("  ...send heartbeat#$_");
        print $cl pack( "H*", join( '', qw(18 03 02 00 03 01 40 00) ) );
    }
    if ( ( $type, $ver, $buf ) = __readframe($cl) ) {
        if ( $type == 21 ) {
            _vprint2("  received alert (probably not vulnerable)");
        }
        elsif ( $type != 24 ) {
            _vprint2("  unexpected reply type $type");
        }
        elsif ( length($buf) > 3 ) {
            $ret = "heartbleed";
            _vprint2( "  BAD! got " . length($buf) . " bytes back instead of 3 (vulnerable)" );
        }
        else {
            _vprint2("  GOOD proper heartbeat reply (not vulnerable)");
        }
    }
    else {
        _vprint2("  no reply - probably not vulnerable");
    }
    close($cl);
    trace("_is_vuln_bleed()\t= $ret }");
    return $ret;
}

sub _is_vuln_css {
    my ( $host, $port, $ssl ) = @_;
    my $heartbeats = 1;
    my $cl         = undef;
    my $ret        = "";
    my ( $type, $ver, $buf, @msg ) = ( "", "", "", () );
    undef $\;

    $cl = IO::Socket::INET->new( PeerAddr => "$host:$port", Timeout => $cfg{'timeout'} ) or do {
        OCfg::warn("331: _is_vuln_css: failed to connect: '$!'");
        return "failed to connect";
    };
    print $cl pack(
        "H*",
        join(
            '', qw(
              53 9c b2 cb 4b 42 f9 2d  0b e5 9c 21 f5 a3 89 ca
              7a d9 b4 ab 3f d3 22 21  5e c4 65 0d 1e ce ed c2
              00
              00 68
              c0 13 c0 12 c0 11 c0 10  c0 0f c0 0e c0 0d c0 0c
              c0 0b c0 0a c0 09 c0 08  c0 07 c0 06 c0 05 c0 04
              c0 03 c0 02 c0 01 00 39  00 38 00 37 00 36 00 35
              00 34 00 33 00 32 00 31  00 30 00 2f 00 16 00 15
              00 14 00 13 00 12 00 11  00 10 00 0f 00 0e 00 0d
              00 0c 00 0b 00 0a 00 09  00 08 00 07 00 06 00 05
              00 04 00 03 00 02 00 01  01 00
            )
        )
    );
    while (1) {
        ( $type, $ver, @msg ) = __readframe($cl) or do {
            OCfg::warn("332: _is_vuln_css: no reply: '$!'");
            return "no reply";
        };
        last if $type == 22 and grep { $_->[0] == 0x0e } @msg;
    }
    if ( ( $type, $ver, $buf ) = __readframe($cl) ) {
        if ( $type == 21 ) {
            _vprint2("  received alert (probably not vulnerable)");
        }
        elsif ( $type != 24 ) {
            _vprint2("  unexpected reply type $type");
        }
        elsif ( length($buf) > 3 ) {
            $ret = "heartbleed";
            _vprint2( "  BAD! got " . length($buf) . " bytes back instead of 3 (vulnerable)" );
        }
        else {
            _vprint2("  GOOD proper heartbeat reply (not vulnerable)");
        }
    }
    else {
        _vprint2("  no reply - probably not vulnerable");
    }
    close($cl);
    return $ret;
}

sub _is_vuln_breach {
    my ( $host, $port ) = @_;
    my $val = $data{'https_content_enc'}->{'val'}( $host, $port );
    return $val if ( $val =~ /$cfg{'regex'}->{'BREACH'}/ );
    $val = $data{'https_transfer_enc'}->{'val'}( $host, $port );
    return $val if ( $val =~ /$cfg{'regex'}->{'BREACH'}/ );
    return "";
}

sub _is_beast_skipped {
    my ( $host, $port ) = @_;
    my @ret;
    foreach my $ssl (qw(SSLv2 SSLv3 TLSv1)) {
        if ( $cfg{$ssl} == 0 ) {
            push( @ret, _get_text( 'disabled', "--no-$ssl" ) );
        }
    }
    return join( " ", @ret );
}

sub _check_prot_cipher {
    my $ssl = shift;
    my $key = shift;
    my $typ = shift;
    my $rex;

    if ( 'CRIME' eq $typ ) {
        my $protocols = $ssl;
        my $compress  = ( $key =~ /$cfg{'regex'}->{'nocompression'}/ ) ? "" : $key . " ";
        $compress .= "SPDY/3 " if ( $protocols =~ /$cfg{'regex'}->{'isSPDY3'}/ );
        return $compress;
    }
    $key = Ciphers::get_key($key) if not Ciphers::is_valid_key($key);
    if ( defined $OCfg::cfg{'regex'}->{$typ} ) {
        $rex = $OCfg::cfg{'regex'}->{$typ};
    }

    if ( 'DROWN' eq $typ ) {
        return "" if ( $ssl eq "SSLv2" );
        return Ciphers::get_name($key);
    }
    if ( 'POODLE' eq $typ ) {
        return "" if ( $ssl !~ /(?:SSL|TLSv1$)/ );
        return Ciphers::get_name($key);
    }

    if ( 'BEAST' eq $typ ) {
        return "" if ( $ssl !~ /(?:SSL|TLSv1$)/ );
        return Ciphers::is_typ( 'BEAST', $key );
    }
    if ( 'FREAK' eq $typ ) {
        return "" if ( $ssl !~ /(?:SSLv3)/ );
        return Ciphers::is_typ( 'FREAK', $key );
    }
    if ( 'Sweet32' eq $typ ) {
        return "" if Ciphers::is_typ( 'notSweet32', $key );
        return Ciphers::is_typ( 'Sweet32', $key );
    }
    if ( 'Logjam' eq $typ )  { return Ciphers::is_typ( 'Logjam',  $key ); }
    if ( 'Lucky13' eq $typ ) { return Ciphers::is_typ( 'Lucky13', $key ); }
    if ( 'RC4' eq $typ )     { return Ciphers::is_typ( 'RC4',     $key ); }
    if ( 'ROBOT' eq $typ )   { return Ciphers::is_typ( 'ROBOT',   $key ); }
    if ( 'SLOTH' eq $typ )   { return Ciphers::is_typ( 'SLOTH',   $key ); }
    if ( 'TIME' eq $typ )    { return ""; }

    my $cipher = Ciphers::get_name($key);
    if ( 'FIPS-140' eq $typ ) {
        return $cipher if ( $ssl ne "TLSv1" );
        return $cipher if Ciphers::is_typ( 'notFIPS-140', $key );
        return $cipher if Ciphers::is_typ( 'FIPS-140',    $key );
        return "";
    }
    if ( 'NSA-B' eq $typ ) {
        return "";
    }
    if ( 'PCI' eq $typ ) {
        return $cipher if ( $ssl eq "SSLv2" );
        return $cipher if Ciphers::is_typ( 'notPCI', $key );
        return "";
    }
    if ( 'RFC7525' eq $typ ) {
        my $bit = Ciphers::get_bits( Ciphers::get_key($cipher) );
        return $cipher if not Ciphers::is_typ( 'RFC7525', $key );
        return $cipher if Ciphers::is_typ( 'NULL', $key );
        return $cipher if Ciphers::is_typ( 'EXP',  $key );
        return $cipher if Ciphers::is_typ( 'RC4',  $key );
        return ""      if ( $bit =~ m/^\s*$/ );
        return $cipher if ( $bit < 128 );
        return "";
    }
    if ( 'PFS' eq $typ ) {
        return ""      if Ciphers::is_typ( 'notPFS', $key );
        return $cipher if ( $ssl eq "TLSv13" );
        return $cipher if ( "$ssl-$cipher" =~ /$cfg{'regex'}->{'PFS'}/ );
        return "";
    }
    if ( $typ =~ m/^TR02102/ ) {
        return $cipher if Ciphers::is_typ( 'EXP',         $key );
        return $cipher if Ciphers::is_typ( 'notTR-02102', $key );
        return $cipher if not Ciphers::is_typ( 'TR-02102', $key );
        if ( "TR02102-lazy" eq $typ ) {
            return "";
        }
        if ( "TR02102-strict" eq $typ ) {
            return $cipher if not Ciphers::is_typ( 'AES-GCM', $key );
        }
        return "";
    }
    if ( $typ =~ m/^TR03116/ ) {
        return $cipher if ( $ssl !~ /^TLSv1[2]/ );
        return $cipher if Ciphers::is_typ( 'EXP', $key );
        if ( "TR03116-lazy" eq $typ ) {
            return $cipher if not Ciphers::is_typ( 'TR-03116-', $key );
        }
        if ( "TR03116-strict" eq $typ ) {
            return $cipher if Ciphers::is_typ( 'notTR-03116', $key );
            return $cipher if not Ciphers::is_typ( 'TR-03116+', $key );
        }
        return "";
    }

    return "";
}

sub _prot_cipher {
    my $p1 = shift || "";
    my $p2 = shift || "";
    return "" if ( ( "" eq $p1 ) or ( "" eq $p2 ) );
    return " $p1:$p2";
}

sub _is_compliant {
    my ( $ssl, $cipher, $typ ) = @_;
    return _prot_cipher( $ssl, _check_prot_cipher( $ssl, $cipher, $typ ) );
}

sub _is_vulnerable {
    my ( $ssl, $cipher, $typ ) = @_;
    if ( 'CRIME' eq $typ ) {
        return _check_prot_cipher( $data{'next_protocols'}->{val}($host), $cipher, $typ );
    }
    return _prot_cipher( $ssl, _check_prot_cipher( $ssl, $cipher, $typ ) );
}

sub _checkwildcard {
    my ( $host, $port ) = @_;
    my ( $cn_host, $rex );
    $cn_host = $data{'cn'}->{val}($host);
    $checks{'wildcard'}->{val} = "<<CN:>>$cn_host" if ( $cn_host =~ m/[*]/ );
    foreach my $value ( split( " ", $data{'altname'}->{val}($host) ) ) {
        $value =~ s/.*://;
        if ( $value =~ m/\*/ ) {

            $checks{'wildcard'}->{val} .= " " . $value;
            ( $rex = $value ) =~ s/[*]/[^.]*/;

            $checks{'wildhost'}->{val} = $value if ( $host =~ m/^$rex$/ );
            $checks{'cnt_wildcard'}->{val}++;
        }
        $checks{'cnt_altname'}->{val}++;
        $checks{'len_altname'}->{val} = length($value) + 1;
    }
    return;
}

sub _usesocket {
    my ( $ssl, $host, $port, $ciphers ) = @_;
    my $cipher = "";
    my $sni    = ( not _is_cfg_use('sni') )  ? "" : $host;
    my $npns   = ( not _is_cfg_use('npn') )  ? [] : $cfg{'cipher_npns'};
    my $alpns  = ( not _is_cfg_use('alpn') ) ? [] : $cfg{'cipher_alpns'};
    my $version   = "";
    my $sslsocket = undef;
    trace1("_usesocket($ssl, $host, $port, $ciphers) { sni: $sni");

    if ( ( $ssl eq "SSLv2" ) && ( not defined &Net::SSLeay::CTX_v2_new ) ) {
        OCfg::warn("303: SSL version '$ssl': not supported by Net::SSLeay");
        return "";
    }
    if ( ( $ssl eq "SSLv3" ) && ( not defined &Net::SSLeay::CTX_v3_new ) ) {
        OCfg::warn("304: SSL version '$ssl': not supported by Net::SSLeay");
        return "";
    }
    if (
        eval {

            unless ( ( $cfg{'starttls'} ) || ( ( $cfg{'proxyhost'} ) && ( $cfg{'proxyport'} ) ) ) {
                trace1("_usesocket: using 'IO::Socket::SSL' with '$ssl'");
                local $? = 0;
                local $! = undef;
                $sslsocket = IO::Socket::SSL->new(
                    PeerAddr        => $host,
                    PeerPort        => $port,
                    Proto           => "tcp",
                    Timeout         => $cfg{'timeout'},
                    SSL_hostname    => $sni,
                    SSL_verify_mode => 0x0,
                    SSL_ca_file     => undef,
                    SSL_ca_path     => undef,
                    SSL_check_crl   => 0,
                    SSL_version     => $ssl,
                    SSL_cipher_list => $ciphers,
                    SSL_ecdh_curve  => "prime256v1",

                    SSL_alpn_protocols => $alpns,
                    SSL_npn_protocols  => $npns,
                );
            }
            else {
                trace1("_usesocket: using 'SSLhello'");
                local $? = 0;
                local $! = undef;
                $sslsocket = SSLhello::openTcpSSLconnection( $host, $port );
                if ( ( not defined($sslsocket) ) || ($@) ) {
                    local $@ = " Did not get a valid SSL-Socket from Function openTcpSSLconnection -> Fatal Exit" unless ($@);
                    OCfg::warn("305: _usesocket: openTcpSSLconnection() failed: $@\n");
                    return ("");
                }
                else {
                    trace1("_usesocket: start_SSL ($host, $port, $ciphers)\t= $cipher");
                    IO::Socket::SSL->start_SSL(
                        $sslsocket,
                        Timeout            => $cfg{'timeout'},
                        SSL_hostname       => $sni,
                        SSL_verify_mode    => 0x0,
                        SSL_ca_file        => undef,
                        SSL_ca_path        => undef,
                        SSL_check_crl      => 0,
                        SSL_version        => $ssl,
                        SSL_cipher_list    => $ciphers,
                        SSL_ecdh_curve     => "prime256v1",
                        SSL_alpn_protocols => $alpns,
                        SSL_npn_protocols  => $npns,
                      )
                      or do {
                        trace1("_usesocket: ssl handshake failed: $!");
                        return "";
                      };
                }
            }
        }
      )
    {
        if ($sslsocket) {
            $version = $sslsocket->get_sslversion() if ( $IO::Socket::SSL::VERSION > 1.964 );
            $cipher  = $sslsocket->get_cipher();
            $sslsocket->close( SSL_ctx_free => 1 );
            trace1("_usesocket: SSL version (for $ssl $ciphers): $version");
        }
    }
    else {

        trace1("_usesocket: connection failed (for $ssl $ciphers): $!");
    }
    trace1("_usesocket()\t= $cipher }");
    return $version, $cipher;
}

sub _useopenssl {
    my ( $ssl, $host, $port, $ciphers ) = @_;
    trace1("_useopenssl($ssl, $host, $port, $ciphers)");
    my $args = ( $cfg{'openssl_option_map'}->{$ssl} || '' );
    $args .= " $cfg{'openssl_msg'} ";
    $args .= " -servername $host"                                      if _is_cfg_use('sni');
    $args .= " -alpn " . join( ",", @{ $cfg{'cipher_alpns'} } )        if _is_cfg_use('alpn');
    $args .= " -nextprotoneg " . join( ",", @{ $cfg{'cipher_npns'} } ) if _is_cfg_use('npn');
    $args .= " -cipher $ciphers"                                       if ( "" ne $ciphers );
    trace1("_useopenssl: echo | $cmd{'timeout'} $cfg{'timeout'} $cmd{'openssl'} s_client $args -connect $host:$port 2>&1");
    my $data = qx(echo | $cmd{'timeout'} $cfg{'timeout'} $cmd{'openssl'} s_client $args -connect $host:$port 2>&1);

    trace2("_useopenssl: data #{ $data }");
    return "", "", "" if ( $data =~ m#New,.*?Cipher is .?NONE# );

    my $version = $data;
    $version =~ s#^.*[\r\n]+ +Protocol\s*:\s*([^\r\n]*).*#$1#s;
    if ( $data eq $version ) {
        trace("_useopenssl: no line with Protocol: found, trying line with New,");
        $version =~ s#^.*[\r\n]+New,\s*([^,]*).*#$1#s;
    }
    my $cipher = $data;
    if ( $cipher =~ m#New, [A-Za-z0-9/.,-]+ Cipher is# ) {
        $cipher =~ s#^.*[\r\n]+New,\s*##s;
        $cipher =~ s#[A-Za-z0-9/.,-]+ Cipher is\s*([^\r\n]*).*#$1#s;
        my $dh = OCfg::get_dh_paramter( $cipher, $data );
        trace1("_useopenssl()\t= $cipher $dh }");
        return $version, $cipher, $dh;
    }

    return "", "", "" if ( $data =~ m#SSL routines.*(?:handshake failure|null ssl method passed|no ciphers? (?:available|match))# );

    if ( $data =~ m#^\s*$# ) {
        OCfg::warn("311: SSL version '$ssl': empty result from openssl");
    }
    else {
        chomp($cipher);
        OCfg::warn("312: SSL version '$ssl': unknown result from openssl or '$cipher'");
        OCfg::warn("312: result from openssl: '$data'") if _is_trace();
    }
    trace2("_useopenssl: #{ $data }");
    return "", "", "";
}

sub _can_connect {
    my ( $host, $port, $sni, $timeout, $ssl ) = @_;
    trace("_can_connect($host, $port, $sni, $timeout, $ssl) {");
    if ( not defined $sni ) { $sni = $OText::STR{UNDEF}; }
    local $? = 0;
    local $! = undef;
    my $socket;
    my $ret = 0;
    if ( $ssl == 1 ) {
        if ( $cfg{'trace'} > 2 ) { $IO::Socket::SSL::debug3 = 1; my $keep_perl_quiet = $IO::Socket::SSL::debug3; }
        $socket = IO::Socket::SSL->new(
            PeerAddr => $host,
            PeerPort => $port,
            Proto    => "tcp",
            Timeout  => $timeout,
            SSL_version        => "SSLv23",
            SSL_cipher_list    => "ALL:NULL:eNULL:aNULL:LOW:EXP",
            SSL_verify_mode    => 0x0,
            SSL_check_crl      => 0,
            SSL_ocsp_mode      => 0,
            SSL_startHandshake => 0,
        ) or do { trace1( "_can_connect: IO::Socket::SSL->new(): $! #" . IO::Socket::SSL::errstr() ); };
    }
    else {
        $socket = IO::Socket::INET->new(
            PeerAddr => $host,
            PeerPort => $port,
            Proto    => "tcp",
            Timeout  => $timeout,
        ) or do { trace1("_can_connect: IO::Socket::INET->new(): $!"); };
    }
    if ( defined $socket ) {
        close($socket);
        $ret = 1;
    }
    else {
        OCfg::warn("324: failed to connect target '$host:$port': '$!'");
    }
    trace("_can_connect()\t= $ret }");
    return $ret;
}

sub _get_target {
    my $last = shift;
    my $arg  = shift;

    return ( "https", $arg, $last, "", "" ) if ( $arg =~ m#^\s*$# );
    return ( "https", $arg, $last, "", "" ) if ( $arg !~ m#[:@\\/?]# );

    my $prot = $arg;
    $prot =~ s#^\s*([a-z][A-Z0-9]*:)?//.*#$1#i;

    $prot = "https" if ( $prot eq $arg );
    $prot = "https" if ( $prot eq "" );
    $prot =~ s#:##g;
    my $auth = "";
    my $path = $arg;
    $path =~ s#^.*?/#/#;
    my $port = "";
    my $host = $arg;
    $host =~ s#^\s*(?:[a-z][A-Z0-9]*:)?//##i;
    $host =~ s#^(?:[^@]+@)?##i;
    $host =~ s#/.*$##;
    ( $host, $port ) = split( /:([^:\]]+)$/, $host );
    $port = $last if not defined $port;
    trace_arg("target arg=$arg => prot=$prot, host=$host, port=$port");
    return ( $prot, $host, $port, $auth, $path );
}

sub _get_data0 {
    my ( $host, $port ) = @_;
    trace("_get_data0($host, $port) {");
    _trace_time("no SNI{");
    $SSLinfo::use_SNI = 0;
    if ( defined SSLinfo::do_ssl_open( $host, $port, ( join( " ", @{ $cfg{'version'} } ) ), join( " ", @{ $cfg{'ciphers'} } ) ) ) {
        trace(" cn_nosni: method= $SSLinfo::method");
        $data{'cn_nosni'}->{val}               = $data{'cn'}->{val}( $host, $port );
        $OData::data0{'session_ticket'}->{val} = $data{'session_ticket'}->{val}( $host, $port );
        foreach my $key ( sort( keys %data ) ) {
            next if ( $key =~ m/$cfg{'regex'}->{'commands_int'}/i );
            $OData::data0{$key}->{val} = $data{$key}->{val}( $host, $port );
        }
    }
    else {
        OCfg::warn("204: Can't make a connection to '$host:$port' without SNI; no initial data (compare with and without SNI not possible)");
    }
    if ( 0 < ( length SSLinfo::errors() ) ) {
        OCfg::warn("203: connection without SNI succeded with errors; errors ignored");
        if ( _is_cfg_verbose() or ( 1 < $cfg{'trace'} ) ) {
            OCfg::warn("206: $_") foreach SSLinfo::errors();
        }
        else {
            OCfg::hint("203: use '--v' to show more information about SSLinfo::do_ssl_open() errors");
        }
    }
    _trace_time("no SNI}");

    SSLinfo::do_ssl_close( $host, $port );
    $SSLinfo::use_SNI = $cfg{'use'}->{'sni'};
    trace("_get_data0() }");
    return;
}

sub _get_cipherslist {
    my $mode = shift;

    my $ssl = shift;
    trace("_get_cipherslist($mode, $ssl) {");
    my @ciphers = ();
    my $pattern = "";
    trace(" get list --cipher = [ @{$cfg{'cipher'}} ]");
    foreach my $name ( @{ $cfg{'cipher'} } ) {
        if ( $name =~ m/^0x[0-9A-F]+$/i ) {
            $name = Ciphers::get_name($name) if 'names' eq $mode;
            push( @ciphers, $name )          if $name;
        }
        else {
            $pattern .= ":$name";
        }
        $pattern =~ s/^://;
    }
    if ($pattern) {
        if ( _is_cfg_ciphermode('openssl') ) {
            $pattern = $cfg{'cipherpattern'} if $pattern =~ m/^ *$/;
            trace(" get list openssl  = $pattern");
            push( @ciphers, SSLinfo::cipher_openssl($pattern) );
            if ( 0 >= @ciphers ) {
                print "Errors: " . SSLinfo::errors();
                die $OText::STR{ERROR}, "015: no ciphers found; may happen with openssl pre 1.0.0 according given pattern\n";
            }
        }
        else {

            foreach my $name ( split( ":", $pattern ) ) {
                trace(" get list pattern  = $pattern");
                push( @ciphers, Ciphers::find_names_any($name) );
            }
        }
    }
    if ( 0 >= @ciphers ) {

        trace(" get list --range  = $pattern");
        $pattern = $cfg{'cipherrange'} if $pattern =~ m/^\s*$/;
        $pattern = 'SSLv2'             if 'sslv2' eq lc($pattern);

        push( @ciphers, OCfg::get_ciphers_range( $ssl, $pattern ) );
        if ( 0 >= @ciphers ) {
            OCfg::warn("063: given pattern '$pattern' did not return cipher list");
        }
    }
    @ciphers = sort( grep { !/^\s*$/ } @ciphers );
    if ( 'names' eq $mode ) {
        trace(" get list names    = 0..$#ciphers");
        for my $i ( 0 .. $#ciphers ) {
            my $c = $ciphers[$i];
            $ciphers[$i] = Ciphers::get_name($c) || "" if _is_cipher_key($c);
        }
    }
    if ( 'keys' eq $mode ) {
        trace(" get list keys     = 0..$#ciphers");
        for my $i ( 0 .. $#ciphers ) {
            my $c = $ciphers[$i];
            $ciphers[$i] = Ciphers::get_key($c) || "" if not _is_cipher_key($c);
        }
    }
    @ciphers = sort( grep { !/^\s*$/ } @ciphers );
    trace("_get_cipherslist\t= [ @ciphers ] }");
    return @ciphers;
}

sub _get_cipher_default {

    my ( $ssl, $host, $port, $mode ) = @_;
    trace("_get_cipher_default($ssl, $host, $port, $mode) {");
    $cfg{'done'}->{'default_get'}++;
    my $dh      = "";
    my $version = "";
    my $cipher  = "";
    my @list    = ();
    @list = Ciphers::sort_names( @{ $cfg{'ciphers'} } );
    @list = reverse Ciphers::sort_names( @{ $cfg{'ciphers'} } ) if ( $mode eq 'weak' );
    my $cipher_list = join( ":", @list );

    if ( _is_cfg_ciphermode('socket') ) {
        ( $version, $cipher ) = _usesocket( $ssl, $host, $port, $cipher_list );
    }
    if ( _is_cfg_ciphermode('openssl') ) {
        ( $version, $cipher, $dh ) = _useopenssl( $ssl, $host, $port, $cipher_list );
    }

    $cipher = "" if not defined $cipher;
    if ( $cipher =~ m#^\s*$# ) {
        my $txt = "SSL version '$ssl': cannot get preferred cipher; ignored";
        trace($txt) if ( $ssl !~ m/SSLv[2]/ );
    }
    else {
        trace1("preferred cipher: $ssl:\t$cipher");
    }
    trace("_get_cipher_default()\t= $cipher }");
    return $cipher;
}

sub ciphers_default_openssl {
    my ( $host, $port ) = @_;
    trace("ciphers_default_openssl($host, $port) {");
    $cfg{'done'}->{'ssl_failed'} = 0;
    foreach my $ssl ( @{ $cfg{'version'} } ) {
        next if not defined $prot{$ssl}->{opt};
        my $anf = time();
        $prot{$ssl}->{'cipher_strong'} = _get_cipher_default( $ssl, $host, $port, 'strong' );
        $prot{$ssl}->{'cipher_weak'}   = _get_cipher_default( $ssl, $host, $port, 'weak' );
        $prot{$ssl}->{'default'}       = _get_cipher_default( $ssl, $host, $port, 'default' );
        last if ( 0 < _is_ssl_error( $anf, time(), "$ssl: abort getting preferred cipher" ) );
        my $cipher = $prot{$ssl}->{'cipher_strong'};
        if ( "" ne $cipher ) {
            $prot{$ssl}->{'cipher_pfs'} = $cipher if _is_compliant( $ssl, $cipher, 'PFS' );
        }
    }
    checkpreferred( $host, $port );
    trace("ciphers_default_openssl() }");
    return;
}

sub ciphers_prot_openssl {
    my ( $ssl, $host, $port, $arr ) = @_;
    my @ciphers = @{$arr};
    my $version = "";
    my $dh      = "";

    trace("ciphers_prot_openssl($ssl, $host, $port, @ciphers) {");
    my @res = ();
    $cfg{'done'}->{'ssl_failed'} = 0;
    if ( 0 < $cfg{'connect_delay'} ) {
        _vprint("  connect delay: $cfg{'connect_delay'} second(s)") if ( 1 < _is_cfg_verbose() );
    }
    my $cnt   = 0;
    my $len   = 0;
    my $total = scalar(@ciphers);
    foreach my $c (@ciphers) {
        next if ( $c =~ m/^\s*$/ );
        $cnt++;
        my $anf       = time();
        my $supported = "";
        my $txt       = "$ssl: ($cnt of $total ciphers checked) abort connection attempts";
        $len = ( $len < length($c) ) ? 1 : ( $len - length($c) );
        printf( "$OText::STR{'INFO'}  cipher %4d/%d %s%s\r", $cnt, $total, $c, " " x $len ) if ( 0 < _is_cfg_verbose() );
        $len = length($c);

        if ( _is_cfg_ciphermode('socket') ) {
            ( $version, $supported ) = _usesocket( $ssl, $host, $port, $c );
        }
        else {
            ( $version, $supported, $dh ) = _useopenssl( $ssl, $host, $port, $c );
        }
        $supported = "" if not defined $supported;
        sleep( $cfg{'connect_delay'} );
        last if ( _is_ssl_error( $anf, time(), $txt ) > 0 );
        if ( ( $c !~ /(?:HIGH|ALL)/ ) and ( $supported ne "" ) ) {
            if ( ( $c !~ $supported ) and ( $ssl ne "SSLv2" ) ) {
                printf("\n") if _is_cfg_verbose();
                OCfg::warn("411: checked $ssl cipher '$c' does not match returned cipher '$supported'");
            }
        }
        if ( ( $c =~ /^(?:TLS(?:13)?)/ ) and ( 3 gt $cmd{'version'} ) ) {

            printf("\n") if _is_cfg_verbose();
            OCfg::warn("413: some openssl fail with '-cipher $c', the cipher may not be listed then");
        }
        push( @res, "$version:$supported" ) if ( $supported ne "" );
        my $yesno = ( $supported eq "" ) ? "no" : "yes";
        _vprint("  check cipher: $ssl:$c\t$yesno") if ( 1 < $cfg{'verbose'} );
    }
    printf("\n") if _is_cfg_verbose();
    trace("connection errors: $cfg{'done'}->{'ssl_errors'}                  ");
    trace( "ciphers_prot_openssl()\t= " . $#res . " @res }" );
    return @res;
}

sub ciphers_scan_openssl {
    my ( $host, $port ) = @_;
    trace("ciphers_scan_openssl($host, $port) {");
    @{ $cfg{'ciphers'} } = _get_cipherslist( 'names', "" );
    my $cnt     = scalar( @{ $cfg{'ciphers'} } );
    my $results = {};
    foreach my $ssl ( @{ $cfg{'version'} } ) {
        my $usesni = $cfg{'use'}->{'sni'};
        _vprint("  test $cnt ciphers for $ssl ... ($cfg{'ciphermode'}) ");
        trace("  test $cnt ciphers for $ssl ... ($cfg{'ciphermode'}) ");
        trace("  using cipherpattern=[ @{$cfg{'cipher'}} ], cipherrange=$cfg{'cipherrange'}");
        if ( $ssl =~ m/^SSLv[23]/ ) {
            if ( _is_cfg_do('cipher') or _is_cfg_verbose() ) {
                _warn_nosni( "410:", $ssl, $cfg{'use'}->{'sni'} );
            }
            $cfg{'use'}->{'sni'} = 0;
        }
        my $__verbose = $cfg{'verbose'};
        $cfg{'verbose'} = 2 if ( $cfg{'v_cipher'} > 0 );
        my @supported = ciphers_prot_openssl( $ssl, $host, $port, \@{ $cfg{'ciphers'} } );
        $cfg{'verbose'}                            = $__verbose if ( $__verbose != 2 );
        $results->{'_admin'}{$ssl}{'ciphers'}      = \@{ $cfg{'ciphers'} };
        $results->{'_admin'}{$ssl}{'cnt_offered'}  = scalar @{ $cfg{'ciphers'} };
        $results->{'_admin'}{$ssl}{'cnt_accepted'} = @supported;

        for my $i ( 0 .. $#supported ) { $supported[$i] =~ s/^[^:]*://; }

        my $last_a = "";
        foreach my $cipher ( @{ $cfg{'ciphers'} } ) {
            next if ( $last_a eq $cipher );
            my $key = Ciphers::get_key($cipher);
            $results->{$ssl}{$key} = [ ( ( grep { /^$cipher$/ } @supported ) > 0 ) ? "yes" : "no", "" ];
        }
        $cfg{'use'}->{'sni'} = $usesni;
    }
    if ( 1 < $cfg{'trace'} ) {
        trace("ciphers_scan_openssl()\t= $results }");
    }
    else {
        trace("ciphers_scan_openssl()\t= <<result prined with --trace=2>> }");
    }
    return $results;
}

sub ciphers_scan_intern {
    my ( $host, $port ) = @_;
    trace("ciphers_scan_intern($host, $port) {");
    my $total   = 0;
    my $enabled = 0;
    my $results = {};
    my $usesni  = $SSLhello::usesni;
    my $typ     = "raw";
    $typ = "all" if ( _is_cfg_do('cipher_intern') );
    $results->{'_admin'}{'session_protocol'} = "";

    foreach my $ssl ( @{ $cfg{'version'} } ) {
        $results->{'_admin'}{$ssl}{'cnt_offered'}  = 0;
        $results->{'_admin'}{$ssl}{'cnt_accepted'} = 0;
        next if ( $cfg{$ssl} == 0 );
        if ( $usesni >= 1 ) {

            $SSLhello::usesni = $usesni;
            if ( $ssl =~ m/^SSLv/ ) {
                _warn_nosni( "409:", $ssl, $usesni );
                $SSLhello::usesni = 0;
            }
        }
        my %accepted;

        my $accepted_cnt = 0;
        my @all          = _get_cipherslist( 'keys', $ssl );
        $total += scalar(@all);
        _vprint( "  test " . scalar(@all) . " ciphers for $ssl ... (SSLhello)" );
        trace( "  test " . scalar(@all) . " ciphers for $ssl ... (SSLhello)" );
        trace("  using cipherpattern=[ @{$cfg{'cipher'}} ], cipherrange=$cfg{'cipherrange'}");
        if ( "@all" =~ /^\s*$/ ) {
            OCfg::warn("407: no valid ciphers specified; no check done for '$ssl'");
            next;
        }
        %accepted = SSLhello::getSSLciphersWithParam( $host, $port, $ssl, @all );
        Dumper(%accepted);
        $accepted_cnt = scalar( keys %accepted );
        $accepted_cnt--;
        if ( exists $accepted{'0'}[1] ) {
            if ( $accepted{'0'}[0] eq $accepted{'0'}[1] ) {
                $results->{'_admin'}{$ssl}{'cipher_selected'} = $accepted{'0'}[0];
                trace(" cipher_selected= $accepted{'0'}[0]");
            }
        }
        $results->{'_admin'}{$ssl}{'ciphers'}      = @all;
        $results->{'_admin'}{$ssl}{'cnt_offered'}  = scalar @all;
        $results->{'_admin'}{$ssl}{'cnt_accepted'} = $accepted_cnt;
        $results->{'_admin'}{'session_protocol'}   = $ssl if ( 0 < $accepted_cnt );
        if ( _is_cfg_do('cipher_dump') ) {
            trace( sprintf( " total number of accepted ciphers= %4d", $accepted_cnt ) );
        }

        if ( exists $accepted{'0'}[1] ) {
            my $cipher = Ciphers::get_name( $accepted{'0'}[1] ) || $OText::STR{UNDEF};
            $prot{$ssl}->{'cipher_strong'} = $cipher;
            $prot{$ssl}->{'default'}       = $cipher;
            $prot{$ssl}->{'cipher_pfs'}    = $cipher if _is_compliant( $ssl, $cipher, 'PFS' );
            trace( sprintf( " default cipher %7s: %s", $ssl, $cipher ) );
        }

        my $last_a = "";
        foreach my $_i ( sort( keys(%accepted) ) ) {
            next if ( '0' eq $_i );
            my $key = $accepted{$_i}[0];
            next if ( $last_a eq $key );
            $results->{$ssl}{$key} = [ "yes", $accepted{$_i}[1] ];
            $last_a = $key;
        }

    }
    if ( 1 < $cfg{'trace'} ) {
        trace( "ciphers_scan_intern()\t= " . join( " ", sort( keys( %{$results} ) ) ) . " }" );
    }
    else {
        trace("ciphers_scan_intern()\t= <<result prined with --trace=2>> }");
    }
    return $results;
}

sub check_certchars {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'check_certchars'}++;
    return if ( 1 < $cfg{'done'}->{'check_certchars'} );
    trace("check_certchars($host, $port) {");
    my $value;
    my $txt;

    foreach my $label ( @{ $cfg{'need-checkchr'} }, qw(email aux) ) {
        $value = $data{$label}->{val}($host);
        if ( $value ne "" ) {
            $checks{'nonprint'}->{val} .= " $label" if ( $value =~ m/$cfg{'regex'}->{'nonprint'}/ );
            $checks{'crnlnull'}->{val} .= " $label" if ( $value =~ m/$cfg{'regex'}->{'crnlnull'}/ );
        }
    }

    foreach my $label ( @{ $cfg{'need-checkchr'} }, qw(extensions) ) {
        $value = $data{$label}->{val}($host);
        $value =~ s#[\r\n]##g;
        if ( $value =~ m/$cfg{'regex'}->{'notEV-chars'}/ ) {
            $txt = _get_text( 'cert_chars', $label );
            $checks{'ev_chars'}->{val} .= $txt;
            $checks{'ev+'}->{val}      .= $txt;
            $checks{'ev-'}->{val}      .= $txt;
            $checks{'dv'}->{val}       .= $txt;
            if ( _is_cfg_verbose() ) {
                $value =~ s#($cfg{'regex'}->{'EV-chars'}+)##msg;
                _vprint2("  EV:  wrong characters in $label: $value");
            }
        }
    }
    trace("check_certchars() }");
    return;
}

sub check_dh {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'check_dh'}++;
    return if ( 1 < $cfg{'done'}->{'check_dh'} );
    trace("check_dh($host, $port) {");
    my $txt = $data{'dh_parameter'}->{val}($host);
    if ( $txt eq "" ) {
        $txt = "<<openssl did not return DH Paramter>>";
        checkciphers( $host, $port, $cipher_results );

        my $exp = $checks{'logjam'}->{val};
        $checks{'logjam'}->{val} .= $txt;
        $checks{'logjam'}->{val} .= "; but has WEAK ciphers: $exp" if ( $exp ne "" );
        $checks{'dh_512'}->{val}   = $txt;
        $checks{'dh_2048'}->{val}  = $txt;
        $checks{'ecdh_256'}->{val} = $txt;
        $checks{'ecdh_512'}->{val} = $txt;
        goto FIN;
    }
    my $dh = $txt;
    $dh =~ s/.*?[^\d]*(\d+) *bits.*/$1/i;

    if ( $dh =~ m/^\d+$/ ) {
        if ( $txt !~ m/ECDH/ ) {
            $checks{'dh_512'}->{val}  = $txt if ( $dh < 512 );
            $checks{'dh_2048'}->{val} = $txt if ( $dh < 2048 );
        }
        else {
            $checks{'ecdh_256'}->{val} = $txt if ( $dh < 256 );
            $checks{'ecdh_512'}->{val} = $txt if ( $dh < 512 );
        }
        my $val = join( " ", $checks{'dh_512'}->{val}, $checks{'dh_2048'}->{val}, $checks{'ecdh_256'}->{val} );
        $checks{'logjam'}->{val} = $val if ( $val ne "" );
    }
    else {
        $checks{'logjam'}->{val} = $txt;
    }
  FIN:
    $checks{'logjam'}->{val} =~ s/^\s*$//;
    trace("check_dh() }");
    return;
}

sub check_url {
    my ( $uri, $type ) = @_;
    $cfg{'done'}->{'check_url'}++;
    trace("check_url($uri, $type) {}");
    return " " if ( $uri =~ m#^\s*$# );

    my ( $accept, $binary, $ctype, $chunk, $length );
    my $txt = "<<unexpected type: $type>>";
    my $src = 'Net::SSLeay::get_http()';
    $uri =~ m#^\s*(?:(?:http|ldap)s?:)?//([^/]+)(/.*)?$#;
    my $host = $1;
    my $url  = $2 || "/";
    return "" if not defined $host;
    $host =~ m#^([^:]+)(?::[0-9]{1,5})?#;
    $host = $1;
    my $port = $2 || 80;
    $port =~ s/^://;

    trace2( "check_url: use_http= " . _is_cfg_use('http') );
    trace2("check_url: get_http($host, $port, $url)");
    my ( $response, $status, %headers ) = Net::SSLeay::get_http(
        $host, $port, $url,
        Net::SSLeay::make_headers(
            'Host'       => $host,
            'Connection' => 'close',
        )
    );
    trace2("check_url: STATUS= $status");

    if ( $status !~ m#^HTTP/... (?:[1234][0-9][0-9]|500) # ) {
        return "<<connection to '$host:$port$url' failed>>";
    }
    trace2( "check_url: header= #{ " . join( ": ", %headers ) . " }" );
    if ( $status =~ m#^HTTP/... 200 # ) {
        $accept = $headers{ ( grep { /^Accept-Ranges$/i } keys %headers )[0]             || "" } || " ";
        $ctype  = $headers{ ( grep { /^Content-Type$/i } keys %headers )[0]              || "" } || " ";
        $length = $headers{ ( grep { /^Content-Length$/i } keys %headers )[0]            || "" } || "-1";
        $binary = $headers{ ( grep { /^Content-transfer-encoding$/i } keys %headers )[0] || "" };
        $chunk  = $headers{ ( grep { /^Transfer-Encoding$/i } keys %headers )[0]         || "" } || " ";
        trace2("check_url: length=$length, accept=$accept, ctype=$ctype");
    }
    else {
        return _get_text( 'unexpected', "response from '$host:$port$url': $status" );
    }

    if ( $type eq 'ocsp_uri' ) {
        trace2("check_url: ocsp_uri ...");
        return _get_text( 'invalid', "Content-Type: $ctype" )    if ( $ctype !~ m:application/ocsp-response:i );
        return _get_text( 'invalid', "Content-Length: $length" ) if ( $length < 4 );
        return "";
    }

    if ( $type eq 'ext_crl' ) {
        trace2("check_url: ext_crl ...");
        if ( ( defined $accept ) && ( defined $chunk ) ) {
            if ( $accept !~ m/bytes/i ) {
                if ( ( $accept !~ m/^none/i ) && ( $chunk !~ m/^chunked/i ) ) {
                    return _get_text( 'invalid', "Accept-Ranges: $accept" );
                }
            }
        }
        if ( $ctype !~ m#application/(?:pkix-crl|x-pkcs7-crl)#i ) {
            return _get_text( 'invalid', "Content-Type: $ctype" );
        }
        return "";
    }

    return $txt;
}

sub check_nextproto {
    my ( $host, $port, $type, $mode ) = @_;
    trace("check_nextproto($host, $port, $type, $mode) {");
    my @protos = split( ",", $cfg{'protos_next'} );
    @protos = $cfg{'protos_next'} if ( $mode eq 'all' );
    my @npn;
    my ( $ssl, $ctx, $method );
    my $socket;
    foreach my $proto (@protos) {
        $ssl    = undef;
        $ctx    = undef;
        $socket = undef;
        ( $ssl, $ctx, $socket, $method ) = SSLinfo::do_ssl_new(
            $host, $port, ( join( " ", @{ $cfg{'version'} } ) ),
            $cfg{'cipherpattern'},
            ( ( $type eq 'ALPN' ) ? $proto : "" ),
            ( ( $type eq 'NPN' ) ? $proto : "" ), $socket
        );
        if ( not defined $ssl ) {
            OCfg::warn("601: $type connection failed with '$proto'");
        }
        else {
            my $np;
            $np = Net::SSLeay::P_alpn_selected($ssl)         if ( $type eq 'ALPN' );
            $np = Net::SSLeay::P_next_proto_negotiated($ssl) if ( $type eq 'NPN' );
            if ( defined $np && $mode eq 'single' ) {
                OCfg::warn("602: $type name mismatch: (send) $proto <> $np (returned)") if ( $proto ne $np );
            }
            trace("check_nextproto: type=$type, np=$np") if ( defined $np );
            if ( defined $np ) {
                push( @npn, $np ) if ( $proto eq $np );
            }
        }
        SSLinfo::do_ssl_free( $ctx, $ssl, $socket );
    }
    trace("check_nextproto()\t= @npn }");
    return @npn;
}

sub checkalpn {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checkalpn'}++;
    return if ( 1 < $cfg{'done'}->{'checkalpn'} );
    if ( $cfg{'ssleay'}->{'get_alpn'} > 0 ) {
        $OData::info{'alpns'} = join( ",", check_nextproto( $host, $port, 'ALPN', 'single' ) );
        $OData::info{'alpn'}  = join( ",", check_nextproto( $host, $port, 'ALPN', 'all' ) );
    }
    if ( $cfg{'ssleay'}->{'get_npn'} > 0 ) {
        $OData::info{'npns'} = join( ",", check_nextproto( $host, $port, 'NPN', 'single' ) );
        $OData::info{'npn'}  = join( ",", check_nextproto( $host, $port, 'NPN', 'all' ) );
    }
    return;
}

sub checkpreferred {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checkpreferred'}++;
    return if ( 1 < $cfg{'done'}->{'checkpreferred'} );
    trace("checkpreferred($host, $port) {");
    foreach my $ssl ( @{ $cfg{'version'} } ) {
        my $strong = $prot{$ssl}->{'cipher_strong'};
        my $weak   = $prot{$ssl}->{'cipher_weak'};
        my $txt    = ( $weak ne $strong ) ? _prot_cipher( $ssl, "$strong,$weak" ) : "";
        $checks{'cipher_strong'}->{val} .= $txt;
        $checks{'cipher_order'}->{val}  .= $txt;
        $checks{'cipher_weak'}->{val}   .= $txt;
        if ( $weak eq $strong ) {
            _set_cfg( 'CFG-hint', 'cipher_weak=check if "weak" cipher was returned may be misleading if the strongest cipher is returned always' );
        }
    }
    trace("checkpreferred() }");
    return;
}

sub checkcipher {
    my ( $ssl, $key ) = @_;
    my $c     = Ciphers::get_name($key);
    my $risk  = Ciphers::get_sec($key);
    my @const = Ciphers::get_consts($key);
    trace("checkcipher($host, $port) {");
    $checks{'cipher_null'}->{val} .= _prot_cipher( $ssl, $c ) if Ciphers::is_typ( 'NULL', $c );
    $checks{'cipher_adh'}->{val}  .= _prot_cipher( $ssl, $c ) if Ciphers::is_typ( 'ADH',  $c );
    $checks{'cipher_exp'}->{val}  .= _prot_cipher( $ssl, $c ) if Ciphers::is_typ( 'EXP',  $c );
    $checks{'cipher_cbc'}->{val}  .= _prot_cipher( $ssl, $c ) if Ciphers::is_typ( 'CBC',  $c );
    $checks{'cipher_des'}->{val}  .= _prot_cipher( $ssl, $c ) if Ciphers::is_typ( 'DES',  $c );
    $checks{'cipher_rc4'}->{val}  .= _prot_cipher( $ssl, $c ) if Ciphers::is_typ( 'RC4',  $c );
    $checks{'cipher_edh'}->{val}  .= _prot_cipher( $ssl, $c ) if Ciphers::is_typ( 'EDH',  $c );
    $checks{'ism'}->{val}       .= _prot_cipher( $ssl, $c ) if Ciphers::is_typ( 'notISM', $c );
    $checks{'pci'}->{val}       .= _is_compliant( $ssl, $c, 'PCI' );
    $checks{'fips'}->{val}      .= _is_compliant( $ssl, $c, 'FIPS-140' );
    $checks{'rfc_7525'}->{val}  .= _is_compliant( $ssl, $c, 'RFC7525' );
    $checks{'tr_02102+'}->{val} .= _is_compliant( $ssl, $c, 'TR02102-strict' );
    $checks{'tr_02102-'}->{val} .= _is_compliant( $ssl, $c, 'TR02102-lazy' );
    $checks{'tr_03116+'}->{val} .= _is_compliant( $ssl, $c, 'TR03116-strict' );
    $checks{'tr_03116-'}->{val} .= _is_compliant( $ssl, $c, 'TR03116-lazy' );
    $checks{'rc4'}->{val} = $checks{'cipher_rc4'}->{val};
    $checks{'beast'}->{val}   .= _is_vulnerable( $ssl, $c, 'BEAST' );
    $checks{'freak'}->{val}   .= _is_vulnerable( $ssl, $c, 'FREAK' );
    $checks{'lucky13'}->{val} .= _is_vulnerable( $ssl, $c, 'Lucky13' );
    $checks{'robot'}->{val}   .= _is_vulnerable( $ssl, $c, 'ROBOT' );
    $checks{'sloth'}->{val}   .= _is_vulnerable( $ssl, $c, 'SLOTH' );
    $checks{'sweet32'}->{val} .= _is_vulnerable( $ssl, $c, 'Sweet32' );
    $prot{$ssl}->{'-?-'}++    if ( $risk =~ /-\?-/ );
    $prot{$ssl}->{'WEAK'}++   if ( $risk =~ /WEAK/i );
    $prot{$ssl}->{'LOW'}++    if ( $risk =~ /LOW/i );
    $prot{$ssl}->{'MEDIUM'}++ if ( $risk =~ /MEDIUM/i );
    $prot{$ssl}->{'HIGH'}++   if ( $risk =~ /HIGH/i );
    $risk = OCfg::get_cipher_owasp($c);
    $prot{$ssl}->{'OWASP_miss'}++ if ( $risk eq 'miss' );
    $prot{$ssl}->{'OWASP_NA'}++   if ( $risk eq '-?-' );
    $prot{$ssl}->{'OWASP_D'}++    if ( $risk eq 'D' );
    $prot{$ssl}->{'OWASP_C'}++    if ( $risk eq 'C' );
    $prot{$ssl}->{'OWASP_B'}++    if ( $risk eq 'B' );
    $prot{$ssl}->{'OWASP_A'}++    if ( $risk eq 'A' );
    trace("checkcipher() }");
    return;
}

sub checkciphers_pfs {
    my $cnt_all = shift;
    my $cnt_pfs = shift;
    my $ssl     = shift;
    trace("checkciphers_pfs($cnt_all, $cnt_pfs, $ssl) {");
    my $cipher = $prot{$ssl}->{'default'};
    my @prots  = grep { /(^$ssl$)/i } @{ $cfg{'versions'} };
    if ( 1 > $cnt_all ) {
        $checks{'cipher_pfs'}->{val} = $text{'miss_protocol'};
        goto FIN;
    }
    if ( 1 > $#prots ) {
        $checks{'cipher_pfs'}->{val} = ( "" eq _is_compliant( $ssl, $cipher, 'PFS' ) ) ? $cipher : "";
    }
    else {
        OCfg::warn( "631: protocol '" . join( ';', @prots ) . "' multiple protocols with selected cipher available" );
        $checks{'cipher_pfs'}->{val} .= "$ssl:" . $prot{$_}->{'default'} . " " foreach (@prots);
    }
    $checks{'cipher_pfsall'}->{val} = ( $checks{'cnt_ciphers'}->{val} > $cnt_pfs ) ? " " : "";
    $checks{'cipher_pfsall'}->{val} = $text{'na'} if ( 1 > $checks{'cnt_ciphers'}->{val} );
  FIN:
    trace("checkciphers_pfs() }");
    return;
}

sub checkciphers {
    my ( $host, $port, $results ) = @_;
    $cfg{'done'}->{'checkciphers'}++;
    return if ( 1 < $cfg{'done'}->{'checkciphers'} );
    trace("checkciphers($host, $port) {");

    my $cnt_all = 0;
    my $cnt_pfs = 0;
    $prot{'cipher_selected'} = "";
    foreach my $ssl ( reverse( @{ $cfg{'version'} } ) ) {
        $cnt_all += $prot{$ssl}->{'cnt'};
        $cnt_pfs += scalar( @{ $prot{$ssl}->{'ciphers_pfs'} } );
        if ( not $results->{$ssl} ) {
            foreach my $key ( @{ $cfg{'need-cipher'} } ) {
                if ( $key =~ m/(drown|poodle|has(?:ssl|tls))/ ) {
                    next if ( $checks{$key}->{val} !~ m/$text{'undef'}/ );
                }
            }
            @{ $prot{$ssl}->{'ciphers_pfs'} } = _get_text( 'miss_cipher', "" );
        }
        my $cipher = $prot{$ssl}->{'default'};
        next                                 if not $cipher;
        next                                 if ( $OText::STR{UNDEF} eq $cipher );
        $cipher = Ciphers::get_name($cipher) if _is_cipher_key($cipher);
        if ( not grep { /$cipher/ } $prot{'cipher_selected'} ) {
            $prot{'cipher_selected'} .= " $ssl:$cipher";
        }
        $prot{'cipher_selected'} =~ s/^\s*//;
    }

    my %hasecdsa;
    my %hasrsa;
    foreach my $ssl ( sort( keys %$results ) ) {
        next if '_admin' eq $ssl;
        next if not $results->{$ssl};
        foreach my $key ( sort( keys %{ $results->{$ssl} } ) ) {

            next if ( $key =~ m/^\s*$/ );
            next if not $results->{$ssl}{$key};
            my $yesno  = $results->{$ssl}{$key}[0];
            my $cipher = Ciphers::get_name($key);
            if ( ( $cipher =~ m/^\s*$/ ) || ( $yesno =~ m/^\s*$/ ) ) {
                OCfg::warn("420: empty value for $key => '$cipher: [$yesno]'; check ignored");
                next;
            }
            if ( $yesno =~ m/yes/i ) {
                $prot{$ssl}->{'cnt'}++;
                checkcipher( $ssl, $key );
                $checks{'logjam'}->{val} .= _is_vulnerable( $ssl, $cipher, 'Logjam' );
            }
            $hasrsa{$ssl}   = 1 if Ciphers::is_typ( 'EC-RSA', $cipher );
            $hasecdsa{$ssl} = 1 if Ciphers::is_typ( 'EC-DSA', $cipher );
            push( @{ $prot{$ssl}->{'ciphers_pfs'} }, $cipher ) if _is_compliant( $ssl, $cipher, 'PFS' );
        }
        my @_pfs = sort( @{ $prot{$ssl}->{'ciphers_pfs'} } );
        trace("checkciphers:  {$ssl}->{ciphers_pfs}=@_pfs");
    }

    my $beastskipped = _is_beast_skipped( $host, $port );
    $checks{'beast'}->{val} .= " " . ${beastskipped} if "" ne $beastskipped;

    foreach my $ssl ( @{ $cfg{'version'} } ) {
        $cnt_all += $prot{$ssl}->{'cnt'};
        $cnt_pfs += scalar( @{ $prot{$ssl}->{'ciphers_pfs'} } );
        $hasrsa{$ssl}   = 0 if not defined $hasrsa{$ssl};
        $hasecdsa{$ssl} = 0 if not defined $hasecdsa{$ssl};

        if ( $prot{$ssl}->{'cnt'} > 0 ) {
            $checks{'tr_02102+'}->{val} .= _prot_cipher( $ssl, $text{'miss_RSA'} )   if ( $hasrsa{$ssl} != 1 );
            $checks{'tr_02102+'}->{val} .= _prot_cipher( $ssl, $text{'miss_ECDSA'} ) if ( $hasecdsa{$ssl} != 1 );
            $checks{'tr_03116+'}->{val} .= $checks{'tr_02102+'}->{val};
            $checks{'tr_03116-'}->{val} .= $checks{'tr_02102-'}->{val};
        }
        $checks{'cnt_ciphers'}->{val} += $prot{$ssl}->{'cnt'};
    }
    $checks{'cipher_edh'}->{val} = "" if ( $checks{'cipher_edh'}->{val} ne "" );

    if ( defined $results->{'_admin'}{'session_protocol'} ) {
        checkciphers_pfs( $cnt_all, $cnt_pfs, $results->{'_admin'}{'session_protocol'} );
    }
    else {
        OCfg::hint("no session protocol detected, PFS ciphers may be wrong; consider using '--ciphermode=intern'");

    }
    trace("checkciphers() }");
    return;
}

sub checkbleed {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checkbleed'}++;
    return if ( 1 < $cfg{'done'}->{'checkbleed'} );
    my $bleed = _is_vuln_bleed( $host, $port );
    if ( 0 < $cfg{'ignorenoreply'} ) {
        $checks{'heartbleed'}->{val} = "";
        return if ( $bleed =~ m/no reply/ );
    }
    $checks{'heartbleed'}->{val} = $bleed;
    return;
}

sub checkdates {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checkdates'}++;
    return if ( 1 < $cfg{'done'}->{'checkdates'} );
    trace("checkdates($host, $port) {");

    my $before = $data{'before'}->{val}( $host, $port );
    my $after  = $data{'after'}->{val}( $host, $port );
    my @since  = split( / +/, $before );
    my @until  = split( / +/, $after );
    if ( "$before$after" =~ m/^\s*$/ ) {
        $checks{'dates'}->{val}        = $text{'na'};
        $checks{'expired'}->{val}      = $text{'na'};
        $checks{'sts_expired'}->{val}  = $text{'na'};
        $checks{'valid_years'}->{val}  = 0;
        $checks{'valid_months'}->{val} = 0;
        $checks{'valid_days'}->{val}   = 0;
        goto FIN;
    }

    my @now   = gmtime(time);
    my @mon   = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
    my $m     = 0;
    my $s_mon = 0;
    my $u_mon = 0;
    if (@since) {
        my $dum = map( {
                $m++;
                $s_mon = $m if /$since[0]/
        } @mon );
        $m = 0;
    }
    if (@until) {
        my $dum = map( {
                $m++;
                $u_mon = $m if /$until[0]/
        } @mon );
        $m = 0;
    }
    my $now   = sprintf( "%4d%02d%02d", $now[5] + 1900, $now[4] + 1, $now[3] );
    my $start = sprintf( "%s%02s%02s",  $since[3],      $s_mon,      $since[1] );
    my $end   = sprintf( "%s%02s%02s",  $until[3],      $u_mon,      $until[1] );
    my $txt   = "";
    $checks{'dates'}->{val} = $before          if ( $now < $start );
    $checks{'dates'}->{val} .= " .. " . $after if ( $now > $end );
    $checks{'expired'}->{val}    = $after if ( $now > $end );
    $data{'valid_years'}->{val}  = ( $until[3] - $since[3] );
    $data{'valid_months'}->{val} = ( $until[3] * 12 ) - ( $since[3] * 12 ) + $u_mon - $s_mon;
    $data{'valid_days'}->{val}   = ( $data{'valid_years'}->{val} * 5 ) + ( $data{'valid_months'}->{val} * 30 );
    $data{'valid_days'}->{val}   = ( $until[1] - $since[1] ) if ( $data{'valid_days'}->{val} < 60 );

  MAXAGE_CHECK: {
        $txt = $text{'na_STS'};
        last MAXAGE_CHECK if ( $data{'https_sts'}->{val}($host) eq "" );
        $txt = $OText::STR{UNDEF};
        last MAXAGE_CHECK if ( not _is_cfg_do('sts_expired') );
        $txt = "";
        $now = time();
        my $maxage = $data{'hsts_maxage'}->{val}($host);
        my $ts     = "@until";

        if ( exists &Time::Local::timelocal ) {
            $ts  = Time::Local::timelocal( reverse( split( /:/, $until[2] ) ), $until[1], $u_mon - 1, $until[3] );
            $txt = "$now + $maxage > $ts" if ( $now + $maxage > $ts );
        }
        else {
            $txt = "$now + $maxage > $ts ??";
        }
    }
    $checks{'sts_expired'}->{val} = $txt;

    $now = "<<time()>>" if ( defined $ENV{'OSAFT_MAKE'} );
    trace(" start, now, end= $start, $now, $end");
    trace( " valid dates = " . $checks{'dates'}->{val} );
    trace( " valid_years = " . $data{'valid_years'}->{val} );
    trace( " valid_months= " . $data{'valid_months'}->{val} . "  = ($until[3]*12) - ($since[3]*12) + $u_mon - $s_mon" );
    if ( 60 > $data{'valid_days'}->{val} ) {
        trace( " valid_days  = " . $data{'valid_days'}->{val} . " = ($until[1] - $since[1])" );
    }
    else {
        trace( " valid_days  = " . $data{'valid_days'}->{val} . " = (" . $data{'valid_years'}->{val} . "*5) + (" . $data{'valid_months'}->{val} . "*30)" );
    }
  FIN:
    trace("checkdates() }");
    return;
}

sub checkcert {
    my ( $host, $port ) = @_;
    my ( $value, $label );
    $cfg{'done'}->{'checkcert'}++;
    return if ( 1 < $cfg{'done'}->{'checkcert'} );
    trace("checkcert($host, $port) {");

    _checkwildcard( $host, $port );

    $checks{'rootcert'}->{val} = $data{'issuer'}->{val}($host) if ( $data{'subject'}->{val}($host) eq $data{'issuer'}->{val}($host) );
    $checks{'ocsp_uri'}->{val} = " "                           if ( $data{'ocsp_uri'}->{val}($host) eq "" );
    $checks{'cps'}->{val}      = " "                           if ( $data{'ext_cps'}->{val}($host) eq "" );
    $checks{'crl'}->{val}      = " "                           if ( $data{'ext_crl'}->{val}($host) eq "" );

    if ( _is_cfg_use('http') ) {
        $checks{'crl_valid'}->{val} = "";
        $value = $data{'ext_crl'}->{val}($host);
        if ( $value eq '<<openssl>>' ) {
            $checks{'crl_valid'}->{val} = $text{'na_openssl'};
        }
        else {
            trace(" ext_crl: $value");
            foreach my $url ( split( /\s+/, $value ) ) {
                next if ( $url =~ m/^\s*$/ );
                if ( $url !~ m/^\s*http$/ ) {
                    trace(" ext_uri skipped: $url");
                    next;
                }
                $checks{'crl_valid'}->{val} .= check_url( $url, 'ext_crl' ) || "";
            }
        }
    }
    else {
        $checks{'crl_valid'}->{val} = _get_text( 'disabled', "--no-http" );
    }
    if ( $checks{'ocsp_uri'}->{val} eq '' ) {
        $checks{'ocsp_valid'}->{val} = "";
        $value = $data{'ocsp_uri'}->{val}($host);
        if ( $value eq '<<openssl>>' ) {
            $checks{'crl_valid'}->{val} = $text{'na_openssl'};
        }
        else {
            trace(" ocsp_uri: $value");
            foreach my $url ( split( /\s+/, $value ) ) {
                next if ( $url =~ m/^\s*$/ );
                if ( $url !~ m/^\s*http/ ) {
                    trace(" ocsp_uri skipped: $url");
                    next;
                }
                $checks{'ocsp_valid'}->{val} .= check_url( $url, 'ocsp_uri' ) || "";
            }
        }
    }
    else {
        $checks{'ocsp_valid'}->{val} = " ";
    }

    $value                        = $data{'ext_constraints'}->{val}($host);
    $checks{'constraints'}->{val} = " "    if ( $value eq "" );
    $checks{'constraints'}->{val} = $value if ( $value !~ m/CA:FALSE/i );

    check_certchars( $host, $port );

    if ( _is_cfg_verbose() ) {
        foreach my $label (qw(verify selfsigned)) {
            $value = $data{$label}->{val}($host);
            $checks{$label}->{val} = $value if ( $value eq "" );

        }
    }
    $value                           = $data{'selfsigned'}->{val}($host);
    $checks{'selfsigned'}->{val}     = $value               if ( $value !~ m/^(?:0\s+.ok.)*$/ );
    $checks{'fp_not_md5'}->{val}     = $data{'fingerprint'} if ( 'MD5' eq $data{'fingerprint'} );
    $value                           = $data{'signame'}->{val}($host);
    $checks{'sha2signature'}->{val}  = $value if ( $value !~ m/^$cfg{'regex'}->{'SHA2'}/ );
    $checks{'sig_encryption'}->{val} = $value if ( $value !~ m/$cfg{'regex'}->{'encryption'}/i );
    $checks{'sig_enc_known'}->{val}  = $value if ( $value !~ m/^$cfg{'regex'}->{'encryption_ok'}|$cfg{'regex'}->{'encryption_no'}$/i );
    $value                           = $data{'pubkey_algorithm'}->{val}($host);
    $checks{'pub_encryption'}->{val} = $value if ( $value !~ m/$cfg{'regex'}->{'encryption'}/i );
    $checks{'pub_enc_known'}->{val}  = $value if ( $value !~ m/^$cfg{'regex'}->{'encryption_ok'}|$cfg{'regex'}->{'encryption_no'}$/i );

    trace("checkcert() }");
    return;
}

sub checksni {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checksni'}++;
    return if ( 1 < $cfg{'done'}->{'checksni'} );
    trace("checksni($host, $port) {");
    my $cn       = $data{'cn'}->{val}( $host, $port );
    my $lc_nosni = lc( $data{'cn_nosni'}->{val} ) || $OText::STR{UNDEF};
    my $lc_host  = lc($host);
    my $lc_cn    = lc($cn);
    my $rex_cn   = $cn;
    $rex_cn =~ s/[*][.]/(?:.*\\.)?/g;

    if ( _is_cfg_use('sni') ) {
        if ( $lc_host eq $lc_nosni ) {
            $checks{'sni'}->{val} = "";
        }
        else {
            $checks{'sni'}->{val} = $data{'cn_nosni'}->{val};
        }
    }
    if ( not _is_cfg_use('cert') ) {
        $checks{'certfqdn'}->{val} = $cfg{'no_cert_txt'};
        $checks{'hostname'}->{val} = $cfg{'no_cert_txt'};
        goto FIN;
    }
    if ( $lc_host eq $lc_cn ) {
        $checks{'hostname'}->{val} = "";
    }
    else {
        $checks{'hostname'}->{val} = $host . " <> " . $data{'cn'}->{val}($host);
    }
    if ( $host =~ m/$rex_cn/i ) {
        $checks{'certfqdn'}->{val} = "";
    }
    else {
        $checks{'certfqdn'}->{val} = $data{'cn_nosni'}->{val} . " <> " . $host;
    }
  FIN:
    trace("checksni() }");
    return;
}

sub checksizes {
    my ( $host, $port ) = @_;
    my $value;
    $cfg{'done'}->{'checksizes'}++;
    return if ( 1 < $cfg{'done'}->{'checksizes'} );
    trace("checksizes($host, $port) {");

    checkcert( $host, $port ) if ( _is_cfg_use('cert') );
    $value = $data{'pem'}->{val}($host);
    $checks{'len_pembase64'}->{val} = length($value);
    $value =~ s/(----.+----\n)//g;
    chomp $value;
    $checks{'len_pembinary'}->{val} = sprintf( "%d", length($value) / 8 * 6 ) + 1;
    $checks{'len_subject'}->{val}   = length( $data{'subject'}->{val}($host) );
    $checks{'len_issuer'}->{val}    = length( $data{'issuer'}->{val}($host) );
    $checks{'len_cps'}->{val}       = length( $data{'ext_cps'}->{val}($host) );
    $checks{'len_crl'}->{val}       = length( $data{'ext_crl'}->{val}($host) );
    $checks{'len_ocsp'}->{val} = length( $data{'ocsp_uri'}->{val}($host) );
    $checks{'len_sernumber'}->{val} = int( length( $data{'serial_hex'}->{val}($host) ) / 2 );

    if ( $cmd{'extopenssl'} == 1 ) {
        $value                          = $data{'modulus_len'}->{val}($host);
        $checks{'len_publickey'}->{val} = ( ( $value =~ m/^\s*$/ ) ? 0 : $value );
        $value                          = $data{'modulus_exponent'}->{val}($host);
        if ( $value =~ m/prime/i ) {
            $value =~ s/\n */ /msg;
            $checks{'modulus_exp_1'}->{val}       = "<<N/A $value>>";
            $checks{'modulus_exp_65537'}->{val}   = "<<N/A $value>>";
            $checks{'modulus_exp_oldssl'}->{val}  = "<<N/A $value>>";
            $checks{'modulus_size_oldssl'}->{val} = "<<N/A $value>>";
        }
        else {
            if ( $value eq '<<openssl>>' ) {
                $checks{'modulus_exp_1'}->{val}      = $text{'na_openssl'};
                $checks{'modulus_exp_65537'}->{val}  = $text{'na_openssl'};
                $checks{'modulus_exp_oldssl'}->{val} = $text{'na_openssl'};
            }
            else {
                $value =~ s/^(\d+).*/$1/;
                if ( $value =~ m/^\d+$/ ) {
                    $checks{'modulus_exp_1'}->{val}      = $value if ( $value == 1 );
                    $checks{'modulus_exp_65537'}->{val}  = $value if ( $value != 65537 );
                    $checks{'modulus_exp_oldssl'}->{val} = $value if ( $value > 65536 );
                }
                else {
                    $checks{'modulus_exp_1'}->{val}      = $text{'na'};
                    $checks{'modulus_exp_65537'}->{val}  = $text{'na'};
                    $checks{'modulus_exp_oldssl'}->{val} = $text{'na'};
                }
            }
            $value = $data{'modulus'}->{val}($host);
            if ( $value eq '<<openssl>>' ) {
                $checks{'modulus_size_oldssl'}->{val} = $text{'na_openssl'};
            }
            else {
                $value = length($value) * 4;
                $checks{'modulus_size_oldssl'}->{val} = $value if ( $value > 16384 );
            }
        }
        $value = $data{'serial_int'}->{val}($host);
        $value = 0 if ( $value =~ m/^\s*$/ );
        $value += 0;
        my $bits_of_value = _get_base2($value);
        $checks{'sernumber'}->{val}   = "$bits_of_value  > 160" if ( $bits_of_value > 160 );
        $value                        = $data{'sigkey_len'}->{val}($host);
        $checks{'len_sigdump'}->{val} = ( ( $value =~ m/^\s*$/ ) ? 0 : $value );
    }
    else {
        $checks{'sernumber'}->{val}           = $text{'na_openssl'};
        $checks{'len_sigdump'}->{val}         = $text{'na_openssl'};
        $checks{'len_publickey'}->{val}       = $text{'na_openssl'};
        $checks{'modulus_exp_1'}->{val}       = $text{'na_openssl'};
        $checks{'modulus_exp_65537'}->{val}   = $text{'na_openssl'};
        $checks{'modulus_exp_oldssl'}->{val}  = $text{'na_openssl'};
        $checks{'modulus_size_oldssl'}->{val} = $text{'na_openssl'};
    }
    trace("checksizes() }");
    return;
}

sub check02102 {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'check02102'}++;
    return if ( 1 < $cfg{'done'}->{'check02102'} );
    my $txt = "";
    my $val = "";

    $val = ( $data{'session_protocol'}->{val}( $host, $port ) !~ m/TLSv1.?2/ ) ? " <<not TLSv12>>" : "";
    $val                        .= ( $prot{'SSLv2'}->{'cnt'} > 0 ) ? _get_text( 'insecure', "protocol SSLv2" ) : "";
    $val                        .= ( $prot{'SSLv3'}->{'cnt'} > 0 ) ? _get_text( 'insecure', "protocol SSLv3" ) : "";
    $val                        .= ( $prot{'TLSv1'}->{'cnt'} > 0 ) ? _get_text( 'insecure', "protocol TLSv1" ) : "";
    $checks{'tr_02102-'}->{val} .= $val;
    $val                        .= ( $prot{'TLSv11'}->{'cnt'} > 0 ) ? _get_text( 'insecure', "protocol TLSv11" ) : "";
    $checks{'tr_02102+'}->{val} .= $val;

    $val = ( $checks{'renegotiation'}->{val} ne "" ) ? $text{'no_reneg'} : "";
    $checks{'tr_02102+'}->{val} .= "$val ";
    $checks{'tr_02102-'}->{val} .= "$val ";

    $val = ( $data{'tlsextensions'}->{val}( $host, $port ) =~ m/truncated.*hmac/i ) ? _get_text( 'enabled_extension', 'truncated HMAC' ) : "";
    $checks{'tr_02102+'}->{val} .= $val;
    $checks{'tr_02102-'}->{val} .= $val;

    $val = ( $checks{'crime'}->{val} =~ m/<<undefined/ ) ? "" : $checks{'crime'}->{val};
    $checks{'tr_02102+'}->{val} .= $val;
    $checks{'tr_02102-'}->{val} .= $val;

    $val = $checks{'lucky13'}->{val};
    $val = ( $val ne "" ) ? _get_text( 'insecure', "cipher $val; Lucky13" ) : "";
    $checks{'tr_02102+'}->{val} .= "$val ";

    $val = "";
    $val = ( $data{'heartbeat'}->{val}( $host, $port ) ne "" ) ? _get_text( 'enabled_extension', 'heartbeat' ) : "";
    $checks{'tr_02102+'}->{val} .= $val;
    $checks{'tr_02102-'}->{val} .= $val;

    $val = $checks{'len_sigdump'}->{val};
    if ( $val =~ m/\d+/ ) {
        $val = ( $val < 2000 ) ? _get_text( 'bit2048', $val ) : "";
    }
    else {
        $val = " len_sigdump missing $val";
    }
    $checks{'tr_02102+'}->{val} .= $val;
    $checks{'tr_02102-'}->{val} .= $val;

    $checks{'tr_02102+'}->{val} =~ s/^\s*$//;
    $checks{'tr_02102-'}->{val} =~ s/^\s*$//;

    return;
}

sub check2818 {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'check2818'}++;
    return if ( 1 < $cfg{'done'}->{'check2818'} );
    my $val = $data{'verify_altname'}->{val}($host);
    $checks{'rfc_2818_names'}->{val} = $val if ( $val !~ m/matches/ );
    return;
}

sub check03116 {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'check03116'}++;
    return if ( 1 < $cfg{'done'}->{'check03116'} );
    my $txt = "";

    $txt = ( $data{'session_protocol'}->{val}( $host, $port ) !~ m/TLSv1.?2/ ) ? " <<not TLSv12>>" : "";
    $txt .= ( $prot{'SSLv2'}->{'cnt'} > 0 )  ? _get_text( 'insecure', "protocol SSLv2" )  : "";
    $txt .= ( $prot{'SSLv3'}->{'cnt'} > 0 )  ? _get_text( 'insecure', "protocol SSLv3" )  : "";
    $txt .= ( $prot{'TLSv1'}->{'cnt'} > 0 )  ? _get_text( 'insecure', "protocol TLSv1" )  : "";
    $txt .= ( $prot{'TLSv11'}->{'cnt'} > 0 ) ? _get_text( 'insecure', "protocol TLSv11" ) : "";
    $checks{'tr_03116-'}->{val} .= $txt;
    $checks{'tr_03116+'}->{val} .= $txt;

    $checks{'tr_03116+'}->{val} .= $checks{'tr_03116+'}->{val};
    $checks{'tr_03116-'}->{val} .= $checks{'tr_03116-'}->{val};

    $checks{'tr_03116+'}->{val} .= _get_text( 'missing', 'OCSP' ) if ( $data{'ocsp_uri'}->{val}($host) eq "" );

    $txt = _get_text( 'cert_valid', $data{'valid_years'}->{val} );
    $checks{'tr_03116+'}->{val} .= $txt if ( $data{'valid_years'}->{val} > 3 );
    $txt = $checks{'wildcard'}->{val};
    if ( ( $data{'ext_crl'}->{val}($host) eq "" ) && ( $data{'ext_authority'}->{val}($host) eq "" ) ) {
        $checks{'tr_03116+'}->{val} .= _get_text( 'missing', 'AIA or CRL' );
    }
    $checks{'tr_03116+'}->{val} .= _get_text( 'wildcards', $txt ) if ( $txt ne "" );
    $txt = $data{'subject'}->{val}($host);
    $checks{'tr_03116+'}->{val} .= _get_text( 'wildcards', "Subject:$txt" ) if ( $txt =~ m/[*]/ );

    $txt = $checks{'dates'}->{val};
    $checks{'tr_03116+'}->{val} .= _get_text( 'cert_dates', $txt ) if ( $txt ne "" );
    $txt = $checks{'expired'}->{val};
    $checks{'tr_03116+'}->{val} .= _get_text( 'cert_valid', $txt ) if ( $txt ne "" );

    $checks{'tr_03116-'}->{val} .= $checks{'tr_03116+'}->{val};
    $checks{'tr_03116+'}->{val} =~ s/^\s*$//;
    $checks{'tr_03116-'}->{val} =~ s/^\s*$//;

    return;
}

sub check6125 {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'check6125'}++;
    return if ( 1 < $cfg{'done'}->{'check6125'} );

    my $txt = "";
    my $val = "";

    $txt = $data{'cn'}->{val}($host);
    $val .= " <<6.4.2:cn $txt>>" if ( $txt !~ m!$cfg{'regex'}->{'isDNS'}! );
    $val .= " <<6.4.3:cn $txt>>" if ( $txt =~ m!$cfg{'regex'}->{'doublewild'}! );
    $val .= " <<6.4.3:cn $txt>>" if ( $txt =~ m!$cfg{'regex'}->{'invalidwild'}! );
    $val .= " <<7.2.o:cn $txt>>" if ( $txt =~ m!$cfg{'regex'}->{'invalidIDN'}! );
    $val .= " <<7.3:cn $txt>>"   if ( $txt =~ m!$cfg{'regex'}->{'isIDN'}! );
    $txt = $data{'subject'}->{val}($host);
    $txt =~ s!^.*CN=!!;
    $val .= " <<6.4.2:subject $txt>>" if ( $txt !~ m!$cfg{'regex'}->{'isDNS'}! );
    $val .= " <<6.4.3:subject $txt>>" if ( $txt =~ m!$cfg{'regex'}->{'doublewild'}! );
    $val .= " <<6.4.3:subject $txt>>" if ( $txt =~ m!$cfg{'regex'}->{'invalidwild'}! );
    $val .= " <<7.2.o:subject $txt>>" if ( $txt =~ m!$cfg{'regex'}->{'invalidIDN'}! );
    $val .= " <<7.3:subject $txt>>"   if ( $txt =~ m!$cfg{'regex'}->{'isIDN'}! );

    foreach my $txt ( split( " ", $data{'altname'}->{val}($host) ) ) {
        $txt =~ s!.*:!!;
        $val .= " <<6.4.2:altname $txt>>" if ( $txt !~ m!$cfg{'regex'}->{'isDNS'}! );
        $val .= " <<6.4.3:altname $txt>>" if ( $txt =~ m!$cfg{'regex'}->{'doublewild'}! );
        $val .= " <<6.4.3:altname $txt>>" if ( $txt =~ m!$cfg{'regex'}->{'invalidwild'}! );
        $val .= " <<7.2.o:altname $txt>>" if ( $txt =~ m!$cfg{'regex'}->{'invalidIDN'}! );
        $val .= " <<7.3:altname $txt>>"   if ( $txt =~ m!$cfg{'regex'}->{'isIDN'}! );
    }
    $checks{'rfc_6125_names'}->{val} = $val;
    $checks{'rfc_6125_names'}->{val} =~ s/^\s*$//;

    return;
}

sub check7525 {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'check7525'}++;
    return if ( 1 < $cfg{'done'}->{'check7525'} );
    my $val = "";

    $val = " <<not TLSv12>>" if ( $data{'session_protocol'}->{val}( $host, $port ) !~ m/TLSv1.?2/ );
    $val .= " SSLv2"  if ( $prot{'SSLv2'}->{'cnt'} > 0 );
    $val .= " SSLv3"  if ( $prot{'SSLv3'}->{'cnt'} > 0 );
    $val .= " TLSv1"  if ( ( $prot{'TLSv11'}->{'cnt'} + $prot{'TLSv12'}->{'cnt'} ) > 0 );
    $val .= " TLSv11" if ( ( $prot{'TLSv11'}->{'cnt'} > 0 ) and ( $prot{'TLSv12'}->{'cnt'} > 0 ) );

    $val .= " DTLSv1"  if ( $prot{'DTLSv1'}->{'cnt'} > 0 );
    $val .= " DTLSv11" if ( $prot{'DTLSv11'}->{'cnt'} > 0 );

    $val .= " DTLSv11" if ( $prot{'DTLSv11'}->{'cnt'} > 0 );
    checkhttp( $host, $port );
    $val .= " " . _get_text( 'missing', 'STS' ) if ( $checks{'hsts_sts'} eq "" );

    if ( $data{'compression'}->{val}($host) =~ /$cfg{'regex'}->{'nocompression'}/ ) {
        $val .= " " . $data{'compression'}->{val}($host);
    }

    if ( $data{'resumption'}->{val}($host) eq "" ) {
        $val .= " " . _get_text( 'insecure', 'resumption' );
        $val .= " " . _get_text( 'missing',  'session ticket' )        if ( $data{'session_ticket'}->{val}($host) eq "" );
        $val .= " " . _get_text( 'insecure', 'randomness of session' ) if ( $checks{'session_random'}->{val} ne "" );
    }

    $val .= " " . _get_text( 'missing',  'renegotiation_info extension' ) if ( $data{'tlsextensions'}->{val}( $host, $port ) !~ m/renegotiation info/ );
    $val .= " " . _get_text( 'insecure', 'renegotiation' )                if ( $data{'renegotiation'}->{val}($host) eq "" );

    checksni( $host, $port );
    $val .= " <<SNI not supported>>" if ( $checks{'sni'}->{val} eq "" );

    check_dh( $host, $port );
    if ( $data{'dh_parameter'}->{val}($host) =~ m/ECDH/ ) {
        $val .= " " . _get_text( 'insecure', "DH Parameter: $checks{'ecdh_256'}->{val}" ) if ( $checks{'ecdh_256'}->{val} ne "" );
    }
    else {
        $val .= " " . _get_text( 'insecure', "DH Parameter: $checks{'dh_2048'}->{val}" ) if ( $checks{'dh_2048'}->{val} ne "" );
    }

    $val .= " " . _get_text( 'missing', 'truncated HMAC extension' ) if ( $data{'tlsextensions'}->{val}( $host, $port ) =~ m/truncated.*hmac/i );

    $val .= " " . $text{'EV_subject_host'} if ( $checks{'hostname'}->{val} ne "" );

    $val .= " " . _get_text( 'missing', 'OCSP' ) if ( $checks{'ocsp_uri'}->{val} ne "" );
    $val .= " " . $checks{'ocsp_valid'}->{val};
    $val .= " " . _get_text( 'missing', 'CRL in certificate' ) if ( $checks{'crl'}->{val} ne "" );
    $val .= " " . $checks{'crl_valid'}->{val};

    $checks{'rfc_7525'}->{val} = $val . " " . $checks{'rfc_7525'}->{val};
    $checks{'rfc_7525'}->{val} =~ s/^\s*$//;

    return;
}

sub checkdv {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checkdv'}++;
    return if ( 1 < $cfg{'done'}->{'checkdv'} );

    my $cn      = $data{'cn'}->{val}($host);
    my $subject = $data{'subject'}->{val}($host);
    my $altname = $data{'altname'}->{val}($host);
    my $oid     = '2.5.4.3';
    my $txt     = "";

    check_certchars( $host, $port );

    if ( $cn =~ m/^\s*$/ ) {
        $checks{'dv'}->{val} .= _get_text( 'missing', "Common Name" );
        return;
    }

    if (    ( $subject !~ m#/$cfg{'regex'}->{$oid}=(?:[^/\n]*)# )
        and ( $altname !~ m#/$cfg{'regex'}->{$oid}=(?:[^\s\n]*)# ) )
    {
        $checks{'dv'}->{val} .= _get_text( 'missing', $data_oid{$oid}->{txt} );
        return;
    }
    ( $txt = $subject ) =~ s#/.*?$cfg{'regex'}->{$oid}=##;
    $txt = "" if not defined $txt;

    $data_oid{$oid}->{val} = $txt if ( $txt !~ m/^\s*$/ );
    $data_oid{$oid}->{val} = $cn  if ( $cn  !~ m/^\s*$/ );

    if ( $txt ne $cn ) {
        $checks{'dv'}->{val} .= $text{'EV_subject_CN'};
    }
    if ( $txt ne $host ) {
        if ( 0 >= ( grep { /^DNS:$host$/ } split( /[\s]/, $altname ) ) ) {
            $checks{'dv'}->{val} .= $text{'EV_subject_host'};
        }
    }

    return;
}

sub checkev {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checkev'}++;
    return if ( 1 < $cfg{'done'}->{'checkev'} );
    trace("checkev($host, $port) {");

    my $oid     = "";
    my $subject = $data{'subject'}->{val}($host);
    my $cn      = $data{'cn'}->{val}($host);
    my $alt     = $data{'altname'}->{val}($host);
    my $txt     = "";
    my $key     = "";

    check_certchars( $host, $port );
    checkdv( $host, $port );
    $checks{'ev+'}->{val} = $checks{'dv'}->{val};

    foreach my $oid (
        qw(
        1.3.6.1.4.1.311.60.2.1.1   1.3.6.1.4.1.311.60.2.1.3
        2.5.4.5    2.5.4.7   2.5.4.10   2.5.4.15
        )
      )
    {
        if ( $subject =~ m#/$cfg{'regex'}->{$oid}=([^/\n]*)# ) {
            $data_oid{$oid}->{val} = $1;
            _vprint2( "  EV: " . $cfg{'regex'}->{$oid} . " = $1" );
        }
        else {
            _vprint2( "  EV: " . _get_text( 'missing', $cfg{'regex'}->{$oid} ) . "; required" );
            $txt = _get_text( 'missing', $data_oid{$oid}->{txt} );
            $checks{'ev+'}->{val} .= $txt;
            $checks{'ev-'}->{val} .= $txt;
        }
    }
    $oid = '1.3.6.1.4.1.311.60.2.1.2';
    if ( $subject !~ m#/$cfg{'regex'}->{$oid}=(?:[^/\n]*)# ) {
        $txt = _get_text( 'missing', $data_oid{$oid}->{txt} );
        $checks{'ev+'}->{val} .= $txt;
        $oid = '2.5.4.8';
        if ( $subject =~ m#/$cfg{'regex'}->{'2.5.4.8'}=([^/\n]*)# ) {
            $data_oid{$oid}->{val} = $1;
        }
        else {
            $checks{'ev-'}->{val} .= $txt;
            _vprint2( "  EV: " . _get_text( 'missing', $cfg{'regex'}->{$oid} ) . "; required" );
        }
    }
    $oid = '2.5.4.9';
    if ( $subject !~ m#/$cfg{'regex'}->{$oid}=(?:[^/\n]*)# ) {
        $txt = _get_text( 'missing', $data_oid{$oid}->{txt} );
        $checks{'ev+'}->{val} .= $txt;
        _vprint2( "  EV: " . $cfg{'regex'}->{$oid} . " = missing+" );
        _vprint2( "  EV: " . _get_text( 'missing', $cfg{'regex'}->{$oid} ) . "; required" );
    }
    foreach my $oid (qw(2.5.4.6 2.5.4.17)) {
    }
    if ( 64 < length( $data_oid{'2.5.4.10'}->{val} ) ) {
        $txt = _get_text( 'EV_large', "64 < " . $data_oid{$oid}->{txt} );
        $checks{'ev+'}->{val} .= $txt;
        _vprint2( "  EV: " . $txt );
    }
    if ( $data{'valid_months'}->{val} > 27 ) {
        $txt = _get_text( 'cert_valid', "27 < " . $data{'valid_months'}->{val} );
        $checks{'ev+'}->{val} .= $txt;
        _vprint2( "  EV: " . $txt );
    }

    trace("checkev() }");
    return;
}

sub checkroot {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checkroot'}++;
    return if ( 1 < $cfg{'done'}->{'checkroot'} );

    return;
}

sub checkprot {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checkprot'}++;
    return if ( 1 < $cfg{'done'}->{'checkprot'} );
    trace("checkprot($host, $port) {");
    my $notxt = "";

    if ( _is_cfg_ssl('SSLv2') ) {
        $notxt = ( 0 < $prot{'SSLv2'}->{'cnt'} ) ? " " : "";
        $checks{'hassslv2'}->{val} = ( _is_cfg_use('nullssl2') ) ? $notxt : "";
        $checks{'drown'}->{val} = $notxt;
    }
    if ( _is_cfg_ssl('SSLv3') ) {
        $notxt                     = ( 0 < $prot{'SSLv3'}->{'cnt'} ) ? " " : "";
        $checks{'hassslv3'}->{val} = $notxt;
        $checks{'poodle'}->{val}   = ( 0 < $prot{'SSLv3'}->{'cnt'} ) ? "SSLv3" : "";
    }
    if ( _is_cfg_ssl('TLSv1') ) {
        $checks{'hastls10_old'}->{val} = " " if ( $prot{'TLSv1'}->{'cnt'} <= 0 );
        $checks{'poodle'}->{val}       = ( 0 < $prot{'TLSv1'}->{'cnt'} ) ? "TLSv1" : "";
    }
    if ( _is_cfg_ssl('TLSv11') ) {
        $checks{'hastls11_old'}->{val} = " " if ( $prot{'TLSv11'}->{'cnt'} <= 0 );
    }
    if ( 0 >= $prot{'TLSv13'}->{'cnt'} ) {
        OCfg::hint("TLSv1.3 did not return ciphers, consider using '+hastls10_old' and '+hastls10_old'");
    }
    if ( _is_cfg_ssl('TLSv1') ) {
        $notxt = ( 0 < $prot{'TLSv1'}->{'cnt'} ) ? " " : "";
        $checks{'hastls10'}->{val} = $notxt;
    }
    if ( _is_cfg_ssl('TLSv11') ) {
        $notxt = ( 0 < $prot{'TLSv11'}->{'cnt'} ) ? " " : "";
        $checks{'hastls11'}->{val} = $notxt;
    }
    if ( _is_cfg_ssl('TLSv12') ) {
        $checks{'hastls12'}->{val} = " " if ( $prot{'TLSv12'}->{'cnt'} <= 0 );
    }
    if ( _is_cfg_ssl('TLSv13') ) {
        $checks{'hastls13'}->{val} = " " if ( $prot{'TLSv13'}->{'cnt'} <= 0 );
    }
    if ( _is_cfg_ssl('DTLSv1') ) {
        $checks{'hasdtls1'}->{val} = " " if ( $prot{'DTLSv1'}->{'cnt'} <= 0 );
    }
    if ( _is_cfg_ssl('DTLSv12') ) {
        $checks{'hasdtls12'}->{val} = " " if ( $prot{'DTLSv12'}->{'cnt'} <= 0 );
    }
    if ( _is_cfg_ssl('DTLSv13') ) {
        $checks{'hasdtls13'}->{val} = " " if ( $prot{'DTLSv13'}->{'cnt'} <= 0 );
    }

    checkalpn( $host, $port );
    my ( $key, $value );
    $key                      = 'alpns';
    $value                    = $data{$key}->{val}( $host, $port );
    $checks{'hasalpn'}->{val} = " " if ( $value eq "" );
    $key                      = 'npns';
    $value                    = $data{$key}->{val}( $host, $port );
    $checks{'hasnpn'}->{val}  = " " if ( $value eq "" );
    trace("checkprot() }");
    return;
}

sub checkdest {
    my ( $host, $port ) = @_;
    my $ciphers = shift;
    my ( $key, $value, $ssl, $cipher );
    $cfg{'done'}->{'checkdest'}++;
    return if ( 1 < $cfg{'done'}->{'checkdest'} );
    trace("checkdest($host, $port) {");

    checksni( $host, $port );

    $checks{'reversehost'}->{val} = $host . " <> " . $cfg{'rhost'} if ( $cfg{'rhost'} ne $host );
    $checks{'reversehost'}->{val} = $text{'na_dns'}                if ( not _is_cfg_use('dns') );

    $ssl = $data{'session_protocol'}->{val}( $host, $port );
    $ssl =~ s/[ ._-]//g;

    $key   = 'session_ticket';
    $value = $data{$key}->{val}( $host, $port );
    if ( defined $OData::data0{$key}->{val} ) {
        $checks{'session_random'}->{val} = $value if ( $value eq $OData::data0{$key}->{val} );
    }
    else {
        $checks{'session_random'}->{val} = $text{'na'};
    }

    checkprot( $host, $port );

    check_dh( $host, $port );

    $checks{'ccs'}->{val}   = "<<NOT YET IMPLEMENTED>>";
    $key                    = 'compression';
    $value                  = $data{$key}->{val}($host);
    $checks{$key}->{val}    = ( $value =~ m/$cfg{'regex'}->{'nocompression'}/ ) ? "" : $value;
    $checks{'crime'}->{val} = _is_vulnerable( $data{'next_protocols'}->{val}($host), $value, 'CRIME' );
    foreach my $key (qw(resumption renegotiation)) {
        next if ( $checks{$key}->{val} !~ m/$text{'undef'}/ );
        $value = $data{$key}->{val}($host);
        $checks{$key}->{val} = ( $value eq "" ) ? " " : "";
    }
    $value                          = $data{'renegotiation'}->{val}($host);
    $checks{'renegotiation'}->{val} = $value if ( $value =~ m/ IS NOT /i );
    $value                          = $data{'resumption'}->{val}($host);
    $checks{'resumption'}->{val}    = $value if ( $value !~ m/^Reused/ );

    foreach my $key (qw(krb5 psk_hint psk_identity master_secret srp session_ticket session_lifetime)) {
        next if ( $checks{$key}->{val} !~ m/$text{'undef'}/ );
        $value               = $data{$key}->{val}($host);
        $checks{$key}->{val} = ( $value eq "" ) ? " " : "";
        $checks{$key}->{val} = "None" if ( $value =~ m/^\s*None\s*$/i );
    }

    my $currenttime = time();
    $key                 = 'session_starttime';
    $value               = $data{$key}->{val}($host);
    $checks{$key}->{val} = "$value < $currenttime" if ( $value < ( $currenttime - 5 ) );
    $checks{$key}->{val} = "$value > $currenttime" if ( $value > ( $currenttime + 5 ) );

    foreach my $key (qw(heartbeat)) {
        next if ( $checks{$key}->{val} !~ m/$text{'undef'}/ );
        $checks{$key}->{val} = $data{$key}->{val}($host);
        $checks{$key}->{val} = "" if ( $checks{$key}->{val} =~ m/^\s*$/ );
    }
    $value = $data{'ocsp_response'}->{val}($host);
    $checks{'ocsp_stapling'}->{val} = ( $value =~ /.*no\s*response.*/i ) ? $value : "";
    trace("checkdest() }");
    return;
}

sub checkhttp {
    my ( $host, $port ) = @_;
    my $key = "";
    $cfg{'done'}->{'checkhttp'}++;
    return if ( 1 < $cfg{'done'}->{'checkhttp'} );
    trace("checkhttp($host, $port) {");

    my $notxt         = " ";
    my $https_body    = $data{'https_body'}->{val}($host)     || "";
    my $http_sts      = $data{'http_sts'}->{val}($host)       || "";
    my $http_location = $data{'http_location'}->{val}($host)  || "";
    my $hsts_equiv    = $data{'hsts_httpequiv'}->{val}($host) || "";
    my $hsts_maxage   = $data{'hsts_maxage'}->{val}($host);
    $hsts_maxage = -1 if ( $hsts_maxage =~ m/^\s*$/ );
    my $hsts_fqdn = $http_location;
    $hsts_fqdn =~ s|^(?:https:)?//([^/]*)|$1|i;
    $hsts_fqdn =~ s|/.*$||;

    if ( $https_body =~ /^<</ ) {
        OCfg::warn("641: HTTPS response failed, some information and checks are missing");
        OCfg::hint("641: consider using '--proto-alpn=,' also") if ( $https_body =~ /bad client magic byte string/ );
    }

    $checks{'breach'}->{val}     = _is_vuln_breach( $host, $port );
    $checks{'hsts_is301'}->{val} = $data{'http_status'}->{val}($host) if ( $data{'http_status'}->{val}($host) !~ /301/ );
    $checks{'hsts_is30x'}->{val} = $data{'http_status'}->{val}($host) if ( $data{'http_status'}->{val}($host) =~ /30[0235678]/ );

    $checks{'http_https'}->{val}    = ( $http_location !~ m/^\s*https:/ ) ? $http_location : "";
    $checks{'http_https'}->{val}    = $notxt if ( $http_location =~ m/^\s*$/ );
    $checks{'hsts_redirect'}->{val} = $http_sts;
    if ( $data{'https_sts'}->{val}($host) ne "" ) {
        my $fqdn = $hsts_fqdn;
        $checks{'hsts_location'}->{val} = $data{'https_location'}->{val}($host);
        $checks{'hsts_refresh'}->{val}  = $data{'https_refresh'}->{val}($host);
        $checks{'hsts_ip'}->{val}       = ( $host =~ m/\d+\.\d+\.\d+\.\d+/ ) ? $host : "";
        $checks{'hsts_fqdn'}->{val}     = $hsts_fqdn if ( $http_location !~ m|^https://$host|i );
        $checks{'hsts_samehost'}->{val} = $hsts_fqdn if ( $fqdn ne $host );
        $checks{'hsts_sts'}->{val}      = ( $data{'https_sts'}->{val}($host) ne "" )                                    ? "" : $notxt;
        $checks{'sts_subdom'}->{val}    = ( $data{'hsts_subdom'}->{val}($host) ne "" )                                  ? "" : $notxt;
        $checks{'sts_preload'}->{val}   = ( $data{'hsts_preload'}->{val}($host) ne "" )                                 ? "" : $notxt;
        $checks{'sts_maxage'}->{val}    = ( ( $hsts_maxage < $checks{'sts_maxage1m'}->{val} ) or ( $hsts_maxage > 1 ) ) ? "" : $hsts_maxage;
        $checks{'sts_maxage'}->{val} .= ( $checks{'sts_maxage'}->{val} eq "" ) ? "" : " = " . int( $hsts_maxage / $checks{'sts_maxage1d'}->{val} ) . " days";
        $checks{'sts_maxagexy'}->{val}   = ( $hsts_maxage > $checks{'sts_maxagexy'}->{val} ) ? ""  : "< $checks{'sts_maxagexy'}->{val}";
        $checks{'sts_maxage18'}->{val}   = ( $hsts_maxage > $checks{'sts_maxage18'}->{val} ) ? ""  : "< $checks{'sts_maxage18'}->{val}";
        $checks{'sts_maxage0d'}->{val}   = ( $hsts_maxage == 0 )                             ? "0" : "";
        $checks{'hsts_httpequiv'}->{val} = $hsts_equiv;

        checkdates( $host, $port );
    }
    else {
        foreach my $key (qw(sts_maxage00 sts_maxage0d sts_maxagexy sts_maxage18 sts_maxage1d sts_maxage1m sts_maxage1y )) {
            $checks{$key}->{val} = $text{'na_STS'};
        }
    }
    $checks{'hsts_fqdn'}->{val} = $text{'na'} if ( $http_location eq "" );
    $checks{'https_pins'}->{val} = $notxt if ( $data{'https_pins'}->{val}($host) eq "" );

    $notxt = $text{'na_STS'};
    $notxt = $text{'na_http'} if ( not _is_cfg_use('http') );
    foreach my $key (qw(sts_maxage1y sts_maxage1m sts_maxage1d)) {
        if ( $data{'https_sts'}->{val}($host) ne "" ) {
            $checks{'sts_maxage'}->{score} = $checks{$key}->{score} if ( $hsts_maxage < $checks{$key}->{val} );
            $checks{$key}->{val} = ( $hsts_maxage < $checks{$key}->{val} ) ? "" : "> $checks{$key}->{val}";
        }
        else {
            $checks{$key}->{val}   = $notxt;
            $checks{$key}->{score} = 0;
        }
    }
    trace("checkhttp() }");
    return;
}

sub _get_sstp_https {
    my ( $host, $port ) = @_;
    trace("_get_sstp_https($host, $port) ..,");
    my $ulonglong_max = '18446744073709551615';
    my $url           = '/sra_{BA195980-CD49-458b-9E23-C84EE0ADCD75}/';
    my $length        = "";
    my $server        = "";
    my ( $status, %headers );
    my $request = << "EoREQ";
SSTP_DUPLEX_POST $url HTTP/1.1\r
SSTPCORRELATIONID:{deadbeef-cafe-affe-caba-0000000000}\r
Content-Length:   $ulonglong_max\r
Connection:       close\r
Host:             $host\r
User-Agent:       $cfg{'use'}->{'user_agent'}\r
\r
EoREQ

    $Net::SSLeay::slowly = 1;
    my $dum      = $Net::SSLeay::slowly;
    my $response = Net::SSLeay::sslcat( $host, $port, $request );
    trace2("_get_sstp_https: response {\n$response#}");

    $response =~ s#HTTP/1.. #STATUS: #;
    $response =~ s#(?:\r\n\r\n|\n\n|\r\r).*$##ms;
    trace2("_get_sstp_https: response= #{\n$response\n#}");
    return "<<empty response -1>>" if ( $response =~ m/^\s*-1/ );
    return "<<empty response  1>>" if ( $response =~ m/^\s*1\s*$/ );
    %headers = map { split( /:/, $_, 2 ) } split( /[\r\n]+/, $response );
    trace2( "_get_sstp_https: headers= " . keys %headers );
    foreach my $key ( keys %headers ) {
        trace2("_get_sstp_https: headers: $key=$headers{$key}");
    }
    return '401' if ( $headers{'STATUS'} =~ m#^\s*401*# );
    return '400' if ( $headers{'STATUS'} =~ m#^\s*400*# );

    if ( $headers{'STATUS'} !~ m#^\s*(?:[1234][0-9][0-9]|500)\s*$# ) {
        return "<<connection to '$url' failed>>";
    }
    if ( $headers{'STATUS'} =~ m#^\s*200\s*$# ) {
        $server = $headers{'Server'};
        $length = $headers{'Content-Length'};
        return _get_text( 'invalid', "Content-Length: $length" ) if ( $length != $ulonglong_max );
        return _get_text( 'invalid', "Server: $server" )         if ( $server !~ /Microsoft-HTTPAPI/ );
    }
    else {
        return "<<unexpected response: $headers{'STATUS'}>>";
    }
    return '';
}

sub checksstp {
    my ( $host, $port ) = @_;
    $cfg{'done'}->{'checksstp'}++;
    return if ( 1 < $cfg{'done'}->{'checksstp'} );
    return if not defined $host;
    my $value = _get_sstp_https( $host, $port );
    $checks{'sstp'}->{val} = ( 0 < length($value) ) ? "" : " ";
    trace("checksstp: $value") if length($value);
    return;
}

sub checkssl {
    my ( $host, $port ) = @_;
    my $ciphers = shift;
    $cfg{'done'}->{'checkssl'}++;
    return if ( 1 < $cfg{'done'}->{'checkssl'} );
    trace("checkssl($host, $port) {");

    $cfg{'no_cert_txt'} = $text{'na_cert'} if ( $cfg{'no_cert_txt'} eq "" );
    if ( _is_cfg_use('cert') ) {
        checkcert( $host, $port );
        checkdates( $host, $port );
        checkdv( $host, $port );
        checkev( $host, $port );
        check02102( $host, $port );
        check03116( $host, $port );
        check7525( $host, $port );
        check6125( $host, $port );
        check2818( $host, $port );
        checksni( $host, $port );
        checksizes( $host, $port );
    }
    else {
        $cfg{'done'}->{'checksni'}++;
        $cfg{'done'}->{'checkdates'}++;
        $cfg{'done'}->{'checksizes'}++;
        $cfg{'done'}->{'check02102'}++;
        $cfg{'done'}->{'check03116'}++;
        $cfg{'done'}->{'check7525'}++;
        $cfg{'done'}->{'check6125'}++;
        $cfg{'done'}->{'check2818'}++;
        $cfg{'done'}->{'checkdv'}++;
        $cfg{'done'}->{'checkev'}++;

        foreach my $key ( sort( keys %checks ) ) {
            $checks{$key}->{val} = $cfg{'no_cert_txt'} if ( _is_member( $key, \@{ $cfg{'check_cert'} } ) );
        }
        $checks{'hostname'}->{val}       = $cfg{'no_cert_txt'};
        $checks{'tr_02102+'}->{val}      = $cfg{'no_cert_txt'};
        $checks{'tr_02102-'}->{val}      = $cfg{'no_cert_txt'};
        $checks{'tr_03116+'}->{val}      = $cfg{'no_cert_txt'};
        $checks{'tr_03116-'}->{val}      = $cfg{'no_cert_txt'};
        $checks{'rfc_6125_names'}->{val} = $cfg{'no_cert_txt'};
        $checks{'rfc_2818_names'}->{val} = $cfg{'no_cert_txt'};
    }

    if ( _is_cfg_use('http') ) {
        checkhttp( $host, $port );
    }
    else {
        $cfg{'done'}->{'checkhttp'}++;
        foreach my $key ( sort( keys %checks ) ) {
            $checks{$key}->{val} = $text{'na_http'} if ( _is_member( $key, \@{ $cfg{'cmd-http'} } ) );
        }
    }
    checkdest( $host, $port );

    foreach my $key (qw(verify_hostname verify_altname verify dates fingerprint)) {
    }

    trace("checkssl() }");
    return;
}

sub check_exitcode {
    trace("check_exitcode() {");
    my $exitcode = 0;
    my $cnt_prot = 0;

    my $cnt_ciph    = 0;
    my $cnt_ciphs   = 0;
    my $cnt_pfs     = 0;
    my $cnt_nopfs   = 0;
    my $old_verbose = $cfg{'verbose'};
    $cfg{'verbose'} += $cfg{'out'}->{'exitcode'};
    if ( _is_cfg_out('exitcode_checks') ) {
        $exitcode = $checks{'cnt_checks_no'}->{val};
        $exitcode -= $checks{'cnt_checks_noo'}->{val};
    }
    my $__tableline = "-----------------+---+---+---+---+-----+------------";
    my $__exitline  = "---------------------------------------------------- exitcode";
    _vprint("$__exitline {");
    _vprint( sprintf( "%-12s\t%3s %3s %3s %3s %7s %s", qw(protocol H M L W no-PFS insecure) ) );
    _vprint($__tableline);
    foreach my $ssl ( @{ $cfg{'versions'} } ) {
        next if ( 0 == $cfg{$ssl} );
        $cnt_prot++ if ( 0 < $cfg{$ssl} );
        $cnt_pfs = $prot{$ssl}->{'cnt'} - $#{ $prot{$ssl}->{'ciphers_pfs'} };
        $cnt_pfs = 0 if ( 0 >= $prot{$ssl}->{'cnt'} );
        $exitcode += $cnt_pfs if ( _is_cfg_out('exitcode_pfs') );
        $cnt_ciph = 0;
        $cnt_ciph += $prot{$ssl}->{'MEDIUM'} if ( _is_cfg_out('exitcode_medium') );
        $cnt_ciph += $prot{$ssl}->{'WEAK'}   if ( _is_cfg_out('exitcode_weak') );
        $cnt_ciph += $prot{$ssl}->{'LOW'}    if ( _is_cfg_out('exitcode_low') );
        $exitcode += $cnt_ciph;
        _vprint(
            sprintf(
                "%-12s\t%3s %3s %3s %3s %3s\t%s",
                $ssl,
                $prot{$ssl}->{'HIGH'},
                $prot{$ssl}->{'MEDIUM'},
                $prot{$ssl}->{'LOW'},
                $prot{$ssl}->{'WEAK'},
                $cnt_pfs, $cnt_ciph,
            )
        );
        $cnt_ciphs += $cnt_ciph;
        $cnt_nopfs += $cnt_pfs;
    }
    my $ign_ciphs  = ( _is_cfg_out('exitcode_low') or _is_cfg_out('exitcode_weak') or _is_cfg_out('exitcode_medium') ) ? "" : " (count ignored)";
    my $ign_checks = ( _is_cfg_out('exitcode_checks') )                                                                ? "" : " (count ignored)";
    my $ign_prot   = ( _is_cfg_out('exitcode_prot') )                                                                  ? "" : " (count ignored)";
    my $ign_pfs    = ( _is_cfg_out('exitcode_pfs') )                                                                   ? "" : " (count ignored)";
    _vprint($__tableline);
    $cnt_prot--            if ( 0 < $cfg{'TLSv12'} );
    $cnt_prot--            if ( 0 < $cfg{'TLSv13'} );
    $exitcode += $cnt_prot if ( _is_cfg_out('exitcode_prot') );
    $checks{'cnt_exitcode'}->{val} = $exitcode;
    _vprint( sprintf( "%s\t%5s%s", "Total number of insecure protocols",  $cnt_prot,                              $ign_prot ) );
    _vprint( sprintf( "%s\t%5s%s", "Total number of insecure ciphers",    $cnt_ciphs,                             $ign_ciphs ) );
    _vprint( sprintf( "%s\t%5s%s", "Total number of ciphers without PFS", $cnt_nopfs,                             $ign_pfs ) );
    _vprint( sprintf( "%s\t%5s%s", $checks{'cnt_checks_no'}->{txt},       $checks{'cnt_checks_no'}->{val},        $ign_checks ) );
    _vprint( sprintf( "%s %3s%s",  $checks{'cnt_checks_noo'}->{txt},      "-" . $checks{'cnt_checks_noo'}->{val}, $ign_checks ) );
    _vprint( sprintf( "%s\t%5s",   $checks{'cnt_exitcode'}->{txt},        $checks{'cnt_exitcode'}->{val} ) );
    _vprint("$__exitline }");
    $cfg{'verbose'} = $old_verbose;
    trace("check_exitcode()\t= $checks{'cnt_exitcode'}->{val} }");
    return $checks{'cnt_exitcode'}->{val};
}

sub scoring {
    my ( $host, $port ) = @_;
    my $value;

    my $http_location = $data{'http_location'}->{val}($host) || "";
    $scores{'check_http'}->{val}  = 100;
    $checks{'hsts_fqdn'}->{score} = 0 if ( $http_location eq "" );

    foreach my $key ( sort( keys %checks ) ) {
        next if ( $key =~ m/^(ip|reversehost)/ );
        next if ( $key =~ m/^(sts_)/ );
        next if ( $key =~ m/^(closure|fallback|cps|krb5|lzo|open_pgp|order|https_pins|psk_|rootcert|srp|zlib)/ );

        next if ( $key =~ m/^TLSv1[123]/ );
        $value = $checks{$key}->{val};
        $scores{'check_size'}->{val} -= _getscore( $key, $value, \%checks ) if ( $checks{$key}->{typ} eq "sizes" );
        $scores{'check_http'}->{val} -= _getscore( $key, $value, \%checks ) if ( $checks{$key}->{typ} eq "https" );
        $scores{'check_cert'}->{val} -= _getscore( $key, $value, \%checks ) if ( $checks{$key}->{typ} eq "certificate" );
        $scores{'check_conn'}->{val} -= _getscore( $key, $value, \%checks ) if ( $checks{$key}->{typ} eq "connection" );
        $scores{'check_dest'}->{val} -= _getscore( $key, $value, \%checks ) if ( $checks{$key}->{typ} eq "destination" );
    }
    return;
}

sub _cleanup_data {
    my ( $key, $value ) = @_;
    if ( $key eq "https_status" ) {
        _vprint("  removing non-printable characters from $key: $value");
        $value =~ s/[^[:print:]]+//g;
    }
    if ( $key =~ m/X509$/ ) {
        $value =~ s#/([^=]*)#\n   ($1)#g;
        $value =~ s#=#\t#g;
    }
    return $value;
}

sub _printdump {
    my ( $label, $value ) = @_;
    $label =~ s/\n//g;
    $label = sprintf( "%s %s", $label, '_' x ( 75 - length($label) ) );
    $value = "" if not defined $value;
    printf( "#{ %s\n\t%s\n#}\n", $label, $value );
    return;
}

sub printdump {
    my ( $legacy, $host, $port ) = @_;
    print '######################################################################### %data';
    foreach my $key ( sort( keys %data ) ) {
        next if ( _is_cfg_intern($key) > 0 );
        _printdump( $data{$key}->{txt}, $data{$key}->{val}($host) );
    }
    print '######################################################################## %check';
    foreach my $key ( sort( keys %checks ) ) { _printdump( $checks{$key}->{txt}, $checks{$key}->{val} ); }
    return;
}

sub print_ruler { printf( "=%s+%s\n", '-' x 38, '-' x 35 ) if ( _is_cfg_out('header') ); return; }

sub print_header {
    my ( $txt, $desc, $rest, $header ) = @_;
    return if ( 0 >= $header );
    printf("$txt\n");
    return if ( $desc =~ m/^ *$/ );
    printf( "= %-37s %s\n", $text{'desc'}, $desc );
    print_ruler();
    return;
}

sub print_footer {
    my $legacy = shift;
    if ( $legacy eq 'sslyze' ) { print "\n\n SCAN COMPLETED IN ...\n\n"; }
    return;
}

sub print_title {
    my ( $legacy, $ssl, $host, $port, $header ) = @_;
    if ( $legacy eq 'sslyze' ) {
        my $txt = " SCAN RESULTS FOR " . $host . " - " . $cfg{'IP'};
        print "$txt";
        print " " . "-" x length($txt);
    }
    if ( $legacy eq 'sslaudit' )  { }
    if ( $legacy eq 'sslcipher' ) { print "Testing $host ..."; }
    if ( $legacy eq 'ssldiagnos' ) {
        print
          "----------------TEST INFO---------------------------\n",
          "[*] Target IP: $cfg{'IP'}\n",
          "[*] Target Hostname: $host\n",
          "[*] Target port: $port\n",
          "----------------------------------------------------\n";
    }
    if ( $legacy eq 'sslscan' )       { $host =~ s/;/ on port /; print "Testing SSL server $host\n"; }
    if ( $legacy eq 'ssltest' )       { print "Checking for Supported $ssl Ciphers on $host..."; }
    if ( $legacy eq 'ssltest-g' )     { print "Checking for Supported $ssl Ciphers on $host..."; }
    if ( $legacy eq 'testsslserver' ) { print "Supported cipher suites (ORDER IS NOT SIGNIFICANT):\n  " . $ssl; }
    if ( $legacy eq 'thcsslcheck' )   { print "\n[*] now testing $ssl\n" . "-" x 76; }
    if ( $legacy =~ /(compact|full|owasp|quick|simple)/ ) {
        my $txt = _get_text( 'out_ciphers', $ssl );
        print_header( $txt, "", "", 1 );
    }
    return;
}

sub print_line {
    my ( $legacy, $host, $port, $key, $text, $value ) = @_;
    $text  = $OText::STR{NOTXT} if not defined $text;
    $value = $OText::STR{UNDEF} if not defined $value;
    $value = Encode::decode( "UTF-8", $value );
    my $label = "";
    $label = sprintf( "%s:%s%s", $host, $port, $text{'separator'} ) if ( _is_cfg_out('hostname') );
    if ( $legacy eq '_cipher' ) {
        printf( "%s", $label ) if ( _is_cfg_out('hostname') );
        printf( "#[%s]%s", $key, $text{'separator'} ) if ( _is_cfg_out('traceKEY') );
        return;
    }
    $label .= sprintf( "#[%-18s", $key . ']' . $text{'separator'} ) if ( _is_cfg_out('traceKEY') );
    if ( $legacy =~ m/(compact|full|quick)/ ) {
        $label .= sprintf( "%s", $text . $text{'separator'} );
    }
    else {
        if ( $cfg{'label'} eq 'key' ) {
            $label .= sprintf( "[%s]", $key );
        }
        else {
            $label .= sprintf( "%-36s", $text . $text{'separator'} );
        }
    }
    my $sep = "\t";
    $sep = "\n\t" if ( $legacy eq 'full' );
    $sep = ""     if ( $legacy =~ m/(compact|quick)/ );
    printf( "%s%s%s\n", $label, $sep, $value );
    return;
}

sub print_data {
    my ( $legacy, $host, $port, $key ) = @_;
    if ( _is_hashkey( $key, \%data ) < 1 ) {
        OCfg::warn("801: unknown label '$key'; output ignored");
        return;
    }
    my $label = ( $data{$key}->{txt} || "" );
    my $value = $data{$key}->{val}( $host, $port ) || "";
    $value = _cleanup_data( $key, $value );
    if ( $key =~ m/X509$/ ) {
        $key =~ s/X509$//;
        print_line( $legacy, $host, $port, $key, $data{$key}->{txt}, $value );
        return;
    }
    if ( ( 1 == _is_cfg_hexdata($key) ) && ( $value !~ m/^\s*$/ ) ) {
        $value =~ s/([Mm]odulus):/$1=/;
        my ( $k, $v ) = split( /=/, $value );
        if ( defined $v ) {
            $k .= "=";
        }
        else {
            $v = $k;
            $k = "";
        }
        if ( $value =~ m/^(Signature Value)(.*)$/i ) {
            $k = "$1 ";
            $v = $2;
        }
        if ( $cfg{'format'} eq "hex" ) {
            $v =~ s#(..)#$1:#g;
            $v =~ s#:$##;
        }
        if ( $cfg{'format'} eq "esc" ) {
            $v =~ s#(..)#\\x$1#g;
        }
        if ( $cfg{'format'} eq "0x" ) {
            $v =~ s#(..)#0x$1 #g;
            $v =~ s# $##;
        }
        $value = $k . $v;
    }
    $value = "\n" . $value if ( _is_member( $key, \@{ $cfg{'need-NL'} } ) );
    if ( $legacy eq 'compact' ) {
        $value =~ s#:\n\s+#:#g;
        $value =~ s#\n\s+# #g;
        $value =~ s#[\n\r]#; #g;
        $label =~ s#[\n]##g;
    }
    if ( $legacy eq 'full' ) {
        if ( $label =~ m/(^altname)/ ) { $value =~ s#^ ##;     $value =~ s# #\n\t#g; }
        if ( $label =~ m/(subject)/ )  { $value =~ s#/#\n\t#g; $value =~ s#^\n\t##m; }
        if ( $label =~ m/(issuer)/ )   { $value =~ s#/#\n\t#g; $value =~ s#^\n\t##m; }
        if ( $label =~ m/(serial|modulus|sigkey_value)/ ) {
            $value =~ s#(..)#$1:#g;
            $value =~ s#:$##;
        }
        if ( $label =~ m/((?:pubkey|sigkey)_algorithm|signame)/ ) {
            $value =~ s#(with)# $1 #ig;
            $value =~ s#(encryption)# $1 #ig;
        }
    }
    print_line( $legacy, $host, $port, $key, $label, $value );
    OCfg::printhint($key) if ( _is_cfg_out('hint_info') );
    return;
}

sub print_check {
    my ( $legacy, $host, $port, $key, $value ) = @_;
    $value = $checks{$key}->{val} if not defined $value;
    my $label = "";
    $label = $checks{$key}->{txt} if ( $cfg{'label'} ne 'key' );
    print_line( $legacy, $host, $port, $key, $label, $value );
    OCfg::printhint($key) if ( _is_cfg_out('hint_check') );
    return;
}

sub print_size {
    my ( $legacy, $host, $port, $key ) = @_;
    my $value = "";
    $value = " bytes" if ( $key =~ /^(len)/ );
    $value = " bits"  if ( $key =~ /^len_(modulus|publickey|sigdump)/ );
    print_check( $legacy, $host, $port, $key, $checks{$key}->{val} . $value );
    return;
}

sub print_cipherruler_dh { printf( "=   %s+%s\n", "-" x 35, "-" x 25 ) if ( _is_cfg_out('header') ); return; }
sub print_cipherruler { printf( "=   %s+%s+%s\n", "-" x 35, "-" x 7, "-" x 8 ) if ( _is_cfg_out('header') ); return; }

sub print_cipherhead {
    my $legacy = shift;
    return if ( not _is_cfg_out('header') );
    if ( $legacy eq 'sslscan' ) { print "\n  Supported Server Cipher(s):"; }
    if ( $legacy eq 'ssltest' ) { printf( "   %s, %s (%s)\n", 'Cipher', 'Enc, bits, Auth, MAC, Keyx', 'supported' ); }
    if ( $legacy eq 'ssltest-g' ) { printf("Status(Compliant,Non-compliant,Disabled);Hostname:Port;SSL-Protocol;Cipher-Name;Cipher-Description\n"); }
    if ( $legacy eq 'simple' ) {
        printf( "=   %-34s%s\t%s\n", $text{'cipher'}, $text{'support'}, $text{'security'} );
        print_cipherruler();
    }
    if ( $legacy eq 'owasp' ) {
        printf( "=   %-34s\t%s\n", $text{'cipher'}, $text{'security'} );
        print_cipherruler();
    }
    if ( $legacy eq 'cipher_dh' ) {
        printf( "=   %-34s\t%s\n", $text{'cipher'}, $text{'dh_param'} );
        print_cipherruler_dh();
    }
    if ( $legacy eq 'full' ) {
        printf("= host:port\tsupport\tprot.\tsec\tkeyx\tauth\tenc      bits\tmac\tcipher key\tcipher name\tcomment\n");
    }
    return;
}

sub print_cipherline {
    my ( $legacy, $ssl, $host, $port, $key, $support ) = @_;
    my $cipher = Ciphers::get_name($key);
    my $bits   = Ciphers::get_bits($key);
    my $sec    = Ciphers::get_sec($key);
    $sec = OCfg::get_cipher_owasp($cipher) if ( 'owasp' eq $legacy );
    $sec = "-"                             if ( ( 'no' eq $support ) and ( 'owasp' eq $legacy ) );
    my $yesno = $text{'legacy'}->{$legacy}->{$support};
    if ( $legacy =~ m/compact|full|owasp|quick|simple|key/ ) {
        my $k = sprintf( "%s", Ciphers::get_key($cipher) );
        print_line( '_cipher', $host, $port, $key, $cipher, "" );
        if ( 'key' eq $cfg{'label'} ) {
            $k = "[$key]\t";
        }
        else {
            $k = "    ";
        }
        $cipher = "$key $OText::STR{UNDEF}" if ( $cipher =~ m/^\s*$/ );
        $sec    = $OText::STR{UNDEF}        if ( $sec    =~ m/^\s*$/ );
        printf( "%s%-28s\t%s\n", $k, $cipher, $sec )              if ( $legacy eq 'owasp' );
        printf( "%s%-28s\t(%s)\t%s\n", $k, $cipher, $bits, $sec ) if ( $legacy eq 'quick' );
        printf( "%s%-28s\t%s\t%s\n", $k, $cipher, $yesno, $sec )  if ( $legacy eq 'simple' );
        printf( "%s %s %s\n", $cipher, $yesno, $sec )             if ( $legacy eq 'compact' );
        printf(
            "%s%s:%s\t%s\t%s\t%s\t%s\t%s\t%s%7s\t%s\t%s\t%s\t%s\n",
            $k, $host, $port, $yesno, $ssl, $sec, Ciphers::get_keyx($key), Ciphers::get_auth($key), Ciphers::get_enc($key), $bits, Ciphers::get_mac($key), $key,
            $cipher, Ciphers::get_const($key),
        ) if ( $legacy eq 'full' );
        return;
    }
    if ( $legacy eq 'sslyze' ) {
        if ( $support eq 'yes' ) {
            $support = sprintf( "%4s bits", $bits ) if ( $support eq 'yes' );
        }
        else {
            $support = $yesno;
        }
        printf( "\t%-24s\t%s\n", $cipher, $support );
    }
    if ( $legacy eq 'sslaudit' ) {
        printf( "%s - %s - %s\n", $ssl, $cipher, $yesno );
    }
    if ( $legacy eq 'sslcipher' ) {
        $sec = 'INTERMEDIATE:' if ( $sec =~ /LOW/i );
        $sec = 'STRONG'        if ( $sec =~ /high/i );
        $sec = 'WEAK'          if ( $sec =~ /weak/i );
        printf( "   %s:%s - %s - %s %s bits\n", $ssl, $cipher, $yesno, $sec, $bits );
    }
    if ( $legacy eq 'ssldiagnos' ) {
        $sec = ( $sec =~ /high/i ) ? 'STRONG' : 'WEAK';
        printf( "[+] Testing %s: %s, %s (%s bits) ... %s\n", $sec, $ssl, $cipher, $bits, $yesno );
    }
    if ( $legacy eq 'sslscan' ) {
        $bits = sprintf( "%3s bits", $bits );
        printf( "Accepted  %s    %s  %s\n", $ssl, $bits, $cipher );
    }
    if ( $legacy eq 'thcsslcheck' ) {
        printf( "%30s - %3s Bits - %11s\n", $cipher, $bits, $yesno );
    }
    if ( $legacy eq 'ssltest' ) {
        return if ( "" eq $cipher );

        printf( "   %s, %s %s bits, %s Auth, %s MAC, %s Kx (%s)\n",
            $cipher, Ciphers::get_enc($key), $bits, Ciphers::get_auth($key), Ciphers::get_mac($key), Ciphers::get_keyx($key), $yesno );
    }
    if ( $legacy eq 'ssltest-g' ) {
        return if ( "" eq $cipher );
        printf(
            "%s;%s;%s;%s;%s %s bits, %s Auth, %s MAC, %s Kx\n",
            'C',   $host . ":" . $port,     $ssl, $cipher, Ciphers::get_enc($key),
            $bits, Ciphers::get_auth($key), Ciphers::get_mac($key), Ciphers::get_keyx($key),
        );
    }
    if ( $legacy eq 'testsslserver' ) { printf( "    %s\n", $cipher ); }
    return;
}

sub print_cipherpreferred {
    my ( $legacy, $ssl, $host, $port ) = @_;
    trace("print_cipherpreferred($legacy, $ssl, $host, $port) {");
    my $yesno = 'yes';
    if ( $legacy eq 'sslyze' )   { print "\n\n      Preferred Cipher Suites:"; }
    if ( $legacy eq 'sslaudit' ) { }
    if ( $legacy eq 'sslscan' )  { print "\n  Preferred Server Cipher(s):"; $yesno = ""; }
    if ( not _is_cfg_ciphermode('intern') ) {
        my $key = Ciphers::get_key( $data{'cipher_selected'}->{val}($host) );
        print_cipherline( $legacy, $ssl, $host, $port, $key, $yesno );
    }
    trace("print_cipherpreferred() }");
    return;
}

sub print_ciphertotals {
    my ( $legacy, $ssl, $host, $port ) = @_;
    trace("print_ciphertotals($legacy, $ssl, $host, $port) {");
    if ( $legacy eq 'ssldiagnos' ) {
        print "\n-= SUMMARY =-\n";
        printf( "Weak:         %s\n", $prot{$ssl}->{'WEAK'} );
        printf( "Intermediate: %s\n", $prot{$ssl}->{'MEDIUM'} );
        printf( "Strong:       %s\n", $prot{$ssl}->{'HIGH'} );
    }
    if ( $legacy =~ /(compact|full|owasp|quick|simple)/ ) {
        print_header( _get_text( 'out_summary', $ssl ), "", $cfg{'out'}->{'header'} );
        foreach my $key (qw(LOW WEAK MEDIUM HIGH -?-)) {
            print_line( $legacy, $host, $port, "$ssl-$key", $OCfg::prot_txt{$key}, $prot{$ssl}->{$key} );
        }
    }
    trace("print_ciphertotals() }");
    return;
}

sub printciphers_dh {
    my ( $legacy, $host, $port, $result ) = @_;
    trace("printciphers_dh($legacy, $host, $port, ...) {");
    foreach my $ssl ( @{ $cfg{'version'} } ) {
        print_title( $legacy, $ssl, $host, $port, $cfg{'out'}->{'header'} );
        print_cipherhead('cipher_dh');
        if ( exists $result->{$ssl} ) {
            foreach my $c ( sort( keys %{ $result->{$ssl} } ) ) {
                print_line( $legacy, $host, $port, $c, Ciphers::get_name($c), ${ $result->{$ssl}{$c} }[1] );
            }
        }
        print_cipherruler_dh();
    }
    trace("printciphers_dh() }");
    return;
}

sub printciphers_dh_openssl {
    my ( $legacy, $host, $port ) = @_;
    trace("printciphers_dh_openssl($legacy, $host, $port) {");
    if ( $cmd{'version'} lt "1.0.2" ) {
        require SSLhello;

    }
    foreach my $ssl ( @{ $cfg{'version'} } ) {
        print_title( $legacy, $ssl, $host, $port, $cfg{'out'}->{'header'} );
        print_cipherhead('cipher_dh');
        foreach my $c ( @{ $cfg{'ciphers'} } ) {
            my ( $version, $supported, $dh ) = _useopenssl( $ssl, $host, $port, $c );
            next if ( $supported =~ /^\s*$/ );
            print_line( $legacy, $host, $port, $c, Ciphers::get_name($c), $dh );
        }

        print_cipherruler_dh();
    }
    trace("printciphers_dh_openssl() }");
    return;
}

sub printcipherpreferred {
    my ( $legacy, $host, $port ) = @_;
    trace("printcipherpreferred($legacy, $host, $port) {");
    if ( _is_cfg_out('header') ) {
        printf( "= prot.\t%-31s\t%s\n", "preferred cipher (strong first)", "preferred cipher (weak first)" );
        printf("=------+-------------------------------+-------------------------------\n");
    }
    foreach my $ssl ( @{ $cfg{'versions'} } ) {
        next if ( ( $cfg{$ssl} == 0 ) and ( $verbose <= 0 ) );
        next if ( $ssl =~ m/^SSLv2/ );
        my $key = $ssl . $text{'separator'};
        $key = sprintf( "[0x%x]", $prot{$ssl}->{hex} ) if ( $legacy eq 'key' );
        printf( "%-7s\t%-31s\t%s\n", $key, $prot{$ssl}->{'cipher_strong'}, $prot{$ssl}->{'cipher_weak'}, );
    }
    if ( _is_cfg_out('header') ) {
        printf("=------+-------------------------------+-------------------------------\n");
    }
    if ( not _is_cfg_ciphermode('intern') ) {
        print_data( $legacy, $host, $port, 'cipher_selected' );
    }
    trace("printcipherpreferred() }");
    return;
}

sub printprotocols {
    my ( $legacy, $host, $port ) = @_;
    trace("printprotocols($legacy, $host, $port) {");
    my @score = qw(A B C D);
    if ( _is_cfg_out('header') ) {
        printf("# amount of detected ciphers for:\n");
        if ( 'owasp' eq $legacy ) {
            @score = qw(A B C D);
            printf("#   A, B, C OWASP rating;  D=known broken;  tot=total enabled ciphers\n");
        }
        else {
            @score = qw(H M L W);
            printf("#   H=HIGH  M=MEDIUM  L=LOW  W=WEAK;  tot=total enabled ciphers\n");
        }
        printf("#   preferred=offered by server;   PFS=enabled cipher with PFS\n");
        printf( "%s\t%3s %3s %3s %3s %3s %3s %-31s %s\n", "=", @score, qw(PFS tot preferred-strong-cipher PFS-cipher) );
        printf( "=------%s%s\n", ( '+---' x 6 ), '+-------------------------------+---------------' );
    }
    foreach my $ssl ( @{ $cfg{'versions'} } ) {
        next if ( ( $cfg{$ssl} == 0 ) and ( $verbose <= 0 ) );
        my $cnt = scalar( @{ $prot{$ssl}->{'ciphers_pfs'} } );
        my $key = $ssl . $text{'separator'};
        $key = sprintf( "[0x%x]", $prot{$ssl}->{hex} ) if ( $legacy eq 'key' );
        my $cipher_strong = $prot{$ssl}->{'cipher_strong'};
        my $cipher_pfs    = $prot{$ssl}->{'cipher_pfs'};
        if ( $cfg{'trace'} <= 0 ) {
            $cipher_strong = "" if ( $OText::STR{UNDEF} eq $cipher_strong );
            $cipher_pfs    = "" if ( $OText::STR{UNDEF} eq $cipher_pfs );
        }
        if (    ( @{ $prot{$ssl}->{'ciphers_pfs'} } )
            and ( ${ $prot{$ssl}->{'ciphers_pfs'} }[0] =~ m/^\s*<</ ) )
        {
            $cipher_strong = ${ $prot{$ssl}->{'ciphers_pfs'} }[0];
            $cnt           = 0;
        }
        $cipher_strong = $text{'na_SSLv2'} if ( $ssl =~ m/^SSLv2/ );
        if ( $cfg{$ssl} == 0 ) {

            $cipher_strong = _get_text( 'disabled', "'--no-$ssl'" );
        }
        print_line( '_cipher', $host, $port, $ssl, $ssl, "" );
        if ( 'owasp' eq $legacy ) {
            printf(
                "%-7s\t%3s %3s %3s %3s %3s %3s %-31s %s\n",
                $key, $prot{$ssl}->{'OWASP_A'}, $prot{$ssl}->{'OWASP_B'}, $prot{$ssl}->{'OWASP_C'}, $prot{$ssl}->{'OWASP_D'},
                $cnt, $prot{$ssl}->{'cnt'},     $cipher_strong,           $cipher_pfs
            );
        }
        else {
            printf(
                "%-7s\t%3s %3s %3s %3s %3s %3s %-31s %s\n",
                $key, $prot{$ssl}->{'HIGH'}, $prot{$ssl}->{'MEDIUM'}, $prot{$ssl}->{'LOW'}, $prot{$ssl}->{'WEAK'},
                $cnt, $prot{$ssl}->{'cnt'},  $cipher_strong,          $cipher_pfs
            );
        }
    }
    if ( _is_cfg_out('header') ) {
        printf( "=------%s%s\n", ( '+---' x 6 ), '+-------------------------------+---------------' );
    }
    trace("printprotocols() }");
    return;
}

sub printciphersummary {
    my ( $legacy, $host, $port, $total ) = @_;
    trace("printciphersummary($legacy, $host, $port, $total) {");
    if ( $legacy =~ /(compact|full|owasp|quick|simple)/ ) {
        print_header( "\n" . _get_text( 'out_summary', "" ), "", "", $cfg{'out'}->{'header'} );
        print_check( $legacy, $host, $port, 'cnt_totals', $total );
        printprotocols( $legacy, $host, $port );
    }
    if ( _is_cfg_ciphermode('openssl|socket') ) {
        print_line( $legacy, $host, $port, 'cipher_selected', $data{'cipher_selected'}->{txt}, $prot{'cipher_selected'} );
    }
    if ( _is_cfg_out('hint_ciphers') ) {
        OCfg::hint("consider using '--cipheralpn=, --ciphernpn=,' also") if _is_cfg_verbose();
    }
    trace("printciphersummary() }");
    return;
}

sub printcipherlines {
    my ( $legacy, $ssl, $host, $port, $match, $results ) = @_;
    foreach my $key ( @{ $results->{$ssl}{'sorted'} } ) {
        my $yesno = $results->{$ssl}{$key}[0];
        next if ( $yesno !~ m/^(?:$match)$/ );
        print_cipherline( $legacy, $ssl, $host, $port, $key, $yesno );
    }
    return;
}

sub printciphers_openssl {
    my ( $legacy, $ssl, $host, $port, $printtitle, $results ) = @_;
    trace("printciphers_openssl($legacy, $ssl, $host, $port, $printtitle, ...) {");
    if ( ( $legacy ne "sslscan" ) or ( $printtitle <= 1 ) ) {
        my $header = $cfg{'out'}->{'header'};
        if ( _is_cfg_out('header') or ( scalar @{ $cfg{'version'} } ) > 1 ) {
            $header = 1;
        }
        print_title( $legacy, $ssl, $host, $port, $header );
    }
    my $yesno = "";
    if ( _is_cfg_out('disabled') == _is_cfg_out('enabled') ) {
        $yesno = "yes|no";
    }
    else {
        $yesno = "yes" if _is_cfg_out('enabled');
        $yesno = "no"  if _is_cfg_out('disabled');
    }
    print_cipherhead($legacy)                            if ( 0 == ( $legacy eq "sslscan" ) ? ($printtitle) : 0 );
    print_cipherpreferred( $legacy, $ssl, $host, $port ) if ( $legacy eq 'sslaudit' );
    my @sorted = Ciphers::sort_results( $results->{$ssl} );
    trace2("printciphers_openssl: sorted $#sorted : @sorted");
    $results->{$ssl}{'sorted'} = \@sorted;
    if ( $legacy ne 'sslyze' ) {
        printcipherlines( $legacy, $ssl, $host, $port, $yesno, $results );
        print_cipherruler() if ( $legacy =~ /(?:owasp|simple)/ );
    }
    else {
        print "\n  * $ssl Cipher Suites :";
        print_cipherpreferred( $legacy, $ssl, $host, $port );
        if ( $yesno =~ m/yes/ ) {
            print "\n      Accepted Cipher Suites:";
            printcipherlines( $legacy, $ssl, $host, $port, "yes", $results );
        }
        if ( $yesno =~ m/no/ ) {
            print "\n      Rejected Cipher Suites:";
            printcipherlines( $legacy, $ssl, $host, $port, "no", $results );
        }
    }
    print_footer($legacy);
    trace("printciphers_openssl() }");
    return;
}

sub printciphers_intern {
    my ( $legacy, $ssl, $host, $port, $printtitle, $results ) = @_;
    trace("printciphers_intern($legacy, $ssl, $host, $port, $printtitle, ...) {");
    print_cipherhead($legacy) if ( 0 == ( $legacy eq "sslscan" ) ? ($printtitle) : 0 );
    my $last_r = "";
    foreach my $key ( sort( keys %{ $results->{$ssl} } ) ) {
        next if ( $last_r eq $key );
        print_cipherline( $legacy, $ssl, $host, $port, $key, "yes" );
        $last_r = $key;
    }
    print_cipherruler() if ( $legacy =~ /(?:owasp|simple)/ );
    print_footer($legacy);
    trace("printciphers_intern() }");
    return;
}

sub printciphers {
    my ( $legacy, $host, $port, $results ) = @_;
    trace("printciphers($legacy, $host, $port, ...) {");
    my $_printtitle = 0;

    if ( _is_cfg_legacy('openssl') ) {
        OCfg::warn("864: invalid '--legacy=$legacy' option; reset to default 'simple'");
        $legacy = 'simple';
    }

    foreach my $ssl ( @{ $cfg{'version'} } ) {
        $_printtitle++;
        if ( _is_cfg_ciphermode('intern|dump') ) {
            print_title( $legacy, $ssl, $host, $port, $cfg{'out'}->{'header'} );
            goto END_SSL if 0 >= ( keys( %{ $results->{$ssl} } ) );
            if ( _is_cfg_do('cipher_intern') ) {
                printciphers_intern( $legacy, $ssl, $host, $port, $_printtitle, $results );
            }
            else {
                SSLhello::printCipherStringArray( 'compact', $host, $port, $ssl, $SSLhello::usesni, sort( keys( %{ $results->{$ssl} } ) ) );
            }
        }
        if ( _is_cfg_ciphermode('openssl|socket') ) {
            printciphers_openssl( $legacy, $ssl, $host, $port, $_printtitle, $results );
        }
      END_SSL:
        if ( _is_cfg_legacy('simple|openssl') ) {
            print_check( $legacy, $host, $port, 'cnt_ciphers', $results->{'_admin'}{$ssl}{'cnt_offered'} );
        }
    }

    if ( $legacy eq 'sslscan' ) {
        my $ssl = ${ $cfg{'version'} }[ $#{ $cfg{'version'} } ];
        print_cipherpreferred( $legacy, $ssl, $host, $port );
    }
    if ( $_printtitle > 0 ) {
        my $total = $checks{'cnt_totals'}->{val};
        printciphersummary( $legacy, $host, $port, $total );
    }
    trace("printciphers() }");
    return;
}

sub printdata {
    my ( $legacy, $host, $port ) = @_;
    trace("printdata($legacy, $host, $port) {");
    print_header( $text{'out_infos'}, $text{'desc_info'}, "", $cfg{'out'}->{'header'} );
    if ( _is_cfg_do('cipher_selected') ) {
        my $key = $data{'cipher_selected'}->{val}( $host, $port );
        print_line( $legacy, $host, $port, 'cipher_selected', $data{'cipher_selected'}->{txt}, "$key " . _get_cipher_sec($key) );
    }
    foreach my $key ( @{ $cfg{'do'} } ) {
        next if ( _is_member( $key, \@{ $cfg{'commands_notyet'} } ) );
        next if ( _is_member( $key, \@{ $cfg{'ignore-out'} } ) );
        next if ( not _is_hashkey( $key, \%data ) );
        next if ( $key eq 'cipher_selected' );
        if ( not _is_cfg_use('experimental') ) {
            next if ( _is_member( $key, \@{ $cfg{'commands_exp'} } ) );
        }
        if ( _is_cfg_do('info--v') ) {
            next if ( $key eq 'info--v' );
            next if ( $key =~ m/$cfg{'regex'}->{'commands_int'}/i );
        }
        else {
            next if ( _is_cfg_intern($key) );
        }
        trace( " (%data)   +" . $key );
        my $value = $data{$key}->{val}($host);
        if ( _is_member( $key, \@{ $cfg{'need-NL'} } ) ) {
            if ( _is_cfg_do('info') and not _is_cfg_verbose() ) {
                OCfg::hint("use '--v' to print multiline data of '+$key' for '+info'");
                next;
            }
        }
        if ( $cfg{'format'} eq "raw" ) {
            print $value;
        }
        else {
            print_data( $legacy, $host, $port, $key );
        }
    }
    trace("printdata() }");
    return;
}

sub printchecks {
    my ( $legacy, $host, $port ) = @_;
    trace("printchecks($legacy, $host, $port) {");
    my $value        = "";
    my $match_cipher = '(?:SSL|D?TLS)v[0-9]+:[A-Z0-9_-]+';
    print_header( $text{'out_checks'}, $text{'desc_check'}, "", $cfg{'out'}->{'header'} );
    OCfg::warn("821: can't print certificate sizes without a certificate (--no-cert)") if ( not _is_cfg_use('cert') );
    foreach my $key ( @{ $cfg{'do'} } ) {
        trace( " (%checks) ?" . $key );
        next if ( not _is_hashkey( $key, \%checks ) );
        next if ( _is_member( $key, \@{ $cfg{'commands_notyet'} } ) );
        next if ( _is_member( $key, \@{ $cfg{'ignore-out'} } ) );
        next if ( _is_cfg_intern($key) );
        next if ( $key =~ m/$cfg{'regex'}->{'SSLprot'}/ );
        if ( not _is_cfg_use('experimental') ) {
            next if ( _is_member( $key, \@{ $cfg{'commands_exp'} } ) );
        }
        $value = _get_yes_no( $checks{$key}->{val} );
        if ( $value =~ m/$match_cipher/ ) {

            my @unsorted = grep { /$match_cipher/ } split( /[ )]/, $value );
            $value =~ s/$match_cipher ?//g;
            $value =~ s/([)])\s*$/sprintf("%s %s", join(" ", sort @unsorted), $1)/ex;
        }
        trace( " (%checks) +" . $key );
        if ( $key =~ /$cfg{'regex'}->{'cmd-sizes'}/ ) {
            print_size( $legacy, $host, $port, $key ) if ( _is_cfg_use('cert') );
        }
        else {
            $checks{'cnt_checks_yes'}->{val}++ if ( $value eq "yes" );
            $checks{'cnt_checks_no'}->{val}++  if ( $value =~ /^no/ );
            $checks{'cnt_checks_noo'}->{val}++ if ( $value =~ /^no\s*\(<</ );
            print_check( $legacy, $host, $port, $key, $value );
        }
    }
    trace("printchecks() }");
    return;
}

sub printquit {

    if ( ( $cfg{'trace'} + $cfg{'verbose'} <= 0 ) and not _is_cfg_out('traceARG') ) {
        OCfg::warn("831: '+quit' command should be used with '--trace=arg' option");
    }
    $cfg{'verbose'} = 2 if ( $cfg{'verbose'} < 2 );
    $cfg{'trace'}   = 2 if ( $cfg{'trace'} < 2 );
    _set_cfg_out( 'traceARG', 1 );
    print("#$cfg{'me'}: +quit using:  --trace --trace=2 --traceARG");
    _vprint(" +quit : some information may appear multiple times#");
    trace_init();
    print "# TEST done.";
    return;
}

sub __SSLeay_version {
    if ( 1.49 > $Net::SSLeay::VERSION ) {
        my $txt = "ancient Net::SSLeay $Net::SSLeay::VERSION < 1.49;";
        $txt .= " cannot compare SSLeay with openssl version";
        warn $OText::STR{WARN}, "080: $txt";
        return "$Net::SSLeay::VERSION";
    }
    else {
        return Net::SSLeay::SSLeay();
    }
}

sub printversionmismatch {
    my $o = Net::SSLeay::OPENSSL_VERSION_NUMBER();
    my $s = __SSLeay_version();
    if ( $o ne $s ) {
        OCfg::warn("841: used openssl version '$o' differs from compiled Net::SSLeay '$s'; ignored");
    }
    return;
}

sub printversion {
    trace("printversion() {");
    local $\ = "\n";
    if ( defined $ENV{PWD} ) {
        print("=== started in: $ENV{PWD} ===");
    }

    my $version_openssl = Net::SSLeay::OPENSSL_VERSION_NUMBER() || $OText::STR{UNDEF};
    my @my_modules      = qw(Ciphers OCfg OData ODoc error_handler SSLinfo SSLhello OMan OText OTrace OUsr);
    my $me              = $cfg{'me'};
    print( "= $0 " . _VERSION() . " =" );
    if ( not _is_cfg_verbose() ) {
        printf( "    %-21s%s\n", $me, "3.164" );
    }
    else {
        printf( "    %-21s%s\n", $me, $SID_main );

        my $cmd = '/ *\$SID_[a-z]* /{ printf("    %-21s%s\n",FILENAME,$2); }';
        system( 'awk', '-F"', "$cmd", glob('lib/*.pm') );
    }
    print( "= perl " . $] . " =" );
    print '    @perl_incorig        ', "@perl_incorig";
    print '    @perl_inc            ', "@perl_inc";
    print '    @perl_noinc          ', "@perl_noinc";
    print '    @INC                 ', "@INC";
    print( "= Net::SSLeay " . $Net::SSLeay::VERSION . " =" );
    print("    osaft_vm_build = $ENV{'osaft_vm_build'}") if ( defined $ENV{'osaft_vm_build'} );
    print("    Net::SSLeay::");
    printf( "       ::OPENSSL_VERSION_NUMBER()    0x%x (%s)\n", $version_openssl,   $version_openssl );
    printf( "       ::SSLeay()                    0x%x (%s)\n", __SSLeay_version(), __SSLeay_version() );

    if ( 1.49 > $Net::SSLeay::VERSION ) {
        OCfg::warn("851: ancient Net::SSLeay $Net::SSLeay::VERSION < 1.49; detailed version not available");
    }
    else {
        if ( _is_cfg_verbose() ) {
            print "       ::SSLEAY_DIR                  " . Net::SSLeay::SSLeay_version(5);
            print "       ::SSLEAY_BUILD_ON             " . Net::SSLeay::SSLeay_version(3);
            print "       ::SSLEAY_PLATFORM             " . Net::SSLeay::SSLeay_version(4);
            print "       ::SSLEAY_CFLAGS               " . Net::SSLeay::SSLeay_version(2);
        }
        print "    Net::SSLeay::SSLeay_version()    " . Net::SSLeay::SSLeay_version();

    }

    $SSLinfo::verbose = 0;
    print "= openssl =";
    print "    external executable              " . ( ( $cmd{'openssl'} eq "" )  ? "<<executable not found>>" : $cmd{'openssl'} );
    print "    external executable (TLSv1.3)    " . ( ( $cmd{'openssl3'} eq "" ) ? "<<executable not found>>" : $cmd{'openssl3'} );
    print "    external executable version      " . SSLinfo::do_openssl( 'version', '', '', '' );
    print "    used environment variable (name) " . $cmd{'envlibvar'};
    print "    environment variable (content)   " . ( $ENV{ $cmd{'envlibvar'} } || $OText::STR{UNDEF} );
    print "    path to shared libraries         " . join( " ", @{ $cmd{'libs'} } );

    if ( scalar @{ $cmd{'libs'} } > 0 ) {
        foreach my $l (qw(libcrypto.a libcrypto.so libssl.a libssl.so)) {
            foreach my $p ( @{ $cmd{'libs'} } ) {
                my $lib = "$p/$l";
                $lib = "<<$p/$l not found>>" if ( !-e $lib );
                print "    library                          " . $lib;
                if ( $cfg{'verbose'} > 1 ) {
                    next if not -e "$lib";
                    print "#   strings '$lib' | grep 'part of OpenSSL')";
                    print qx(strings "$lib" | grep 'part of OpenSSL');
                }
            }
        }
    }
    print "    full path to openssl.cnf file    " . ( $cfg{'openssl_cnf'} || $OText::STR{UNDEF} );
    print "    common openssl.cnf files         " . join( " ", @{ $cfg{'openssl_cnfs'} } );
    print "    URL where to find CRL file       " . ( $cfg{'ca_crl'}  || $OText::STR{UNDEF} );
    print "    directory with PEM files for CAs " . ( $cfg{'ca_path'} || $OText::STR{UNDEF} );
    print "    PEM format file with CAs         " . ( $cfg{'ca_file'} || $OText::STR{UNDEF} );
    print "    common paths to PEM files for CAs " . join( " ", @{ $cfg{'ca_paths'} } );
    if ( _is_cfg_verbose() ) {
        foreach my $p ( @{ $cfg{'ca_paths'} } ) {
            print "       existing path to CA PEM files " . $p if -e $p;
        }
    }
    print "    common PEM filenames for CAs     " . join( " ", @{ $cfg{'ca_files'} } );
    if ( _is_cfg_verbose() ) {
        foreach my $p ( @{ $cfg{'ca_paths'} } ) {
            foreach my $f ( @{ $cfg{'ca_files'} } ) {
                print "       existing PEM file for CA      " . "$p/$f" if -e "$p/$f";
            }
        }
    }

    print "= $me =";
    print "    list of supported elliptic curves " . join( " ", @{ $cfg{'ciphercurves'} } );
    print "    list of supported ALPN, NPN      " . join( " ", $cfg{'protos_next'} );
    if ( _is_cfg_verbose() ) {
        print "    list of supported ALPN           " . join( " ", @{ $cfg{'protos_alpn'} } );
        print "    list of supported NPN            " . join( " ", @{ $cfg{'protos_npn'} } );
        print "    list of supported ALPN for +cipher " . join( " ", @{ $cfg{'cipher_alpns'} } );
        print "    list of supported NPN  for +cipher " . join( " ", @{ $cfg{'cipher_npns'} } );
    }

    print "= $me +cipher --ciphermode=openssl or --ciphermode=socket =";
    my @ciphers = SSLinfo::cipher_openssl();
    my $cnt     = 0;
    $cnt = @ciphers if ( not grep { /<<openssl>>/ } @ciphers );
    print "    number of supported ciphers      " . $cnt;
    print "    list of supported ciphers        " . join( " ", @ciphers ) if _is_cfg_verbose();
    OCfg::hint("use '--v' to get list of ciphers")                        if not _is_cfg_verbose();
    print "    openssl supported SSL versions   " . join( " ", @{ $cfg{'version'} } );
    print "    $me known SSL versions     " . join( " ", @{ $cfg{'versions'} } );
    printversionmismatch();

    print "= $me +cipher --ciphermode=intern =";
    my @cnt  = ( OCfg::get_ciphers_range( 'TLSv13', $cfg{'cipherrange'} ) );
    my $list = $cfg{'cipherranges'}->{ $cfg{'cipherrange'} };
    $list =~ s/     */        /g;
    print "    used cipherrange                 " . $cfg{'cipherrange'};
    print "    number of supported ciphers      " . scalar @cnt;
    print "    default list of ciphers          " . $list;
    if ( _is_cfg_verbose() ) {
        print "    RFC list of ciphers          " . $cfg{'cipherranges'}->{'rfc'};
        print "    IANA list of ciphers         " . $cfg{'cipherranges'}->{'iana'};
        print "    long list of ciphers         " . $cfg{'cipherranges'}->{'long'};
        print "    huge list of ciphers         " . $cfg{'cipherranges'}->{'huge'};
        print "    safe list of ciphers         " . $cfg{'cipherranges'}->{'safe'};
        print "    full list of ciphers         " . $cfg{'cipherranges'}->{'full'};
        print "    C0xx list, range C0xx..C0FF  " . $cfg{'cipherranges'}->{'c0xx'};
        print "    CCxx list, range CCxx..CCFF  " . $cfg{'cipherranges'}->{'c0xx'};
        print "    ECC list, ephermeral ciphers " . $cfg{'cipherranges'}->{'ecc'};
        print "    SSLv2 list of ciphers        " . $cfg{'cipherranges'}->{'SSLv2'};
        print "    SSLv2_long list of ciphers   " . $cfg{'cipherranges'}->{'SSLv2_long'};
        print "    shifted list of ciphers      " . $cfg{'cipherranges'}->{'shifted'};
    }

    print "= Required (and used) Modules =";
    my ( $d, $v, %p );
    printf( "=   %-22s %-9s%s\n", "module name", "VERSION", "found in" );
    printf( "=   %s+%s+%s\n",     "-" x 22,      "-" x 8,   "-" x 42 );
    foreach my $m ( qw(IO::Socket::INET IO::Socket::SSL Socket Time::Local Net::DNS Net::SSLeay Carp), @my_modules, qw(Config Math::BigInt) ) {
        if ( $m =~ m/(?:Config|Math::BigInt)/ ) {
            printf( "    %-22s %-9s%s\n", $m, "-", "<<module loaded on 32-bit systems only>>" );
            next;
        }
        no strict 'refs';

        ( $d = $m ) =~ s#::#/#g;
        $d .= '.pm';
        $v = $m . "::VERSION";
        if ( defined $$v ) {
            $v = $$v;
        }
        else {
            $v = qx(lib/$m.pm +VERSION);

            if ( defined $v ) {
                chomp $v;
                $v = " " if ( $v =~ m/^\s*$/ );
            }
            else {
                $v = "<<err>>";
                OCfg::hint("try 'chmod +x lib/$m.pm'");
            }
        }
        printf( "    %-22s %-9s%s\n", $m, $v, ( $INC{$d} || $INC{"lib/$d"} || "<<not needed, hence not loaded>>" ) );
    }
    OCfg::hint("use '--v' to get list of all modules") if not _is_cfg_verbose();
    if ( _is_cfg_verbose() ) {
        print "\n= Loaded Modules =";
        printf( "=   %-22s %s\n", "module name", "found in" );
        printf( "=   %s+%s\n",    "-" x 22,      "-" x 51 );
        foreach my $m ( sort( keys %INC ) ) {
            $d = $INC{$m} || $OText::STR{UNDEF};
            printf( "    %-22s %6s\n", $m, $d );
            $d =~ s#$m$##;
            $p{$d} = 1;
        }
        print "\n= Loaded Module Versions =";
        no strict 'refs';

        foreach my $m ( sort( keys %main:: ) ) {
            next if $m !~ /::/;
            $d = "?";
            $d = ${ $$m{'VERSION'} } if defined ${ $$m{'VERSION'} };
            printf( "    %-22s %6s\n", $m, $d );
        }
    }
    return if ( $^O =~ m/MSWin32/ );
    if ( $cfg{'verbose'} > 1 ) {
        print "\n= Used Shared Objects =";
        foreach my $d ( sort( keys %p ) ) {
            next if ( $d =~ m/^\s*$/ );
            next if not -e $d;
            print "# find '$d' -name SSLeay.so\\* -o -name libssl.so\\* -o -name libcrypto.so\\*";
            print qx(find "$d" -name SSLeay.so\\* -o -name libssl.so\\* -o -name libcrypto.so\\*);
        }
    }
    trace("printversion() }");
    return;
}

sub printciphers_list {
    my $do = shift;
    trace("printciphers_list($do) {");
    trace( " database version: " . _VERSION() );
    trace(" options: --legacy=$cfg{'legacy'} , --format=$cfg{'format'} , --header=$cfg{'out'}->{'header'}");
    trace(" options: --v=$cfg{'verbose'}, -v=$cfg{'opt-v'} , -V=$cfg{'opt-V'}");
    _vprint( "  database version: ", _VERSION() );
    if ( 'ciphers' eq $do ) {
        $cfg{'out'}->{'header'} = 0;
        $cfg{'legacy'} = 'openssl';
    }
    $cfg{'legacy'} = 'openssl-v' if ( 0 < $cfg{'opt-v'} );
    $cfg{'legacy'} = 'openssl-V' if ( 0 < $cfg{'opt-V'} );
    Ciphers::show( $cfg{'legacy'} );
    trace("printciphers_list() }");
    return;
}

sub printscores {
    my ( $legacy, $host, $port ) = @_;
    scoring( $host, $port );
    $scores{'checks'}->{val} = int(
        (
            (
                $scores{'check_cert'}->{val} +
                  $scores{'check_conn'}->{val} +
                  $scores{'check_dest'}->{val} +
                  $scores{'check_http'}->{val} +
                  $scores{'check_size'}->{val}
            ) / 5
        ) + 0.5
    );
    print_header( $text{'out_scoring'} . "\n", $text{'desc_score'}, "", $cfg{'out'}->{'header'} );
    foreach my $key ( sort( keys %scores ) ) {
        next if ( $key !~ m/^check_/ );
        print_line( $legacy, $host, $port, $key, $scores{$key}->{txt}, $scores{$key}->{val} );
    }
    print_line( $legacy, $host, $port, 'checks', $scores{'checks'}->{txt}, $scores{'checks'}->{val} );
    print_ruler();
    if ( _is_cfg_out('traceKEY') and ( 0 < $verbose ) ) {
        trace(" verbose score table");
        print "\n";
        printtable('score');
        print_ruler();
    }
    return;
}

sub printopenssl {
    print SSLinfo::do_openssl( 'version', '', '', '' );
    printversionmismatch();
    return;
}

sub printusage_exit {
    my @txt = @_;
    print $OText::STR{USAGE}, @txt;
    print <<"EoUSAGE";
# most common usage:
  $cfg{'me'} +info     your.tld
  $cfg{'me'} +check    your.tld
  $cfg{'me'} +cipher   your.tld
# for more help use:
  $cfg{'me'} --h
  $cfg{'me'} --help
EoUSAGE
    exit 2;
}

OUsr::pre_args();

_init_all();

_vprint("read command-line arguments");
my $typ = 'HOST';
push( @argv, "" );
while ( $#argv >= 0 ) {
    $arg = shift @argv;
    trace_arg("cli_arg= $arg");
    push( @{ $dbx{argv} }, $arg ) if ( ( $arg !~ m/^--cfg[_-]/ ) && ( ( $arg =~ m/^[+-]/ ) || ( $typ ne "HOST" ) ) );
    push( @{ $dbx{cfg} },  $arg ) if ( $arg =~ m/^--cfg[_-]/ );
    my $arg_orig = $arg;

    if ( $typ ne 'HOST' ) {

        trace_arg("argument? $arg, typ= $typ");
        push( @{ $dbx{exe} }, join( "=", $typ, $arg ) ) if ( $typ =~ m/OPENSSL|ENV|EXE|LIB/ );
        if ( $typ eq 'CFG_INIT' )   { _set_cfg_init( $typ, $arg ); }
        if ( $typ eq 'CFG_CIPHER' ) { _set_cipher_sec( $typ, $arg ); $typ = 'HOST'; }
        if ( $typ =~ m/^CFG/ )      { _set_cfg( $typ, $arg ); }
        if ( $typ eq 'LD_ENV' )       { $cmd{'envlibvar'}    = $arg; }
        if ( $typ eq 'LD_ENV3' )      { $cmd{'envlibvar3'}   = $arg; }
        if ( $typ eq 'OPENSSL' )      { $cmd{'openssl'}      = $arg; }
        if ( $typ eq 'OPENSSL3' )     { $cmd{'openssl3'}     = $arg; }
        if ( $typ eq 'OPENSSL_CNF' )  { $cfg{'openssl_cnf'}  = $arg; }
        if ( $typ eq 'OPENSSL_FIPS' ) { $cfg{'openssl_fips'} = $arg; }
        if ( $typ eq 'VERBOSE' )      { $cfg{'verbose'}      = $arg; }
        if ( $typ eq 'DO' )           { push( @{ $cfg{'do'} },   $arg ); }
        if ( $typ eq 'EXE' )          { push( @{ $cmd{'path'} }, $arg ); }
        if ( $typ eq 'LIB' )          { push( @{ $cmd{'libs'} }, $arg ); }
        if ( $typ eq 'CALL' )         { push( @{ $cmd{'call'} }, $arg ); }
        if ( $typ eq 'SEP' )          { $text{'separator'} = $arg; }
        if ( $typ eq 'OPT' )          { $cfg{'sclient_opt'} .= " $arg"; }
        if ( $typ eq 'TIMEOUT' )      { $cfg{'timeout'}     = $arg; }
        if ( $typ eq 'CERT_TEXT' )    { $cfg{'no_cert_txt'} = $arg; }
        if ( $typ eq 'CA_FILE' )      { $cfg{'ca_file'}     = $arg; }
        if ( $typ eq 'CA_PATH' )      { $cfg{'ca_path'}     = $arg; }
        if ( $typ eq 'CA_DEPTH' )     { $cfg{'ca_depth'}    = $arg; }
        if ( $typ eq 'PROXY_PORT' )     { $cfg{'proxyport'} = $arg; }
        if ( $typ eq 'PROXY_USER' )     { $cfg{'proxyuser'} = $arg; }
        if ( $typ eq 'PROXY_PASS' )     { $cfg{'proxypass'} = $arg; }
        if ( $typ eq 'PROXY_AUTH' )     { $cfg{'proxyauth'} = $arg; }
        if ( $typ eq 'SNINAME' )        { $cfg{'sni_name'}  = $arg; }
        if ( $typ eq 'TTY_ARROW' )      { _set_cfg_tty( 'arrow', $arg ); }
        if ( $typ eq 'TTY_IDENT' )      { _set_cfg_tty( 'ident', $arg ); }
        if ( $typ eq 'TTY_WIDTH' )      { _set_cfg_tty( 'width', $arg ); }
        if ( $typ eq 'ANON_OUT' )       { $cfg{'regex'}->{'anon_output'}   = qr($arg); }
        if ( $typ eq 'FILE_SCLIENT' )   { $cfg{'data'}->{'file_sclient'}   = $arg; }
        if ( $typ eq 'FILE_CIPHERS' )   { $cfg{'data'}->{'file_ciphers'}   = $arg; }
        if ( $typ eq 'FILE_PCAP' )      { $cfg{'data'}->{'file_pcap'}      = $arg; }
        if ( $typ eq 'FILE_PEM' )       { $cfg{'data'}->{'file_pem'}       = $arg; }
        if ( $typ eq 'SSLHELLO_RETRY' ) { $cfg{'sslhello'}->{'retry'}      = $arg; }
        if ( $typ eq 'SSLHELLO_TOUT' )  { $cfg{'sslhello'}->{'timeout'}    = $arg; }
        if ( $typ eq 'SSLHELLO_MAXC' )  { $cfg{'sslhello'}->{'maxciphers'} = $arg; }
        if ( $typ eq 'SSLERROR_MAX' )   { $cfg{'sslerror'}->{'max'}        = $arg; }
        if ( $typ eq 'SSLERROR_TOT' )   { $cfg{'sslerror'}->{'total'}      = $arg; }
        if ( $typ eq 'SSLERROR_DLY' )   { $cfg{'sslerror'}->{'delay'}      = $arg; }
        if ( $typ eq 'SSLERROR_TOUT' )  { $cfg{'sslerror'}->{'timeout'}    = $arg; }
        if ( $typ eq 'SSLERROR_PROT' )  { $cfg{'sslerror'}->{'per_prot'}   = $arg; }
        if ( $typ eq 'CONNECT_DELAY' )  { $cfg{'connect_delay'}            = $arg; }
        if ( $typ eq 'STARTTLS' )       { $cfg{'starttls'}                 = $arg; }
        if ( $typ eq 'TLS_DELAY' )      { $cfg{'starttls_delay'}           = $arg; }
        if ( $typ eq 'SLOW_DELAY' )     { $cfg{'slow_server_delay'}        = $arg; }
        if ( $typ eq 'STARTTLSE1' )     { $cfg{'starttls_error'}[1]        = $arg; }
        if ( $typ eq 'STARTTLSE2' )     { $cfg{'starttls_error'}[2]        = $arg; }
        if ( $typ eq 'STARTTLSE3' )     { $cfg{'starttls_error'}[3]        = $arg; }
        if ( $typ eq 'STARTTLSP1' )     { $cfg{'starttls_phase'}[1]        = $arg; }
        if ( $typ eq 'STARTTLSP2' )     { $cfg{'starttls_phase'}[2]        = $arg; }
        if ( $typ eq 'STARTTLSP3' )     { $cfg{'starttls_phase'}[3]        = $arg; }
        if ( $typ eq 'STARTTLSP4' )     { $cfg{'starttls_phase'}[4]        = $arg; }
        if ( $typ eq 'STARTTLSP5' )     { $cfg{'starttls_phase'}[5]        = $arg; }
        if ( $typ eq 'PORT' )           { $cfg{'port'}                     = $arg; }
        if ( $typ eq 'CIPHER_CURVES' ) { _set_cfg_list( 'ciphercurves', lc($arg) ); }
        if ( $typ eq 'CIPHER_ALPNS' )  { _set_cfg_list( 'cipher_alpns', lc($arg) ); }
        if ( $typ eq 'CIPHER_NPNS' )   { _set_cfg_list( 'cipher_npns',  lc($arg) ); }
        if ( $typ eq 'PROTOS_ALPN' )   { _set_cfg_list( 'protos_alpn',  lc($arg) ); }
        if ( $typ eq 'PROTOS_NPN' )    { _set_cfg_list( 'protos_npn',   lc($arg) ); }

        if ( $typ eq 'WARN_IGNORE' ) {
            printf("#$cfg{'me'}: ignore warning $arg $make_text\n") if ( defined $ENV{'OSAFT_MAKE'} );
            push( @{ $cfg{out}->{'warnings_no_dups'} }, $arg );
            push( @{ $cfg{out}->{'warnings_printed'} }, $arg );
        }
        if ( $typ eq 'HTTP_USER_AGENT' ) { $cfg{'use'}->{'user_agent'} = $arg; }
        if ( $typ eq 'NO_OUT' ) {
            if ( $arg =~ /^[,:]*$/ ) {
                $cfg{'ignore-out'} = [];
            }
            else {
                push( @{ $cfg{'ignore-out'} }, $arg );
            }
        }
        if ( $typ eq 'CIPHER_ITEM' ) {
            if ( defined $cfg{'cipherpatterns'}->{$arg} ) {
                $arg = $cfg{'cipherpatterns'}->{$arg}[1];
            }
            else {
                if ( $arg !~ m/^[XxA-Z0-9_-]+$/ ) {

                    OCfg::warn("062: given pattern '$arg' for cipher unknown");
                    printusage_exit("--cipher=valid-key-or-name\n");
                    $arg = "";
                }
            }
            push( @{ $cfg{'cipher'} }, $arg ) if ( $arg !~ m/^\s*$/ );
        }
        if ( $typ eq 'CIPHER_RANGE' ) {
            if ( 1 == ( grep { /^$arg$/i } keys %{ $cfg{'cipherranges'} } ) ) {
                $cfg{'cipherrange'} = $arg;
                if ( $arg eq 'openssl' ) {
                    push( @{ $cfg{'cipher'} }, $cfg{'cipherpatterns'}->{$arg}[1] );
                }
            }
            else {
                OCfg::warn("056: option with unknown cipher range '$arg'; setting ignored") if ( $arg !~ /^\s*$/ );
            }
        }
        if ( $typ eq 'CIPHER_MODE' ) {
            $arg = lc($arg);
            if ( 1 == ( grep { /^$arg$/i } @{ $cfg{'ciphermodes'} } ) ) {
                $cfg{'ciphermode'} = $arg;
            }
            else {
                OCfg::warn("057: option with unknown cipher mode '$arg'; setting ignored") if ( $arg !~ /^\s*$/ );
            }
        }
        if ( $typ eq 'STD_FORMAT' ) {
            $arg = lc($arg);
            if ( $arg =~ /$cfg{'regex'}->{'std_format'}/ ) {
                _set_binmode($arg);
            }
            else {
                _set_binmode(":encoding($arg)") if ( $arg =~ /^[a-zA-Z0-9_.-]+$/ );
            }
        }
        if ( $typ eq 'PROTOCOL' ) {
            if ( $arg =~ /^?sslv?2$/i )         { $cfg{'SSLv2'}   = 1; }
            if ( $arg =~ /^?sslv?3$/i )         { $cfg{'SSLv3'}   = 1; }
            if ( $arg =~ /^?tlsv?1$/i )         { $cfg{'TLSv1'}   = 1; }
            if ( $arg =~ /^?tlsv?1[-_.]?1$/i )  { $cfg{'TLSv11'}  = 1; }
            if ( $arg =~ /^?tlsv?1[-_.]?2$/i )  { $cfg{'TLSv12'}  = 1; }
            if ( $arg =~ /^?tlsv?1[-_.]?3$/i )  { $cfg{'TLSv13'}  = 1; }
            if ( $arg =~ /^dtlsv?0[-_.]?9$/i )  { $cfg{'DTLSv09'} = 1; }
            if ( $arg =~ /^dtlsv?1[-_.]?0?$/i ) { $cfg{'DTLSv1'}  = 1; }
            if ( $arg =~ /^dtlsv?1[-_.]?1$/i )  { $cfg{'DTLSv11'} = 1; }
            if ( $arg =~ /^dtlsv?1[-_.]?2$/i )  { $cfg{'DTLSv12'} = 1; }
            if ( $arg =~ /^dtlsv?1[-_.]?3$/i )  { $cfg{'DTLSv13'} = 1; }
        }
        if ( $typ eq 'PROXY_HOST' ) {
            $cfg{'proxyhost'} = $arg;
            if ( $arg =~ m#([^@]*)@(.*)# ) {
                $arg = $2;
                if ( $1 =~ m#([^:@]*?):([^@]*)# ) {
                    $cfg{'proxyuser'} = $1;
                    $cfg{'proxypass'} = $2;
                }
            }
            if ( $arg =~ m#([^:]*):(\d+)# ) {
                $cfg{'proxyhost'} = $1;
                $cfg{'proxyport'} = $2;
            }
        }
        if ( $typ eq 'LABEL' ) {
            $arg = lc($arg);
            if ( 1 == ( grep { /^$arg$/i } @{ $cfg{'labels'} } ) ) {
                $cfg{'label'} = $arg;
            }
            else {
                OCfg::warn("051: option with unknown label '$arg'; setting ignored") if ( $arg !~ /^\s*$/ );
            }
        }
        if ( $typ eq 'LEGACY' ) {
            $arg = lc($arg);
            $arg = 'sslcipher' if ( $arg eq 'ssl-cipher-check' );
            if ( 1 == ( grep { /^$arg$/ } @{ $cfg{'legacys'} } ) ) {
                $cfg{'legacy'} = $arg;
            }
            else {
                OCfg::warn("054: option with unknown legacy '$arg'; setting ignored") if ( $arg !~ /^\s*$/ );
            }
        }
        if ( $typ eq 'FORMAT' ) {
            $arg = lc($arg);
            $arg = 'esc' if ( $arg =~ m#^[/\\]x$# );
            if ( 1 == ( grep { /^$arg$/ } @{ $cfg{'formats'} } ) ) {
                $cfg{'format'} = $arg;
            }
            else {
                OCfg::warn("055: option with unknown format '$arg'; setting ignored") if ( $arg !~ /^\s*$/ );
            }
        }

        trace_arg("argument= $arg");

        if ( $typ eq 'TRACE' ) {
            $typ = 'HOST';
            _set_cfg_out( 'traceARG', 1 )  if ( $arg =~ m#^ARG$#i );
            _set_cfg_out( 'traceCMD', 1 )  if ( $arg =~ m#^CMD$#i );
            _set_cfg_out( 'traceKEY', 1 )  if ( $arg =~ m#^KEY$#i );
            _set_cfg_out( 'traceTIME', 1 ) if ( $arg =~ m#^TIME$#i );
            $cfg{'traceME'}++              if ( $arg =~ m#^ME(?:only)?#i );
            $cfg{'traceME'}--              if ( $arg =~ m#^notme$#i );
            $cfg{'trace'} = $arg           if ( $arg =~ m#^\d+$#i );
            next if ( $arg =~ m#^(ARG|CMD|KEY|ME|TIME|\d+)$#i );

            $cfg{'trace'}++;
            unshift( @argv, $arg );
        }
        $typ = 'HOST';
        next;
    }

    next if ( $arg =~ /^\s*$/ );

    trace_arg("arg_val? $arg");
    if ( $arg =~ m/^-[^=]*=$/ ) {
        if ( $arg =~ /$cfg{'regex'}->{'opt_empty'}/ ) {
            OCfg::warn("050: option with empty argument '$arg'; option ignored") if ( $cgi == 0 );
            next;
        }
        $arg =~ s/=+$//;
    }

    trace_arg("opt_old? $arg");
    if ( $arg =~ /^--cfg(cmd|score|text)-([^=]*)=(.*)/ ) {
        $typ = 'CFG-' . $1;
        unshift( @argv, $2 . "=" . $3 );
        OCfg::warn("022: old (pre 13.12.12) syntax '--cfg-$1-$2'; converted to '--cfg-$1=$2'; please consider changing your files");
        next;
    }
    if ( $arg =~ /^--set[_-]?score=(.*)/ ) {
        OCfg::warn("021: old (pre 13.12.11) syntax '--set-score=*' obsolete, please use '--cfg-score=*'; option ignored");
        next;
    }
    if ( $arg =~ /^--legacy=key/ ) {
        OCfg::warn("023: old (pre 19.01.14) syntax '--legacy=key' obsolete, please use '--label=key'; option ignored");
        next;
    }
    if ( $arg eq '--openssl' ) { $arg = '--extopenssl'; }

    next if ( $arg =~ /^-post=(.*)/ );

    trace_arg("opt_usr? $arg");
    if ( $arg =~ /^--use?r/ ) {
        $arg =~ s/^(?:--|\+)//;
        push( @{ $cfg{'usr_args'} }, $arg );
        next;
    }

    trace_arg("opt_--h? $arg");
    if ( $arg =~ /^--h$/ )                          { $arg  = "--help=help_brief"; }
    if ( $arg =~ /^\+(help.*)/ )                    { $arg  = "--$1"; }
    if ( $arg =~ /^(?:--|\+)help$/ )                { $arg  = "--help=NAME"; }
    if ( $arg =~ /^[+,](abbr|abk|glossar|todo)$/i ) { $arg  = "--help=$1"; }
    if ( $arg =~ /^(?:--|\+|,)help=?(.*)?$/ )       { $help = $1; next; }

    trace_arg("opt_--t? $arg");
    if ( $arg =~ /^(?:--|,)(test.*)$/ ) { $arg = "+$1"; }
    if ( $arg =~ /^\+(test.*)/ ) {

        _vprint("test $arg");
        $test = $arg;
        my ( $k, $v ) = split( /=/, $arg );
        $k =~ s/([a-zA-Z0-9])(?:[_.-])/$1/g;
        if ( defined $v ) {
            $test = "$k=$v";
        }
        else {
            $test = "$k";
        }
        _trace_info("  TEST    - prepare for test functions");
        $cfg{'need_netdns'}    = 1;
        $cfg{'need_timelocal'} = 1;
        $cfg{'need_netinfo'}   = 1;
        next;
    }

    trace_arg("optmisc? $arg");
    if ( $arg =~ /^--trace.?CLI$/i )  { next; }
    if ( $arg =~ /^--v(?:erbose)?$/ ) { $cfg{'verbose'}++; next; }
    if ( $arg =~ /^--?starttls$/i ) { $cfg{'starttls'} = "SMTP"; next; }
    if ( $arg =~ /^--cgi.?(?:exec|trace)/ ) { $cgi = 1; next; }
    if ( $arg =~ /^--exit=(.*)/ )     { next; }
    if ( $arg =~ /^--inc=/ )          { next; }
    if ( $arg =~ /^--no[,._-]?inc=/ ) { next; }
    if ( $arg =~ /^--cmd=\+?(.*)/ )   { $arg = '+' . $1; }
    if ( $arg =~ /^--rc/ )            { next; }
    if ( $arg eq '+VERSION' )         { _version_exit(); exit 0; }

    trace_arg("opt=val? $arg");
    if ( $arg =~ m/(^-[^=]*)=(.*)/ ) {
        $arg = $1;
        unshift( @argv, $2 );
        trace_arg("opt val  $arg $2");
    }

    $arg =~ s/([a-zA-Z0-9])(?:[_.,-])/$1/g if ( $arg =~ /^-/ );
    trace_arg("optnorm= $arg");

    if ( $arg =~ /^--(?:(?:no)?opensslciphers|cipheropenssl|forceopenssl)/ ) {
        OCfg::warn("061: option no longer supported; '$arg' ignored");
        next;
    }

    trace_arg("option?  $arg");
    if ( $arg eq '-t' )                          { $arg = '--starttls'; }
    if ( $arg eq '-b' )                          { $arg = '--enabled'; }
    if ( $arg eq '-c' )                          { $arg = '--capath'; }
    if ( $arg =~ /^--?CApath/ )                  { $arg = '--capath'; }
    if ( $arg =~ /^--?CAfile/ )                  { $arg = '--cafile'; }
    if ( $arg =~ /^--ca(?:cert(?:ificate)?)$/i ) { $arg = '--cafile'; }
    if ( $arg =~ /^--cadirectory$/i )            { $arg = '--capath'; }
    if ( $arg =~ /^--fuzz/i )                    { $arg = '--cipherrange'; unshift( @argv, 'huge' ); }
    if ( $arg =~ /^--httpget/i )                 { $arg = '--http'; }
    if ( $arg =~ /^--httpstunnel/i )             { $arg = '--proxyhost'; }
    if ( $arg eq '--hiderejectedciphers' )       { $arg = '--nodisabled'; }
    if ( $arg eq '--regular' )                   { $arg = '--http'; }
    if ( $arg =~ /^--?interval$/ )               { $arg = '--timeout'; }
    if ( $arg =~ /^--?nofailed$/ )               { $arg = '--enabled'; }
    if ( $arg =~ /^--show-?each$/ )              { $arg = '--disabled'; }
    if ( $arg =~ /^--(?:no|ignore)cmd$/ )        { $arg = '--ignoreout'; }

    if ( $arg eq '--range' )         { $arg = '--cipherrange'; }
    if ( $arg =~ /^--?servername/i ) { $arg = '--sniname'; }

    if ( $arg =~ /^-(e|-each-?cipher)$/ )       { $arg = '+cipher'; }
    if ( $arg =~ /^-(E|-cipher-?perproto)$/ )   { $arg = '+cipher'; }
    if ( $arg =~ /^-(f|-ciphers)$/ )            { $arg = '+ciphercheck'; }
    if ( $arg =~ /^-(x|-single-cipher)$/ )      { $typ = 'CIPHER_ITEM'; }
    if ( $arg =~ /^-(p|-protocols)$/ )          { $arg = '+protocols'; }
    if ( $arg =~ /^-(y|-spdy)$/ )               { $arg = '+spdy'; }
    if ( $arg =~ /^-(Y|-http2)$/ )              { $arg = '+spdy'; }
    if ( $arg =~ /^-(U|-vulnerable)$/ )         { $arg = '+vulns'; }
    if ( $arg =~ /^-(B|-heartbleed)$/ )         { $arg = '+heartbleed'; }
    if ( $arg =~ /^-(I|-ccs(?:-?injection))$/ ) { $arg = '+ccs'; }

    if ( $arg =~ /^-(C|-compression|-crime)$/ ) {
        $arg = '+compression';
        push( @{ $cfg{'do'} }, @{ $cfg{'cmd-crime'} } );
    }
    if ( $arg =~ /^-(T|-breach)$/ )          { $arg = '+breach'; }
    if ( $arg =~ /^-(O|-poodle)$/ )          { $arg = '+poodle'; }
    if ( $arg =~ /^-(F|-freak)$/ )           { $arg = '+freak'; }
    if ( $arg =~ /^-(A|-beast)$/ )           { $arg = '+beast'; }
    if ( $arg =~ /^-(BB|-robot)$/ )          { $arg = '+robot'; }
    if ( $arg =~ /^-(J|-logjam)$/ )          { $arg = '+logjam'; }
    if ( $arg =~ /^-(D|-drown)$/ )           { $arg = '+drown'; }
    if ( $arg =~ /^-(Z|-tls-fallback)$/ )    { $arg = '+fallback_protocol'; }
    if ( $arg =~ /^-(s|4)$/ )                { $arg = '+pfs'; }
    if ( $arg =~ /^--(p?fs|nsa)$/ )          { $arg = '+pfs'; }
    if ( $arg =~ /^--(?:rc4|appelbaum)$/ )   { $arg = '+pfs'; }
    if ( $arg eq '-R' )                      { $arg = '+renegotiation'; }
    if ( $arg =~ /^--reneg(?:otiation)?/ )   { $arg = '+renegotiation'; }
    if ( $arg =~ /^--resum(?:ption)?$/ )     { $arg = '+resumption'; }
    if ( $arg eq '--chain' )                 { $arg = '+chain'; }
    if ( $arg eq '--default' )               { $arg = '+default'; }
    if ( $arg eq '--fingerprint' )           { $arg = '+fingerprint'; }
    if ( $arg eq '--fips' )                  { $arg = '+fips'; }
    if ( $arg eq '-i' )                      { $arg = '+issuer'; }
    if ( $arg eq '--ism' )                   { $arg = '+ism'; }
    if ( $arg eq '--list' )                  { $arg = '+list'; }
    if ( $arg eq '--quit' )                  { $arg = '+quit'; }
    if ( $arg eq '--pci' )                   { $arg = '+pci'; }
    if ( $arg eq '--printavailable' )        { $arg = '+ciphers'; }
    if ( $arg eq '--printcert' )             { $arg = '+text'; }
    if ( $arg eq '--version' )               { $arg = '+version'; }
    if ( $arg eq '--sclient' )               { $arg = '--opensslsclient'; }
    if ( $arg eq '--nosclient' )             { $arg = '--noopensslsclient'; }
    if ( $arg eq '--sslnouseecc' )           { $arg = '--nossluseecc'; }
    if ( $arg eq '--sslnouseecpoint' )       { $arg = '--nossluseecpoint'; }
    if ( $arg eq '--sslnousereneg' )         { $arg = '--nosslusereneg'; }
    if ( $arg eq '--sslnodoublereneg' )      { $arg = '--nossldoublereneg'; }
    if ( $arg eq '--sslnodatanocipher' )     { $arg = '--nodataeqnocipher'; }
    if ( $arg eq '--sslnodataeqnocipher' )   { $arg = '--nodataeqnocipher'; }
    if ( $arg eq '--nosslnodataeqnocipher' ) { $arg = '--nosslnodatanocipher'; }
    if ( $arg eq '--nomd5cipher' )           { $arg = '--nociphermd5'; }
    if ( $arg eq '--md5cipher' )             { $arg = '--ciphermd5'; }

    if ( $arg =~ /^--v(?:erbose)?$/ )      { $typ = 'VERBOSE'; }
    if ( $arg =~ /^--ciphers?-?v$/ )       { $arg = '--v-ciphers'; }
    if ( $arg =~ /^--ciphers?--?v$/ )      { $arg = '--v-ciphers'; }
    if ( $arg =~ /^--v-?ciphers?$/ )       { $cfg{'v_cipher'}++; }
    if ( $arg =~ /^--warnings?$/ )         { _set_cfg_out( 'warning',          1 ); }
    if ( $arg =~ /^--nowarnings?$/ )       { _set_cfg_out( 'warning',          0 ); }
    if ( $arg =~ /^--warningsdups?$/ )     { _set_cfg_out( 'warnings_no_dups', [] ); }
    if ( $arg =~ /^--nowarningsnodups?$/ ) { _set_cfg_out( 'warnings_no_dups', [] ); }
    if ( $arg eq '--warningignore' )       { $typ        = 'WARN_IGNORE'; }
    if ( $arg eq '--ignorewarning' )       { $typ        = 'WARN_IGNORE'; }
    if ( $arg eq '--n' )                   { $cfg{'try'} = 1; }
    if ( $arg eq '--dryrun' )              { $cfg{'try'} = 1; }
    if ( $arg =~ /^--tracearg/i )          { _set_cfg_out( 'traceARG',  1 ); }
    if ( $arg =~ /^--tracecmd/i )          { _set_cfg_out( 'traceCMD',  1 ); }
    if ( $arg =~ /^--trace(?:@|key)/i )    { _set_cfg_out( 'traceKEY',  1 ); }
    if ( $arg =~ /^--tracetime/i )         { _set_cfg_out( 'traceTIME', 1 ); }
    if ( $arg =~ /^--traceme/i )           { $cfg{'traceME'}++; }
    if ( $arg =~ /^--tracenotme/i )        { $cfg{'traceME'}--; }
    if ( $arg eq '--trace' )               { $typ = 'TRACE'; }
    if ( $arg =~ /^--timeabsolute?/i )     { _set_cfg_out( 'time_absolut', 1 ); }
    if ( $arg eq '--timerelative' )        { _set_cfg_out( 'time_absolut', 0 ); }
    if ( $arg eq '--linuxdebug' )          { $cfg{'linux_debug'}++; }
    if ( $arg eq '--slowly' )              { $cfg{'slowly'} = 1; }
    if ( $arg =~ /^--exp(?:erimental)?$/ ) { _set_cfg_use( 'experimental', 1 ); }
    if ( $arg =~ /^--noexp(erimental)?$/ ) { _set_cfg_use( 'experimental', 0 ); }
    if ( $arg eq '--filesclient' )         { $typ = 'FILE_SCLIENT'; }
    if ( $arg eq '--fileciphers' )         { $typ = 'FILE_CIPHERS'; }
    if ( $arg eq '--filepcap' )            { $typ = 'FILE_PCAP'; }
    if ( $arg eq '--filepem' )             { $typ = 'FILE_PEM'; }
    if ( $arg eq '--anonoutput' )          { $typ = 'ANON_OUT'; }

    if ( $arg =~ /^--proxy(?:host)?$/ )   { $typ = 'PROXY_HOST'; }
    if ( $arg eq '--proxyport' )          { $typ = 'PROXY_PORT'; }
    if ( $arg eq '--proxyuser' )          { $typ = 'PROXY_USER'; }
    if ( $arg eq '--proxypass' )          { $typ = 'PROXY_PASS'; }
    if ( $arg eq '--proxyauth' )          { $typ = 'PROXY_AUTH'; }
    if ( $arg =~ /^--?starttls$/i )       { $typ = 'STARTTLS'; }
    if ( $arg =~ /^--starttlsdelay$/i )   { $typ = 'TLS_DELAY'; }
    if ( $arg =~ /^--slowserverdelay$/i ) { $typ = 'SLOW_DELAY'; }
    if ( $arg =~ /^--starttlserror1$/i )  { $typ = 'STARTTLSE1'; }
    if ( $arg =~ /^--starttlserror2$/i )  { $typ = 'STARTTLSE2'; }
    if ( $arg =~ /^--starttlserror3$/i )  { $typ = 'STARTTLSE3'; }
    if ( $arg =~ /^--starttlsphase1$/i )  { $typ = 'STARTTLSP1'; }
    if ( $arg =~ /^--starttlsphase2$/i )  { $typ = 'STARTTLSP2'; }
    if ( $arg =~ /^--starttlsphase3$/i )  { $typ = 'STARTTLSP3'; }
    if ( $arg =~ /^--starttlsphase4$/i )  { $typ = 'STARTTLSP4'; }
    if ( $arg =~ /^--starttlsphase5$/i )  { $typ = 'STARTTLSP5'; }
    if ( $arg eq '-v' )  { $cfg{'opt-v'} = 1; }
    if ( $arg eq '-V' )  { $cfg{'opt-V'} = 1; }
    if ( $arg eq '--V' ) { $cfg{'opt-V'} = 1; }

    if ( $arg =~ /^--checks?$/ )         { $typ = 'DO'; }
    if ( $arg =~ /^--(fips|ism|pci)$/i ) { }
    if ( $arg eq '--openssl' )             { $typ               = 'OPENSSL'; }
    if ( $arg eq '--openssl3' )            { $typ               = 'OPENSSL3'; }
    if ( $arg =~ '--opensslco?nf' )        { $typ               = 'OPENSSL_CNF'; }
    if ( $arg eq '--opensslfips' )         { $typ               = 'OPENSSL_FIPS'; }
    if ( $arg eq '--extopenssl' )          { $cmd{'extopenssl'} = 1; }
    if ( $arg eq '--noopenssl' )           { $cmd{'extopenssl'} = 0; }
    if ( $arg eq '--opensslsclient' )      { $cmd{'extsclient'} = 1; }
    if ( $arg eq '--noopensslsclient' )    { $cmd{'extsclient'} = 0; }
    if ( $arg eq '--alpn' )                { _set_cfg_use( 'alpn', 1 ); }
    if ( $arg eq '--noalpn' )              { _set_cfg_use( 'alpn', 0 ); }
    if ( $arg eq '--npn' )                 { _set_cfg_use( 'npn',  1 ); }
    if ( $arg eq '--nonpn' )               { _set_cfg_use( 'npn',  0 ); }
    if ( $arg =~ /^--?nextprotoneg$/ )     { _set_cfg_use( 'npn',  1 ); }
    if ( $arg =~ /^--nonextprotoneg/ )     { _set_cfg_use( 'npn',  0 ); }
    if ( $arg =~ /^--?comp(?:ression)?$/ ) { $arg = '--sslcompression'; }
    if ( $arg =~ /^--?nocomp(ression)?$/ ) { $arg = '--nosslcompression'; }
    if ( $arg =~ /^--sslcompression$/ )    { _set_cfg_use( 'no_comp',   0 ); }
    if ( $arg =~ /^--nosslcompression$/ )  { _set_cfg_use( 'no_comp',   1 ); }
    if ( $arg =~ /^--?tlsextdebug$/ )      { _set_cfg_use( 'extdebug',  1 ); }
    if ( $arg =~ /^--notlsextdebug/ )      { _set_cfg_use( 'extdebug',  0 ); }
    if ( $arg =~ /^--?reconnect$/ )        { _set_cfg_use( 'reconnect', 1 ); }
    if ( $arg =~ /^--noreconnect$/ )       { _set_cfg_use( 'reconnect', 0 ); }
    if ( $arg eq '--sclientopt' )          { $typ = 'OPT'; }
    if ( $arg eq '--forcesni' )            { _set_cfg_use( 'forcesni', 1 ); }
    if ( $arg =~ /^--ignorenoconn(ect)?/ ) { $cfg{'sslerror'}->{'ignore_no_conn'}   = 1; }
    if ( $arg =~ /^--ignorehandshake/ )    { $cfg{'sslerror'}->{'ignore_handshake'} = 1; }
    if ( $arg =~ /^--noignorehandshake/ )  { $cfg{'sslerror'}->{'ignore_handshake'} = 0; }
    if ( $arg eq '--lwp' )                 { _set_cfg_use( 'lwp',  1 ); }
    if ( $arg eq '--sni' )                 { _set_cfg_use( 'sni',  1 ); }
    if ( $arg eq '--nosni' )               { _set_cfg_use( 'sni',  0 ); }
    if ( $arg eq '--snitoggle' )           { _set_cfg_use( 'sni',  3 ); }
    if ( $arg eq '--togglesni' )           { _set_cfg_use( 'sni',  3 ); }
    if ( $arg eq '--nocert' )              { _set_cfg_use( 'cert', 0 ); }
    if ( $arg eq '--noignorecase' )        { $cfg{'ignorecase'}    = 0; }
    if ( $arg eq '--ignorecase' )          { $cfg{'ignorecase'}    = 1; }
    if ( $arg eq '--noignorenoreply' )     { $cfg{'ignorenoreply'} = 0; }
    if ( $arg eq '--ignorenoreply' )       { $cfg{'ignorenoreply'} = 1; }
    if ( $arg eq '--noexitcode' )          { _set_cfg_use( 'exitcode', 0 ); }
    if ( $arg eq '--exitcode' )            { _set_cfg_use( 'exitcode', 1 ); }
    if ( $arg =~ /^--exitcodev/ )          { _set_cfg_out( 'exitcode',        1 ); }
    if ( $arg =~ /^--traceexit/ )          { _set_cfg_out( 'exitcode',        1 ); }
    if ( $arg =~ /^--exitcodequiet/ )      { _set_cfg_out( 'exitcode_quiet',  1 ); }
    if ( $arg =~ /^--exitcodesilent/ )     { _set_cfg_out( 'exitcode_quiet',  1 ); }
    if ( $arg =~ /^--exitcodenochecks?/ )  { _set_cfg_out( 'exitcode_checks', 0 ); }
    if ( $arg =~ /^--exitcodenomedium/ )   { _set_cfg_out( 'exitcode_medium', 0 ); }
    if ( $arg =~ /^--exitcodenoweak/ )     { _set_cfg_out( 'exitcode_weak',   0 ); }
    if ( $arg =~ /^--exitcodenolow/ )      { _set_cfg_out( 'exitcode_low',    0 ); }
    if ( $arg =~ /^--exitcodenopfs/ )      { _set_cfg_out( 'exitcode_pfs',    0 ); }
    if ( $arg =~ /^--exitcodenoprot/ )     { _set_cfg_out( 'exitcode_prot',   0 ); }
    if ( $arg =~ /^--exitcodenosizes/ )    { _set_cfg_out( 'exitcode_sizes',  0 ); }

    if ( $arg =~ /^--exitcodenociphers?/ ) {
        _set_cfg_out( 'exitcode_cipher', 0 );
        _set_cfg_out( 'exitcode_medium', 0 );
        _set_cfg_out( 'exitcode_weak',   0 );
        _set_cfg_out( 'exitcode_low',    0 );
    }
    if ( $arg =~ /^--?sslv?2$/i )      { $cfg{'SSLv2'}   = 1; }
    if ( $arg =~ /^--?sslv?3$/i )      { $cfg{'SSLv3'}   = 1; }
    if ( $arg =~ /^--?tlsv?1$/i )      { $cfg{'TLSv1'}   = 1; }
    if ( $arg =~ /^--?tlsv?11$/i )     { $cfg{'TLSv11'}  = 1; }
    if ( $arg =~ /^--?tlsv?12$/i )     { $cfg{'TLSv12'}  = 1; }
    if ( $arg =~ /^--?tlsv?13$/i )     { $cfg{'TLSv13'}  = 1; }
    if ( $arg =~ /^--?dtlsv?09$/i )    { $cfg{'DTLSv09'} = 1; }
    if ( $arg =~ /^--?dtlsv?10?$/i )   { $cfg{'DTLSv1'}  = 1; }
    if ( $arg =~ /^--?dtlsv?11$/i )    { $cfg{'DTLSv11'} = 1; }
    if ( $arg =~ /^--?dtlsv?12$/i )    { $cfg{'DTLSv12'} = 1; }
    if ( $arg =~ /^--?dtlsv?13$/i )    { $cfg{'DTLSv13'} = 1; }
    if ( $arg =~ /^--?nosslv?2$/i )    { $cfg{'SSLv2'}   = 0; }
    if ( $arg =~ /^--?nosslv?3$/i )    { $cfg{'SSLv3'}   = 0; }
    if ( $arg =~ /^--?notlsv?1$/i )    { $cfg{'TLSv1'}   = 0; }
    if ( $arg =~ /^--?notlsv?11$/i )   { $cfg{'TLSv11'}  = 0; }
    if ( $arg =~ /^--?notlsv?12$/i )   { $cfg{'TLSv12'}  = 0; }
    if ( $arg =~ /^--?notlsv?13$/i )   { $cfg{'TLSv13'}  = 0; }
    if ( $arg =~ /^--?nodtlsv?09$/i )  { $cfg{'DTLSv09'} = 0; }
    if ( $arg =~ /^--?nodtlsv?10?$/i ) { $cfg{'DTLSv1'}  = 0; }
    if ( $arg =~ /^--?nodtlsv?11$/i )  { $cfg{'DTLSv11'} = 0; }
    if ( $arg =~ /^--?nodtlsv?12$/i )  { $cfg{'DTLSv12'} = 0; }
    if ( $arg =~ /^--?nodtlsv?13$/i )  { $cfg{'DTLSv13'} = 0; }
    if ( $arg =~ /^--no(?:tcp|tls)$/i )  { $cfg{$_} = 0 foreach (qw(SSLv2 SSLv3 TLSv1 TLSv11 TLSv12 TLSv13)); }
    if ( $arg =~ /^--(?:tcp|tls)$/i )    { $cfg{$_} = 1 foreach (qw(SSLv2 SSLv3 TLSv1 TLSv11 TLSv12 TLSv13)); }
    if ( $arg =~ /^--no(?:udp|dtls)$/i ) { $cfg{$_} = 0 foreach (qw(DTLSv09 DTLSv1 DTLSv11 DTLSv12 DTLSv13)); }
    if ( $arg =~ /^--(?:udp|dtls)$/i )   { $cfg{$_} = 1 foreach (qw(DTLSv09 DTLSv1 DTLSv11 DTLSv12 DTLSv13)); }
    if ( $arg eq '--notcp' ) { $arg = '--notls'; }
    if ( $arg eq '--noudp' ) { $arg = '--nodtls'; }
    if ( $arg eq '--tcp' )   { $arg = '--tls'; }
    if ( $arg eq '--udp' )   { $arg = '--dtls'; }

    if ( $arg eq '-cipher' )          { $typ               = 'CIPHER_ITEM'; }
    if ( $arg eq '--cipher' )         { $typ               = 'CIPHER_ITEM'; }
    if ( $arg eq '--ciphermode' )     { $typ               = 'CIPHER_MODE'; }
    if ( $arg eq '--cipherrange' )    { $typ               = 'CIPHER_RANGE'; }
    if ( $arg =~ /^--ciphercurves?/ ) { $typ               = 'CIPHER_CURVES'; }
    if ( $arg =~ /^--cipheralpns?/ )  { $typ               = 'CIPHER_ALPNS'; }
    if ( $arg =~ /^--ciphernpns?/ )   { $typ               = 'CIPHER_NPNS'; }
    if ( $arg eq '--nociphermd5' )    { $cfg{'cipher_md5'} = 0; }
    if ( $arg eq '--ciphermd5' )      { $cfg{'cipher_md5'} = 1; }
    if ( $arg eq '--nocipherdh' )     { $cfg{'cipher_dh'}  = 0; }
    if ( $arg eq '--cipherdh' )       { $cfg{'cipher_dh'}  = 1; }
    if ( $arg eq '--nodns' )          { _set_cfg_use( 'dns',   0 ); }
    if ( $arg eq '--dns' )            { _set_cfg_use( 'dns',   1 ); }
    if ( $arg eq '--http' )           { _set_cfg_use( 'http',  1 ); }
    if ( $arg eq '--httpanon' )       { _set_cfg_use( 'http',  2 ); }
    if ( $arg eq '--nohttp' )         { _set_cfg_use( 'http',  0 ); }
    if ( $arg eq '--https' )          { _set_cfg_use( 'https', 1 ); }
    if ( $arg eq '--httspanon' )      { _set_cfg_use( 'https', 2 ); }
    if ( $arg eq '--nohttps' )        { _set_cfg_use( 'https', 0 ); }
    if ( $arg =~ /^\--https?body$/i ) { _set_cfg_out( 'http_body', 1 ); }
    if ( $arg eq '--nosniname' )      { _set_cfg_use( 'sni', 0 ); }
    if ( $arg eq '--norc' )           { }
    if ( $arg eq '--sslerror' )       { _set_cfg_use( 'ssl_error', 1 ); }
    if ( $arg eq '--nosslerror' )     { _set_cfg_use( 'ssl_error', 0 ); }
    if ( $arg eq '--ssllazy' )        { _set_cfg_use( 'ssl_lazy',  1 ); }
    if ( $arg eq '--nossllazy' )      { _set_cfg_use( 'ssl_lazy',  0 ); }
    if ( $arg =~ /^--nullsslv?2$/i )  { _set_cfg_use( 'nullssl2',  1 ); }
    if ( $arg =~ /^--sslv?2null$/i )  { _set_cfg_use( 'nullssl2',  1 ); }
    if ( $arg eq '--noenabled' )         { _set_cfg_out( 'enabled', 0 ); }
    if ( $arg eq '--enabled' )           { _set_cfg_out( 'enabled', 1 ); _set_cfg_out( 'disabled', 0 ); }
    if ( $arg eq '--disabled' )          { _set_cfg_out( 'disabled', 1 ); _set_cfg_out( 'enabled', 0 ); }
    if ( $arg eq '--nodisabled' )        { _set_cfg_out( 'disabled',    0 ); }
    if ( $arg =~ /^--headers?$/ )        { _set_cfg_out( 'header',      1 ); }
    if ( $arg =~ /^--noheaders?$/ )      { _set_cfg_out( 'header',      0 ); }
    if ( $arg =~ /^--hints?infos?/ )     { _set_cfg_out( 'hint_info',   1 ); }
    if ( $arg =~ /^--nohints?infos?/ )   { _set_cfg_out( 'hint_info',   0 ); }
    if ( $arg =~ /^--hints?checks?/ )    { _set_cfg_out( 'hint_check',  1 ); }
    if ( $arg =~ /^--nohints?checks?/ )  { _set_cfg_out( 'hint_check',  0 ); }
    if ( $arg =~ /^--hints?ciphers?/ )   { _set_cfg_out( 'hint_cipher', 1 ); }
    if ( $arg =~ /^--nohints?ciphers?/ ) { _set_cfg_out( 'hint_cipher', 0 ); }

    if ( $arg =~ /^--hints?$/ ) {
        _set_cfg_out( 'hint',        1 );
        _set_cfg_out( 'hint_info',   1 );
        _set_cfg_out( 'hint_check',  1 );
        _set_cfg_out( 'hint_cipher', 1 );
    }
    if ( $arg =~ /^--nohints?$/ ) {
        _set_cfg_out( 'hint',        0 );
        _set_cfg_out( 'hint_info',   0 );
        _set_cfg_out( 'hint_check',  0 );
        _set_cfg_out( 'hint_cipher', 0 );
    }
    if ( $arg eq '--quiet' )  { $arg = '--silent'; }
    if ( $arg eq '--silent' ) { }
    if ( $arg =~ /^--(?:silent|quiet)$/ ) {
        _set_cfg_out( 'warning',     0 );
        _set_cfg_out( 'hint',        0 );
        _set_cfg_out( 'hint_info',   0 );
        _set_cfg_out( 'hint_check',  0 );
        _set_cfg_out( 'hint_cipher', 0 );
    }
    if ( $arg =~ /^--showhosts?/i ) { _set_cfg_out( 'hostname', 1 ); }
    if ( $arg =~ /^--showkeys?/i )  { _set_cfg_out( 'traceKEY', 1 ); }
    if ( $arg eq '--score' )        { _set_cfg_out( 'score',    1 ); }
    if ( $arg eq '--noscore' )      { _set_cfg_out( 'score',    0 ); }
    if ( $arg eq '--tab' ) { $text{'separator'} = "\t"; }
    if ( $arg eq '--protocol' ) { $typ = 'PROTOCOL'; }
    if ( $arg =~ /^--protoalpns?/ )                         { $typ = 'PROTOS_ALPN'; }
    if ( $arg =~ /^--protonpns?/ )                          { $typ = 'PROTOS_NPN'; }
    if ( $arg =~ /^--?h(?:ost)?$/ )                         { $typ = 'HOST'; }
    if ( $arg =~ /^--?p(?:ort)?$/ )                         { $typ = 'PORT'; }
    if ( $arg =~ /^--exe(?:path)?$/ )                       { $typ = 'EXE'; }
    if ( $arg =~ /^--lib(?:path)?$/ )                       { $typ = 'LIB'; }
    if ( $arg eq '--envlibvar' )                            { $typ = 'LD_ENV'; }
    if ( $arg eq '--envlibvar3' )                           { $typ = 'LD_ENV3'; }
    if ( $arg =~ /^--(?:no|ignore)out(?:put)?$/ )           { $typ = 'NO_OUT'; }
    if ( $arg =~ /^--cfg(cmd|check|data|hint|info|text)$/ ) { $typ = 'CFG-' . $1; }
    if ( $arg =~ /^--cfgcipher$/ )                          { $typ = 'CFG_CIPHER'; }
    if ( $arg =~ /^--cfginit$/ )                            { $typ = 'CFG_INIT'; }
    if ( $arg eq '--call' )                                 { $typ = 'CALL'; }
    if ( $arg eq '--legacy' )                               { $typ = 'LEGACY'; }
    if ( $arg eq '--label' )                                { $typ = 'LABEL'; }
    if ( $arg eq '--format' )                               { $typ = 'FORMAT'; }
    if ( $arg eq '--formatident' )                          { $typ = 'TTY_IDENT'; }
    if ( $arg eq '--formatwidth' )                          { $typ = 'TTY_WIDTH'; }
    if ( $arg eq '--formatarrow' )                          { $typ = 'TTY_ARROW'; }
    if ( $arg =~ /^--(?:format)?tty$/ )                     { _set_cfg_tty( 'width', 0 ) if not defined $cfg{'tty'}->{'width'}; }
    if ( $arg =~ /^--short(?:te?xt)?$/ )                    { $cfg{'label'}        = 'short'; }
    if ( $arg =~ /^--sep(?:arator)?$/ )                     { $typ                 = 'SEP'; }
    if ( $arg =~ /^--?timeout$/ )                           { $typ                 = 'TIMEOUT'; }
    if ( $arg =~ /^--nocertte?xt$/ )                        { $typ                 = 'CERT_TEXT'; }
    if ( $arg =~ /^--sniname/i )                            { $typ                 = 'SNINAME'; }
    if ( $arg =~ /^--sslerrormax/i )                        { $typ                 = 'SSLERROR_MAX'; }
    if ( $arg =~ /^--sslerrortotal/i )                      { $typ                 = 'SSLERROR_TOT'; }
    if ( $arg =~ /^--sslerrortotal(?:max)?/i )              { $typ                 = 'SSLERROR_TOT'; }
    if ( $arg =~ /^--sslerrordelay/i )                      { $typ                 = 'SSLERROR_DLY'; }
    if ( $arg =~ /^--sslerrortimeout/i )                    { $typ                 = 'SSLERROR_TOUT'; }
    if ( $arg =~ /^--sslerrorperprot/i )                    { $typ                 = 'SSLERROR_PROT'; }
    if ( $arg =~ /^--connectdelay/i )                       { $typ                 = 'CONNECT_DELAY'; }
    if ( $arg eq '--socketreuse' )                          { $cfg{'socket_reuse'} = 1; }
    if ( $arg eq '--nosocketreuse' )                        { $cfg{'socket_reuse'} = 0; }
    if ( $arg =~ /^--no(?:dns)?mx/ ) { $cfg{'use'}->{'mx'} = 0; }
    if ( $arg =~ /^--(?:dns)?mx/ )   { $cfg{'use'}->{'mx'} = 1; }
    if ( $arg =~ /^--(?:http)?useragent/ ) { $typ                                  = 'HTTP_USER_AGENT'; }
    if ( $arg eq '--sslretry' )            { $typ                                  = 'SSLHELLO_RETRY'; }
    if ( $arg eq '--ssltimeout' )          { $typ                                  = 'SSLHELLO_TOUT'; }
    if ( $arg eq '--sslmaxciphers' )       { $typ                                  = 'SSLHELLO_MAXC'; }
    if ( $arg eq '--usesignaturealg' )     { $cfg{'sslhello'}->{'usesignaturealg'} = 1; }
    if ( $arg eq '--nousesignaturealg' )   { $cfg{'sslhello'}->{'usesignaturealg'} = 0; }
    if ( $arg eq '--nossluseecc' )         { $cfg{'sslhello'}->{'useecc'}          = 0; }
    if ( $arg eq '--ssluseecc' )           { $cfg{'sslhello'}->{'useecc'}          = 1; }
    if ( $arg eq '--nossluseecpoint' )     { $cfg{'sslhello'}->{'useecpoint'}      = 0; }
    if ( $arg eq '--ssluseecpoint' )       { $cfg{'sslhello'}->{'useecpoint'}      = 1; }
    if ( $arg eq '--nosslusereneg' )       { $cfg{'sslhello'}->{'usereneg'}        = 0; }
    if ( $arg eq '--sslusereneg' )         { $cfg{'sslhello'}->{'usereneg'}        = 1; }
    if ( $arg eq '--nossldoublereneg' )    { $cfg{'sslhello'}->{'double_reneg'}    = 0; }
    if ( $arg eq '--ssldoublereneg' )      { $cfg{'sslhello'}->{'double_reneg'}    = 1; }
    if ( $arg eq '--nodataeqnocipher' )    { $cfg{'sslhello'}->{'nodatanocipher'}  = 1; }
    if ( $arg eq '--nosslnodatanocipher' ) { $cfg{'sslhello'}->{'nodatanocipher'}  = 0; }
    if ( $arg =~ /^--cadepth$/i )  { $typ = 'CA_DEPTH'; }
    if ( $arg =~ /^--cafile$/i )   { $typ = 'CA_FILE'; }
    if ( $arg =~ /^--capath$/i )   { $typ = 'CA_PATH'; }
    if ( $arg =~ /^--stdformat/i ) { $typ = 'STD_FORMAT'; }
    if ( $arg =~ /^--winCR/i ) { _set_binmode(":crlf:utf8"); }

    if ( $arg =~ /^-connect$/ )                                             { }
    if ( $arg eq '--insecure' )                                             { }
    if ( $arg =~ /^--use?r$/ )                                              { }
    if ( $arg =~ /^--(?:ciscospeshul|nocolor|nopct|strictpcigrade|UDP)$/ )  { }
    if ( $arg =~ /^--server(cert|certkey|certpass|cipher|protocol|mode)$/ ) { }
    if ( $arg =~ /^-(?:H|r|s|t|url|u|U|x)$/ )                               { }

    trace_arg("option=  $arg") if ( $arg =~ /^-/ );
    next                       if ( $arg =~ /^-/ );

    trace_arg("command? $arg");
    if ( $arg =~ /^\+(.*)/ ) {
        my $val = $1;

        if ( _is_member( $val, \@{ $cfg{'commands_usr'} } ) == 1 ) {
            trace_arg("cmdsusr= $val");
            push( @{ $cfg{'do'} }, @{ $cfg{"cmd-$val"} } );
            next;
        }
    }
    $arg =~ s/^,/+/;
    $arg =~ s/([.,_-]+)/_/g;

    trace_arg("command= $arg");
    if ( $arg =~ /^\+targets?$/ ) { $arg = '+host'; }
    if ( $arg eq '+host_' )       { $arg = '+host'; }

    if ( $arg eq '+check' )   { $check  = 1; }
    if ( $arg eq '+info' )    { $info   = 1; }
    if ( $arg eq '+quick' )   { $quick  = 1; }
    if ( $arg eq '+sni' )     { $cmdsni = 1; }
    if ( $arg eq '+http2' )   { $arg    = '+protocols'; }
    if ( $arg eq '+spdy' )    { $arg    = '+protocols'; }
    if ( $arg eq '+spdy3' )   { $arg    = '+protocols'; }
    if ( $arg eq '+spdy31' )  { $arg    = '+protocols'; }
    if ( $arg eq '+spdy4' )   { $arg    = '+protocols'; }
    if ( $arg eq '+prots' )   { $arg    = '+protocols'; }
    if ( $arg eq '+tlsv10' )  { $arg    = '+tlsv1'; }
    if ( $arg eq '+dtlsv10' ) { $arg    = '+dtlsv1'; }

    $arg =~ s/^\+session_?(.*)?$/+session_$1/;
    $arg =~ s/^\+ciphers?_?(.*)?$/+cipher_$1/ if ( $arg !~ m/\+ciphers?$/ );
    if ( $arg =~ /^\+cipher_?openssl$/i ) { $arg = '+ciphers_local'; }
    if ( $arg =~ /^\+cipher_v$/ )         { $arg = '+cipher'; $cfg{'v_cipher'}++; }
    if ( $arg =~ /^\+cipher_export$/ )    { $arg = '+cipher_exp'; }
    if ( $arg eq '+adh' )                 { $arg = '+cipher_adh'; }
    if ( $arg eq '+cbc' )                 { $arg = '+cipher_cbc'; }
    if ( $arg eq '+des' )                 { $arg = '+cipher_des'; }
    if ( $arg eq '+edh' )                 { $arg = '+cipher_edh'; }
    if ( $arg eq '+exp' )                 { $arg = '+cipher_exp'; }
    if ( $arg eq '+export' )              { $arg = '+cipher_exp'; }
    if ( $arg eq '+null' )                { $arg = '+cipher_null'; }
    if ( $arg eq '+weak' )                { $arg = '+cipher_weak'; }

    if ( $arg =~ /^[+]cve_?2009_?3555/i ) { $arg = '+renegotiation'; }
    if ( $arg =~ /^[+]cve_?2011_?3389/i ) { $arg = '+beast'; }
    if ( $arg =~ /^[+]cve_?2012_?4929/i ) { $arg = '+crime'; }
    if ( $arg =~ /^[+]cve_?2013_?3587/i ) { $arg = '+breach'; }
    if ( $arg =~ /^[+]cve_?2014_?0160/i ) { $arg = '+heartbleed'; }
    if ( $arg =~ /^[+]cve_?2014_?0224/i ) { $arg = '+ccs'; }
    if ( $arg =~ /^[+]cve_?2014_?3566/i ) { $arg = '+poodle'; }
    if ( $arg =~ /^[+]cve_?2014_?0204/i ) { $arg = '+freak'; }
    if ( $arg =~ /^[+]cve_?2016_?0703/i ) { $arg = '+drown'; }
    if ( $arg =~ /^[+]cve_?2016_?0800/i ) { $arg = '+drown'; }
    if ( $arg =~ /^[+]cve_?2015_?4000/i ) { $arg = '+logjam'; }
    if ( $arg =~ /^[+]cve_?2013_?2566/i ) { $arg = '+rc4'; }
    if ( $arg =~ /^[+]cve_?2015_?2808/i ) { $arg = '+rc4'; }
    if ( $arg =~ /^[+]cve_?2016_?2183/i ) { $arg = '+sweet32'; }

    if ( $arg eq '+owner' )                 { $arg = '+subject'; }
    if ( $arg eq '+authority' )             { $arg = '+issuer'; }
    if ( $arg eq '+expire' )                { $arg = '+after'; }
    if ( $arg eq '+extension' )             { $arg = '+extensions'; }
    if ( $arg eq '+protocol' )              { $arg = '+session_protocol'; }
    if ( $arg eq '+sts' )                   { $arg = '+hsts'; }
    if ( $arg eq '+sigkey' )                { $arg = '+sigdump'; }
    if ( $arg =~ /^\+sigkey_?algorithm/i )  { $arg = '+signame'; }
    if ( $arg =~ /^\+selected_?protocol/i ) { $arg = '+session_protocol'; }
    if ( $arg =~ /^\+rfc_?2818$/i )         { $arg = '+rfc_2818_names'; }
    if ( $arg =~ /^\+rfc_?2818_?names/i )   { $arg = '+rfc_2818_names'; }
    if ( $arg =~ /^\+rfc_?6125$/i )         { $arg = '+rfc_6125_names'; }
    if ( $arg =~ /^\+rfc_?6125_?names/i )   { $arg = '+rfc_6125_names'; }
    if ( $arg =~ /^\+rfc_?6797$/i )         { $arg = '+hsts'; }
    if ( $arg =~ /^\+rfc_?7525$/i )         { $arg = '+rfc_7525'; }

    if ( $arg =~ /^\+fingerprint_?(.{2,})$/ )            { $arg = '+fingerprint_' . $1; }
    if ( $arg =~ /^\+fingerprint_?sha$/i )               { $arg = '+fingerprint_sha1'; }
    if ( $arg =~ /^\+subject_?altnames?/i )              { $arg = '+altname'; }
    if ( $arg =~ /^\+modulus_?exponent_?1$/ )            { $arg = '+modulus_exp_1'; }
    if ( $arg =~ /^\+modulus_?exponent_?65537$/ )        { $arg = '+modulus_exp_65537'; }
    if ( $arg =~ /^\+modulus_?exponent_?size$/ )         { $arg = '+modulus_exp_oldssl'; }
    if ( $arg =~ /^\+pubkey_?enc(?:ryption)?$/ )         { $arg = '+pub_encryption'; }
    if ( $arg =~ /^\+public_?enc(?:ryption)?$/ )         { $arg = '+pub_encryption'; }
    if ( $arg =~ /^\+pubkey_?enc(?:ryption)?_?known/ )   { $arg = '+pub_enc_known'; }
    if ( $arg =~ /^\+public_?enc(?:ryption)?_?known/ )   { $arg = '+pub_enc_known'; }
    if ( $arg =~ /^\+ocsp_?public_?hash$/ )              { $arg = '+ocsp_public_hash'; }
    if ( $arg =~ /^\+ocsp_?subject_?hash$/ )             { $arg = '+ocsp_subject_hash'; }
    if ( $arg =~ /^\+sig(key)?_?enc(?:ryption)?$/ )      { $arg = '+sig_encryption'; }
    if ( $arg =~ /^\+sig(key)?_?enc(?:ryption)?_known/ ) { $arg = '+sig_enc_known'; }
    if ( $arg =~ /^\+server_?(?:temp)?_?key$/ )          { $arg = '+dh_parameter'; }
    if ( $arg =~ /^\+master_?key$/ )                     { $arg = '+master_key'; }
    if ( $arg =~ /^\+master_?secret$/ )                  { $arg = '+master_secret'; }
    if ( $arg =~ /^\+extended_?master_?secret$/ )        { $arg = '+master_secret'; }
    if ( $arg =~ /^\+reneg$/ )                           { $arg = '+renegotiation'; }
    if ( $arg =~ /^\+resum$/ )                           { $arg = '+resumption'; }
    if ( $arg =~ /^\+reused?$/i )                        { $arg = '+resumption'; }
    if ( $arg =~ /^\+resumption_?psk$/i )                { $arg = '+resumption_psk'; }
    if ( $arg =~ /^\+commonName$/i )                     { $arg = '+cn'; }
    if ( $arg =~ /^\+cert(?:ificate)?$/i )               { $arg = '+pem'; }
    if ( $arg =~ /^\+issuer_?X509$/i )                   { $arg = '+issuer'; }
    if ( $arg =~ /^\+subject_?X509$/i )                  { $arg = '+subject'; }
    if ( $arg =~ /^\+sha2sig(?:nature)?$/ )              { $arg = '+sha2signature'; }
    if ( $arg =~ /^\+sni_?check$/ )                      { $arg = '+check_sni'; }
    if ( $arg =~ /^\+check_?sni$/ )                      { $arg = '+check_sni'; }
    if ( $arg =~ /^\+ext_?aia$/i )                       { $arg = '+ext_authority'; }
    if ( $arg =~ /^\+vulnerabilit(y|ies)/ )              { $arg = '+vulns'; }
    if ( $arg =~ /^\+hpkp$/i )                           { $arg = '+https_pins'; }
    if ( $arg =~ /^\+pkp_?pins$/i )                      { $arg = '+https_pins'; }
    if ( $arg =~ /^\+https?_body$/i ) { _set_cfg_out( 'http_body', 1 ); }

    if ( $arg eq '+host' )      { push( @{ $cfg{'do'} }, 'host' ); next; }
    if ( $arg eq '+info' )      { @{ $cfg{'do'} } = ( @{ $cfg{'cmd-info'} }, 'info' );    next; }
    if ( $arg eq '+info_v' )    { @{ $cfg{'do'} } = ( @{ $cfg{'cmd-info--v'} }, 'info' ); next; }
    if ( $arg eq '+quick' )     { @{ $cfg{'do'} } = ( @{ $cfg{'cmd-quick'} }, 'quick' );  next; }
    if ( $arg eq '+check' )     { @{ $cfg{'do'} } = ( @{ $cfg{'cmd-check'} }, 'check' );  next; }
    if ( $arg eq '+vulns' )     { @{ $cfg{'do'} } = ( @{ $cfg{'cmd-vulns'} }, 'vulns' );  next; }
    if ( $arg eq '+check_sni' ) { @{ $cfg{'do'} } = @{ $cfg{'cmd-sni--v'} };              next; }
    if ( $arg eq '+protocols' ) { @{ $cfg{'do'} } = ( @{ $cfg{'cmd-prots'} } );           next; }
    trace_arg("command- $arg");

    if ( $arg =~ /^\+(.*)/ ) {
        my $val = $1;
        trace_arg("command+ $val");
        next if ( $val =~ m/^\+\s*$/ );
        next if ( $val =~ m/^\s*$/ );
        if ( $val =~ m/^exec$/i ) {
            $cfg{'exec'} = 1;
            next;
        }
        $val = lc($val);
        push( @{ $cfg{'done'}->{'arg_cmds'} }, $val );
        if ( $val eq 'sizes' )     { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-sizes'} } );   next; }
        if ( $val eq 'hsts' )      { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-hsts'} } );    next; }
        if ( $val eq 'http' )      { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-http'} } );    next; }
        if ( $val eq 'pfs' )       { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-pfs'} } );     next; }
        if ( $val eq 'sni' )       { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-sni'} } );     next; }
        if ( $val eq 'ev' )        { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-ev'} } );      next; }
        if ( $val eq 'bsi' )       { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-bsi'} } );     next; }
        if ( $val eq 'beast' )     { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-beast'} } );   next; }
        if ( $val eq 'crime' )     { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-crime'} } );   next; }
        if ( $val eq 'drown' )     { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-drown'} } );   next; }
        if ( $val eq 'freak' )     { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-freak'} } );   next; }
        if ( $val eq 'lucky13' )   { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-lucky13'} } ); next; }
        if ( $val eq 'robot' )     { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-robot'} } );   next; }
        if ( $val eq 'sweet32' )   { push( @{ $cfg{'do'} }, @{ $cfg{'cmd-sweet32'} } ); next; }
        if ( $val =~ /tr_?02102/ ) { push( @{ $cfg{'do'} }, qw(tr_02102+ tr_02102-) );  next; }
        if ( $val =~ /tr_?03116/ ) { push( @{ $cfg{'do'} }, qw(tr_03116+ tr_03116-) );  next; }

        if ( _is_member( $val, \@{ $cfg{'commands_notyet'} } ) > 0 ) {
            OCfg::warn("044: command not yet implemented '$val' may be ignored");
        }
        if ( _is_member( $val, \@{ $cfg{'commands'} } ) == 1 ) {
            trace_arg("command= $val");
            push( @{ $cfg{'do'} }, lc($val) );
        }
        else {
            OCfg::warn("049: command '$val' unknown; command ignored");
            if ( _is_cfg_out('hint_cipher') ) {
                OCfg::hint("049: $cfg{'hints'}->{$val}") if ( $val =~ m/^cipher(?:all|raw)/ );
            }
        }
        next;
    }

    if ( $arg =~ /(?:ciphers|s_client|version)/ ) {
        OCfg::warn("041: host-like argument '$arg'; treated as command '+$arg'");
        OCfg::hint("041: please use '+$arg' instead");
        push( @{ $cfg{'do'} }, $arg );
        next;
    }

    $arg = $arg_orig;
    trace_arg("host?    $arg");
    if ( $typ eq 'HOST' ) {
        if ( $arg !~ m/^[a-zA-Z0-9.-]+/ ) {
            OCfg::warn("042: invalid host argument '$arg'; ignored");
            next;
        }
        my $default_port = ( $cfg{'port'} || $OCfg::target_defaults[0]->[3] );
        my ( $_prot, $_host, $_port, $_auth, $_path ) = _get_target( $default_port, $arg );
        if ( ( $_host =~ m/^\s*$/ ) or ( $_port =~ m/^\s*$/ ) ) {
            OCfg::warn("043: invalid port argument '$arg'; ignored");
        }
        else {
            my $idx = $#{ $cfg{'targets'} };
            $idx++;
            my $_proxy = 0;
            trace_arg("host=$_host:$_port,  auth=$_auth,  path=$_path");
            trace("host: $_host:$_port") if ( $cfg{'trace'} > 0 );
            OCfg::set_target_orig( $idx, $arg );
            OCfg::set_target_nr( $idx, $idx );
            OCfg::set_target_prot( $idx, $_prot );
            OCfg::set_target_host( $idx, $_host );
            OCfg::set_target_port( $idx, $_port );
            OCfg::set_target_auth( $idx, $_auth );
            OCfg::set_target_proxy( $idx, $_proxy );
            OCfg::set_target_path( $idx, $_path );
            OCfg::set_target_start( $idx, 0 );
            OCfg::set_target_open( $idx, 0 );
            OCfg::set_target_stop( $idx, 0 );
            OCfg::set_target_error( $idx, 0 );
        }
    }
    else {
        trace_arg("ignore=$typ $arg");
    }

}

_vprint("check command-line arguments");

local $\ = "\n";

if ( $cfg{'proxyhost'} ne "" && 0 == $cfg{'proxyport'} ) {
    my $q = "'";
    _trace_info("  USAGE   -");
    printusage_exit("$q--proxyhost=$cfg{'proxyhost'}$q requires also '--proxyport=NN'");
}
$verbose = $cfg{'verbose'};
$legacy  = $cfg{'legacy'};
if ( _is_cfg_do('cipher') and ( 0 == $#{ $cfg{'do'} } ) ) {
    $cfg{'use'}->{'https'} = 0;
    $cfg{'use'}->{'http'}  = 0;
    $cfg{'use'}->{'dns'}   = 0;
}

if ( _is_cfg_do('list') ) {
    _set_cfg_out( 'header', 1 ) if ( ( grep { /--no.?header/ } @argv ) <= 0 );
    $text{'separator'} = "\t"   if ( ( grep { /--(?:tab|sep(?:arator)?)/ } @argv ) <= 0 );
}
if ( _is_cfg_do('pfs') ) { push( @{ $cfg{'do'} }, 'cipher_pfsall' ) if ( not _is_cfg_do('cipher_pfsall') ); }

if ( _is_cfg_do('version') or ( _is_cfg_use('mx') ) )             { $cfg{'need_netdns'}    = 1; }
if ( _is_cfg_do('version') or ( _is_cfg_do('sts_expired') ) > 0 ) { $cfg{'need_timelocal'} = 1; }

$cfg{'connect_delay'} =~ s/[^0-9]//g;

if ( _is_cfg_out('http_body') ) {
    @{ $cfg{'ignore-out'} } = grep { not /https_body/ } @{ $cfg{'ignore-out'} };
    @{ $cfg{'out'}->{'ignore'} } = grep { not /https_body/ } @{ $cfg{'out'}->{'ignore'} };
}

if ( _is_cfg_do('cipher_default') ) {
    if ( not _is_cfg_ciphermode('openssl|socket') ) {
        OCfg::warn("065: '+cipher-default' is useful with '--ciphermode=openssl' only; command ignored");
        exit 0;
    }
}

@{ $cfg{'do'} } = sort( @{ $cfg{'do'} } ) if ( 0 < _is_argv('(?:--no.?rc)') );
@{ $cfg{'commands_usr'} } = sort( @{ $cfg{'commands_usr'} } );

if ( $help !~ m/^\s*$/ ) {
    _trace_info("  HELP    - OMan::man_printhelp($help)");
    trace_arg("handle help=$help");
    my $_err = _load_file( 'lib/OMan.pm', "help module" );
    warn $OText::STR{ERROR}, "009: $_err" if ( "" ne $_err );
    OMan::man_printhelp($help);
    exit 0;
}

if ( 2 == @{ $cfg{'targets'} } ) {
    my $_host = OCfg::get_target_host(1);
    if ( defined $cfg{'port'} ) {
        OCfg::warn("045: '--port' used with single host argument; using '$_host:$cfg{'port'}'");
        OCfg::set_target_port( 1, $cfg{'port'} );
    }
}

$ENV{'OPENSSL_CONF'} = $cfg{'openssl_cnf'}  if ( defined $cfg{'openssl_cnf'} );
$ENV{'OPENSSL_FIPS'} = $cfg{'openssl_fips'} if ( defined $cfg{'openssl_fips'} );

trace_args();
_vprint_me();
_trace_info("ARGS    - options and arguments completed");

OUsr::pre_exec();

trace_arg("exec? $cfg{'exec'}");
if ( 0 == $cfg{'exec'} ) {
    if ( ( $#{ $cmd{'path'} } + $#{ $cmd{'libs'} } ) > -2 ) {
        _vprint("calling $0 ...");
        my $chr = ( $ENV{PATH} =~ m/;/ ) ? ";" : ":";
        my $lib = $ENV{ $cmd{envlibvar} };
        $ENV{PATH}              = join( $chr, @{ $cmd{'path'} }, $ENV{PATH} ) if ( $#{ $cmd{'path'} } >= 0 );
        $ENV{PATH}              = join( $chr, @{ $cmd{'libs'} }, $ENV{PATH} ) if ( $#{ $cmd{'libs'} } >= 0 );
        $ENV{ $cmd{envlibvar} } = join( $chr, @{ $cmd{'libs'} } ) if ( $#{ $cmd{'libs'} } >= 0 );
        $ENV{ $cmd{envlibvar} } .= $chr . $lib if ($lib);
        _vprint("exec: envlibvar=$cmd{envlibvar}");
        _vprint( "exec: $cmd{envlibvar}=" . ( $ENV{ $cmd{envlibvar} } || "" ) );
        _vprint("exec: PATH=$ENV{PATH}");
        _vprint( "exec: $0 +exec " . join( " ", @ARGV ) );
        _vprint("################################################") if _is_cfg_out('traceARG');
        exec $0, '+exec', @ARGV;
    }
}
_trace_info("CONF0   - runtime configuration start");

foreach my $key (qw(ca_file ca_path ca_crl)) {
    next if not defined $cfg{$key};
    OCfg::warn("053: option with spaces '$key'='$cfg{$key}'; may cause connection problems")
      if ( $cfg{$key} =~ m/\s/ );
}

_vprint("  initialise openssl");
_init_openssl();

if ( 0 < $info ) {

    $cfg{'use'}->{'alpn'} = 0;
    $cfg{'use'}->{'npn'}  = 0;
}

if ( ( 0 < _need_cipher() ) or ( 0 < _need_default() ) ) {
    foreach my $mode ( @{ $cfg{'ciphermodes'} } ) {
        if ( $mode eq $cfg{'ciphermode'} ) {
            my $do = 'cipher_' . $mode;
            push( @{ $cfg{'do'} }, $do ) if ( not _is_cfg_do($do) );

        }
    }
}

_trace_info("  LOAD0   - load modules start");

if ( 1 > ( _need_netinfo() + _need_checkssl() ) and not $test ) {
    $cfg{'need_netinfo'} = 0 if _is_cfg_ciphermode('intern');
    $cfg{'need_netinfo'} = 1 if ( _is_do_cmdvulns() );
    $cfg{'need_netinfo'} = 1 if ( _is_cfg_do('cipher_order') );
    $cfg{'need_netinfo'} = 1 if ( _is_cfg_do('cipher_strong') );
    $cfg{'need_netinfo'} = 1 if ( _is_cfg_do('cipher_weak') );
}
_load_modules();

_trace_info("  LOAD9   - load modules end");
_trace_info("  CHECK0  - check configuration start");

my $do_checks = _is_cfg_do('cipher_openssl') + _is_cfg_do('cipher_socket');

_vprint("  check internals");
_check_modules() if ( 0 < $do_checks );

_check_functions() if ( 0 < $do_checks + _is_cfg_do('cipher') + _need_checkprot() );

_vprint("  check openssl capabilities for '$cmd{'openssl'}'");
_check_openssl();

_check_ssl_methods() if ( 0 < _need_cipher() + _need_default() + _is_cfg_do('version') );

_trace_info("  CHECK9  - check configuration end");

_set_cfg_out( 'header', 1 ) if ( 0 => $verbose );
_set_cfg_out( 'header', 1 ) if ( 0 => grep { /\+(check|info|quick|cipher)$/ } @argv );
_set_cfg_out( 'header', 0 ) if ( 0 => grep { /--no.?header/ } @argv );
$sniname = $cfg{'sni_name'};
if ( not _is_cfg_use('http') ) {

    OCfg::warn("064: STS $text{'na_http'}") if ( 0 => ( grep { /hsts/ } @{ $cfg{'do'} } ) );
}
if ( 1 == $quick ) {
    _set_cfg_out( 'enabled', 1 );
    $cfg{'label'} = 'short';
}
$text{'separator'} = "\t" if _is_cfg_legacy('quick');

_vprint("  initialise SSLinfo, SSLhello");
{
    no warnings qw(once);

    if ( 1 > $cfg{'traceME'} ) {
        $SSLinfo::trace = $cfg{'trace'} if ( 0 < $cfg{'trace'} );
    }
    $SSLinfo::verbose        = $cfg{'verbose'};
    $SSLinfo::prefix_verbose = "$OText::STR{'INFO'}  SSLinfo: ";
    $SSLinfo::linux_debug      = $cfg{'linux_debug'};
    $SSLinfo::use_openssl      = $cmd{'extopenssl'};
    $SSLinfo::use_sclient      = $cmd{'extsclient'};
    $SSLinfo::openssl          = $cmd{'openssl'};
    $SSLinfo::use_SNI          = $cfg{'use'}->{'sni'};
    $SSLinfo::use_alpn         = $cfg{'use'}->{'alpn'};
    $SSLinfo::use_npn          = $cfg{'use'}->{'npn'};
    $SSLinfo::protos_alpn      = ( join( ",", @{ $cfg{'protos_alpn'} } ) );
    $SSLinfo::protos_npn       = ( join( ",", @{ $cfg{'protos_npn'} } ) );
    $SSLinfo::use_extdebug     = $cfg{'use'}->{'extdebug'};
    $SSLinfo::use_reconnect    = $cfg{'use'}->{'reconnect'};
    $SSLinfo::socket_reuse     = $cfg{'socket_reuse'};
    $SSLinfo::slowly           = $cfg{'slowly'};
    $SSLinfo::sclient_opt      = $cfg{'sclient_opt'};
    $SSLinfo::timeout_sec      = $cfg{'timeout'};
    $SSLinfo::no_compression   = $cfg{'use'}->{'no_comp'};
    $SSLinfo::no_cert          = ( ( _is_cfg_use('cert') ) ? 0 : 1 );
    $SSLinfo::no_cert_txt      = $cfg{'no_cert_txt'};
    $SSLinfo::ignore_case      = $cfg{'ignorecase'};
    $SSLinfo::ca_crl           = $cfg{'ca_crl'};
    $SSLinfo::ca_file          = $cfg{'ca_file'};
    $SSLinfo::ca_path          = $cfg{'ca_path'};
    $SSLinfo::ca_depth         = $cfg{'ca_depth'};
    $SSLinfo::ignore_handshake = $cfg{'sslerror'}->{'ignore_handshake'};
    $SSLinfo::starttls         = $cfg{'starttls'};
    $SSLinfo::proxyhost        = $cfg{'proxyhost'};
    $SSLinfo::proxyport        = $cfg{'proxyport'};
    $SSLinfo::proxypass        = $cfg{'proxypass'};
    $SSLinfo::proxyuser        = $cfg{'proxyuser'};
    $SSLinfo::file_sclient     = $cfg{'data'}->{'file_sclient'};
    $SSLinfo::file_pem         = $cfg{'data'}->{'file_pem'};
    $SSLinfo::method           = "";
    $SSLinfo::sni_name   = $cfg{'sni_name'};
    $SSLinfo::use_http   = $cfg{'use'}->{'http'};
    $SSLinfo::use_https  = $cfg{'use'}->{'https'};
    $SSLinfo::target_url = "/";
    $SSLinfo::user_agent = $cfg{'use'}->{'user_agent'};
}
if ( 'cipher' eq join( "", @{ $cfg{'do'} } ) ) {
    $SSLinfo::use_http = 0;
}

if ( defined $SSLhello::VERSION ) {
    no warnings qw(once);

    if ( 1 > $cfg{'traceME'} ) {
        $SSLhello::trace = $cfg{'trace'};
    }
    $SSLhello::verbose        = $cfg{'verbose'};
    $SSLhello::prefix_verbose = "$OText::STR{'INFO'}  SSLhello: ";
    $SSLhello::traceTIME         = $cfg{'out'}->{'traceTIME'};
    $SSLhello::experimental      = $cfg{'use'}->{'experimental'};
    $SSLhello::usemx             = $cfg{'use'}->{'mx'};
    $SSLhello::usesni            = $cfg{'use'}->{'sni'};
    $SSLhello::sni_name          = $cfg{'sni_name'};
    $SSLhello::connect_delay     = $cfg{'connect_delay'};
    $SSLhello::starttls          = ( ( $cfg{'starttls'} eq "" ) ? 0 : 1 );
    $SSLhello::starttlsType      = $cfg{'starttls'};
    $SSLhello::starttlsDelay     = $cfg{'starttls_delay'};
    $SSLhello::slowServerDelay   = $cfg{'slow_server_delay'};
    $SSLhello::timeout           = $cfg{'sslhello'}->{'timeout'};
    $SSLhello::retry             = $cfg{'sslhello'}->{'retry'};
    $SSLhello::max_ciphers       = $cfg{'sslhello'}->{'maxciphers'};
    $SSLhello::use_signature_alg = $cfg{'sslhello'}->{'usesignaturealg'};
    $SSLhello::usereneg          = $cfg{'sslhello'}->{'usereneg'};
    $SSLhello::useecc            = $cfg{'sslhello'}->{'useecc'};
    $SSLhello::useecpoint        = $cfg{'sslhello'}->{'useecpoint'};
    $SSLhello::double_reneg      = $cfg{'sslhello'}->{'double_reneg'};
    $SSLhello::noDataEqNoCipher  = $cfg{'sslhello'}->{'nodatanocipher'};
    $SSLhello::proxyhost         = $cfg{'proxyhost'};
    $SSLhello::proxyport         = $cfg{'proxyport'};
    $SSLhello::cipherrange       = $cfg{'cipherrange'};
    $SSLhello::ciphercurves      = ( join( ":", @{ $cfg{'ciphercurves'} } ) );
    $SSLhello::protos_alpn       = ( join( ",", @{ $cfg{'protos_alpn'} } ) );
    $SSLhello::protos_npn        = ( join( ",", @{ $cfg{'protos_npn'} } ) );
    @SSLhello::starttlsPhaseArray = @{ $cfg{'starttls_phase'} };
    push( @SSLhello::starttlsPhaseArray, @{ $cfg{'starttls_error'} }[ 1 .. 3 ] );
}
$cfg{'trace'} = 1 if ( 0 < $cfg{'traceME'} );
$cfg{'trace'} = 0 if ( 0 > $cfg{'traceME'} );

if ( $cfg{'label'} eq 'short' ) {
    foreach my $key ( keys %data )   { $data{$key}->{'txt'}   = $OData::shorttexts{$key}; }
    foreach my $key ( keys %checks ) { $checks{$key}->{'txt'} = $OData::shorttexts{$key}; }
}

_init_checks_val();

_trace_info("CONF9   - runtime configuration end");

_vprint("check for no connection commands");
if ( $test =~ m/ciphers.*regex/ ) { _vprint("  test regex "); OCfg::test_cipher_regex(); exit 0; }
if ( $test !~ /^\s*$/ )           { _vprint("  show any   "); OTrace::test_show($test);  exit 0; }
if ( _is_cfg_do('list') )       { _vprint("  list       "); printciphers_list('list');    exit 0; }
if ( _is_cfg_do('ciphers') )    { _vprint("  ciphers    "); printciphers_list('ciphers'); exit 0; }
if ( _is_cfg_do('version') )    { _vprint("  version    "); printversion();               exit 0; }
if ( _is_cfg_do('libversion') ) { _vprint("  libversion "); printopenssl();               exit 0; }
if ( _is_cfg_do('quit') )       { _vprint("  quit       "); printquit();                  exit 0; }

if ( ( $cfg{'trace'} + $cfg{'verbose'} ) > 0 ) {
    @{ $cfg{'do'} } = @{ $cfg{'cmd-info--v'} } if ( @{ $cfg{'do'} } eq @{ $cfg{'cmd-info'} } );
}
trace_init() if _is_trace();

if ( 0 > $#{ $cfg{'do'} } ) {
    trace_exit();
    printusage_exit("no command given");
}

trace_arg("commands=@{$cfg{'do'}}");

OUsr::pre_cipher();

@{ $cfg{'do'} } = do {
    my %seen;
    grep { !$seen{$_}++ } @{ $cfg{'do'} };
};

_trace_info("MAIN0   - start");
OTrace::ciphers_show( ( _need_cipher() || 0 ) ) if _is_trace();
OUsr::pre_main();

_vprint("check target arguments");
printusage_exit("no target hosts given") if ( $#{ $cfg{'targets'} } <= 0 );
if ( _is_cfg_do('cipher_openssl') or _is_cfg_do('cipher_socket') ) {
    if ( $#{ $cfg{'done'}->{'arg_cmds'} } > 0 ) {
        printusage_exit( "additional commands in conjunction with '+cipher' are not supported; '+" . join( " +", @{ $cfg{'done'}->{'arg_cmds'} } ) . "'" );
    }
}
if ( ( 0 < $info ) and ( $#{ $cfg{'done'}->{'arg_cmds'} } >= 0 ) ) {
    OCfg::warn( "047: additional commands in conjunction with '+info' are not supported; '+" . join( " +", @{ $cfg{'done'}->{'arg_cmds'} } ) . "' ignored" );
}
if ( ( 0 < $check ) and ( $#{ $cfg{'done'}->{'arg_cmds'} } >= 0 ) ) {
    foreach my $key ( @{ $cfg{'done'}->{'arg_cmds'} } ) {
        if ( _is_member( $key, \@{ $cfg{'cmd-info'} } ) ) {
            OCfg::warn("048: additional commands in conjunction with '+check' are not supported; +'$key' ignored");
        }
    }
}

my $fail = 0;
foreach my $cmd ( @{ $cfg{'ignore-out'} } ) {
    $fail++ if ( _is_cfg_do($cmd) );
}
if ( $fail > 0 ) {
    OCfg::warn("066: $fail data and check outputs are disbaled due to use of '--no-out':");
    if ( _is_cfg_verbose() ) {
        OCfg::warn( "067:  disabled:  +" . join( " +", @{ $cfg{'ignore-out'} } ) );
        OCfg::warn( "068:  given:  +" . join( " +", @{ $cfg{'do'} } ) );
    }
    else {
        OCfg::hint("use '--v' for more information");
    }
    OCfg::hint("066: do not use '--ignore-out=*' or '--no-out=*'");
}
else {
    foreach my $cmd ( @{ $cfg{'do'} } ) {
        if ( _is_member( $cmd, \@{ $cfg{'commands_hint'} } ) ) {
            OCfg::hint("+$cmd : please see '$cfg{'me'} --help=CHECKS' for more information");
        }
    }
}
_trace_info("TARGETS0 - start");

OUsr::pre_host();

_vprint( "check all targets with commands +" . join( ' +', @{ $cfg{'do'} } ) );

$sniname = $cfg{'sni_name'};
my $idx = 0;
foreach my $target ( @{ $cfg{'targets'} } ) {
    next if ( 0 == @{$target}[0] );
    $idx++;
    $host        = OCfg::get_target_host($idx);
    $port        = OCfg::get_target_port($idx);
    $cfg{'port'} = $port;
    $cfg{'host'} = $host;
    next if _trace_next("HOST0   - start $host:$port");
    if ( _is_cfg_use('sni') ) {
        if ( defined $sniname ) {
            if ( $host ne $cfg{'sni_name'} ) {
                OCfg::warn("069: hostname not equal SNI name; checks are done with '$host'");
            }
            $SSLinfo::sni_name  = $cfg{'sni_name'};
            $SSLhello::sni_name = $cfg{'sni_name'};
        }
        else {
            $cfg{'sni_name'}    = $host;
            $SSLinfo::sni_name  = $host;
            $SSLhello::sni_name = $host;
        }
    }
    $SSLinfo::use_https  = $cfg{'use'}->{'https'};
    $SSLinfo::use_http   = $cfg{'use'}->{'http'};
    $SSLinfo::target_url = OCfg::get_target_path($idx);
    $SSLinfo::target_url =~ s:^\s*$:/:;
    _resetchecks();
    print_header( _get_text( 'out_target', "$host:$port" ), "", "", $cfg{'out'}->{'header'} );

    next if _trace_next("  DNS0    - start");

    my $rhost = "";
    $fail = "";
    if ( "" ne $cfg{'proxyhost'} ) {
        $fail         = _get_text( 'disabled', "--proxyhost=$cfg{'proxyhost'}" );
        $cfg{'rhost'} = $fail;
        $cfg{'DNS'}   = $fail;
        $cfg{'IP'}    = $fail;
        $cfg{'ip'}    = $fail;
    }
    else {
        $fail = '<<gethostbyaddr() failed>>';
        $cfg{'ip'} = gethostbyname($host);
        if ( not defined $cfg{'ip'} ) {
            OCfg::warn("201: Can't get IP for host '$host'; host ignored");
            trace(" host}");
            next;
        }
        _vprint("  test IP");
        $cfg{'IP'} = join( ".", unpack( "W4", $cfg{'ip'} ) );
        if ( _is_cfg_use('dns') ) {
            trace(" test DNS (disable with --no-dns)");
            _trace_time("test DNS{");
            local $? = 0;
            local $! = undef;
            ( $cfg{'rhost'} = gethostbyaddr( $cfg{'ip'}, AF_INET ) ) or $cfg{'rhost'} = $fail;
            $cfg{'rhost'} = $fail if ( $? != 0 );
            my ( $fqdn, $aliases, $addrtype, $length, @ips ) = gethostbyname($host);
            my $i = 0;

            foreach my $ip (@ips) {
                local $? = 0;
                local $! = undef;
                ( $rhost = gethostbyaddr( $ip, AF_INET ) ) or $rhost = $fail;
                $rhost = $fail if ( $? != 0 );
                $cfg{'DNS'} .= join( ".", unpack( "W4", $cfg{'ip'} ) ) . " " . $rhost . "; ";
            }
            if ( $cfg{'rhost'} =~ m/gethostbyaddr/ ) {
                OCfg::warn("202: Can't do DNS reverse lookup: for '$host': $fail; ignored");
                OCfg::hint("202: use '--no-dns' to disable this check");
            }
            _trace_time("test DNS}");
        }
    }
    if ( _is_cfg_do('host') or ( ( $info + $check + $cmdsni ) > 0 ) ) {
        _vprint("  print DNS stuff");
        trace(" +info || +check || +sni*");
        if ( $legacy =~ /(compact|full|owasp|simple)/ ) {
            print_ruler();
            print_line( $legacy, $host, $port, 'host_name', $text{'host_name'}, $host );
            print_line( $legacy, $host, $port, 'host_IP',   $text{'host_IP'},   $cfg{'IP'} );
            if ( _is_cfg_use('dns') ) {
                print_line( $legacy, $host, $port, 'host_rhost', $text{'host_rhost'}, $cfg{'rhost'} );
                print_line( $legacy, $host, $port, 'host_DNS',   $text{'host_DNS'},   $cfg{'DNS'} );
            }
            print_ruler();
        }
    }

    next if _trace_next("  DNS9    - end");

    next if _trace_next("  CONN0   - start");
    my $connect_ssl = 1;
    trace( " sni_name= " . ( $cfg{'sni_name'} || $OText::STR{UNDEF} ) );
    if ( not _can_connect( $host, $port, $cfg{'sni_name'}, $cfg{'timeout'}, $connect_ssl ) ) {
        next if ( $cfg{'sslerror'}->{'ignore_no_conn'} <= 0 );
    }
    $connect_ssl = 0;
    if ( not _can_connect( $host, 80, $cfg{'sni_name'}, $cfg{'timeout'}, $connect_ssl ) ) {
        $SSLinfo::use_http = 0;
        OCfg::warn("325: HTTP disabled, using '--no-http'");
    }
    next if _trace_next("  CONN9   - end");

    if ( _is_cfg_do('cipher_dh') ) {
        if ( not _is_cfg_ciphermode('intern') ) {
            OCfg::warn("405: option '--ciphermode=', not supported for '+cipher-dh'; option ignored");
            $cfg{'ciphermode'} = "intern";
        }
        if ( 0 >= $cmd{'extopenssl'} ) {
            OCfg::warn("408: OpenSSL disabled using '--no-openssl', can't check DH parameters; target ignored");
            next;
        }
    }

    next if _trace_next("  CIPHER0 - start (ciphermode=$cfg{'ciphermode'})");
    if ( _need_cipher() ) {
        OCfg::warn("209: No SSL versions for '+cipher' available") if ( $#{ $cfg{'version'} } < 0 );
        if ( 'openssl' eq $cfg{'cipherrange'} ) {
            require SSLinfo;
            $SSLinfo::openssl = $cmd{'openssl'};
            @{ $cfg{'cipher'} } = map( { Ciphers::get_key($_) || ""; } SSLinfo::cipher_openssl("@{$cfg{'cipher'}}") );
            trace( " openssl ciphers: " . scalar @{ $cfg{'cipher'} } );
        }
        $cipher_results = {};
        _vprint("  test protocols @{$cfg{'version'}} ...");
        if ( _is_cfg_ciphermode('intern|dump') ) {
            trace(" use SSLhello ...");
            SSLhello::printParameters() if ( $cfg{'trace'} > 1 );
            $cipher_results = ciphers_scan_intern( $host, $port );
        }
        if ( _is_cfg_ciphermode('openssl|socket') ) {
            trace(" use $cfg{'ciphermode'} ...");
            $cipher_results = ciphers_scan_openssl( $host, $port );

            trace(" get default ...");
            _trace_time("need_default{");
            ciphers_default_openssl( $host, $port );
            _trace_time("need_default}");
        }
        foreach my $ssl ( @{ $cfg{'version'} } ) {
            $checks{'cnt_ciphers'}->{val} += $cipher_results->{'_admin'}{$ssl}{'cnt_offered'};
            $checks{'cnt_totals'}->{val}  += $cipher_results->{'_admin'}{$ssl}{'cnt_accepted'};
        }
        checkciphers( $host, $port, $cipher_results );
    }
    next if _trace_next("  SCAN    - done");

    if ( _is_cfg_do('cipher_dh') ) {
        _vprint("  +cipher-dh");
        if ( _is_cfg_ciphermode('intern') ) {
            printciphers_dh( $legacy, $host, $port, $cipher_results );
        }
        else {
            printciphers_dh_openssl( $legacy, $host, $port );
        }
        goto CLOSE_SSL;
    }
    next if _trace_next("  DH      - done");

    if ( _need_cipher() ) {
        _vprint("  print ciphers");
        if ( _is_cfg_do('cipher') or _is_cfg_do('check') or _is_cfg_do('quick') ) {
            printciphers( $legacy, $host, $port, $cipher_results );
        }
        if ( _is_cfg_do('cipher_default') and ( 0 < $#{ $cfg{'do'} } ) ) {
            printcipherpreferred( $legacy, $host, $port );
        }
        goto CLOSE_SSL if ( _is_cfg_do('cipher') and ( 0 == $quick ) );
    }
    next if _trace_next("  CIPHER9 - end");

    if ( _is_cfg_do('fallback_protocol') ) {
        _vprint("  protocol fallback support ...");
        my ( $version, $supported, $dh );
        if ( _is_cfg_ciphermode('openssl') ) {
            ( $version, $supported, $dh ) = _useopenssl( '', $host, $port, '' );
        }
        else {
            ( $version, $supported ) = _usesocket( '', $host, $port, '' );
        }
        $prot{'fallback'}->{val} = $version;
        trace(" fallback: $version $supported");
    }

    next if _trace_next("  DATA0   - get target data start");
    OUsr::pre_info();
    _get_data0( $host, $port );
    next if _trace_next("  DATA9   - get target data end");

    OUsr::pre_open();

    if ( 0 >= $cfg{'sslerror'}->{'ignore_no_conn'} ) {
        _vprint("  test connection  (disable with  --ignore-no-conn) ...");
        _trace_time("test connection{");
        if ( not defined SSLinfo::do_ssl_open( $host, $port, ( join( " ", @{ $cfg{'version'} } ) ), join( " ", @{ $cfg{'ciphers'} } ) ) ) {
            my @errtxt = SSLinfo::errors( $host, $port );
            if ( 0 < $#errtxt ) {
                trace( join( "\n" . $OText::STR{ERROR}, @errtxt ) );
                OCfg::warn("205: Can't make a connection to '$host:$port'; target ignored");
                OCfg::hint("205: use '--v' to show more information");
                OCfg::hint("205: use '--socket-reuse' it may help in some cases");
                OCfg::hint("205: use '--ignore-no-conn' to disable this check");
                OCfg::hint("205: do not use '--no-ignore-handshake'") if ( $cfg{'sslerror'}->{'ignore_handshake'} <= 0 );
                _trace_time("  test connection} failed");
                goto CLOSE_SSL;
            }
        }
        _trace_time("  connection open.");
        my @errtxt = SSLinfo::errors( $host, $port );
        if ( 0 < ( grep { /\*\*ERROR/ } @errtxt ) ) {
            OCfg::warn("207: Errors occurred when using '$cmd{'openssl'}', some results may be wrong; errors ignored");
            OCfg::hint("207: use '--v' to show more information");
        }
        _trace_time("test connection}");
    }

    next if _trace_next("  PREPARE0 - start");
    OUsr::pre_cmds();

    if ( _is_cfg_do('dump') ) {
        _vprint("  +dump");
        if ( 1 < $cfg{'trace'} ) {
            trace(' ############################################################ %SSLinfo');
            print SSLinfo::datadump();
        }
        printdump( $legacy, $host, $port );
    }

    OUsr::pre_data();

    _vprint("  perform checks ...");
    if ( 0 < _need_checkalpn() ) {
        checkalpn( $host, $port );
        _trace_time("  checkalpn.");
    }
    checkdates( $host, $port );
    _trace_time("  checkdates.");
    if ( 0 < _need_checkhttp() ) {
        checkhttp( $host, $port );
        _trace_time("  checkhttp.");
    }
    checksni( $host, $port );
    _trace_time("  checksni.");
    checksizes( $host, $port );
    _trace_time("  checksizes.");
    if ( 0 == $info ) {
        checkdv( $host, $port );
        _trace_time("  checkdv.");
    }
    if ( 0 < _need_checkalpn() ) {
        checkalpn( $host, $port );
        _trace_time("  checkalpn.");
    }
    if ( 0 < _need_check_dh() ) {
        check_dh( $host, $port );
        _trace_time("  check_dh.");
    }
    if ( 0 < _need_checkprot() ) {
        checkprot( $host, $port );
        _trace_time("  checkprot.");
    }
    if ( 0 < _need_checkdest() ) {
        checkdest( $host, $port );
        _trace_time("  checkdest.");
    }
    if ( 0 < _need_checkbleed() ) {
        checkbleed( $host, $port );
        _trace_time("  checkbleed.");
    }
    if ( 0 < _need_checkssl() ) {
        _vprint("  need_checkssl ...");
        checkssl( $host, $port );
        _trace_time("  checkssl.");
    }
    if ( _is_cfg_do('sstp') ) {
        checksstp( $host, $port );
        _trace_time("  checksstp.");
    }
    next if _trace_next("  PREPARE9 - end");

    next if _trace_next("  PRINT0  - start");
    OUsr::pre_print();

    if ( 0 < $check ) {
        OCfg::warn("208: No openssl, some checks are missing") if ( ( $^O =~ m/MSWin32/ ) and ( $cmd{'extopenssl'} == 0 ) );
    }

    if ( _is_cfg_do('s_client') ) {
        _vprint("  +s_client");
        print "#{\n", SSLinfo::s_client( $host, $port ), "\n#}";
    }
    trace( " do=" . join( " ", @{ $cfg{'do'} } ) );

    _vprint("  print info ...");
    printdata( $legacy, $host, $port ) if ( 1 > $check );
    _vprint("  print checks ...");
    printchecks( $legacy, $host, $port ) if ( 1 > $info );

    if ( _is_cfg_out('score') ) {
        _vprint("  print score ...");
        printscores( $legacy, $host, $port );
    }
    next if _trace_next("  PRINT9  - end");

  CLOSE_SSL:
    {
        no warnings qw(once);
        if ( defined $SSLinfo::socket ) {
            SSLinfo::do_ssl_close( $host, $port );
        }
    }
    $cfg{'done'}->{'hosts'}++;

    OUsr::pre_next();
    next if _trace_next("HOST9   - end");

}

_trace_info("TARGETS9 - end");

OUsr::pre_exit();
trace_exit() if _is_trace();
_trace_info("MAIN9   - end");

_vprint("check exit code");
$cfg{'use'}->{'exitcode'} += $cfg{'out'}->{'exitcode'};
exit 0 if ( not _is_cfg_use('exitcode') );

my $status = check_exitcode();
if ( 0 < $status ) {
    print "# EXIT $status" if ( not _is_cfg_out('exitcode_quiet') );
}
exit $status;

##____________________________________________________________________________
##_____________________________________________________ ciphers definitions __|
## lib/Ciphers.pm DATA .. END
## CIPHERS {

__DATA__

# Format of following data lines:
#   <empty>     - empty lines are ignored
#   comments    - line beginning with a # (hash); lines are ignored
#   0xhhhhhhhh  - data line containing a cipher suite; used columns are:
#       hex     - hex constant for the cipher suite
#       openssl - security value (STRENGTH) used by openssl
#       sec     - security value used by o-saft.pl
#       ssl     - protocol where the cipher is used (PCT just for information)
#       keyx    - key exchange of the cipher suite (Kx= in openssl)
#       auth    - authenticatione of the cipher suite (Au= in openssl)
#       enc     - encryption of the cipher suite (Enc= in openssl)
#       bits    - bits for encryption of the cipher suite (Enc= in openssl)
#       mac     - Mac of the cipher suite (Mac= in openssl)
#       cipher  - list of known cipher suite names, most common first
#       const   - list of known cipher suite constants, most common first
#       notes   - list of notes and comments
#
#   All columns must be separated by TABs (0x9 aka \t), no spaces are allowed.
#   The left-most word must not be prepended by whitespace characters. It must
#   begin with the cipher suite hex key, like:  0x  followed by exactly  8 hex
#   chars [0-9A-F]. Only such lines are used for ciphers.
#   If additional characters  [a-zA-Z-]  are used in the hex key  it then does
#   not match  ^0x[0-9a-fA-F]{8} . The definition is stored in  %ciphers , but
#   will not be used anywhere (except informational lists).  These definitions
#   are mainly used for documentation, for example ancient cipher definitions.
#   
#   Values in left-most column (the cipher's hex key) must be unique.
#
#   In all other columns, following special strings are used:
#       -       - empty value/string, value not existent, value not applicable
#       -?-     - value currently unknown
#       None    - value not used (as in openssl)
#
#   The fomat/syntax of the table is very strict, because some other tools may
#   use and/or ignore this data.  In particular,  the characters  " [ ] =  are
#   not used to avoid conflicts in other tools (for example Excel).
#
#   This table will be read in _ciphers_init() and converted to %ciphers .

# hex const	openssl	sec	ssl	keyx	auth	enc	bits	mac	rfc	cipher,aliases	const	comment
#--------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+---------------+-------+---------------+
0x03005600	-	None	SSL/TLS	None	None	-	0	None	7507	SCSV,TLS_FALLBACK_SCSV	TLS_FALLBACK_SCSV	SCSV
0x030000FE	-	-?-	TLSv13	None	None	-	-?-	SHA256	-	WDM-NULL-SHA256	WDM_WITH_NULL_SHA256	wolfSSL DTLS Multicast
0x030000FF	-	None	SSL/TLS	None	None	-	0	None	5746	INFO_SCSV	EMPTY_RENEGOTIATION_INFO_SCSV	DOC
0x03000A0A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-0A	GREASE_0A	-
0x03001A1A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-1A	GREASE_1A	-
0x03002A2A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-2A	GREASE_2A	-
0x03003A3A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-3A	GREASE_3A	-
0x03004A4A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-4A	GREASE_4A	-
0x03005A5A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-5A	GREASE_5A	-
0x03006A6A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-6A	GREASE_6A	-
0x03007A7A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-7A	GREASE_7A	-
0x03008A8A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-8A	GREASE_8A	-
0x03009A9A	-	-	TLSv13	None	None	-	0	None	8701	GREASE-9A	GREASE_9A	-
0x0300AAAA	-	-	TLSv13	None	None	-	0	None	8701	GREASE-AA	GREASE_AA	-
0x0300BABA	-	-	TLSv13	None	None	-	0	None	8701	GREASE-BA	GREASE_BA	-
0x0300CACA	-	-	TLSv13	None	None	-	0	None	8701	GREASE-CA	GREASE_CA	-
0x0300DADA	-	-	TLSv13	None	None	-	0	None	8701	GREASE-DA	GREASE_DA	-
0x0300EAEA	-	-	TLSv13	None	None	-	0	None	8701	GREASE-EA	GREASE_EA	-
0x0300FAFA	-	-	TLSv13	None	None	-	0	None	8701	GREASE-FA	GREASE_0A	-
#0x00800000	-	nix	PCT	-?-	-?-	-?-	-?-	-?-	-	const	-	for testing only
0x00800001	-	-?-	PCT	-?-	-?-	-?-	-?-	-?-	-	PCT_SSL_CERT_TYPE	PCT1_CERT_X509	PCT
0x00800003	-	-?-	PCT	-?-	-?-	-?-	-?-	-?-	-	PCT_SSL_CERT_TYPE	PCT1_CERT_X509_CHAIN	PCT
0x00810001	-	-?-	PCT	-?-	-?-	-?-	-?-	-?-	-	PCT_SSL_HASH_TYPE	PCT1_HASH_MD5	PCT
0x00810003	-	-?-	PCT	-?-	-?-	-?-	-?-	-?-	-	PCT_SSL_HASH_TYPE	PCT1_HASH_SHA	PCT
0x00820003	-	-?-	PCT	-?-	-?-	-?-	-?-	-?-	-	PCT_SSL_EXCH_TYPE	PCT1_EXCH_RSA_PKCS1	PCT
0x00823004	-	-?-	PCT	-?-	-?-	-?-	-?-	-?-	-	PCT_SSL_CIPHER_TYPE_1ST_HALF	PCT1_CIPHER_RC4	PCT
0x00842840	-	-?-	PCT	-?-	-?-	-?-	-?-	-?-	-	PCT_SSL_CIPHER_TYPE_2ND_HALF	PCT1_ENC_BITS_40|PCT1_MAC_BITS_128	PCT
0x00848040	-	-?-	PCT	-?-	-?-	-?-	-?-	-?-	-	PCT_SSL_CIPHER_TYPE_2ND_HALF	PCT1_ENC_BITS_128|PCT1_MAC_BITS_128	PCT
0x008F8001	-	-?-	PCT	-?-	-?-	-?-	-?-	-?-	-	PCT_SSL_COMPAT	PCT_VERSION_1	PCT
0x02000000	-	weak	SSLv2	RSA(512)	None	None	0	MD5	-?-	NULL-MD5	NULL_WITH_MD5	-
0x02010080	MEDIUM	weak	SSLv2	RSA	RSA	RC4	128	MD5	-?-	RC4-MD5	RC4_128_WITH_MD5	-
0x02020080	WEAK	WEAK	SSLv2	RSA(512)	RSA	RC4	40	MD5	-?-	EXP-RC4-MD5	RC4_128_EXPORT40_WITH_MD5	EXPORT
0x02030080	MEDIUM	weak	SSLv2	RSA	RSA	RC2	128	MD5	-?-	RC2-CBC-MD5,RC2-MD5	RC2_128_CBC_WITH_MD5	-
0x02040080	-?-	weak	SSLv2	RSA(512)	RSA	RC2	40	MD5	-?-	EXP-RC2-CBC-MD5,EXP-RC2-MD5	RC2_128_CBC_EXPORT40_WITH_MD5	EXPORT
0x02050080	MEDIUM	weak	SSLv2	RSA	RSA	IDEA	128	MD5	-?-	IDEA-CBC-MD5	IDEA_128_CBC_WITH_MD5,IDEA_CBC_WITH_MD5	-
0x02060040	LOW	weak	SSLv2	RSA	RSA	DES	56	MD5	-?-	DES-CBC-MD5	DES_64_CBC_WITH_MD5,DES_CBC_WITH_MD5	-
0x02060140	-?-	weak	SSLv2	RSA	RSA	DES	56	SHA1	-?-	DES-CBC-SHA	DES_64_CBC_WITH_SHA	-
0x020700C0	MEDIUM	weak	SSLv2	RSA	RSA	3DES	112	MD5	-?-	DES-CBC3-MD5	DES_192_EDE3_CBC_WITH_MD5	-
0x020701C0	MEDIUM	weak	SSLv2	RSA	RSA	3DES	112	SHA1	-?-	DES-CBC3-SHA	DES_192_EDE3_CBC_WITH_SHA	-
0x02080080	LOW	weak	SSLv2	RSA	RSA	RC4	64	MD5	-?-	RC4-64-MD5,EXP-RC4-64-MD5	RC4_64_WITH_MD5	BSAFE
0x02FF0800	-?-	weak	SSLv2	RSA	RSA	DES	64	MD5	-?-	DES-CFB-M1	DES_64_CFB64_WITH_MD5_1	-
0x02FF0810	-?-	weak	SSLv2	RSA(512)	None	None	0	MD5	-	NULL	NULL	SSLeay
0x02FFFFFF	-	-	SSLv2	None	None	-	0	None	-?-	SSL2_UNFFINED_CIPHER_0x02FFFFFF	SSL2_UNFFINED_CIPHER_0x02FFFFFF	internal
0x03000000	-?-	weak	SSLv3	RSA	None	None	0	MD5	5246	NULL-NULL	NULL_WITH_NULL_NULL	SSLeay
0x03000001	-?-	weak	SSLv3	RSA	RSA	None	0	MD5	5246	NULL-MD5	RSA_WITH_NULL_MD5,RSA_NULL_MD5	EXPORT
0x03000002	-?-	weak	SSLv3	RSA	RSA	None	0	SHA1	5246	NULL-SHA	RSA_WITH_NULL_SHA,RSA_NULL_SHA	-
0x03000003	WEAK	WEAK	SSLv3	RSA(512)	RSA	RC4	40	MD5	4346,6347	EXP-RC4-MD5	RSA_WITH_RC4_40_MD5,RSA_RC4_40_MD5,RSA_EXPORT_WITH_RC4_40_MD5,RC4_128_EXPORT40_WITH_MD5	EXPORT
0x03000004	MEDIUM	weak	SSLv3	RSA	RSA	RC4	128	MD5	5246,6347	RC4-MD5	RSA_WITH_RC4_128_MD5,RSA_RC4_128_MD5,RC4_128_WITH_MD5	-
0x03000005	MEDIUM	weak	SSLv3	RSA	RSA	RC4	128	SHA1	5246,6347	RC4-SHA	RSA_WITH_RC4_128_SHA,RSA_RC4_128_SHA,RC4_128_WITH_SHA	-
0x03000006	-?-	weak	SSLv3	RSA(512)	RSA	RC2	40	MD5	4346	EXP-RC2-CBC-MD5	RSA_WITH_RC2_40_MD5,RSA_RC2_40_MD5,RSA_EXPORT_WITH_RC2_CBC_40_MD5,RC2_128_CBC_EXPORT40_WITH_MD5	EXPORT
0x03000007	MEDIUM	weak	SSLv3	RSA	RSA	IDEA	128	SHA1	5469	IDEA-CBC-SHA	RSA_WITH_IDEA_CBC_SHA,RSA_WITH_IDEA_SHA,RSA_IDEA_128_SHA	-
0x03000008	WEAK	WEAK	SSLv3	RSA(512)	RSA	DES	40	SHA1	4346	EXP-DES-CBC-SHA	RSA_DES_40_CBC_SHA,RSA_EXPORT_WITH_DES40_CBC_SHA	EXPORT
0x03000009	LOW	weak	SSLv3	RSA	RSA	DES	56	SHA1	5469	DES-CBC-SHA	RSA_WITH_DES_CBC_SHA,RSA_DES_64_CBC_SHA	-
0x0300000A	MEDIUM	weak	SSLv3	RSA	RSA	3DES	112	SHA1	5246	DES-CBC3-SHA	RSA_WITH_3DES_EDE_CBC_SHA,RSA_DES_192_CBC3_SHA,DES_192_EDE3_CBC_WITH_SHA	-
0x0300000B	-?-	weak	SSLv3	DH/DSS	DH	DES	40	SHA1	4346	EXP-DH-DSS-DES-CBC-SHA	DH_DSS_DES_40_CBC_SHA,DH_DSS_EXPORT_WITH_DES40_CBC_SHA	EXPORT
0x0300000C	LOW	weak	SSLv3	DH/DSS	DH	DES	56	SHA1	5469	DH-DSS-DES-CBC-SHA	DH_DSS_DES_64_CBC_SHA,DH_DSS_WITH_DES_CBC_SHA	-
0x0300000D	MEDIUM	weak	SSLv3	DH/DSS	DH	3DES	112	SHA1	5246	DH-DSS-DES-CBC3-SHA	DH_DSS_DES_192_CBC3_SHA,DH_DSS_WITH_3DES_EDE_CBC_SHA	-
0x0300000E	-?-	weak	SSLv3	DH/RSA	DH	DES	40	SHA1	4346	EXP-DH-RSA-DES-CBC-SHA	DH_RSA_DES_40_CBC_SHA,DH_RSA_EXPORT_WITH_DES40_CBC_SHA	EXPORT
0x0300000F	LOW	weak	SSLv3	DH/RSA	DH	DES	56	SHA1	5469	DH-RSA-DES-CBC-SHA	DH_RSA_DES_64_CBC_SHA,DH_RSA_WITH_DES_CBC_SHA	-
0x03000010	MEDIUM	weak	SSLv3	DH/RSA	DH	3DES	112	SHA1	5246	DH-RSA-DES-CBC3-SHA	DH_RSA_DES_192_CBC3_SHA,DH_RSA_WITH_3DES_EDE_CBC_SHA	-
0x03000011	-?-	weak	SSLv3	DH(512)	DSS	DES	40	SHA1	4346	EXP-EDH-DSS-DES-CBC-SHA	EDH_DSS_DES_40_CBC_SHA,DHE_DSS_DES_40_CBC_SHA,EDH_DSS_EXPORT_WITH_DES40_CBC_SHA	EXPORT
0x03000012	LOW	weak	SSLv3	DH	DSS	DES	56	SHA1	5469	EDH-DSS-DES-CBC-SHA,EDH-DSS-CBC-SHA	EDH_DSS_DES_64_CBC_SHA,DHE_DSS_DES_64_CBC_SHA,DHE_DSS_WITH_DES_CBC_SHA,EDH_DSS_WITH_DES_CBC_SHA	-
0x03000013	MEDIUM	weak	SSLv3	DH	DSS	3DES	112	SHA1	5246	EDH-DSS-DES-CBC3-SHA,DHE-DSS-DES-CBC3-SHA	EDH_DSS_DES_192_CBC3_SHA,DHE_DSS_DES_192_CBC3_SHA,DHE_DSS_WITH_3DES_EDE_CBC_SHA,EDH_DSS_WITH_3DES_EDE_CBC_SHA	-
0x03000014	LOW	weak	SSLv3	DH(512)	RSA	DES	40	SHA1	4346	EXP-EDH-RSA-DES-CBC-SHA	EDH_RSA_DES_40_CBC_SHA,DHE_RSA_DES_40_CBC_SHA,DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,EDH_RSA_EXPORT_WITH_DES40_CBC_SHA	EXPORT
0x03000015	LOW	weak	SSLv3	DH	RSA	DES	56	SHA1	5469	EDH-RSA-DES-CBC-SHA	EDH_RSA_DES_64_CBC_SHA,DHE_RSA_DES_64_CBC_SHA,DHE_RSA_WITH_DES_CBC_SHA,EDH_RSA_WITH_DES_CBC_SHA	-
0x03000016	MEDIUM	weak	SSLv3	DH	RSA	3DES	112	SHA1	5246	EDH-RSA-DES-CBC3-SHA,DHE-RSA-DES-CBC3-SHA	EDH_RSA_DES_192_CBC3_SHA,DHE_RSA_DES_192_CBC3_SHA,DHE_RSA_WITH_3DES_EDE_CBC_SHA,EDH_RSA_WITH_3DES_EDE_CBC_SHA	-
0x03000017	WEAK	WEAK	SSLv3	DH(512)	None	RC4	40	MD5	4346,6347	EXP-ADH-RC4-MD5	ADH_RC4_40_MD5,DH_anon_EXPORT_WITH_RC4_40_MD5	EXPORT
0x03000018	MEDIUM	weak	SSLv3	DH	None	RC4	128	MD5	5246,6347	ADH-RC4-MD5,DHanon-RC4-MD5	ADH_RC4_128_MD5,DH_anon_WITH_RC4_MD5,DH_anon_WITH_RC4_128_MD5	-
0x03000019	-?-	weak	SSLv3	DH(512)	None	DES	40	SHA1	4346	EXP-ADH-DES-CBC-SHA	ADH_DES_40_CBC_SHA,DH_anon_EXPORT_WITH_DES40_CBC_SHA	EXPORT
0x0300001A	LOW	weak	SSLv3	DH	None	DES	56	SHA1	5469	ADH-DES-CBC-SHA,DHanon-DES-CBC-SHA	ADH_DES_64_CBC_SHA,DH_anon_WITH_DES_CBC_SHA	-
0x0300001B	MEDIUM	weak	SSLv3	DH	None	3DES	112	SHA1	5246	ADH-DES-CBC3-SHA,DHanon-DES-CBC3-SHA	ADH_DES_192_CBC_SHA,DH_anon_WITH_3DES_EDE_CBC_SHA	-
0x0300001C	-?-	weak	SSLv3	FZA	FZA	None	0	SHA1	5246	FZA-NULL-SHA	FZA_DMS_NULL_SHA,FORTEZZA_KEA_WITH_NULL_SHA	M
0x0300001D	MEDIUM	weak	SSLv3	FZA	FZA	FZA	0	SHA1	5246	FZA-FZA-SHA,FZA-FZA-CBC-SHA	FZA_DMS_FZA_SHA,FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA	M
#0x0300001E-bug	WEAK	WEAK	SSLv3	FZA	FZA	RC4	128	SHA1	-	FZA-RC4-SHA	FZA_DMS_RC4_SHA	M
0x0300001E	LOW	weak	SSLv3	KRB5	KRB5	DES	56	SHA1	2712	KRB5-DES-CBC-SHA	KRB5_DES_64_CBC_SHA,KRB5_WITH_DES_CBC_SHA	P
0x0300001F	MEDIUM	weak	SSLv3	KRB5	KRB5	3DES	112	SHA1	2712	KRB5-DES-CBC3-SHA	KRB5_DES_192_CBC3_SHA,KRB5_WITH_3DES_EDE_CBC_SHA	P
0x03000020	MEDIUM	weak	SSLv3	KRB5	KRB5	RC4	128	SHA1	2712,6347	KRB5-RC4-SHA	KRB5_RC4_128_SHA,KRB5_WITH_RC4_128_SHA	P
0x03000021	MEDIUM	weak	SSLv3	KRB5	KRB5	IDEA	128	SHA1	2712	KRB5-IDEA-CBC-SHA	KRB5_IDEA_128_CBC_SHA,KRB5_WITH_IDEA_CBC_SHA	P
0x03000022	LOW	weak	SSLv3	KRB5	KRB5	DES	56	MD5	2712	KRB5-DES-CBC-MD5	KRB5_DES_64_CBC_MD5,KRB5_WITH_DES_CBC_MD5	P
0x03000023	MEDIUM	weak	SSLv3	KRB5	KRB5	3DES	112	MD5	2712	KRB5-DES-CBC3-MD5	KRB5_DES_192_CBC3_MD5,KRB5_WITH_3DES_EDE_CBC_MD5	P
0x03000024	MEDIUM	weak	SSLv3	KRB5	KRB5	RC4	128	MD5	2712,6347	KRB5-RC4-MD5	KRB5_RC4_128_MD5,KRB5_WITH_RC4_128_MD5	P
0x03000025	MEDIUM	weak	SSLv3	KRB5	KRB5	IDEA	128	MD5	2712	KRB5-IDEA-CBC-MD5	KRB5_IDEA_128_CBC_MD5,KRB5_WITH_IDEA_CBC_MD5	P
0x03000026	-?-	weak	SSLv3	KRB5	KRB5	DES	40	SHA1	2712	EXP-KRB5-DES-CBC-SHA	KRB5_DES_40_CBC_SHA,KRB5_EXPORT_WITH_DES_CBC_40_SHA	EXPORT,P
0x03000027	-?-	weak	SSLv3	KRB5	KRB5	RC2	40	SHA1	2712	EXP-KRB5-RC2-CBC-SHA	KRB5_RC2_40_CBC_SHA,KRB5_EXPORT_WITH_RC2_CBC_40_SHA	EXPORT,P
0x03000028	-?-	weak	SSLv3	KRB5	KRB5	RC4	40	SHA1	2712,6347	EXP-KRB5-RC4-SHA	KRB5_RC4_40_SHA,KRB5_EXPORT_WITH_RC4_40_SHA	EXPORT,P
0x03000029	-?-	weak	SSLv3	KRB5	KRB5	DES	40	MD5	2712	EXP-KRB5-DES-CBC-MD5	KRB5_DES_40_CBC_MD5,KRB5_EXPORT_WITH_DES_CBC_40_MD5	EXPORT,P
0x0300002A	-?-	weak	SSLv3	KRB5	KRB5	RC2	40	MD5	2712	EXP-KRB5-RC2-CBC-MD5	KRB5_RC2_40_CBC_MD5,KRB5_EXPORT_WITH_RC2_CBC_40_MD5,KRB5_WITH_RC2_CBC_40_MD5	EXPORT,P
0x0300002B	-?-	weak	SSLv3	KRB5	KRB5	RC4	40	MD5	2712,6347	EXP-KRB5-RC4-MD5	KRB5_RC4_40_MD5,KRB5_EXPORT_WITH_RC4_40_MD5	EXPORT,P
0x0300002C	-?-	weak	SSLv3	DH	RSA	None	0	SHA1	4785	PSK-SHA,PSK-NULL-SHA	PSK_WITH_NULL_SHA	-
0x0300002D	-?-	weak	SSLv3	DHEPSK	PSK	None	0	SHA1	4785	DHE-PSK-SHA,DHE-PSK-NULL-SHA	DHE_PSK_WITH_NULL_SHA	FIXME
0x0300002E	-?-	weak	SSLv3	RSAPSK	PSK	None	0	SHA1	4785	RSA-PSK-SHA,RSA-PSK-NULL-SHA	RSA_PSK_WITH_NULL_SHA	FIXME
0x0300002F	HIGH	weak	SSLv3	RSA	RSA	AES	128	SHA1	5246	AES128-SHA	RSA_WITH_AES_128_CBC_SHA,RSA_WITH_AES_128_SHA	-
0x03000030	HIGH	weak	SSLv3	DH	DSS	AES	128	SHA1	5246	DH-DSS-AES128-SHA	DH_DSS_WITH_AES_128_SHA,DH_DSS_WITH_AES_128_CBC_SHA	-
0x03000031	HIGH	weak	SSLv3	DH	RSA	AES	128	SHA1	5246	DH-RSA-AES128-SHA	DH_RSA_WITH_AES_128_SHA,DH_RSA_WITH_AES_128_CBC_SHA	-
0x03000032	HIGH	weak	SSLv3	DH	DSS	AES	128	SHA1	5246	DHE-DSS-AES128-SHA,EDH-DSS-AES128-SHA	DHE_DSS_WITH_AES_128_CBC_SHA,DHE_DSS_WITH_AES_128_SHA	BSAFE
0x03000033	HIGH	weak	SSLv3	DH	RSA	AES	128	SHA1	5246	DHE-RSA-AES128-SHA,EDH-RSA-AES128-SHA	DHE_RSA_WITH_AES_128_CBC_SHA,DHE_RSA_WITH_AES_128_SHA	-
0x03000034	HIGH	weak	SSLv3	DH	None	AES	128	SHA1	5246	ADH-AES128-SHA	ADH_WITH_AES_128_SHA,DH_anon_WITH_AES_128_CBC_SHA	-
0x03000035	HIGH	weak	SSLv3	RSA	RSA	AES	256	SHA1	5246	AES256-SHA	RSA_WITH_AES_256_SHA,RSA_WITH_AES_256_CBC_SHA	-
0x03000036	HIGH	weak	SSLv3	DH	DSS	AES	256	SHA1	5246	DH-DSS-AES256-SHA	DH_DSS_WITH_AES_256_SHA,DH_DSS_WITH_AES_256_CBC_SHA	-
0x03000037	HIGH	weak	SSLv3	DH	RSA	AES	256	SHA1	5246	DH-RSA-AES256-SHA	DH_RSA_WITH_AES_256_SHA,DH_RSA_WITH_AES_256_CBC_SHA	-
0x03000038	HIGH	weak	SSLv3	DH	DSS	AES	256	SHA1	5246	DHE-DSS-AES256-SHA,EDH-DSS-AES256-SHA	DHE_DSS_WITH_AES_256_SHA,DHE_DSS_WITH_AES_256_CBC_SHA	-
0x03000039	HIGH	weak	SSLv3	DH	RSA	AES	256	SHA1	5246	DHE-RSA-AES256-SHA,EDH-RSA-AES256-SHA	DHE_RSA_WITH_AES_256_SHA,DHE_RSA_WITH_AES_256_CBC_SHA	-
0x0300003A	HIGH	weak	SSLv3	DH	None	AES	256	SHA1	5246	ADH-AES256-SHA	ADH_WITH_AES_256_SHA,DH_anon_WITH_AES_256_CBC_SHA	-
0x0300003B	-?-	weak	TLSv12	RSA	RSA	None	0	SHA256	5246	NULL-SHA256	RSA_WITH_NULL_SHA256	L
0x0300003C	HIGH	weak	TLSv12	RSA	RSA	AES	128	SHA256	5246	AES128-SHA256	RSA_WITH_AES_128_SHA256,RSA_WITH_AES_128_CBC_SHA256	L
0x0300003D	HIGH	weak	TLSv12	RSA	RSA	AES	256	SHA256	5246	AES256-SHA256	RSA_WITH_AES_256_SHA256,RSA_WITH_AES_256_CBC_SHA256	L
0x0300003E	HIGH	weak	TLSv12	DH/DSS	DH	AES	128	SHA256	5246	DH-DSS-AES128-SHA256	DH_DSS_WITH_AES_128_SHA256,DH_DSS_WITH_AES_128_CBC_SHA256	L
0x0300003F	HIGH	weak	TLSv12	DH/RSA	DH	AES	128	SHA256	5246	DH-RSA-AES128-SHA256	DH_RSA_WITH_AES_128_SHA256,DH_RSA_WITH_AES_128_CBC_SHA256	L
0x03000040	HIGH	weak	TLSv12	DH	DSS	AES	128	SHA256	5246	DHE-DSS-AES128-SHA256	DHE_DSS_WITH_AES_128_SHA256,DHE_DSS_WITH_AES_128_CBC_SHA256	L
0x03000041	HIGH	weak	TLSv1	RSA	RSA	CAMELLIA	128	SHA1	4132,5932	CAMELLIA128-SHA	RSA_WITH_CAMELLIA_128_CBC_SHA	-
0x03000042	HIGH	weak	TLSv1	DH	DSS	CAMELLIA	128	SHA1	4132,5932	DH-DSS-CAMELLIA128-SHA	DH_DSS_WITH_CAMELLIA_128_CBC_SHA	-
0x03000043	HIGH	weak	TLSv1	DH	RSA	CAMELLIA	128	SHA1	4132,5932	DH-RSA-CAMELLIA128-SHA	DH_RSA_WITH_CAMELLIA_128_CBC_SHA	-
0x03000044	HIGH	weak	TLSv1	DH	DSS	CAMELLIA	128	SHA1	4132,5932	DHE-DSS-CAMELLIA128-SHA	DHE_DSS_WITH_CAMELLIA_128_CBC_SHA	-
0x03000045	HIGH	weak	TLSv1	DH	RSA	CAMELLIA	128	SHA1	4132,5932	DHE-RSA-CAMELLIA128-SHA	DHE_RSA_WITH_CAMELLIA_128_CBC_SHA	-
0x03000046	HIGH	weak	TLSv1	DH	None	CAMELLIA	128	SHA1	4132,5932	ADH-CAMELLIA128-SHA	ADH_WITH_CAMELLIA_128_CBC_SHA,DH_anon_WITH_CAMELLIA_128_CBC_SHA	-
0x03000060	WEAK	WEAK	SSLv3	RSA(1024)	RSA	RC4	56	MD5	-?-	EXP1024-RC4-MD5	RSA_EXPORT1024_WITH_RC4_56_MD5	EXPORT
0x03000061	-?-	weak	SSLv3	RSA(1024)	RSA	RC2	56	MD5	-?-	EXP1024-RC2-CBC-MD5	RSA_EXPORT1024_WITH_RC2_CBC_56_MD5	EXPORT
0x03000062	-?-	weak	SSLv3	RSA(1024)	RSA	DES	56	SHA1	-?-	EXP1024-DES-CBC-SHA,EXP-DES-56-SHA	RSA_EXPORT1024_WITH_DES_CBC_SHA	EXPORT
0x03000063	-?-	weak	SSLv3	DH(1024)	DSS	DES	56	SHA1	-?-	EXP1024-DHE-DSS-DES-CBC-SHA,EXP-EDH-DSS-DES-56-SHA	DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA,DHE_DSS_EXPORT_WITH_DES40_CBC_SHA	EXPORT,T
0x03000064	WEAK	WEAK	SSLv3	RSA(1024)	RSA	RC4	56	SHA1	-?-	EXP1024-RC4-SHA,EXP-RC4-56-SHA	RSA_EXPORT1024_WITH_RC4_56_SHA	EXPORT
0x03000065	WEAK	WEAK	SSLv3	DH(1024)	DSS	RC4	56	SHA1	-?-	EXP1024-DHE-DSS-RC4-SHA,EXP-EDH-DSS-RC4-56-SHA	DHE_DSS_EXPORT1024_WITH_RC4_56_SHA	EXPORT,BSAFE
0x03000066	MEDIUM	weak	SSLv3	DH	DSS	RC4	128	SHA1	-?-	DHE-DSS-RC4-SHA,EDH-DSS-RC4-SHA	DHE_DSS_WITH_RC4_128_SHA	BSAFE
0x03000067	HIGH	weak	TLSv12	DH	RSA	AES	128	SHA256	5246	DHE-RSA-AES128-SHA256	DHE_RSA_WITH_AES_128_SHA256,DHE_RSA_WITH_AES_128_CBC_SHA256	L
0x03000068	HIGH	weak	TLSv12	DH/DSS	DH	AES	256	SHA256	5246	DH-DSS-AES256-SHA256	DH_DSS_WITH_AES_256_SHA256,DH_DSS_WITH_AES_256_CBC_SHA256	-
0x03000069	HIGH	weak	TLSv12	DH/RSA	DH	AES	256	SHA256	5246	DH-RSA-AES256-SHA256	DH_RSA_WITH_AES_256_SHA256,DH_RSA_WITH_AES_256_CBC_SHA256	-
0x0300006A	HIGH	weak	TLSv12	DH	DSS	AES	256	SHA256	5246	DHE-DSS-AES256-SHA256	DHE_DSS_WITH_AES_256_SHA256,DHE_DSS_WITH_AES_256_CBC_SHA256	L
0x0300006B	HIGH	weak	TLSv12	DH	RSA	AES	256	SHA256	5246	DHE-RSA-AES256-SHA256	DHE_RSA_WITH_AES_256_SHA256,DHE_RSA_WITH_AES_256_CBC_SHA256	L
0x0300006C	HIGH	weak	TLSv12	DH	None	AES	128	SHA256	5246	ADH-AES128-SHA256	ADH_WITH_AES_128_SHA256,DH_anon_WITH_AES_128_CBC_SHA256	L
0x0300006D	HIGH	weak	TLSv12	DH	None	AES	256	SHA256	5246	ADH-AES256-SHA256	ADH_WITH_AES_256_SHA256,DH_anon_WITH_AES_256_CBC_SHA256	L
0x03000070	-?-	weak	SSLv3	DH	DSS	CAST	128	SHA1	-	DHE-DSS-CAST128-CBC-SHA	DHE_DSS_WITH_CAST_128_CBC_SHA	PGP,H
0x03000071	-?-	weak	SSLv3	DH	DSS	CAST	128	RIPEMD	-	DHE-DSS-CAST128-CBC-RMD	DHE_DSS_WITH_CAST_128_CBC_RMD	PGP,H
0x03000072	-?-	weak	SSLv3	DH	DSS	3DES	128	RIPEMD	-?-	DHE-DSS-3DES-EDE-CBC-RMD	DHE_DSS_WITH_3DES_EDE_CBC_RMD	PGP
0x03000073	-?-	weak	SSLv3	DH	DSS	AES	128	RIPEMD	-?-	DHE-DSS-AES128-CBC-RMD	DHE_DSS_WITH_AES_128_CBC_RMD	PGP
0x03000074	-?-	weak	SSLv3	DH	DSS	AES	128	RIPEMD	-?-	DHE-DSS-AES256-CBC-RMD	DHE_DSS_WITH_AES_256_CBC_RMD	PGP
0x03000075	-?-	weak	SSLv3	DH	RSA	CAST	128	SHA1	-	DHE-RSA-CAST128-CBC-SHA	DHE_RSA_WITH_CAST_128_CBC_SHA	PGP,H
0x03000076	-?-	weak	SSLv3	DH	RSA	CAST	128	RIPEMD	-	DHE-RSA-CAST128-CBC-RMD	DHE_RSA_WITH_CAST_128_CBC_RMD	PGP,H
0x03000077	-?-	weak	SSLv3	DH	RSA	3DES	128	RIPEMD	-?-	DHE-RSA-3DES-EDE-CBC-RMD	DHE_RSA_WITH_3DES_EDE_CBC_RMD	PGP
0x03000078	-?-	weak	SSLv3	DH	RSA	AES	128	RIPEMD	-?-	DHE-RSA-AES128-CBC-RMD	DHE_RSA_WITH_AES_128_CBC_RMD	PGP
0x03000079	-?-	weak	SSLv3	DH	RSA	AES	128	RIPEMD	-?-	DHE-RSA-AES256-CBC-RMD	DHE_RSA_WITH_AES_256_CBC_RMD	PGP
0x0300007A	-?-	weak	SSLv3	RSA	RSA	CAST	128	SHA1	-	RSA-CAST128-CBC-SHA	RSA_WITH_CAST_128_CBC_SHA	H
0x0300007B	-?-	weak	SSLv3	RSA	RSA	CAST	128	RIPEMD	-	RSA-CAST128-CBC-RMD	RSA_WITH_CAST_128_CBC_RMD	H
0x0300007C	-?-	weak	SSLv3	RSA	RSA	3DES	128	RIPEMD	-?-	RSA-3DES-EDE-CBC-RMD	RSA_WITH_3DES_EDE_CBC_RMD	-
0x0300007D	-?-	weak	SSLv3	RSA	RSA	AES	128	RIPEMD	-?-	RSA-AES128-CBC-RMD	RSA_WITH_AES_128_CBC_RMD	-
0x0300007E	-?-	weak	SSLv3	RSA	RSA	AES	128	RIPEMD	-?-	RSA-AES256-CBC-RMD	RSA_WITH_AES_256_CBC_RMD	-
0x03000080	HIGH	HIGH	SSLv3	GOST	GOST94	GOST89	256	GOST89	5830	GOST94-GOST89-GOST89	GOSTR341094_WITH_28147_CNT_IMIT	G
0x03000081	HIGH	HIGH	SSLv3	GOST	GOST01	GOST89	256	GOST89	5830	GOST2001-GOST89-GOST89	GOSTR341001_WITH_28147_CNT_IMIT	G
0x03000082	-?-	weak	SSLv3	GOST	GOST94	None	0	GOST94	-?-	GOST94-NULL-GOST94	GOSTR341094_WITH_NULL_GOSTR3411	G
0x03000083	-?-	weak	SSLv3	GOST	GOST01	None	0	GOST94	-?-	GOST2001-NULL-GOST94	GOSTR341001_WITH_NULL_GOSTR3411	G
0x03000084	HIGH	weak	TLSv1	RSA	RSA	CAMELLIA	256	SHA1	4132,5932	CAMELLIA256-SHA	RSA_WITH_CAMELLIA_256_CBC_SHA	-
0x03000085	HIGH	weak	TLSv1	DSS	DH	CAMELLIA	256	SHA1	4132,5932	DH-DSS-CAMELLIA256-SHA	DH_DSS_WITH_CAMELLIA_256_CBC_SHA	-
0x03000086	HIGH	weak	TLSv1	RSA	DH	CAMELLIA	256	SHA1	4132,5932	DH-RSA-CAMELLIA256-SHA	DH_RSA_WITH_CAMELLIA_256_CBC_SHA	-
0x03000087	HIGH	weak	TLSv1	DH	DSS	CAMELLIA	256	SHA1	4132,5932	DHE-DSS-CAMELLIA256-SHA	DHE_DSS_WITH_CAMELLIA_256_CBC_SHA	-
0x03000088	HIGH	weak	TLSv1	DH	RSA	CAMELLIA	256	SHA1	4132,5932	DHE-RSA-CAMELLIA256-SHA	DHE_RSA_WITH_CAMELLIA_256_CBC_SHA	-
0x03000089	HIGH	weak	TLSv1	DH	None	CAMELLIA	256	SHA1	4132,5932	ADH-CAMELLIA256-SHA	ADH_WITH_CAMELLIA_256_CBC_SHA,DH_anon_WITH_CAMELLIA_256_CBC_SHA	-
0x0300008A	MEDIUM	medium	SSLv3	PSK	PSK	RC4	128	SHA1	4279,6347	PSK-RC4-SHA	PSK_WITH_RC4_128_SHA,PSK_WITH_RC4_128_CBC_SHA	-
0x0300008B	MEDIUM	medium	SSLv3	PSK	PSK	3DES	112	SHA1	4279	PSK-3DES-EDE-CBC-SHA,PSK-3DES-SHA	PSK_WITH_3DES_EDE_CBC_SHA	-
0x0300008C	HIGH	medium	SSLv3	PSK	PSK	AES	128	SHA1	4279	PSK-AES128-CBC-SHA	PSK_WITH_AES_128_CBC_SHA	-
0x0300008D	HIGH	medium	SSLv3	PSK	PSK	AES	256	SHA1	4279	PSK-AES256-CBC-SHA	PSK_WITH_AES_256_CBC_SHA	-
0x0300008E	-?-	medium	TLSv12	DHE	PSK	RC4	128	SHA1	4279,6347	DHE-PSK-RC4-SHA	DHE_PSK_WITH_RC4_128_SHA,DHE_PSK_WITH_RC4_128_CBC_SHA	FIXME
0x0300008F	-?-	medium	TLSv12	DHE	PSK	3DES	112	SHA1	4279	DHE-PSK-3DES-SHA	DHE_PSK_WITH_3DES_EDE_CBC_SHA	FIXME
0x03000090	HIGH	medium	TLSv12	DHE	PSK	AES	128	SHA1	4279	DHE-PSK-AES128-SHA,DHE-PSK-AES128-CBC-SHA	DHE_PSK_WITH_AES_128_CBC_SHA	-
0x03000091	HIGH	medium	TLSv12	DHE	PSK	AES	256	SHA1	4279	DHE-PSK-AES256-SHA,DHE-PSK-AES256-CBC-SHA	DHE_PSK_WITH_AES_256_CBC_SHA	-
0x03000092	MEDIUM	medium	SSLv3	RSAPSK	RSA	RC4	128	SHA1	4279,6347	RSA-PSK-RC4-SHA	RSA_PSK_WITH_RC4_128_SHA,RSA_PSK_WITH_RC4_128_CBC_SHA	-
0x03000093	-?-	medium	SSLv3	RSAPSK	RSA	3DES	112	SHA1	4279	RSA-PSK-3DES-SHA,RSA-PSK-3DES-EDE-CBC-SHA	RSA_PSK_WITH_3DES_EDE_CBC_SHA	-
0x03000094	HIGH	medium	SSLv3	RSAPSK	AES	AES	128	SHA1	4279	RSA-PSK-AES128-SHA,RSA-PSK-AES128-CBC-SHA	RSA_PSK_WITH_AES_128_CBC_SHA	-
0x03000095	HIGH	medium	SSLv3	RSAPSK	AES	RSA	256	SHA1	4279	RSA-PSK-AES256-SHA,RSA-PSK-AES256-CBC-SHA	RSA_PSK_WITH_AES_256_CBC_SHA	-
0x03000096	MEDIUM	weak	TLSv1	RSA	RSA	SEED	128	SHA1	4162	SEED-SHA	RSA_WITH_SEED_SHA,RSA_WITH_SEED_CBC_SHA	OSX
0x03000097	MEDIUM	weak	TLSv1	DH/DSS	DH	SEED	128	SHA1	4162	DH-DSS-SEED-SHA	DH_DSS_WITH_SEED_SHA,DH_DSS_WITH_SEED_CBC_SHA	-
0x03000098	MEDIUM	weak	TLSv1	DH/RSA	DH	SEED	128	SHA1	4162	DH-RSA-SEED-SHA	DH_RSA_WITH_SEED_SHA,DH_RSA_WITH_SEED_CBC_SHA	-
0x03000099	MEDIUM	weak	TLSv1	DH	DSS	SEED	128	SHA1	4162	DHE-DSS-SEED-SHA	DHE_DSS_WITH_SEED_SHA,DHE_DSS_WITH_SEED_CBC_SHA	OSX
0x0300009A	MEDIUM	weak	TLSv1	DH	RSA	SEED	128	SHA1	4162	DHE-RSA-SEED-SHA	DHE_RSA_WITH_SEED_SHA,DHE_RSA_WITH_SEED_CBC_SHA	OSX
0x0300009B	MEDIUM	weak	TLSv1	DH	None	SEED	128	SHA1	4162	ADH-SEED-SHA,DHanon-SEED-SHA	ADH_WITH_SEED_SHA,ADH_WITH_SEED_SHA_SHA,DH_anon_WITH_SEED_CBC_SHA	OSX
0x0300009C	HIGH	HIGH	TLSv12	RSA	RSA	AESGCM	128	AEAD	5288	AES128-GCM-SHA256	RSA_WITH_AES_128_GCM_SHA256	L
0x0300009D	HIGH	HIGH	TLSv12	RSA	RSA	AESGCM	256	AEAD	5288	AES256-GCM-SHA384	RSA_WITH_AES_256_GCM_SHA384	L
0x0300009E	HIGH	HIGH	TLSv12	DH	RSA	AESGCM	128	AEAD	5288	DHE-RSA-AES128-GCM-SHA256	DHE_RSA_WITH_AES_128_GCM_SHA256	L
0x0300009F	HIGH	HIGH	TLSv12	DH	RSA	AESGCM	256	AEAD	5288	DHE-RSA-AES256-GCM-SHA384	DHE_RSA_WITH_AES_256_GCM_SHA384	L
0x030000A0	HIGH	HIGH	TLSv12	DH/RSA	DH	AESGCM	128	AEAD	5288	DH-RSA-AES128-GCM-SHA256	DH_RSA_WITH_AES_128_GCM_SHA256	-
0x030000A1	HIGH	HIGH	TLSv12	DH/RSA	DH	AESGCM	256	AEAD	5288	DH-RSA-AES256-GCM-SHA384	DH_RSA_WITH_AES_256_GCM_SHA384	-
0x030000A2	HIGH	HIGH	TLSv12	DH	DSS	AESGCM	128	AEAD	5288	DHE-DSS-AES128-GCM-SHA256	DHE_DSS_WITH_AES_128_GCM_SHA256	L
0x030000A3	HIGH	HIGH	TLSv12	DH	DSS	AESGCM	256	AEAD	5288	DHE-DSS-AES256-GCM-SHA384	DHE_DSS_WITH_AES_256_GCM_SHA384	L
0x030000A4	HIGH	HIGH	TLSv12	DH/DSS	DH	AESGCM	128	AEAD	5288	DH-DSS-AES128-GCM-SHA256	DH_DSS_WITH_AES_128_GCM_SHA256	-
0x030000A5	HIGH	HIGH	TLSv12	DH/DSS	DH	AESGCM	256	AEAD	5288	DH-DSS-AES256-GCM-SHA384	DH_DSS_WITH_AES_256_GCM_SHA384	-
0x030000A6	HIGH	weak	TLSv12	DH	None	AESGCM	128	AEAD	5288	ADH-AES128-GCM-SHA256	ADH_WITH_AES_128_GCM_SHA256,DH_anon_WITH_AES_128_GCM_SHA256	L
0x030000A7	HIGH	weak	TLSv12	DH	None	AESGCM	256	AEAD	5288	ADH-AES256-GCM-SHA384	ADH_WITH_AES_256_GCM_SHA384,DH_anon_WITH_AES_256_GCM_SHA384,DH_anon_WITH_AES_256_GCM_SHA256	L,S
0x030000A8	HIGH	high	TLSv12	PSK	PSK	AESGCM	128	SHA256	5487	PSK-AES128-GCM-SHA256	PSK_WITH_AES_128_GCM_SHA256	-
0x030000A9	HIGH	high	TLSv12	PSK	PSK	AESGCM	256	SHA384	5487	PSK-AES256-GCM-SHA384	PSK_WITH_AES_256_GCM_SHA384	-
0x030000AA	HIGH	high	TLSv12	DHE	PSK	AESGCM	128	SHA256	5487	DHE-PSK-AES128-GCM-SHA256	DHE_PSK_WITH_AES_128_GCM_SHA256	-
0x030000AB	HIGH	high	TLSv12	DHE	PSK	AESGCM	256	SHA384	5487	DHE-PSK-AES256-GCM-SHA384	DHE_PSK_WITH_AES_256_GCM_SHA384	-
0x030000AC	HIGH	high	TLSv12	RSA	PSK	AESGCM	128	SHA256	5487	RSA-PSK-AES128-GCM-SHA256	RSA_PSK_WITH_AES_128_GCM_SHA256	-
0x030000AD	HIGH	high	TLSv12	RSA	PSK	AESGCM	256	SHA384	5487	RSA-PSK-AES256-GCM-SHA384,PSK-RSA-AES256-GCM-SHA384	RSA_PSK_WITH_AES_256_GCM_SHA384	-
0x030000AE	HIGH	medium	TLSv1	PSK	PSK	AES	128	SHA256	5487	PSK-AES128-SHA256,PSK-AES128-CBC-SHA256	PSK_WITH_AES_128_CBC_SHA256	K
0x030000AF	HIGH	medium	TLSv1	PSK	PSK	AES	256	SHA384	5487	PSK-AES256-SHA384,PSK-AES256-CBC-SHA384	PSK_WITH_AES_256_CBC_SHA384	K
0x030000B0	-?-	weak	TLSv1	PSK	PSK	None	0	SHA256	5487	PSK-SHA256,PSK-NULL-SHA256	PSK_WITH_NULL_SHA256	-
0x030000B1	-?-	weak	TLSv1	PSK	PSK	None	0	SHA384	5487	PSK-SHA384,PSK-NULL-SHA384	PSK_WITH_NULL_SHA384	-
0x030000B2	HIGH	medium	TLSv1	DHEPSK	PSK	AES	128	SHA256	5487	DHE-PSK-AES128-SHA256,DHE-PSK-AES128-CBC-SHA256	DHE_PSK_WITH_AES_128_CBC_SHA256	-
0x030000B3	HIGH	medium	TLSv1	DHE	PSK	AES	256	SHA384	5487	DHE-PSK-AES256-SHA384,DHE-PSK-AES256-CBC-SHA384	DHE_PSK_WITH_AES_256_CBC_SHA384	-
0x030000B4	-?-	weak	TLSv12	DHE	PSK	None	0	SHA256	5487	DHE-PSK-SHA256,DHE-PSK-NULL-SHA256	DHE_PSK_WITH_NULL_SHA256	-
0x030000B5	-?-	weak	TLSv12	DHE	PSK	None	0	SHA384	5487	DHE-PSK-SHA384,DHE-PSK-NULL-SHA384	DHE_PSK_WITH_NULL_SHA384	-
0x030000B6	HIGH	medium	TLSv1	RSAPSK	PSK	AES	128	SHA256	5487	RSA-PSK-AES128-SHA256,RSA-PSK-AES128-CBC-SHA256	RSA_PSK_WITH_AES_128_CBC_SHA256	-
0x030000B7	HIGH	medium	TLSv1	RSAPSK	PSK	AES	256	SHA384	5487	RSA-PSK-AES256-SHA384,RSA-PSK-AES256-CBC-SHA384	RSA_PSK_WITH_AES_256_CBC_SHA384	-
0x030000B8	-?-	weak	TLSv1	RSAPSK	RSA	None	0	SHA256	5487	RSA-PSK-SHA256,RSA-PSK-NULL-SHA256	RSA_PSK_WITH_NULL_SHA256	-
0x030000B9	-?-	weak	TLSv1	RSAPSK	RSA	None	0	SHA364	5487	RSA-PSK-SHA384,RSA-PSK-NULL-SHA384	RSA_PSK_WITH_NULL_SHA384	-
0x030000BA	HIGH	weak	TLSv12	RSA	RSA	CAMELLIA	128	SHA256	5932	CAMELLIA128-SHA256,RSA-CAMELLIA128-SHA256	RSA_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000BB	HIGH	weak	TLSv12	DH	DSS	CAMELLIA	128	SHA256	5932	DH-DSS-CAMELLIA128-SHA256	DH_DSS_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000BC	HIGH	weak	TLSv12	DH	RSA	CAMELLIA	128	SHA256	5932	DH-RSA-CAMELLIA128-SHA256	DH_RSA_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000BD	HIGH	weak	TLSv12	DH	DSS	CAMELLIA	128	SHA256	5932	DHE-DSS-CAMELLIA128-SHA256	DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000BE	HIGH	weak	TLSv12	DH	RSA	CAMELLIA	128	SHA256	5932	DHE-RSA-CAMELLIA128-SHA256	DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000BF	HIGH	weak	TLSv12	DH	None	CAMELLIA	128	SHA256	5932	ADH-CAMELLIA128-SHA256	ADH_WITH_CAMELLIA_128_CBC_SHA256,DH_anon_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000C0	HIGH	weak	TLSv12	RSA	RSA	CAMELLIA	256	SHA256	5932	CAMELLIA256-SHA256,RSA-CAMELLIA256-SHA256	RSA_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C1	HIGH	weak	TLSv12	DSS	DH	CAMELLIA	256	SHA256	5932	DH-DSS-CAMELLIA256-SHA256	DH_DSS_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C2	HIGH	weak	TLSv12	RSA	DH	CAMELLIA	256	SHA256	5932	DH-RSA-CAMELLIA256-SHA256	DH_RSA_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C3	HIGH	weak	TLSv12	DH	DSS	CAMELLIA	256	SHA256	5932	DHE-DSS-CAMELLIA256-SHA256	DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C4	HIGH	weak	TLSv12	DH	RSA	CAMELLIA	256	SHA256	5932	DHE-RSA-CAMELLIA256-SHA256	DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C5	HIGH	weak	TLSv12	DH	None	CAMELLIA	256	SHA256	5932	ADH-CAMELLIA256-SHA256	ADH_WITH_CAMELLIA_256_CBC_SHA256,DH_anon_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C6	-?-	-?-	TLSv13	SM2	SM2	SM4GCM	128	SM3	8998	TLS13-SM4-GCM,SM4-GCM-SM3	TLS_SM4_GCM_SM3	-
0x030000C7	-?-	-?-	TLSv13	SM2	SM2	SM4CCM	128	SM3	8998	TLS13-SM4-CCM,SM4-CCM-SM3	TLS_SM4_CCM_SM3	-
0x03001301	HIGH	HIGH	TLSv13	any	any	AESGCM	128	AEAD	8446	TLS13-AES-128-GCM-SHA256,TLS13-AES128-GCM-SHA256,TLS_AES_128_GCM_SHA256	AES_128_GCM_SHA256,DTLS_AES_128_GCM_SHA256,TLS13_AES_128_GCM_SHA256	D,E,F,T
0x03001302	HIGH	HIGH	TLSv13	any	any	AESGCM	256	AEAD	8446	TLS13-AES-256-GCM-SHA384,TLS13-AES256-GCM-SHA384,TLS_AES_256_GCM_SHA384	AES_256_GCM_SHA384,TLS13_AES_256_GCM_SHA384	D,E,F,T
0x03001303	HIGH	HIGH	TLSv13	any	any	ChaCha20-Poly1305	256	AEAD	8446	TLS13-CHACHA20-POLY1305-SHA256,TLS_CHACHA20_POLY1305_SHA256	CHACHA20_POLY1305_SHA256,TLS13_CHACHA20_POLY1305_SHA256	F,T
0x03001304	-?-	high	TLSv13	any	any	AESCCM	128	AEAD	8446	TLS13-AES-128-CCM-SHA256,TLS13-AES128-CCM-SHA256	AES_128_CCM_SHA256,TLS13_AES_128_CCM_SHA256	F,T
0x03001305	-?-	high	TLSv13	any	any	AESCCM	128	AEAD	8446	TLS13-AES-128-CCM8-SHA256,TLS13-AES128-CCM8-SHA256,TLS13-AES128-CCM-8-SHA256,TLS13-AES-128-CCM-8-SHA256	AES_128_CCM_8_SHA256,TLS13_AES_128_CCM_8_SHA256	F,T
0x03001306	-?-	-?-	TLSv13	-?-	-?-	AES	256	AEAD-256	aegis	AEGIS-256-SHA512,AEGIS-256-SHA384	AEGIS_256_SHA512,AEGIS_256_SHA384	U
# 03001306	-?-	-?-	TLSv13	-?-	-?-	AES	256	AEAD-256X2	aegis	AEGIS-256X2-SHA512	AEGIS_256X2_SHA512	-
0x03001307	-?-	-?-	TLSv13	-?-	-?-	AES	128	AEAD-128L	aegis	AEGIS-128L-SHA256	AEGIS_128L_SHA256	-
# 03001307	-?-	-?-	TLSv13	-?-	-?-	AES	128	AEAD-128X2	aegis	AEGIS-128X2-SHA256	AEGIS_128X2_SHA256	-
#     1306 and 1307 for DTLS13 also
0x030016B7	HIGH	HIGH	TLSv12	CECPQ1	RSA	ChaCha20-Poly1305	256	SHA256	-?-	CECPQ1-RSA-CHACHA20-POLY1305-SHA256	CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256	O
0x030016B8	HIGH	HIGH	TLSv12	CECPQ1	ECDSA	ChaCha20-Poly1305	256	SHA256	-?-	CECPQ1-ECDSA-CHACHA20-POLY1305-SHA256	CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256	O
0x030016B9	HIGH	HIGH	TLSv12	CECPQ1	RSA	AESGCM	256	SHA384	-?-	CECPQ1-RSA-AES256-GCM-SHA384	CECPQ1_RSA_WITH_AES_256_GCM_SHA384	O
0x030016BA	HIGH	HIGH	TLSv12	CECPQ1	ECDSA	AESGCM	256	SHA384	-?-	CECPQ1-ECDSA-AES256-GCM-SHA384	CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384	O
0x0300C001	-?-	weak	SSLv3	ECDH/ECDSA	ECDH	None	0	SHA1	4492	ECDH-ECDSA-NULL-SHA	ECDH_ECDSA_WITH_NULL_SHA	-
0x0300C002	MEDIUM	weak	SSLv3	ECDH/ECDSA	ECDH	RC4	128	SHA1	4492,6347	ECDH-ECDSA-RC4-SHA	ECDH_ECDSA_WITH_RC4_128_SHA	-
0x0300C003	MEDIUM	weak	SSLv3	ECDH/ECDSA	ECDH	3DES	112	SHA1	4492	ECDH-ECDSA-DES-CBC3-SHA	ECDH_ECDSA_WITH_DES_192_CBC3_SHA,ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA	-
0x0300C004	HIGH	weak	SSLv3	ECDH/ECDSA	ECDH	AES	128	SHA1	4492	ECDH-ECDSA-AES128-SHA	ECDH_ECDSA_WITH_AES_128_CBC_SHA	-
0x0300C005	HIGH	weak	SSLv3	ECDH/ECDSA	ECDH	AES	256	SHA1	4492	ECDH-ECDSA-AES256-SHA	ECDH_ECDSA_WITH_AES_256_CBC_SHA	-
0x0300C006	-?-	weak	SSLv3	ECDH	ECDSA	None	0	SHA1	8422	ECDHE-ECDSA-NULL-SHA	ECDHE_ECDSA_WITH_NULL_SHA	-
0x0300C007	MEDIUM	weak	SSLv3	ECDH	ECDSA	RC4	128	SHA1	4492,6347	ECDHE-ECDSA-RC4-SHA	ECDHE_ECDSA_WITH_RC4_128_SHA	-
0x0300C008	MEDIUM	weak	SSLv3	ECDH	ECDSA	3DES	112	SHA1	8422	ECDHE-ECDSA-DES-CBC3-SHA	ECDHE_ECDSA_WITH_DES_192_CBC3_SHA,ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA	-
0x0300C009	HIGH	weak	SSLv3	ECDH	ECDSA	AES	128	SHA1	8422	ECDHE-ECDSA-AES128-SHA	ECDHE_ECDSA_WITH_AES_128_CBC_SHA	-
0x0300C00A	HIGH	weak	SSLv3	ECDH	ECDSA	AES	256	SHA1	8422	ECDHE-ECDSA-AES256-SHA	ECDHE_ECDSA_WITH_AES_256_CBC_SHA	-
0x0300C00B	-?-	weak	SSLv3	ECDH/RSA	ECDH	None	0	SHA1	4492	ECDH-RSA-NULL-SHA	ECDH_RSA_WITH_NULL_SHA	-
0x0300C00C	MEDIUM	weak	SSLv3	ECDH/RSA	ECDH	RC4	128	SHA1	4492,6347	ECDH-RSA-RC4-SHA	ECDH_RSA_WITH_RC4_128_SHA	-
0x0300C00D	MEDIUM	weak	SSLv3	ECDH/RSA	ECDH	3DES	112	SHA1	4492	ECDH-RSA-DES-CBC3-SHA	ECDH_RSA_WITH_DES_192_CBC3_SHA,ECDH_RSA_WITH_3DES_EDE_CBC_SHA	-
0x0300C00E	HIGH	weak	SSLv3	ECDH/RSA	ECDH	AES	128	SHA1	4492	ECDH-RSA-AES128-SHA	ECDH_RSA_WITH_AES_128_CBC_SHA	-
0x0300C00F	HIGH	weak	SSLv3	ECDH/RSA	ECDH	AES	256	SHA1	4492	ECDH-RSA-AES256-SHA	ECDH_RSA_WITH_AES_256_CBC_SHA	-
0x0300C010	-?-	weak	SSLv3	ECDH	RSA	None	0	SHA1	8422	ECDHE-RSA-NULL-SHA	ECDHE_RSA_WITH_NULL_SHA	-
0x0300C011	MEDIUM	weak	SSLv3	ECDH	RSA	RC4	128	SHA1	4492,6347	ECDHE-RSA-RC4-SHA	ECDHE_RSA_WITH_RC4_128_SHA	-
0x0300C012	MEDIUM	weak	SSLv3	ECDH	RSA	3DES	112	SHA1	8422	ECDHE-RSA-DES-CBC3-SHA	ECDHE_RSA_WITH_DES_192_CBC3_SHA,ECDHE_RSA_WITH_3DES_EDE_CBC_SHA	-
0x0300C013	HIGH	weak	SSLv3	ECDH	RSA	AES	128	SHA1	8422	ECDHE-RSA-AES128-SHA	ECDHE_RSA_WITH_AES_128_CBC_SHA	-
0x0300C014	HIGH	weak	SSLv3	ECDH	RSA	AES	256	SHA1	8422	ECDHE-RSA-AES256-SHA	ECDHE_RSA_WITH_AES_256_CBC_SHA	-
0x0300C015	-?-	weak	SSLv3	ECDH	None	None	0	SHA1	8422	AECDH-NULL-SHA	ECDH_anon_WITH_NULL_SHA,ECDH_anon_NULL_WITH_SHA	T
0x0300C016	MEDIUM	weak	SSLv3	ECDH	None	RC4	128	SHA1	4492,6347	AECDH-RC4-SHA	ECDH_anon_WITH_RC4_128_SHA,ECDH_anon_WITH_RC4_128_SHA	T
0x0300C017	MEDIUM	weak	SSLv3	ECDH	None	3DES	112	SHA1	8422	AECDH-DES-CBC3-SHA	ECDH_anon_WITH_DES_192_CBC3_SHA,ECDH_anon_WITH_3DES_EDE_CBC_SHA	-
0x0300C018	HIGH	weak	SSLv3	ECDH	None	AES	128	SHA1	8422	AECDH-AES128-SHA	ECDH_anon_WITH_AES_128_CBC_SHA	-
0x0300C019	HIGH	weak	SSLv3	ECDH	None	AES	256	SHA1	8422	AECDH-AES256-SHA	ECDH_anon_WITH_AES_256_CBC_SHA	-
0x0300C01A	MEDIUM	weak	SSLv3	SRP	None	3DES	112	SHA1	5054	SRP-3DES-EDE-CBC-SHA	SRP_SHA_WITH_3DES_EDE_CBC_SHA	L
0x0300C01B	MEDIUM	weak	SSLv3	SRP	RSA	3DES	112	SHA1	5054	SRP-RSA-3DES-EDE-CBC-SHA	SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA	L
0x0300C01C	MEDIUM	weak	SSLv3	SRP	DSS	3DES	112	SHA1	5054	SRP-DSS-3DES-EDE-CBC-SHA	SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA	L
0x0300C01D	HIGH	weak	SSLv3	SRP	None	AES	128	SHA1	5054	SRP-AES-128-CBC-SHA	SRP_SHA_WITH_AES_128_CBC_SHA	L
0x0300C01E	HIGH	weak	SSLv3	SRP	RSA	AES	128	SHA1	5054	SRP-RSA-AES-128-CBC-SHA	SRP_SHA_RSA_WITH_AES_128_CBC_SHA	L
0x0300C01F	HIGH	weak	SSLv3	SRP	DSS	AES	128	SHA1	5054	SRP-DSS-AES-128-CBC-SHA	SRP_SHA_DSS_WITH_AES_128_CBC_SHA	L
0x0300C020	HIGH	weak	SSLv3	SRP	None	AES	256	SHA1	5054	SRP-AES-256-CBC-SHA	SRP_SHA_WITH_AES_256_CBC_SHA	L
0x0300C021	HIGH	weak	SSLv3	SRP	RSA	AES	256	SHA1	5054	SRP-RSA-AES-256-CBC-SHA	SRP_SHA_RSA_WITH_AES_256_CBC_SHA	L
0x0300C022	HIGH	weak	SSLv3	SRP	DSS	AES	256	SHA1	5054	SRP-DSS-AES-256-CBC-SHA	SRP_SHA_DSS_WITH_AES_256_CBC_SHA	L
0x0300C023	HIGH	weak	TLSv12	ECDH	ECDSA	AES	128	SHA256	5289	ECDHE-ECDSA-AES128-SHA256	ECDHE_ECDSA_WITH_AES_128_SHA256,ECDHE_ECDSA_WITH_AES_128_CBC_SHA256	L
0x0300C024	HIGH	weak	TLSv12	ECDH	ECDSA	AES	256	SHA384	5289	ECDHE-ECDSA-AES256-SHA384	ECDHE_ECDSA_WITH_AES_256_SHA384,ECDHE_ECDSA_WITH_AES_256_CBC_SHA384	L
0x0300C025	HIGH	weak	TLSv12	ECDH/ECDSA	ECDH	AES	128	SHA256	5289	ECDH-ECDSA-AES128-SHA256	ECDH_ECDSA_WITH_AES_128_SHA256,ECDH_ECDSA_WITH_AES_128_CBC_SHA256	L
0x0300C026	HIGH	weak	TLSv12	ECDH/ECDSA	ECDH	AES	256	SHA384	5289	ECDH-ECDSA-AES256-SHA384	ECDH_ECDSA_WITH_AES_256_SHA384,ECDH_ECDSA_WITH_AES_256_CBC_SHA384	L
0x0300C027	HIGH	weak	TLSv12	ECDH	RSA	AES	128	SHA256	5289	ECDHE-RSA-AES128-SHA256	ECDHE_RSA_WITH_AES_128_SHA256,ECDHE_RSA_WITH_AES_128_CBC_SHA256	L
0x0300C028	HIGH	weak	TLSv12	ECDH	RSA	AES	256	SHA384	5289	ECDHE-RSA-AES256-SHA384	ECDHE_RSA_WITH_AES_256_SHA384,ECDHE_RSA_WITH_AES_256_CBC_SHA384	L
0x0300C029	HIGH	weak	TLSv12	ECDH/RSA	ECDH	AES	128	SHA256	5289	ECDH-RSA-AES128-SHA256	ECDH_RSA_WITH_AES_128_SHA256,ECDH_RSA_WITH_AES_128_CBC_SHA256	L
0x0300C02A	HIGH	weak	TLSv12	ECDH/RSA	ECDH	AES	256	SHA384	5289	ECDH-RSA-AES256-SHA384	ECDH_RSA_WITH_AES_256_SHA384,ECDH_RSA_WITH_AES_256_CBC_SHA384	L
0x0300C02B	HIGH	HIGH	TLSv12	ECDH	ECDSA	AESGCM	128	AEAD	5289	ECDHE-ECDSA-AES128-GCM-SHA256	ECDHE_ECDSA_WITH_AES_128_GCM_SHA256	L
0x0300C02C	HIGH	HIGH	TLSv12	ECDH	ECDSA	AESGCM	256	AEAD	5289	ECDHE-ECDSA-AES256-GCM-SHA384	ECDHE_ECDSA_WITH_AES_256_GCM_SHA384	L
0x0300C02D	HIGH	HIGH	TLSv12	ECDH/ECDSA	ECDH	AESGCM	128	AEAD	5289	ECDH-ECDSA-AES128-GCM-SHA256	ECDH_ECDSA_WITH_AES_128_GCM_SHA256	L
0x0300C02E	HIGH	HIGH	TLSv12	ECDH/ECDSA	ECDH	AESGCM	256	AEAD	5289	ECDH-ECDSA-AES256-GCM-SHA384	ECDH_ECDSA_WITH_AES_256_GCM_SHA384	L
0x0300C02F	HIGH	HIGH	TLSv12	ECDH	RSA	AESGCM	128	AEAD	5289,8442	ECDHE-RSA-AES128-GCM-SHA256	ECDHE_RSA_WITH_AES_128_GCM_SHA256	L
0x0300C030	HIGH	HIGH	TLSv12	ECDH	RSA	AESGCM	256	AEAD	5289,8442	ECDHE-RSA-AES256-GCM-SHA384	ECDHE_RSA_WITH_AES_256_GCM_SHA384	L
0x0300C031	HIGH	HIGH	TLSv12	ECDH/RSA	ECDH	AESGCM	128	AEAD	5289	ECDH-RSA-AES128-GCM-SHA256	ECDH_RSA_WITH_AES_128_GCM_SHA256	L
0x0300C032	HIGH	HIGH	TLSv12	ECDH/RSA	ECDH	AESGCM	256	AEAD	5289	ECDH-RSA-AES256-GCM-SHA384	ECDH_RSA_WITH_AES_256_GCM_SHA384	L
0x0300C033	-?-	medium	TLSv12	ECDHEPSK	PSK	RC4	128	SHA1	5489,6347	ECDHE-PSK-RC4-SHA,ECDHE-PSK-RC4-128-SHA	ECDHE_PSK_WITH_RC4_128_SHA	-
0x0300C034	-?-	medium	TLSv12	ECDHEPSK	PSK	3DES	192	SHA1	5489	ECDHE-PSK-3DES-SHA,ECDHE-PSK-3DES-EDE-CBC-SHA	ECDHE_PSK_WITH_3DES_EDE_CBC_SHA	-
0x0300C035	HIGH	medium	TLSv1	ECDHEPSK	PSK	AES	128	SHA1	5489	ECDHE-PSK-AES128-SHA,ECDHE-PSK-AES128-CBC-SHA	ECDHE_PSK_WITH_AES_128_CBC_SHA	-
0x0300C036	HIGH	medium	TLSv12	ECDHEPSK	PSK	AES	256	SHA1	5489	ECDHE-PSK-AES256-SHA,ECDHE-PSK-AES256-CBC-SHA	ECDHE_PSK_WITH_AES_256_CBC_SHA	-
0x0300C037	HIGH	medium	TLSv1	ECDHEPSK	PSK	AES	128	SHA256	5489	ECDHE-PSK-AES128-SHA256,ECDHE-PSK-AES128-CBC-SHA256	ECDHE_PSK_WITH_AES_128_CBC_SHA256	-
0x0300C038	HIGH	medium	TLSv1	ECDHEPSK	PSK	AES	256	SHA384	5489	ECDHE-PSK-AES256-SHA384,ECDHE-PSK-AES256-CBC-SHA384	ECDHE_PSK_WITH_AES_256_CBC_SHA384	-
0x0300C039	-?-	weak	TLSv1	ECDHEPSK	PSK	None	0	SHA1	5489	ECDHE-PSK-SHA,ECDHE-PSK-NULL-SHA	ECDHE_PSK_WITH_NULL_SHA	-
0x0300C03A	-?-	weak	TLSv1	ECDHEPSK	PSK	None	0	SHA1	5489	ECDHE-PSK-SHA256,ECDHE-PSK-NULL-SHA256	ECDHE_PSK_WITH_NULL_SHA256	-
0x0300C03B	-?-	weak	TLSv1	ECDHEPSK	PSK	None	0	SHA1	5489	ECDHE-PSK-SHA384,ECDHE-PSK-NULL-SHA384	ECDHE_PSK_WITH_NULL_SHA384	-
0x0300C03C	-?-	weak	TLSv12	RSA	RSA	ARIA	128	SHA256	6209	RSA-ARIA128-SHA256	RSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C03D	-?-	weak	TLSv12	RSA	RSA	ARIA	256	SHA384	6209	RSA-ARIA256-SHA384	RSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C03E	-?-	weak	TLSv12	DH	DSS	ARIA	128	SHA256	6209	DH-DSS-ARIA128-SHA256	DH_DSS_WITH_ARIA_128_CBC_SHA256	-
0x0300C03F	-?-	weak	TLSv12	DH	DSS	ARIA	256	SHA384	6209	DH-DSS-ARIA256-SHA384	DH_DSS_WITH_ARIA_256_CBC_SHA384	-
0x0300C040	-?-	weak	TLSv12	DH	RSA	ARIA	128	SHA256	6209	DH-RSA-ARIA128-SHA256	DH_RSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C041	-?-	weak	TLSv12	DH	RSA	ARIA	256	SHA384	6209	DH-RSA-ARIA256-SHA384	DH_RSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C042	-?-	weak	TLSv12	DHE	DSS	ARIA	128	SHA256	6209	DHE-DSS-ARIA128-SHA256	DHE_DSS_WITH_ARIA_128_CBC_SHA256	-
0x0300C043	-?-	weak	TLSv12	DHE	DSS	ARIA	256	SHA384	6209	DHE-DSS-ARIA256-SHA384	DHE_DSS_WITH_ARIA_256_CBC_SHA384	-
0x0300C044	-?-	weak	TLSv12	DHE	RSA	ARIA	128	SHA256	6209	DHE-RSA-ARIA128-SHA256,DHE-RSA-ARIA256-SHA256	DHE_RSA_WITH_ARIA_256_CBC_SHA256	I
0x0300C045	-?-	weak	TLSv12	DHE	RSA	ARIA	256	SHA384	6209	DHE-RSA-ARIA256-SHA384	DHE_RSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C046	-?-	weak	TLSv12	DH	None	ARIA	128	SHA256	6209	ADH-ARIA128-SHA256	DH_anon_WITH_ARIA_128_CBC_SHA256	-
0x0300C047	-?-	weak	TLSv12	DH	None	ARIA	256	SHA384	6209	ADH-ARIA256-SHA384	DH_anon_WITH_ARIA_256_CBC_SHA384	-
0x0300C048	-?-	weak	TLSv12	ECDHE	ECDSA	ARIA	128	SHA256	6209	ECDHE-ECDSA-ARIA128-SHA256	ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C049	-?-	weak	TLSv12	ECDHE	ECDSA	ARIA	256	SHA384	6209	ECDHE-ECDSA-ARIA256-SHA384	ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C04A	-?-	weak	TLSv12	ECDH	ECDSA	ARIA	128	SHA256	6209	ECDH-ECDSA-ARIA128-SHA256	ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C04B	-?-	weak	TLSv12	ECDH	ECDSA	ARIA	256	SHA384	6209	ECDH-ECDSA-ARIA256-SHA384	ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C04C	-?-	weak	TLSv12	ECDHE	RSA	ARIA	128	SHA256	6209	ECDHE-RSA-ARIA128-SHA256	ECDHE_RSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C04D	-?-	weak	TLSv12	ECDHE	RSA	ARIA	256	SHA384	6209	ECDHE-RSA-ARIA256-SHA384	ECDHE_RSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C04E	-?-	weak	TLSv12	ECDH	RSA	ARIA	128	SHA256	6209	ECDH-RSA-ARIA128-SHA256	ECDH_RSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C04F	-?-	weak	TLSv12	ECDH	RSA	ARIA	256	SHA384	6209	ECDH-RSA-ARIA256-SHA384	ECDH_RSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C050	HIGH	HIGH	TLSv12	RSA	RSA	ARIAGCM	128	AEAD	6209	RSA-ARIA128-GCM-SHA256,ARIA128-GCM-SHA256	RSA_WITH_ARIA_128_GCM_SHA256	-
0x0300C051	HIGH	HIGH	TLSv12	RSA	RSA	ARIAGCM	256	AEAD	6209	RSA-ARIA256-GCM-SHA384,ARIA256-GCM-SHA384	RSA_WITH_ARIA_256_GCM_SHA384	-
0x0300C052	HIGH	HIGH	TLSv12	DH	RSA	ARIAGCM	128	AEAD	6209	DHE-RSA-ARIA128-GCM-SHA256	DHE_RSA_WITH_ARIA_128_GCM_SHA256	-
0x0300C053	HIGH	HIGH	TLSv12	DH	RSA	ARIAGCM	256	AEAD	6209	DHE-RSA-ARIA256-GCM-SHA384	DHE_RSA_WITH_ARIA_256_GCM_SHA384	-
0x0300C054	-?-	-?-	TLSv12	DH	RSA	ARIAGCM	128	AEAD	6209	DH-RSA-ARIA128-GCM-SHA256	DH_RSA_WITH_ARIA_128_GCM_SHA256	-
0x0300C055	-?-	-?-	TLSv12	DH	RSA	ARIAGCM	256	AEAD	6209	DH-RSA-ARIA256-GCM-SHA384	DH_RSA_WITH_ARIA_256_GCM_SHA384	-
0x0300C056	HIGH	HIGH	TLSv12	DH	DSS	ARIAGCM	128	AEAD	6209	DHE-DSS-ARIA128-GCM-SHA256	DHE_DSS_WITH_ARIA_128_GCM_SHA256	-
0x0300C057	HIGH	HIGH	TLSv12	DH	DSS	ARIAGCM	256	AEAD	6209	DHE-DSS-ARIA256-GCM-SHA384	DHE_DSS_WITH_ARIA_256_GCM_SHA384	-
0x0300C058	-?-	-?-	TLSv12	DH	DSS	ARIAGCM	128	AEAD	6209	DH-DSS-ARIA128-GCM-SHA256	DH_DSS_WITH_ARIA_128_GCM_SHA256	-
0x0300C059	-?-	-?-	TLSv12	DH	DSS	ARIAGCM	256	AEAD	6209	DH-DSS-ARIA256-GCM-SHA384	DH_DSS_WITH_ARIA_256_GCM_SHA384	-
0x0300C05A	-?-	-?-	TLSv12	DH	None	ARIAGCM	128	AEAD	6209	ADH-ARIA128-GCM-SHA256	DH_anon_WITH_ARIA_128_GCM_SHA256	-
0x0300C05B	-?-	-?-	TLSv12	DH	None	ARIAGCM	256	AEAD	6209	ADH-ARIA256-GCM-SHA384	DH_anon_WITH_ARIA_256_GCM_SHA384	-
0x0300C05C	HIGH	HIGH	TLSv12	ECDH	ECDSA	ARIAGCM	128	AEAD	6209	ECDHE-ECDSA-ARIA128-GCM-SHA256	ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256	-
0x0300C05D	HIGH	HIGH	TLSv12	ECDH	ECDSA	ARIAGCM	256	AEAD	6209	ECDHE-ECDSA-ARIA256-GCM-SHA384	ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384	-
0x0300C05E	-?-	-?-	TLSv12	ECDH	ECDSA	ARIAGCM	128	AEAD	6209	ECDH-ECDSA-ARIA128-GCM-SHA256	ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256	-
0x0300C05F	-?-	-?-	TLSv12	ECDH	ECDSA	ARIAGCM	256	AEAD	6209	ECDH-ECDSA-ARIA256-GCM-SHA384	ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384	-
0x0300C060	HIGH	HIGH	TLSv12	ECDH	RSA	ARIAGCM	128	AEAD	6209	ECDHE-RSA-ARIA128-GCM-SHA256,ECDHE-ARIA128-GCM-SHA256	ECDHE_RSA_WITH_ARIA_128_GCM_SHA256	-
0x0300C061	HIGH	HIGH	TLSv12	ECDH	RSA	ARIAGCM	256	AEAD	6209	ECDHE-RSA-ARIA256-GCM-SHA384,ECDHE-ARIA256-GCM-SHA384	ECDHE_RSA_WITH_ARIA_256_GCM_SHA384	-
0x0300C062	-?-	high	TLSv12	ECDH	RSA	ARIAGCM	128	AEAD	6209	ECDH-RSA-ARIA128-GCM-SHA256,ECDH-ARIA128-GCM-SHA256	ECDH_RSA_WITH_ARIA_128_GCM_SHA256	-
0x0300C063	-?-	high	TLSv12	ECDH	RSA	ARIAGCM	256	AEAD	6209	ECDH-RSA-ARIA256-GCM-SHA384,ECDH-ARIA256-GCM-SHA384	ECDH_RSA_WITH_ARIA_256_GCM_SHA384	-
0x0300C064	HIGH	medium	TLSv12	PSK	PSK	ARIA	128	SHA256	6209	PSK-ARIA128-SHA256	PSK_WITH_ARIA_128_CBC_SHA256	-
0x0300C065	HIGH	medium	TLSv12	PSK	PSK	ARIA	256	SHA384	6209	PSK-ARIA256-SHA384	PSK_WITH_ARIA_256_CBC_SHA384	-
0x0300C066	-?-	medium	TLSv12	DHE	PSK	ARIA	128	SHA256	6209	DHE-PSK-ARIA128-SHA256	DHE_PSK_WITH_ARIA_128_CBC_SHA256	-
0x0300C067	-?-	medium	TLSv12	DHE	PSK	ARIA	256	SHA384	6209	DHE-PSK-ARIA256-SHA384	DHE_PSK_WITH_ARIA_256_CBC_SHA384	-
0x0300C068	-?-	medium	TLSv12	RSA	PSK	ARIA	128	SHA256	6209	RSA-PSK-ARIA128-SHA256	RSA_PSK_WITH_ARIA_128_CBC_SHA256	-
0x0300C069	-?-	medium	TLSv12	RSA	PSK	ARIA	256	SHA384	6209	RSA-PSK-ARIA256-SHA384	RSA_PSK_WITH_ARIA_256_CBC_SHA384	-
0x0300C06A	HIGH	HIGH	TLSv12	PSK	PSK	ARIAGCM	128	AEAD	6209	PSK-ARIA128-GCM-SHA256	PSK_WITH_ARIA_128_GCM_SHA256	-
0x0300C06B	HIGH	HIGH	TLSv12	PSK	PSK	ARIAGCM	256	AEAD	6209	PSK-ARIA256-GCM-SHA384	PSK_WITH_ARIA_256_GCM_SHA384	-
0x0300C06C	HIGH	HIGH	TLSv12	DHEPSK	PSK	ARIAGCM	128	AEAD	6209	DHE-PSK-ARIA128-GCM-SHA256	DHE_PSK_WITH_ARIA_128_GCM_SHA256	-
0x0300C06D	HIGH	HIGH	TLSv12	DHEPSK	PSK	ARIAGCM	256	AEAD	6209	DHE-PSK-ARIA256-GCM-SHA384	DHE_PSK_WITH_ARIA_256_GCM_SHA384	-
0x0300C06E	HIGH	HIGH	TLSv12	RSAPSK	RSA	ARIAGCM	128	AEAD	6209	RSA-PSK-ARIA128-GCM-SHA256	RSA_PSK_WITH_ARIA_128_GCM_SHA256	-
0x0300C06F	HIGH	HIGH	TLSv12	RSAPSK	PSK	ARIAGCM	256	AEAD	6209	RSA-PSK-ARIA256-GCM-SHA384	RSA_PSK_WITH_ARIA_256_GCM_SHA384	-
0x0300C070	-?-	medium	TLSv12	ECDHE	PSK	ARIA	128	SHA256	6209	ECDHE-PSK-ARIA128-SHA256	ECDHE_PSK_WITH_ARIA_128_CBC_SHA256	-
0x0300C071	-?-	medium	TLSv12	ECDHE	PSK	ARIA	256	SHA384	6209	ECDHE-PSK-ARIA256-SHA384	ECDHE_PSK_WITH_ARIA_256_CBC_SHA384	-
0x0300C072	HIGH	weak	TLSv12	ECDH	ECDSA	CAMELLIA	128	SHA256	6367	ECDHE-ECDSA-CAMELLIA128-SHA256	ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C073	HIGH	weak	TLSv12	ECDH	ECDSA	CAMELLIA	256	SHA384	6367	ECDHE-ECDSA-CAMELLIA256-SHA384	ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C074	HIGH	weak	TLSv12	ECDH/ECDSA	ECDH	CAMELLIA	128	SHA256	6367	ECDH-ECDSA-CAMELLIA128-SHA256	ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C075	HIGH	weak	TLSv12	ECDH/ECDSA	ECDH	CAMELLIA	256	SHA384	6367	ECDH-ECDSA-CAMELLIA256-SHA384	ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C076	HIGH	weak	TLSv12	ECDH	RSA	CAMELLIA	128	SHA256	6367	ECDHE-RSA-CAMELLIA128-SHA256	ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C077	HIGH	weak	TLSv12	ECDH	RSA	CAMELLIA	256	SHA384	6367	ECDHE-RSA-CAMELLIA256-SHA384	ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C078	HIGH	weak	TLSv12	ECDH/RSA	ECDH	CAMELLIA	128	SHA256	6367	ECDH-RSA-CAMELLIA128-SHA256	ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C079	HIGH	weak	TLSv12	ECDH/RSA	ECDH	CAMELLIA	256	SHA384	6367	ECDH-RSA-CAMELLIA256-SHA384	ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C07A	HIGH	HIGH	TLSv12	RSA	RSA	CAMELLIAGCM	128	SHA256	6367	RSA-CAMELLIA128-GCM-SHA256	RSA_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C07B	HIGH	HIGH	TLSv12	RSA	RSA	CAMELLIAGCM	256	SHA384	6367	RSA-CAMELLIA256-GCM-SHA384	RSA_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C07C	HIGH	HIGH	TLSv12	RSA	DHE	CAMELLIAGCM	128	SHA256	6367	DHE-RSA-CAMELLIA128-GCM-SHA256	DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C07D	HIGH	HIGH	TLSv12	RSA	DHE	CAMELLIAGCM	256	SHA384	6367	DHE-RSA-CAMELLIA256-GCM-SHA384	DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C07E	HIGH	HIGH	TLSv12	RSA	DH	CAMELLIAGCM	128	SHA256	6367	DH-RSA-CAMELLIA128-GCM-SHA256	DH_RSA_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C07F	HIGH	HIGH	TLSv12	RSA	DH	CAMELLIAGCM	256	SHA384	6367	DH-RSA-CAMELLIA256-GCM-SHA384	DH_RSA_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C080	HIGH	HIGH	TLSv12	DSS	DHE	CAMELLIAGCM	128	SHA256	6367	DHE-DSS-CAMELLIA128-GCM-SHA256	DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C081	HIGH	HIGH	TLSv12	DSS	DHE	CAMELLIAGCM	256	SHA384	6367	DHE-DSS-CAMELLIA256-GCM-SHA384	DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C082	HIGH	HIGH	TLSv12	DSS	DH	CAMELLIAGCM	128	SHA256	6367	DH-DSS-CAMELLIA128-GCM-SHA256	DH_DSS_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C083	HIGH	HIGH	TLSv12	DSS	DH	CAMELLIAGCM	256	SHA384	6367	DH-DSS-CAMELLIA256-GCM-SHA384	DH_DSS_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C084	HIGH	HIGH	TLSv12	DSS	ADH	CAMELLIAGCM	128	SHA256	6367	ADH-DSS-CAMELLIA128-GCM-SHA256	DH_anon_DSS_WITH_CAMELLIA_128_GCM_SHA256,DH_anon_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C085	HIGH	HIGH	TLSv12	DSS	ADH	CAMELLIAGCM	256	SHA384	6367	ADH-DSS-CAMELLIA256-GCM-SHA384	DH_anon_DSS_WITH_CAMELLIA_256_GCM_SHA384,DH_anon_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C086	HIGH	HIGH	TLSv12	ECDH	ECDHE	CAMELLIAGCM	128	SHA256	6367	ECDHE-ECDSA-CAMELLIA128-GCM-SHA256	ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C087	HIGH	HIGH	TLSv12	ECDH	ECDHE	CAMELLIAGCM	256	SHA384	6367	ECDHE-ECDSA-CAMELLIA256-GCM-SHA384	ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C088	HIGH	HIGH	TLSv12	ECDH	ECDH	CAMELLIAGCM	128	SHA256	6367	ECDH-ECDSA-CAMELLIA128-GCM-SHA256	ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C089	HIGH	HIGH	TLSv12	ECDH	ECDH	CAMELLIAGCM	256	SHA384	6367	ECDH-ECDSA-CAMELLIA256-GCM-SHA384	ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C08A	HIGH	HIGH	TLSv12	RSA	ECDHE	CAMELLIAGCM	128	SHA256	6367	ECDHE-RSA-CAMELLIA128-GCM-SHA256	ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C08B	HIGH	HIGH	TLSv12	RSA	ECDHE	CAMELLIAGCM	256	SHA384	6367	ECDHE-RSA-CAMELLIA256-GCM-SHA384	ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C08C	HIGH	HIGH	TLSv12	RSA	ECDH	CAMELLIAGCM	128	SHA256	6367	ECDH-RSA-CAMELLIA128-GCM-SHA256	ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C08D	HIGH	HIGH	TLSv12	RSA	ECDH	CAMELLIAGCM	256	SHA384	6367	ECDH-RSA-CAMELLIA256-GCM-SHA384	ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C08E	HIGH	HIGH	TLSv12	PSK	RSA	CAMELLIAGCM	128	SHA256	6367	PSK-CAMELLIA128-GCM-SHA256	PSK_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C08F	HIGH	HIGH	TLSv12	PSK	RSA	CAMELLIAGCM	256	SHA384	6367	PSK-CAMELLIA256-GCM-SHA384	PSK_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C090	HIGH	HIGH	TLSv12	PSK	DHE	CAMELLIAGCM	128	SHA256	6367	DHE-PSK-CAMELLIA128-GCM-SHA256	DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C091	HIGH	HIGH	TLSv12	PSK	DHE	CAMELLIAGCM	256	SHA384	6367	DHE-PSK-CAMELLIA256-GCM-SHA384	DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C092	HIGH	HIGH	TLSv12	PSK	RSA	CAMELLIAGCM	128	SHA256	6367	RSA-PSK-CAMELLIA128-GCM-SHA256	RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256	-
0x0300C093	HIGH	HIGH	TLSv12	PSK	RSA	CAMELLIAGCM	256	SHA384	6367	RSA-PSK-CAMELLIA256-GCM-SHA384	RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384	-
0x0300C094	HIGH	medium	TLSv12	PSK	PSK	CAMELLIA	128	SHA256	6367	PSK-CAMELLIA128-SHA256	PSK_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C095	HIGH	medium	TLSv12	PSK	PSK	CAMELLIA	256	SHA384	6367	PSK-CAMELLIA256-SHA384	PSK_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C096	HIGH	medium	TLSv12	PSK	DHE	CAMELLIA	128	SHA256	6367	DHE-PSK-CAMELLIA128-SHA256	DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C097	HIGH	medium	TLSv12	PSK	DHE	CAMELLIA	256	SHA384	6367	DHE-PSK-CAMELLIA256-SHA384	DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C098	HIGH	medium	TLSv12	PSK	RSA	CAMELLIA	128	SHA256	6367	RSA-PSK-CAMELLIA128-SHA256	RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C099	HIGH	medium	TLSv12	PSK	RSA	CAMELLIA	256	SHA384	6367	RSA-PSK-CAMELLIA256-SHA384	RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C09A	HIGH	medium	TLSv12	PSK	ECDHE	CAMELLIA	128	SHA256	6367	ECDHE-PSK-CAMELLIA128-SHA256	ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C09B	HIGH	medium	TLSv12	PSK	ECDHE	CAMELLIA	256	SHA384	6367	ECDHE-PSK-CAMELLIA256-SHA384	ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C09C	HIGH	HIGH	TLSv12	RSA	RSA	AESCCM	128	AEAD	6655	RSA-AES128-CCM,AES128-CCM	RSA_WITH_AES_128_CCM	-
0x0300C09D	HIGH	HIGH	TLSv12	RSA	RSA	AESCCM	256	AEAD	6655	RSA-AES256-CCM,AES256-CCM	RSA_WITH_AES_256_CCM	-
0x0300C09E	HIGH	HIGH	TLSv12	DH	RSA	AESCCM	128	AEAD	6655	DHE-RSA-AES128-CCM	DHE_RSA_WITH_AES_128_CCM	-
0x0300C09F	HIGH	HIGH	TLSv12	DH	RSA	AESCCM	256	AEAD	6655	DHE-RSA-AES256-CCM	DHE_RSA_WITH_AES_256_CCM	-
0x0300C0A0	HIGH	HIGH	TLSv12	RSA	RSA	AESCCM8	128	AEAD	6655	RSA-AES128-CCM8,AES128-CCM8,RSA-AES128-CCM-8	RSA_WITH_AES_128_CCM_8	-
0x0300C0A1	HIGH	HIGH	TLSv12	RSA	RSA	AESCCM8	256	AEAD	6655	RSA-AES256-CCM8,AES256-CCM8,RSA-AES256-CCM-8	RSA_WITH_AES_256_CCM_8	-
0x0300C0A2	HIGH	HIGH	TLSv12	DH	RSA	AESCCM8	128	AEAD	6655	DHE-RSA-AES128-CCM8,DHE-RSA-AES128-CCM-8	DHE_RSA_WITH_AES_128_CCM_8	-
0x0300C0A3	HIGH	HIGH	TLSv12	DH	RSA	AESCCM8	256	AEAD	6655	DHE-RSA-AES256-CCM8,DHE-RSA-AES256-CCM-8	DHE_RSA_WITH_AES_256_CCM_8	-
0x0300C0A4	HIGH	HIGH	TLSv12	PSK	PSK	AESCCM	128	AEAD	6655	PSK-AES128-CCM,PSK-RSA-AES128-CCM	PSK_WITH_AES_128_CCM	-
0x0300C0A5	HIGH	HIGH	TLSv12	PSK	PSK	AESCCM	256	AEAD	6655	PSK-AES256-CCM,PSK-RSA-AES256-CCM	PSK_WITH_AES_256_CCM	-
0x0300C0A6	HIGH	high	TLSv12	DHE	RSA	AESGCM	128	AEAD	6655	DHE-PSK-AES128-CCM,DHE-PSK-RSA-AES128-CCM	DHE_PSK_WITH_AES_128_CCM	-
0x0300C0A7	HIGH	high	TLSv12	DHE	RSA	AESGCM	256	AEAD	6655	DHE-PSK-AES256-CCM,DHE-PSK-RSA-AES256-CCM	DHE_PSK_WITH_AES_256_CCM	-
0x0300C0A8	HIGH	HIGH	TLSv12	PSK	PSK	AESCCM8	128	AEAD	6655	PSK-AES128-CCM8,PSK-AES128-CCM-8,PSK-RSA-AES128-CCM-8	PSK_WITH_AES_128_CCM_8	-
0x0300C0A9	HIGH	HIGH	TLSv12	PSK	PSK	AESCCM8	256	AEAD	6655	PSK-AES256-CCM8,PSK-AES256-CCM-8,PSK-RSA-AES256-CCM-8	PSK_WITH_AES_256_CCM_8	-
0x0300C0AA	HIGH	HIGH	TLSv12	DHEPSK	PSK	AESGCM8	128	AEAD	6655	DHE-PSK-AES128-CCM8,PSK-DHE-AES128-CCM8,DHE-PSK-AES128-CCM-8	DHE_PSK_WITH_AES_128_CCM_8,PSK_DHE_WITH_AES_128_CCM_8	FIXME
0x0300C0AB	HIGH	HIGH	TLSv12	DHEPSK	PSK	AESGCM8	256	AEAD	6655	DHE-PSK-AES256-CCM8,PSK-DHE-AES256-CCM8,DHE-PSK-AES256-CCM-8	DHE_PSK_WITH_AES_256_CCM_8,PSK_DHE_WITH_AES_256_CCM_8	FIXME
0x0300C0AC	HIGH	HIGH	TLSv12	ECDH	ECDSA	AESCCM	128	AEAD	7251	ECDHE-ECDSA-AES128-CCM	ECDHE_ECDSA_WITH_AES_128_CCM	-
0x0300C0AD	HIGH	HIGH	TLSv12	ECDH	ECDSA	AESCCM	256	AEAD	7251	ECDHE-ECDSA-AES256-CCM	ECDHE_ECDSA_WITH_AES_256_CCM	-
0x0300C0AE	HIGH	HIGH	TLSv12	ECDH	ECDSA	AESCCM8	128	AEAD	7251	ECDHE-ECDSA-AES128-CCM8,ECDHE-ECDSA-AES128-CCM-8	ECDHE_ECDSA_WITH_AES_128_CCM_8	-
0x0300C0AF	HIGH	HIGH	TLSv12	ECDH	ECDSA	AESCCM8	256	AEAD	7251	ECDHE-ECDSA-AES256-CCM8,ECDHE-ECDSA-AES256-CCM-8	ECDHE_ECDSA_WITH_AES_256_CCM_8	-
0x0300C0B0	-?-	-?-	TLSv12	ECCPWD	RSA	AESGCM	128	AEAD	8492	ECCPWD-AES128-GCM-SHA256	ECCPWD_WITH_AES_128_GCM_SHA256	R
0x0300C0B1	-?-	-?-	TLSv12	ECCPWD	RSA	AESGCM	256	AEAD	8492	ECCPWD-AES256-GCM-SHA384	ECCPWD_WITH_AES_256_GCM_SHA384	R
0x0300C0B2	-?-	-?-	TLSv12	ECCPWD	RSA	AESCCM	128	AEAD	8492	ECCPWD-AES128-CCM-SHA256	ECCPWD_WITH_AES_128_CCM_SHA256	R
0x0300C0B3	-?-	-?-	TLSv12	ECCPWD	RSA	AESCCM	256	AEAD	8492	ECCPWD-AES256-CCM-SHA384	ECCPWD_WITH_AES_256_CCM_SHA384	R
0x0300C0B4	-?-	-?-	TLSv13	-?-	-?-	-?-	-?-	SHA256	9150	SHA256-SHA256,TLS13-SHA256-SHA256	SHA256_SHA256	-
0x0300C0B5	-?-	-?-	TLSv13	-?-	-?-	-?-	-?-	SHA384	9150	SHA384-SHA384,TLS13-SHA384-SHA384	SHA384_SHA384	-
0x0300C100	HIGH	HIGH	TLSv12	DH	GOST	Kuznyechik	256	Kuznyechik	9189	GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC	GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC	-
0x0300C101	HIGH	HIGH	TLSv12	DH	GOST	Magma	256	Magma	9189	GOSTR341112_256_WITH_MAGMA_CTR_OMAC,CTR-OMAC	GOSTR341112_256_WITH_MAGMA_CTR_OMAC	-
0x0300C102	HIGH	HIGH	TLSv12	DH	GOST	GOST89	256	GOST89	9189	GOSTR341112_256_WITH_28147_CNT_IMIT,CNT-IMIT,IANA-GOST2012-GOST8912-GOST8912,GOST2012-GOST8912-GOST8912	GOSTR341112_256_WITH_28147_CNT_IMIT	-
0x0300C103	HIGH	HIGH	TLSv13	DH	GOST	Kuznyechik	256	Kuznyechik	-?-	TLS13_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L	TLS13_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L	-
0x0300C104	HIGH	HIGH	TLSv13	DH	GOST	Magma	256	Magma	-?-	TLS13_GOSTR341112_256_WITH_MAGMA_MGM_L	TLS13_GOSTR341112_256_WITH_MAGMA_MGM_L	-
0x0300C105	HIGH	HIGH	TLSv13	DH	GOST	Kuznyechik	256	Magma	-?-	TLS13_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S	TLS13_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S	-
0x0300C106	HIGH	HIGH	TLSv13	DH	GOST	Magma	256	Magma	-?-	TLS13_GOSTR341112_256_WITH_MAGMA_MGM_S	TLS13_GOSTR341112_256_WITH_MAGMA_MGM_S	-
0x0300CC12	-?-	high	TLSv12	RSA	RSA	ChaCha20-Poly1305	256	AEAD	-?-	RSA-CHACHA20-POLY1305	RSA_WITH_CHACHA20_POLY1305	C
0x0300CC13	HIGH	HIGH	TLSv12	ECDH	RSA	ChaCha20-Poly1305	256	AEAD	-?-	ECDHE-RSA-CHACHA20-POLY1305-SHA256-OLD,ECDHE-RSA-CHACHA20-POLY1305-OLD	ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_CHACHA20_POLY1305	C
#0x0300CC13-old	HIGH	HIGH	TLSv12	ECDH	RSA	ChaCha20-Poly1305	256	AEAD	-	ECDHE-RSA-CHACHA20-POLY1305	ECDHE_RSA_WITH_CHACHA20_POLY1305	B
0x0300CC14	HIGH	HIGH	TLSv12	ECDH	ECDSA	ChaCha20-Poly1305	256	AEAD	-?-	ECDHE-ECDSA-CHACHA20-POLY1305-SHA256-OLD,ECDHE-ECDSA-CHACHA20-POLY1305-OLD	ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_ECDSA_CHACHA20_POLY1305	C
#0x0300CC14-old	HIGH	HIGH	TLSv12	ECDH	ECDSA	ChaCha20-Poly1305	256	AEAD	-	ECDHE-ECDSA-CHACHA20-POLY1305	ECDHE_ECDSA_WITH_CHACHA20_POLY1305	B
0x0300CC15	HIGH	HIGH	TLSv12	DH	RSA	ChaCha20-Poly1305	256	AEAD	-?-	DHE-RSA-CHACHA20-POLY1305-SHA256-OLD,DHE-RSA-CHACHA20-POLY1305-OLD	DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,DHE_RSA_CHACHA20_POLY1305	C
#0x0300CC15-old	HIGH	HIGH	TLSv12	DH	RSA	ChaCha20-Poly1305	256	AEAD	-	DHE-RSA-CHACHA20-POLY1305	DHE_RSA_WITH_CHACHA20_POLY1305	B
0x0300CC16	HIGH	HIGH	TLSv12	DH	PSK	ChaCha20-Poly1305	256	AEAD	-	DHE-PSK-CHACHA20-POLY1305	DHE_PSK_WITH_CHACHA20_POLY1305	B
0x0300CC17	HIGH	HIGH	TLSv12	PSK	PSK	ChaCha20-Poly1305	256	AEAD	-	PSK-CHACHA20-POLY1305	PSK_WITH_CHACHA20_POLY1305	B
0x0300CC18	HIGH	HIGH	TLSv12	ECDHEPSK	ECDHE	ChaCha20-Poly1305	256	AEAD	-	ECDHE-PSK-CHACHA20-POLY1305	ECDHE_PSK_WITH_CHACHA20_POLY1305	B
0x0300CC19	HIGH	HIGH	TLSv12	DH	RSA	ChaCha20-Poly1305	256	AEAD	-	RSA-PSK-CHACHA20-POLY1305	RSA_PSK_WITH_CHACHA20_POLY1305	B
0x0300CC20	HIGH	HIGH	TLSv12	RSA	RSA	ChaCha20	256	SHA1	-?-	RSA-CHACHA20-SHA	RSA_WITH_CHACHA20_SHA	C
0x0300CC21	HIGH	HIGH	TLSv12	ECDH	RSA	ChaCha20	256	SHA1	-?-	ECDHE-RSA-CHACHA20-SHA	ECDHE_RSA_WITH_CHACHA20_SHA	C
0x0300CC22	HIGH	HIGH	TLSv12	ECDH	RSA	ChaCha20	256	SHA1	-?-	ECDHE-ECDSA-CHACHA20-SHA	ECDHE_ECDSA_WITH_CHACHA20_SHA	C
0x0300CC23	HIGH	HIGH	TLSv12	DH	RSA	ChaCha20	256	SHA1	-?-	DHE-RSA-CHACHA20-SHA	DHE_RSA_WITH_CHACHA20_SHA	C
0x0300CC24	HIGH	HIGH	TLSv12	DH	PSK	ChaCha20	256	SHA1	-?-	DHE-PSK-CHACHA20-SHA	DHE_PSK_WITH_CHACHA20_SHA	C
0x0300CC25	HIGH	HIGH	TLSv12	PSK	PSK	ChaCha20	256	SHA1	-?-	PSK-CHACHA20-SHA	PSK_WITH_CHACHA20_SHA	C
0x0300CC26	HIGH	HIGH	TLSv12	ECDH	RSA	ChaCha20	256	SHA1	-?-	ECDHE-PSK-CHACHA20-SHA	ECDHE_PSK_WITH_CHACHA20_SHA	C
0x0300CC27	HIGH	HIGH	TLSv12	RSAPSK	RSA	ChaCha20	256	SHA1	-?-	RSA-PSK-CHACHA20-SHA	RSA_PSK_WITH_CHACHA20_SHA	C
0x0300CCA0	-?-	high	TLSv12	RSA	RSA	ChaCha20-Poly1305	256	AEAD	-?-	RSA-CHACHA20-POLY1305	RSA_WITH_CHACHA20_POLY1305	C
0x0300CCA1	HIGH	HIGH	TLSv12	ECDH	RSA	ChaCha20-Poly1305	256	AEAD	-?-	ECDHE-RSA-CHACHA20-POLY1305	ECDHE_RSA_WITH_CHACHA20_POLY1305	C
0x0300CCA2	HIGH	HIGH	TLSv12	ECDH	ECDSA	ChaCha20-Poly1305	256	AEAD	-?-	ECDHE-ECDSA-CHACHA20-POLY1305	ECDHE_ECDSA_WITH_CHACHA20_POLY1305	C
0x0300CCA3	HIGH	HIGH	TLSv12	DH	RSA	ChaCha20-Poly1305	256	AEAD	-?-	DHE-RSA-CHACHA20-POLY1305	DHE_RSA_WITH_CHACHA20_POLY1305	C
0x0300CCA4	HIGH	HIGH	TLSv12	DH	PSK	ChaCha20-Poly1305	256	AEAD	-?-	DHE-PSK-CHACHA20-POLY1305	DHE_PSK_WITH_CHACHA20_POLY1305	C
0x0300CCA5	HIGH	HIGH	TLSv12	PSK	PSK	ChaCha20-Poly1305	256	AEAD	-?-	PSK-CHACHA20-POLY1305	PSK_WITH_CHACHA20_POLY1305	C
0x0300CCA6	HIGH	HIGH	TLSv12	ECDHEPSK	ECDHE	ChaCha20-Poly1305	256	AEAD	-?-	ECDHE-PSK-CHACHA20-POLY1305	ECDHE_PSK_WITH_CHACHA20_POLY1305	C
0x0300CCA7	HIGH	HIGH	TLSv12	DH	RSA	ChaCha20-Poly1305	256	AEAD	-?-	RSA-PSK-CHACHA20-POLY1305	RSA_PSK_WITH_CHACHA20_POLY1305	C
0x0300CCA8	HIGH	HIGH	TLSv12	ECDH	RSA	ChaCha20-Poly1305	256	AEAD	7905	ECDHE-RSA-CHACHA20-POLY1305-SHA256	ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256	C
0x0300CCA9	HIGH	HIGH	TLSv12	ECDH	ECDSA	ChaCha20-Poly1305	256	AEAD	7905	ECDHE-ECDSA-CHACHA20-POLY1305-SHA256	ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256	C
0x0300CCAA	HIGH	HIGH	TLSv12	DH	RSA	ChaCha20-Poly1305	256	AEAD	7905	DHE-RSA-CHACHA20-POLY1305-SHA256	DHE_RSA_WITH_CHACHA20_POLY1305_SHA256	C
0x0300CCAB	HIGH	HIGH	TLSv12	PSK	PSK	ChaCha20-Poly1305	256	AEAD	7905	PSK-CHACHA20-POLY1305-SHA256	PSK_WITH_CHACHA20_POLY1305_SHA256	C
0x0300CCAC	HIGH	HIGH	TLSv12	ECDHEPSK	ECDHE	ChaCha20-Poly1305	256	AEAD	7905	ECDHE-PSK-CHACHA20-POLY1305-SHA256	ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256	C
0x0300CCAD	HIGH	HIGH	TLSv12	DHEPSK	DHE	ChaCha20-Poly1305	256	AEAD	7905	DHE-PSK-CHACHA20-POLY1305-SHA256	DHE_PSK_WITH_CHACHA20_POLY1305_SHA256	C
0x0300CCAE	HIGH	HIGH	TLSv12	RSAPSK	RSA	ChaCha20-Poly1305	256	AEAD	7905	RSA-PSK-CHACHA20-POLY1305-SHA256	RSA_PSK_WITH_CHACHA20_POLY1305_SHA256	C
0x0300D001	-?-	high	TLSv12	ECDH	PSK	AESGCM	128	AEAD	8442	ECDHE-PSK-AES128-GCM-SHA256	ECDHE_PSK_WITH_AES_128_GCM_SHA256	R
0x0300D002	-?-	high	TLSv12	ECDH	PSK	AESGCM	256	AEAD	8442	ECDHE-PSK-AES256-GCM-SHA384	ECDHE_PSK_WITH_AES_256_GCM_SHA384	R
0x0300D003	-?-	high	TLSv12	ECDH	PSK	AESCCM8	128	AEAD	8442	ECDHE-PSK-AES128-CCM8-SHA256	ECDHE_PSK_WITH_AES_128_CCM_8_SHA256	R
0x0300D005	-?-	high	TLSv12	ECDH	PSK	AESCCM	128	AEAD	8442	ECDHE-PSK-AES128-CCM-SHA256	ECDHE_PSK_WITH_AES_128_CCM_SHA256	R
0x0300FEE0	-?-	weak	SSLv3	RSA_FIPS	RSA_FIPS	3DES	112	SHA1	-?-	RSA-FIPS-3DES-EDE-SHA-2	RSA_FIPS_WITH_3DES_EDE_CBC_SHA_2	M
0x0300FEE1	-?-	weak	SSLv3	RSA_FIPS	RSA_FIPS	DES	56	SHA1	-?-	RSA-FIPS-DES-CBC-SHA-2	RSA_FIPS_WITH_DES_CBC_SHA_2	M
0x0300FEFE	-?-	weak	SSLv3	RSA_FIPS	RSA_FIPS	DES	56	SHA1	-?-	RSA-FIPS-DES-CBC-SHA	RSA_FIPS_WITH_DES_CBC_SHA	N
0x0300FEFF	-?-	weak	SSLv3	RSA_FIPS	RSA_FIPS	3DES	112	SHA1	-?-	RSA-FIPS-3DES-EDE-SHA	RSA_FIPS_WITH_3DES_EDE_CBC_SHA	N
0x0300FF00	HIGH	weak	SSLv3	RSA	RSA	GOST89	256	MD5	5830	GOST-MD5	GOSTR341094_RSA_WITH_28147_CNT_MD5	G
0x0300FF01	HIGH	HIGH	SSLv3	RSA	RSA	GOST89	256	GOST94	5830	GOST-GOST94	RSA_WITH_28147_CNT_GOST94	G
0x0300FF02	HIGH	HIGH	SSLv3	RSA	RSA	GOST89	256	GOST89	-?-	GOST-GOST89MAC	GOST-GOST89MAC	G
0x0300FF03	HIGH	HIGH	SSLv3	RSA	RSA	GOST89	256	GOST89	-?-	GOST-GOST89STREAM	GOST-GOST89STREAM	G
0x0300FF85	HIGH	HIGH	TLSv13	GOST	GOST	GOST89	256	GOST89	-?-	LEGACY-GOST2012-GOST8912-GOST8912,GOST2012-GOST8912-GOST891	GOSTR341112_256_WITH_28147_CNT_IMIT	FIXME
0x0300FF87	-?-	weak	TLSv13	GOST	GOST	None	0	GOST89	-?-	GOST2012-NULL-GOST12	GOSTR341112_256_WITH_NULL_GOSTR3411	FIXME
#--------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+---------------+-------+---------------+
# hex const	openssl	sec	ssl	keyx	auth	enc	bits	mac	rfc	cipher,aliases	const	comment

__END__

## CIPHERS }

