#!/usr/bin/perl

no warnings 'redefine';

no warnings 'once';

our $SID_main = "@(#) yeast.pl 3.15 24/02/19 12:41:59";
my $VERSION = _VERSION();    ## no critic qw(ValuesAndExpressions::RequireConstantVersion)

our $time0 = time();
$time0 = 0 if ( defined $ENV{'OSAFT_MAKE'} );

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";

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 because $make_text\n") if $make_text;
        $make_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).*/n) ) {
        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).*/n) ) {
        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 {
    _trace_info("BEGIN0  - start");
    sub _VERSION { return "24.01.24"; }

    my $_path = $0;
    $_path =~ s#[/\\][^/\\]*$##;
    my $_pwd = $ENV{PWD} || ".";

    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/ );
    _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 OCfg;

    our $SID_ocfg = "@(#) OCfg.pm 3.14 24/02/19 15:34:16";
    $OCfg::VERSION = "24.01.24";

    BEGIN {
        my $_me = $0;
        $_me =~ s#.*[/\\]##x;
        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 );
    }


    my @EXPORT = qw(
      %ciphers
      %prot
      %prot_txt
      %tls_compression_method
      %tls_handshake_type
      %tls_key_exchange_type
      %tls_record_type
      %tls_error_alerts
      %TLS_EXTENSIONS
      %TLS_EC_POINT_FORMATS
      %TLS_MAX_FRAGMENT_LENGTH
      %TLS_NAME_TYPE
      %TLS_PROTOCOL_VERSION
      %TLS_PSK_KEY_EXCHANGE_MODE
      %TLS_SIGNATURE_SCHEME
      %TLS_SUPPORTED_GROUPS
      %TLS_ID_TO_EXTENSIONS
      %ec_curve_types
      %tls_curves
      %target_desc
      @target_defaults
      %data_oid
      %dbx
      %cfg
      get_ciphers_range
      get_cipher_owasp
      get_openssl_version
      get_dh_paramter
      get_target_nr
      get_target_prot
      get_target_host
      get_target_port
      get_target_auth
      get_target_proxy
      get_target_path
      get_target_orig
      get_target_start
      get_target_open
      get_target_stop
      get_target_error
      set_target_nr
      set_target_prot
      set_target_host
      set_target_port
      set_target_auth
      set_target_proxy
      set_target_path
      set_target_orig
      set_target_start
      set_target_open
      set_target_stop
      set_target_error
      set_user_agent
      tls_const2text
      tls_key2text
      tls_text2key
      printhint
      test_cipher_regex
      ocfg_done
    );

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

    our %ciphers = ();

    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,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,
        'cipher'     => [],

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

        'cipherpatterns' => {

            '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 ssleay)],
        'ciphers' => [],

        'cipherrange'  => 'intern',
        'cipherranges' => {

            'rfc' => "0x03000000 .. 0x030000FF, 0x03001300 .. 0x030013FF,
                        0x0300C000 .. 0x0300C1FF, 0x0300CC00 .. 0x0300CCFF,
                        0x0300D000 .. 0x0300D0FF,
                        0x0300FE00 .. 0x0300FFFF,
                       ",
            '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' => "",

            '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, 0x0300FFFF,
                       ",
            'TLSv13' => "0x03001301 .. 0x03001305, 0x0300FF85, 0x0300FF87,
                        0x030000C6,   0x030000C7, 0x0300C0B4, 0x0300C0B5,
                        0x0300C100 .. 0x0300C107,
                       ",
            '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_ssleay
              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
              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 hasalpn hasnpn session_protocol fallback_protocol alpn alpns npns next_protocols https_protocols http_protocols https_svc http_svc)
        ],
        'cmd-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_ssleay cipher_openssl
              cipher_null cipher_adh cipher_cbc cipher_des cipher_edh
              cipher_exp  cipher_rc4 cipher_pfs cipher_pfsall
              beast crime time breach 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_ssleay 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_pfs cipher_pfsall cipher_cbc cipher_des
              cipher_edh cipher_exp cipher_rc4 cipher_selected
              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(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
              master_key session_id session_ticket
            )
        ],

        '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' => {
            '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-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_|ARIA|CAMELLIA|ECDS[AS]|GOST|IDEA|SEED|CECPQ|SM4|FZA[_-]FZA)',
            'notOWASP_A    ' => '^(?:TLSv11?)',
            'notOWASP_B'     => '',
            'notOWASP_C'     => '',
            'notOWASP_D'     => '',
            'notCipher'      => '^GREASE|SCSV',

            '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'    => '(?:[_-](?:CBC||CBC3|3DES|DES|192)[_-])',
            'notSweet32' => '(?:[_-]AES[_-])',

            'PFS'      => '^(?:(?:SSLv?3|TLSv?1(?:[12])?|PCT1?)[_-])?((?:EC)?DHE|EDH)[_-]',
            'TR-02102' => '(?:DHE|EDH)[_-](?:PSK[_-])?(?:(?:EC)?[DR]S[AS])[_-]',
            'notTR-02102' => '[_-]SHA$',
            'TR-02102-noPFS' => '(?:EC)?DH)[_-](?:EC)?(?:[DR]S[AS])[_-]',
            'TR-03116+' => 'EC(?:DHE|EDH)[_-](?:PSK|(?:EC)?(?:[DR]S[AS]))[_-]AES128[_-](?:GCM[_-])?SHA256',
            'TR-03116-' => 'EC(?:DHE|EDH)[_-](?:PSK|(?:EC)?(?:[DR]S[AS]))[_-]AES(?:128|256)[_-](?:GCM[_-])?SHA(?:256|384)',
            'notTR-03116' => '(?:PSK[_-]AES256|[_-]SHA$)',
            'RFC7525'           => 'EC(?:DHE|EDH)[_-](?:PSK|(?:EC)?(?:[DR]S[AS]))[_-]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 serverside according RFC 5746 ",
            'drown'         => "checks only if the target server itself is vulnerable to DROWN ",
            'robot'         => "checks only if the target offers ciphers vulnerable to ROBOT ",
            'cipher'        => "+cipher : functionality changed, please see '$cfg__me --help=TECHNIC'",
            '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 fow OpenSSL 3.x may result in many warnings, consider using '--no-warning'",
        },

        '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-text   --help=ciphers-text
                )
            ],
        },

        '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,
    );

    *_warn = sub { print( join( " ", "**WARNING:", @_ ), "\n" ); return; }
      if not defined &_warn;
    *_dbx = sub { print( join( " ", "#dbx#", @_ ), "\n" ); return; }
      if not defined &_dbx;
    *_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 _get_keys_list {
        return Ciphers::get_keys_list() if ( defined(&Ciphers::get_keys_list) );
        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;
        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 $cmd  = shift;
        my @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";
        print "
=== internal data structure: various RegEx to check cipher properties ===
=
= Check RegEx to detect ciphers, which support PFS using the internal function
= ::_is_ssl_pfs() .
    \$cfg{'regex'}->{'PFS'}:      # match ciphers supporting PFS
      $cfg{'regex'}->{'PFS'}
=
= 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();
        foreach my $key ( sort ( _get_keys_list() ) ) {
            my $ssl    = Ciphers::get_ssl($key);
            my $cipher = Ciphers::get_name($key);
            my $is_pfs = ( ::_is_ssl_pfs( $ssl, $cipher ) 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 );
        }
        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 or -?- as returned by get_cipher_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 _ocfg_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.14");
        return;
    }

    sub _ocfg_main {
        my @argv = @_;
        push( @argv, "--help" ) if ( 0 > $#argv );
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );
        while ( my $arg = shift @argv ) {
            if ( $arg =~ m/^--?h(?:elp)?$/ ) {
                OText::print_pod( $0, __FILE__, $SID_ocfg );
                exit 0;
            }
            if ( $arg =~ /^version$/ )         { print "$SID_ocfg\n";      next; }
            if ( $arg =~ /^[-+]?V(ERSION)?$/ ) { print "$OCfg::VERSION\n"; next; }
            if ( $arg =~ m/^--(?:test[_.-]?)regex/ ) {
                $arg = "--test-regex";
                test_cipher_regex();
                printf("#$0: direct testing not yet possible, please try:\n   o-saft.pl $arg\n");
            }
        }
        exit 0;
    }

    sub ocfg_done { }

    _ocfg_init();

}

{

    package Ciphers;

    our @CARP_NOT = qw(Ciphers);

    my $SID_ciphers = "@(#) Ciphers.pm 3.13 24/01/31 13:37:19";
    our $VERSION = "24.01.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 );
    }

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



    our @EXPORT_OK = qw(
      %ciphers
      %ciphers_desc
      %ciphers_notes
      $cipher_results
      ciphers_done
    );

    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' => '(Alias) Names',
        '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 %ciphers = (

    );

    our @cipher_iana_recomended =
      qw(
      0x0300009E 0x0300009F 0x030000AA 0x030000AB 0x03001301 0x03001302 0x03001303 0x0300130$
      0x0300C02B 0x0300C02C 0x0300C02F 0x0300C030 0x0300C09E 0x0300C09F
      0x0300C0A6 0x0300C0A7 0x0300C0A8 0x0300C0A9 0x0300CCAA 0x0300CCAC 0x0300CCAD
      0x0300D001 0x0300D002 0x0300D005
      );

    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 = (

    );

    *_warn = sub { print( join( " ", "**WARNING:", @_ ), "\n" ); return; }
      if not defined &_warn;
    *_dbx = sub { print( join( " ", "#dbx#", @_ ), "\n" ); return; }
      if not defined &_dbx;
    *_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_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;
        return $key if defined $ciphers{$key};
        foreach my $key ( keys %ciphers ) {
            my @names = get_names($key);
            return $key if ( 0 < ( grep { /^$txt$/i } @names ) );
        }
        $txt =~ s/^(?:SSL[23]?|TLS1?)_//;
        $txt =~ s/^(?:CK|TXT)_//;
        foreach my $key ( keys %ciphers ) {
            my @names = get_const($key);
            return $key if ( 0 < ( grep { /^$txt$/i } @names ) );
        }
        _warn("521: no key found 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 } @cipher_iana_recomended ) ? "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_keys {
        my $pattern = shift;
        _trace("find_keys($pattern)");
        return map( { get_key($_); } grep { /$pattern/ } get_names_list() );
    }

    sub find_names {
        my $pattern = shift;
        $pattern =~ s/:/|/g;
        _trace("find_names($pattern)");
        return grep { /$pattern/ } get_names_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);
            }
            _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(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 ) {
                _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 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( "%-8s %s\t%s\t%-14s\t# %s\n", "get_bits",    $cipher, "bits",    get_bits($cipher),    "40" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_enc",     $cipher, "enc",     get_enc($cipher),     "RC4" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_keyx",    $cipher, "keyx",    get_keyx($cipher),    "RSA(512)" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_auth",    $cipher, "auth",    get_auth($cipher),    "RSA" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_mac",     $cipher, "mac",     get_mac($cipher),     "MD5" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_rfc",     $cipher, "rfc",     get_rfc($cipher),     "4346,6347" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_sec",     $cipher, "sec",     get_sec($cipher),     "WEAK" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_ssl",     $cipher, "ssl",     get_ssl($cipher),     "SSLv3" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_notes",   $cipher, "tags",    get_notes($cipher),   "export" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_name",    $cipher, "name",    get_name($cipher),    "EXP-RC4-MD5" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_iana",    $cipher, "iana",    get_iana($cipher),    "no" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_pfs",     $cipher, "pfs",     get_iana($cipher),    "no" );
        printf( "%-8s %s\t%s\t%-14s\t# %s\n", "get_encsize", $cipher, "encsize", get_encsize($cipher), "-" );
        printf( "%-8s %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} ) {
            _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",   get_names($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_const",   $key, "const",   get_const($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",   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};
            printf( "%6s = \$ciphers{'%s'}{%s};\t# %-7s\t# %s\n", '$' . $var, $hex, $col, $ciphers_desc{sample}->{$hex}[$idx], $txt );
            $idx++;
        }
        print("= additional following methods are available:");
        printf( "%6s = \$ciphers{'%s'}{%s};\t# %-7s\t# %s\n", '$' . 'name',  $hex, 'name',  'AES256-SHA256', $ciphers_desc{'name'} );
        printf( "%6s = \$ciphers{'%s'}{%s};\t# %-7s\t# %s\n", '$' . 'alias', $hex, 'alias', 'Alias',         $ciphers_desc{'names'} );
        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", 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)/ ) {
            _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";
        return                  if ( $arg =~ m/^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_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($1) )   if ( $arg =~ m/^find.?names=(.*)/ );
        print join( " ", find_keys($1) )    if ( $arg =~ m/^find.?keys=(.*)/ );
        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}/ ) {
                _warn( "504: DATA line" . sprintf( "%4d", $. ) . ": wrong hex key '$key'" );
                next;
            }
            if ( 13 != $len + 1 ) {
                _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 _ciphers_usage {
        my $name = ( caller(0) )[1];
        print "# commands to show internal cipher tables:\n";
        foreach my $cmd (qw(alias const dump description openssl rfc simple sort ssltest overview )) {
            printf( "\t%s %s\n", $name, $cmd );
        }
        print "# commands to show cipher data:\n";
        foreach my $cmd (qw(key=CIPHER-NAME getter=KEY is_valid_key=KEY)) {
            printf( "\t%s %s\n", $name, $cmd );
        }
        print "# various commands (examples):\n";
        printf("\t$name version\n");
        printf("\t$name getter=0x0300CCA9\n");
        printf("\t$name getter=0xCC,0xA9\n");
        printf("\t$name getter=0x03,0x00,0xCC,0xA9\n");
        foreach my $cmd (qw(key=ECDHE-ECDSA-CHACHA20-POLY1305-SHA256 is_valid_key=0300Cca9)) {
            printf( "\t%s %s\n", $name, $cmd );
        }
        print "#\n# all commands can also be used as '--test-ciphers-CMD\n";
        return;
    }

    sub _ciphers_main {
        my @argv = @_;
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );
        if ( 0 > $#argv ) { OText::print_pod( $0, __FILE__, $SID_ciphers ); exit; }
        while ( my $arg = shift @argv ) {
            if ( $arg =~ m/^--?h(?:elp)?$/ ) { OText::print_pod( $0, __FILE__, $SID_ciphers ); exit; }
            if ( $arg eq '--usage' )         { _ciphers_usage();                               next; }
            if ( $arg eq '--v' ) { $OCfg::cfg{'verbose'}++; next; }
            if ( $arg =~ /^version$/ )                  { print "$SID_ciphers\n"; next; }
            if ( $arg =~ /^[-+]?V(ERSION)?$/ )          { print "$VERSION\n";     next; }
            if ( $arg =~ /^--test.?ciphers.?version/i ) { print "$VERSION\n";     next; }
            $arg =~ s/^--test.?ciphers//;
            show("--test-ciphers$arg");
        }
        exit 0;
    }

    sub ciphers_done { }

    _ciphers_init();


}

{

    package error_handler;

    my $SID_error = "@(#) error_handler.pm 3.9 24/01/29 11:05:03";
    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.19 24/02/19 12:24:46";
    our $VERSION = "24.01.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 %OERR = %error_handler::OERR;

    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 _carp {
        my @txt = @_;
        return if ( ( grep { /(:?--no.?warn)/ix } @main::ARGV ) > 0 );
        local $\ = "\n";
        Carp::carp( $OText::STR{WARN}, join( " ", @txt ) );
        return;
    }

    sub _hint {
        my @txt = @_;
        return if ( ( grep { /(:?--no.?hint)/ix } @main::ARGV ) > 0 );
        local $\ = "\n";
        print( $OText::STR{HINT}, join( " ", @txt ) );
        return;
    }

    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( "**WARNING: 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( "**WARNING: 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( "**WARNING: 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( "**WARNING: 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( "**WARNING: 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("**WARNING: 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(
"**WARNING: 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("**WARNING: 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( "**WARNING: 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("**WARNING: 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("**WARNING: 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(
"**WARNING: 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("**WARNING: 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(" 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(
"**WARNING: 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(
"**WARNING: checkSSLciphers (3.2): => Unexpected Lack of Data or unexpected Answer while checking the priority of the ciphers \'$str\' -     > Exit loop. Reason: '$my_error'"
                        );
                        _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(
"**WARNING: checkSSLciphers (3.3): => Received no cipher while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'"
                        );
                        _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( "**WARNING: 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(
"**WARNING: 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("**WARNING: 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(
"**WARNING: checkSSLciphers (6.2): => Unexpected Lack of Data or unexpected Answer while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'"
                        );
                        _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(
"**WARNING: checkSSLciphers (6.3): => Received no cipher while checking the priority of the ciphers \'$str\' -> Exit loop. Reason: '$my_error'"
                        );
                        _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("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 = "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( "**WARNING: " . 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( "**WARNING: " . 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( "**WARNING: " . 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( "**WARNING: " . 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 = "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( "**WARNING: " . 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("**WARNING: openTcpSSLconnection: ... $my_error");
                                    close($socket) or Carp::carp("**WARNING: 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( "**WARNING: " . 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: openTcpSSLconnection: ... $my_error");
                                    close($socket) or Carp::carp("**WARNING: 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( "**WARNING: " . 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: openTcpSSLconnection: ... $my_error");
                                    close($socket) or Carp::carp("**WARNING: 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( "**WARNING: " . 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: 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 = "";
      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( "**WARNING: _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 =
                      "**WARNING: _doCheckSSLciphers: $host:$port looks like an SMTP-Service, probably the option '--starttls' is needed -> target ignored\n";
                    Carp::carp($my_error);
                    return ("");
                }
                $my_error =
                  "**WARNING: _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("**WARNING: _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_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_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_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_error = "failed to select data: $@";
                        alarm(0);
                        error_handler->new(
                            {
                                type    => $OERR{'SSLHELLO_RETRY_CIPHERS'},
                                id      => '_readRecord (udp): unknown Timeout error (1)',
                                message => $my_error,
                                warn    => 0,
                            }
                        );
                        Carp::carp("_readRecord (udp): $my_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_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_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_error,
                                    warn    => 0,
                                }
                            );
                            Carp::carp("_readRecord (udp): $my_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_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_error,
                                    warn    => 0,
                                }
                            );
                            _trace4("_readRecord (udp) : $my_error\n");
                            last RETRY_TO_RECEIVE_A_RECORD;
                        }
                        else {
                            $my_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_error,
                                    warn    => 0,
                                }
                            );
                            _trace1("_readRecord (udp): $my_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_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_error,
                            warn    => 0,
                        }
                    );
                    _trace1("_readRecord (udp): ... Received data: $my_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_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_error,
                                warn    => 0,
                            }
                        );
                        Carp::carp("_readRecord (tcp): $my_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_error      = "no known SSL/TLS PDU type";
                                $recordType    = 0;
                                $recordVersion = 0;
                                $recordLen     = 0;
                                _trace1("_readRecord (tcp): $my_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_error = "Server '$host:$port': no known DTLS PDU type -> unknown protocol";
                            _trace1("_readRecord (udp): $my_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_error) && ( length($$input_ref) < $pduLen ) ) {
            $my_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_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_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_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 =
                "**WARNING: 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 =
"**WARNING: 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 = "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 =
                "**WARNING: 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 =
"**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: 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("**WARNING: 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(
"**WARNING: 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(
"**WARNING: 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("**WARNING: 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(
"**WARNING: 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( "**WARNING: 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(
"**WARNING: 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(
"**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"
                        );
                        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(
"**WARNING: 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("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( "**WARNING: 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(
"**WARNING: 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("**WARNING: $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("**WARNING: $my_error\n");
                                    _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(
"**WARNING: 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 ) {

                                    _carp("$client_ssl not supported by '$host:$port'; no ciphers detected, ignored\n");
                                    _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("**WARNING: $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("**WARNING: $my_error\n");
                                    _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(
"**WARNING: parseHandshakeRecord: Server '$host:$port' ($client_ssl): received fatal SSL/TLS error (2c): Description: $description ($serverHello{'description'})\n"
                                );
                                if ( $serverHello{'description'} == 50 ) {
                                    _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(
"**WARNING: 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(
"**WARNING: 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( "**WARNING: 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( "**WARNING: 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( "**WARNING: 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( "**WARNING: 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(
"**WARNING: 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 = @_;
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );
        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.9 24/02/19 15:25:23";
    our $VERSION = "24.01.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 %CST = (
        '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   = "#$CST{'ME'}::";
    $SSLinfo::prefix_verbose = "#$CST{'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("**WARNING: 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; }
        }
        if ( not exists &_warn ) {
            sub _warn { my @txt = @_; printf( "%s%s\n", ( $OText::STR{'WARN'} || "**wARNING: " ), "@txt" ); return; }
        }
        if ( not exists &_dbx ) {
            sub _dbx { my @txt = @_; printf( "%s%s\n", ( $OText::STR{'DBX'} || "#dbx# " ), "@txt" ); return; }
        }
    }


    my @EXPORT = qw(
      net_sslinfo_done
      ssleay_methods
      test_methods
      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
      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;
        return ( 1 < $trace ) ? $val : "<<use --trace=2 to print pointer>>";
    }

    sub _setcommand {
        my $command = shift;
        return '' if ( '' eq $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'    => "",
        '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'      => "",
        '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|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
            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 = "$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( "#%22s\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( "#%21s\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 = "# 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
#      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 }
# 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 $ssl    = 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    = undef;
        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 $CST{'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 $CST{'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 $CST{'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.88 <= $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" . $CST{'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( $CST{'ERROR'} );
            printf( "%s%s\n", $CST{'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 .= "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_sts'}      = _header_get( 'Strict-Transport-Security', $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:",
                '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( $CST{'ERROR'} );
            printf( "%s%s\n", $CST{'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 $CST{'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 $CST{'OPENSSL'};
            }
            if ( 0 == $SSLinfo::use_sclient ) {
                _trace2("do_openssl($mode): WARNING: no openssl s_client");
                return $CST{'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" | $_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 | $_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 $CST{'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 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 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 );
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );
        local $\ = "\n";
        while ( my $arg = shift @argv ) {
            if ( $arg =~ m/^--?h(?:elp)?$/ ) { local $\ = ""; undef $\; OText::print_pod( $0, __PACKAGE__, $SID_sslinfo ); }
            if ( $arg =~ m/^--(?:v|trace.?)/i ) { $SSLinfo::verbose++; next; }
            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 OData;

    my $SID_odata = "@(#) OData.pm 3.12 24/02/19 11:56:14";
    our $VERSION = "24.01.24";

    BEGIN {
        my $_me = $0;
        $_me =~ s#.*[/\\]##x;
        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
      %check_cert
      %check_conn
      %check_dest
      %check_http
      %check_size
      %data
      %data0
      %info
      %shorttexts
      odata_done
    );

    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 { join( " ", SSLinfo::ciphers( $_[0], $_[1] ) ) },  'txt' => "Client Ciphers" },
        'dates'            => { 'val' => sub { 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" },
        '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_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}->{val} in _odata_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" },
        '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",
        '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",
        '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",
    );

    *_warn = sub { print( join( " ", "**WARNING:", @_ ), "\n" ); return; }
      if not defined &_warn;
    *_dbx = sub { print( join( " ", "#dbx#", @_ ), "\n" ); return; }
      if not defined &_dbx;

    sub __SSLinfo {

        my ( $cmd, $host, $port ) = @_;
        my $val = "<<__SSLinfo: unknown command: '$cmd'>>";
        my $ext = "";
        my %cfg = %OCfg::cfg;
        my %dum = %OCfg::cfg;
        $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 ( $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 ( $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 _odata_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'; }
        foreach my $key ( keys %checks )     { $checks{$key}->{val} = ""; }

        return;
    }

    sub _odata_main {
        my @argv = @_;
        push( @argv, "--help" ) if ( 0 > $#argv );
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );

        while ( my $arg = shift @argv ) {
            OText::print_pod( $0, __PACKAGE__, $SID_odata ) if ( $arg =~ m/^--?h(?:elp)?$/x );
            if ( $arg =~ /^version$/x )        { print "$SID_odata\n"; next; }
            if ( $arg =~ /^[-+]?V(ERSION)?$/ ) { print "$VERSION\n";   next; }
            $arg =~ s/^(?:--test.?data)//x;
            show($arg);
        }
        exit 0;
    }

    sub odata_done { }

    _odata_init();


}

{

    package ODoc;

    my $SID_odoc = "@(#) ODoc.pm 3.14 24/02/19 15:29:08";
    our $VERSION = "24.01.24";

    BEGIN {

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


    *_warn = sub { print( join( " ", "**WARNING:", @_ ), "\n" ); return; }
      if not defined &_warn;
    *_dbx = sub { print( join( " ", "#dbx#", @_ ), "\n" ); return; }
      if not defined &_dbx;

    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 $orig = $file;
        my $name = $file;
        $name =~ s#(.*[/\\]+)##g;
        $file =~ s#^\.\./##;
        $file =~ s#usr/##;
        $file = "$OCfg::cfg{'dirs'}->{'doc'}/$name" if ( not -e $file );
        $file = ""                                  if ( not -e $file );
        _warn("189: no '$orig' found, consider installing") if "" eq $file;
        return $file;
    }

    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 ) ) {
                _warn("190: open('$file'): $!");
            }
        }
        else {
            $fh = __PACKAGE__ . "::DATA";
            _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 $file    = shift;
        my $name    = shift || "o-saft.pl";
        my $version = shift || $VERSION;
        my $fh      = _get_filehandle($file);
        return _replace_var( $name, $version, <$fh> );
    }


    sub get_as_text { my $fh = _get_filehandle(shift); return <$fh>; }


    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/((?:Net::SSL(?:hello|info)|o-saft(?:-dbx|-man|-usr|-README)(?:\.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_text {
        my $file  = shift;
        my $label = shift || "";
        $label = lc($label);
        my $anf = uc($label);
        my $end = "[A-Z]";
        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 ) ) {

            _warn("192: using workaround to print large strings.\n\n");
            print foreach split( //, $txt );
        }
        else {
        }
        if ( $label =~ m/^todo/i ) {
            print "\n  NOT YET IMPLEMENTED\n";
        }
        return $txt;
    }


    sub print_as_text { my $fh = _get_filehandle(shift); print <$fh>; close($fh); return; }


    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 _odoc_usage {
        my $name = ( caller(0) )[1];
        print "# various commands:\n";
        foreach my $cmd (qw(version +VERSION)) {
            printf( "\t%s %s\n", $name, $cmd );
        }
        printf("\t$name list\t# list available files\n");
        print "# commands to get text from file in various formats(examples):\n";
        foreach my $cmd (qw(get get-markup get-text get-as-text print)) {
            printf( "\t%s %s help.txt\n", $name, $cmd );
        }
        printf("\t$name ciphers=dumptab > c.csv; libreoffice c.csv\n");
        return;
    }

    sub _odoc_main {
        my @argv = @_;
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );
        OText::print_pod( $0, __PACKAGE__, $SID_odoc ) if ( 0 > $#argv );
        while ( my $cmd = shift @argv ) {
            my $arg = shift @argv;
            OText::print_pod( $0, __PACKAGE__, $SID_odoc ) if ( $cmd =~ /^--?h(?:elp)?$/ );
            _odoc_usage()                                  if ( $cmd eq '--usage' );
            print list($0) . "\n"   if ( $cmd =~ /^list$/ );
            print get($arg)         if ( $cmd =~ /^get$/ );
            print get_as_text($arg) if ( $cmd =~ /^get.?as.?text/ );
            print get_markup($arg)  if ( $cmd =~ /^get.?mark(up)?/ );
            print get_text($arg)    if ( $cmd =~ /^get.?text/ );
            print_as_text($arg)     if ( $cmd =~ /^print$/ );
            print "$SID_odoc\n"     if ( $cmd =~ /^version$/ );
            print "$VERSION\n"      if ( $cmd =~ /^[-+]?V(ERSION)?$/ );
        }
        exit 0;
    }

    sub odoc_done { }


}

{

    package OMan;

    my $SID_oman = "@(#) OMan.pm 3.21 24/02/19 11:31:24";
    our $VERSION = "24.01.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 oman_done );

    my $parent = ( caller(0) )[1] || "o-saft.pl";
    $parent =~ s:.*/::;
    $parent =~ s:\\:/:g;
    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 );
    our $TRACE = 0;
    $TRACE++ if ( 0 < ( grep { /^--trace(?:=\d+)?$/ } @ARGV ) );
    local $\ = "";

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

    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);id.checked=(id.checked=='false')?'true':'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--html5");       // nice output in browser
  toggle_checked("o--header");      // .. also as option ..
  toggle_checked("o--enabled");     // .. also as option ..
  toggle_checked("o--html5");       // .. also as option ..
 </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.55em 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 */
 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

    );

    *_warn = sub { print( $OText::STR{WARN}, join( " ", @_ ), "\n" ); }
      if not defined &_warn;
    *_hint = sub { print( $OText::STR{HINT}, join( " ", @_ ), "\n" ); }
      if not defined &_hint;
    *_dbx = sub { print( $OText::STR{DBX}, join( " ", @_ ), "\n" ); }
      if not defined &_dbx;

    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 = " -->";
        }
        if ( 0 < $TRACE ) {
            print $anf . "#" . $ich . ": " . join( ' ', @txt ) . "$end\n";
        }
        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_get_version {
        no strict;
        my $v = '3.21';
        $v = _VERSION() if ( defined &_VERSION );
        return $v;
    }

    sub _man_html_init {
        my $tipp    = _man_get_version();
        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__/$tipp/g;
        $html{'body_anf'}      =~ s/__HTML_title__/$html{'title'}/g;
        $html{'meta'}          =~ s/__HTML_title__/$html{'title'}/g;
        return;
    }

    sub _man_file_get {
        my $typ = shift;
        return ODoc::get_as_text('glossary.txt') if ( 'abbr' eq $typ );
        return ODoc::get_as_text('links.txt')    if ( 'links' eq $typ );
        return ODoc::get_as_text('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-dns dns no-cert BR
            no-sni sni   BR
            no-http http BR
            header  no-header  no-warnings html4 html5   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 $len1 = shift;
        my @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 = _man_file_get($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 = _man_file_get($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_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_source {
        _man_dbx("_man_cmd_from_source() ...");
        my $txt  = "";
        my $skip = 1;
        my $fh   = undef;
        _man_dbx("_man_cmd_from_source: 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  = "";
        my $skip = 1;
        my $fh   = undef;
        if ( open( $fh, '<:encoding(UTF-8)', $cfg{'RC-FILE'} ) ) {
            while (<$fh>) {
                if (m/^##[?]\s+([a-zA-Z].*)/) {
                    $skip = 0;
                    $val  = $1;
                    next;
                }
                if (m/^--cfg_cmd=([^=]*)=/) {
                    next if ( 1 == $skip );
                    $skip = 1;
                    $txt .= sprintf( "+%-17s%s\n", $1, $val );
                    $val = "";
                }
            }
            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::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::ciphers_desc{'hex'}</th>\n";
        $tab .= "      <th rowspan=2>$Ciphers::ciphers_desc{'sec'}</th>\n";
        $tab .= "      <th colspan=3>Names</th>\n";
        $tab .= "      <th rowspan=2>$Ciphers::ciphers_desc{'openssl'}</th>\n";
        $tab .= "      <th rowspan=2>$Ciphers::ciphers_desc{'ssl'}</th>\n";
        $tab .= "      <th rowspan=2>$Ciphers::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::ciphers_desc{'pfs'}</th>\n";
        $tab .= "      <th rowspan=2>RFC(s)&#xa0;URL</th>\n";
        $tab .= "      <th rowspan=2>$Ciphers::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::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/ ) {
            _warn( "094:", "'$parent' used as program name in generated files" );
            _hint("documentation files should be generated using '$cfg{files}{SELF} --help=gen-docs'");
        }
        my $fh = undef;
        foreach my $mode ( keys %{ $cfg{'files'} } ) {
            next if $mode !~ m/^--help/;
            next if $mode =~ m/^--help=warnings/;
            my $doc = "$cfg{'files'}{$mode}";
            _man_dbx("man_docs_write: mode=$mode  ->  doc=$doc");
            open( $fh, '>:encoding(UTF-8)', $doc ) or do {
                _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_rcfile() );
        $txt .= _man_foot(15) . "\n";
        return $txt;
    }

    sub man_warnings {
        _man_dbx("man_warnings($parent) ...");
        my $pod = "";
        my $txt = "";
        my $rex = '.STR\{(?:ERROR|WARN|HINT)},|' . join( '|', $OText::STR{ERROR}, $STR{WARN}, $STR{HINT} );
        $rex =~ s/([*!])/\\$1/g;
        $rex = qr($rex);
        my $fh  = undef;
        my $doc = "$cfg{'dirs'}->{'doc'}/o-saft.pl.--help=warnings";
        _man_dbx("man_warnings: rex=$rex");
        _man_dbx("man_warnings: $doc");

        if ( not open( $fh, '<:encoding(UTF-8)', $doc ) ) {
            _warn( "091:", "help file '$doc' cannot be opened: $! ; ignored" );
            _hint( $cfg{'hints'}->{'help=warnings'} );
            return $pod;
        }

        while (<$fh>) {
            next if (m/^\s*#/);
            next if (m/^\s*$/);
            if ( not m/$rex/ ) {
                warn( $OText::STR{WARN}, "092:", " help file '$doc' unknown syntax: '$_' ; ignored" );
                next;
            }
            my ( $err, $nr, $msg ) = m/($rex\s*)"?([0-9]{3}:?)(.*)/;
            my $bad = 0;
            $bad = 1 if ( not defined $err or $err =~ m/^$/ );
            $bad = 1 if ( not defined $nr  or $nr  =~ m/^$/ );
            $bad = 1 if ( not defined $msg or $msg =~ m/^$/ );
            if ( $bad == 1 ) {
                $txt .= $_;
                next;
            }
            $err =~ s/\$OText::STR\{ERROR}/$STR{ERROR}/;
            $err =~ s/\$OText::STR\{WARN}/$STR{WARN}/;
            $err =~ s/, *//;
            $msg =~ s/^[", ]*//;
            $txt .= sprintf( "%s%s\t- %s\n", $err, $nr, $msg );
        }
        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.

# 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::ciphers_desc ) {
                next if "additional_notes" eq $key;
                $keys .= "#\t$key\t$Ciphers::ciphers_desc{$key}\n";
            }
        }
        foreach my $key ( keys %Ciphers::ciphers_desc ) {
            $txt =~ s/\n$key\s/\n\t$Ciphers::ciphers_desc{$key}\t/g;
        }
        my $note = $Ciphers::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; used pattern" ],
            'rfc'     => [ "Number",       " - ",  " RFC Title and URL" ],
            'links'   => [ "Title",        " - ",  " URL" ],
            'check'   => [ "key",          " - ",  " Label text" ],
            'data'    => [ "key",          " - ",  " Label text" ],
            'hint'    => [ "key",          " - ",  " Hint text" ],
            'text'    => [ "key",          " - ",  " text" ],
            'cmd'     => [ "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 %::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/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 $p   = '[._-]';
        my $fh  = undef;
        my $src = _get_filename($parent);
        _man_dbx("man_alias: $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/\$p\?/-/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]";
        my $hlp;
        _man_dbx("man_help($anf, $end) ...");
        if ( 0 < $::osaft_standalone ) {
            @help = ODoc::get_markup( "help.txt", $0, $version );
        }
        my $txt = join( '', @help );
        if ( 1 < ( grep { /^--v/ } @ARGV ) ) {
            return ODoc::get_egg("help.txt");
        }
        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;
        $txt = _man_squeeze( undef, $txt );

        if ( 0 < ( grep { /^--v/ } @ARGV ) ) {

            print "**WARNING: using workaround to print large strings.\n\n";
            $hlp .= $_ foreach split( //, $txt );
        }
        else {
            $hlp .= $txt;
        }
        if ( $label =~ m/^todo/i ) {
            $hlp .= "\n  NOT YET IMPLEMENTED\n";
            foreach my $label ( sort keys %OData::checks ) {
                next if ( 0 >= grep( { lc($label) eq lc($_) } \@{ $cfg{'commands_notyet'} } ) );
                $hlp .= "        $label\t- " . $OData::checks{$label}->{txt} . "\n";
            }
        }
        return $hlp;
    }

    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: $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?$/ );
        $txt = man_table($1)                                                if ( $hlp =~ /^(cmd|check|data|info|ourstr|text)s?$/ );
        $txt = man_table( 'cfg_' . $1 )                                     if ( $hlp =~ /^cfg[_.-]?(cmd|check|data|info|hint|text|range|regex|ourstr)s?$/ );
        $txt = man_src_grep( qr/\s*_trace_(exit|info|next)\(/n, "--exit=" ) if ( $hlp =~ /^exit$/ );

        if ( $hlp =~ /^cmds$/ ) {
            $txt = "# $parent commands:\t+" . join( ' +', @{ $cfg{'commands'} } );
        }
        if ( $hlp =~ /^legacys?$/ ) {
            $txt = "# $parent legacy values:\t" . join( ' ', @{ $cfg{'legacys'} } );
        }
        if ( $hlp =~ m/^tools$/ ) {
            my @txt = ODoc::get( "tools.txt", $parent, $version );
            $txt = join( "", @txt );
        }
        if ( $hlp =~ m/^(coding|Program.?Code)$/i ) {
            my @txt = ODoc::get( "coding.txt", $parent, $version );
            $txt = join( "", @txt );
        }
        if ( $hlp =~ m/^(devel|developer|development)$/i ) {
            $txt = join( "", ODoc::get( "devel.txt", $parent, $version ) );
        }
        if ( not $txt ) {
            _man_dbx( "man_printhelp: " . uc($hlp) );
            $txt = man_help( uc($hlp) ) if ( $hlp !~ m/^[+-]-?/ );
        }
        print $txt || "";
        return;
    }

    sub _oman_main {
        push( @ARGV, "--help" ) if 0 > $#ARGV;
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );
        while ( my $arg = shift @ARGV ) {
            OText::print_pod( $0, __FILE__, $SID_oman ) if ( $arg =~ m/--?h(?:elp)?$/x );
            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; }
            man_docs_write() if ( $arg =~ m/--(?:help=)?gen[_.=-]?docs/x );
            $arg =~ s/--help[_.=-]?//;
            $arg =~ s/--test[_.=-]?//;
            next if ( $arg =~ m/^[+-]-?/ );
            man_printhelp($arg);
        }
        exit 0;
    }

    sub oman_done { }


}

{

    package OText;

    my $SID_otext = "@(#) OText.pm 3.8 24/02/19 15:26:59";
    our $VERSION = "24.01.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)>>",
    );

    our @EXPORT_OK = qw( %STR print_pod otext_done );


    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 otext_test {
        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 _otext_main {
        my @argv = @_;
        push( @argv, "--help" ) if ( 0 > $#argv );
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );

        while ( my $arg = shift @argv ) {
            if ( $arg =~ m/^--?h(?:elp)?$/msx )       { print_pod( $0, __PACKAGE__, $SID_otext ); }
            if ( $arg =~ /^version$/x )               { print "$SID_otext\n"; next; }
            if ( $arg =~ /^[-+]?V(ERSION)?$/x )       { print "$VERSION\n"; next; }
            if ( $arg =~ m/^--(?:test[_.-]?)text/mx ) { otext_test($arg); }
        }
        exit 0;
    }

    sub otext_done { }

}

{

    package OTrace;

    no warnings 'redefine';

    no warnings 'once';

    my $SID_trace = "@(#) OTrace.pm 3.15 24/02/19 15:31:22";
    our $VERSION = "24.01.24";

    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 );
        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
      trace_arg
      trace_cmd
      trace_args
      trace_init
      trace_exit
      trace_test
      trace_time
      trace_ciphers_list
      trace_ciphers_list
      trace_targets
      trace_done
    );

    sub trace_time {
        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, trace_time(), "@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 trace_arg { 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 __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; };
            /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 _trace_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/option  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 _trace_test_avail {
        local $\ = "\n";
        printf( "#%s:\n", ( caller(0) )[3] );
        print <<'EoT';

=== internal data structure: 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       desciption 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 %::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 $::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 _trace_test_init {
        local $\                     = "\n";
        local $Data::Dumper::Deparse = 1;
        my $line = "#--------------------+-------------------------------------------";
        printf( "#%s:\n", ( caller(0) )[3] );
        print <<'EoT';

=== internal data structure: initialisation of %cfg, %data and %checks ===
=
= Print initialised data structure  %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*$//gn;
            $code =~ s/use\s*(strict|warnings);//gn;
            $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 _trace_test_maps {
        printf( "#%s:\n", ( caller(0) )[3] );
        print <<'EoT';

=== internal data structure %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 _trace_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 ) . $checks{$key}->{txt} ) if ( $key =~ m/$ssl/ );
        }
        _pline("}");
        _pline("%shorttexts {");
        foreach my $key ( sort keys %shorttexts ) {
            _ptext( sprintf( "%14s= ", $key ) . $shorttexts{$key} ) if ( $key =~ m/$ssl/ );
        }
        _pline("}");
        return;
    }

    sub _trace_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";
        return;
    }

    sub _trace_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";
        return;
    }

    sub _trace_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();
        return;
    }

    sub _trace_test_ssleay {
        printf( "#%s:\n", ( caller(0) )[3] );
        print <<'EoT';
 
=== internal data of from Net::SSLeay ===
=
= Print information about Net::SSLeay capabilities.
=
EoT
        print SSLinfo::test_ssleay();
        return;
    }

    sub _trace_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 ===
=
= Use  --v  to get more details.
=
EoT
        if ( 0 < $cfg{'trace'} ) {
            foreach my $k ( keys %cfg ) {
                printf( "%6s\t%s\n", Devel::Size::total_size( \$cfg{$k} ), "%cfg{$k}" );
            }
            foreach my $k ( keys %::checks ) {
                printf( "%6s\t%s\n", Devel::Size::total_size( \$checks{$k} ), "%checks{$k}" );
            }
            foreach my $k ( keys %dbx ) {
                printf( "%6s\t%s\n", Devel::Size::total_size( \$dbx{$k} ), "%dbx{$k}" );
            }
        }
        my $bytes = 0;
        my $line  = "=------+----------------";
        print "= Bytes variable\n$line";
        foreach my $v ( sort keys %main:: ) {
            next if ( "*{$main::{$v}}" !~ m/\*main::/ );
            next if ( $main::{$v}      =~ m/::$/ );
            next if ( not grep { /^(cfg|check|cipher|cmd|data|dbx|info|osaft|short|text)/ } $v );
            next if ( grep { /^check(cipher|http)/ } $v );

            my $size = Devel::Size::total_size( \$main::{$v} );
            $bytes += $size;
            printf( "%7s\t%s\n", $size, $v );
        }
        print "$line";
        printf( "%7s\t(%2.2f MB) total\n", $bytes, $bytes / 1024 / 1024 );
        return;
    }

    sub __trace_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 _trace_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
        __trace_dump_var( '$', 'cipher_results' );
        __trace_dump_var( '%', 'ciphers' );
        __trace_dump_var( '%', 'ciphers_desc' );
        __trace_dump_var( '%', 'prot' );
        __trace_dump_var( '%', 'cfg' );
        __trace_dump_var( '%', 'data' );
        __trace_dump_var( '%', 'info' );
        __trace_dump_var( '%', 'checks' );
        return;
    }

    sub trace_ciphers_list {
        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 );
            _p_k_v( "cmd{extciphers}", $::cmd{'extciphers'} . " (1=use cipher from openssl)" );
            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 trace_targets {
        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 trace_init {

        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("");
        _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 {");
        if ( 2 > $cfg{'trace'} ) {
            _p_k_v( "path",      ___ARR( @{ $::cmd{'path'} } ) );
            _p_k_v( "libs",      ___ARR( @{ $::cmd{'libs'} } ) );
            _p_k_v( "envlibvar", $::cmd{'envlibvar'} );
            _p_k_v( "timeout",   $::cmd{'timeout'} );
            _p_k_v( "openssl",   $::cmd{'openssl'} );
        }
        else {
            foreach my $key ( sort keys %::cmd ) { _ptype( \%::cmd, $key ); }
        }
        _p_k_v( "extopenssl", $::cmd{'extopenssl'} . " (1= use openssl to check ciphers)" );
        _p_k_v( "extciphers", $::cmd{'extciphers'} . " (1= use cipher from openssl)" );
        _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= {");
                    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/ ) {
                        trace_targets( @{ $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 {
                            _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)" );
        trace_targets( @{ $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 trace_exit {
        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 trace_args {
        return if ( 0 >= $cfg{'out'}->{'traceARG'} );
        _pline("ARGV {");
        trace_arg("# summary of all arguments and options from command-line");
        trace_arg( "       called program ARG0= " . $cfg{'ARG0'} );
        trace_arg( "     passed arguments ARGV= " . ___ARR( @{ $cfg{'ARGV'} } ) );
        trace_arg( "                   RC-FILE= " . $cfg{'RC-FILE'} );
        trace_arg("      from RC-FILE RC-ARGV= ($#{$cfg{'RC-ARGV'}} more args ...)");
        if ( 2 > $cfg{'trace'} ) {
            trace_arg("      !!Hint:  use --trace=2 to get the list of all RC-ARGV");
            trace_arg("      !!Hint:  use --trace=3 to see the processed RC-ARGV");
        }
        trace_arg( "      from RC-FILE RC-ARGV= " . ___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 .= "]";
        trace_arg( "         collected targets= " . $txt );
        if ( 2 < $cfg{'trace'} ) {
            trace_arg(" #--v { processed files, arguments and options");
            trace_arg( "    read files and modules= " . ___ARR( @{ $dbx{file} } ) );
            trace_arg( "processed  exec  arguments= " . ___ARR( @{ $dbx{exe} } ) );
            trace_arg( "processed normal arguments= " . ___ARR( @{ $dbx{argv} } ) );
            trace_arg( "processed config arguments= " . ___ARR( map { "`" . $_ . "'" } @{ $dbx{cfg} } ) );
            trace_arg(" #--v }");
        }
        _pline("ARGV }");
        return;
    }

    sub trace_rcfile {
        return if ( 0 >= $cfg{'trace'} );
        _pline("RC-FILE {");
        _pline("RC-FILE }");
        return;
    }

    sub trace_test {
        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;
            trace_ciphers_list(1);
            return;
        }
        Ciphers::show($arg)   if ( $arg =~ /^--test.?cipher/ );
        _trace_test_help()    if ( '--test' eq $arg );
        _trace_test_help()    if ( '--tests' eq $arg );
        _trace_test_sclient() if ( '--testsclient' eq $arg );
        _trace_test_ssleay()  if ( '--testssleay' eq $arg );
        _trace_test_sslmap()  if ( '--testsslmap' eq $arg );
        _trace_test_methods() if ( '--testmethods' eq $arg );
        _trace_test_memory()  if ( '--testmemory' eq $arg );
        $arg =~ s/^[+-]-?tests?[._-]?//;
        OCfg::test_cipher_regex() if ( 'regex' eq $arg );
        _trace_test_avail()       if ( $arg =~ m/^avail(?:able)?$/ );
        _trace_test_init()        if ( 'init' eq $arg );
        _trace_test_maps()        if ( 'maps' eq $arg );
        _trace_test_prot()        if ( 'prot' eq $arg );
        _trace_test_vars()        if ( 'vars' eq $arg );
        return;
    }

    sub _trace_main {
        my $arg = shift || "--help";
        $cfg{'time0'} = $OText::STR{MAKEVAL} if ( defined $ENV{'OSAFT_MAKE'} );
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );
        OText::print_pod( $0, __FILE__, $SID_trace ) if ( $arg =~ m/--?h(elp)?$/x );
        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/--tests?$/ )         { _trace_test_help(); exit 0; }

        if ( $arg =~ m/--(yeast|test)[_.-]?(.*)/ ) {
            $arg = "--test-$2";
            printf("#$0: direct testing not yet possible, please try:\n   o-saft.pl $arg\n");
        }
        exit 0;
    }

    sub trace_done { }


}

{

    package OUsr;

    my $SID_ousr = "@(#) OUsr.pm 3.13 24/02/19 15:32:40";
    our $VERSION = "24.01.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
      ousr_done
    );

    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 _ousr_main {
        my $arg = shift || "--help";
        binmode( STDOUT, ":unix:utf8" );
        binmode( STDERR, ":unix:utf8" );
        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 ousr_done { }


}

package main;

foreach my $file (
    qw(
    lib/OCfg.pm
    lib/Ciphers.pm
    lib/error_handler.pm
    lib/SSLhello.pm
    lib/SSLinfo.pm
    lib/OData.pm
    lib/ODoc.pm
    lib/OMan.pm
    lib/OText.pm
    lib/OTrace.pm
    lib/OUsr.pm
    )
  )
{
    push( @{ $OCfg::dbx{'files'} }, $file );
}

printf( "#$cfg{'me'} %s\n", join( " ", @ARGV ) ) if _is_argv('(?:--trace[_.-]?(?:CLI$)?)');

sub _dprint { my @txt = @_; printf( STDERR "%s%s\n", $OText::STR{DBX}, join( " ", @txt ) ); return; }
sub _dbx { my @txt = @_; _dprint(@txt); return; }
sub _tprint { my @txt = @_; printf( "#%s: %s\n", $cfg{'me'}, join( " ", @txt ) ); return; }

sub _hint {
    my @txt = @_;
    return if _is_argv('(?:--no.?hint)');
    printf( $OText::STR{HINT} . "%s\n", join( " ", @txt ) );
    return;
}

sub _warn {
    my @txt = @_;
    my $_no = "@txt";
    $_no =~ s/^\s*([0-9(]{3}):?.*/$1/;
    return if _is_argv('(?:--no.?warn(?:ings?)$)');

    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 ) );
    _trace_exit("WARN - exit on first warning");
    return;
}

sub _warn_nosni {
    my $err = shift;
    my $ssl = shift;
    my $sni = shift;
    return if ( $sni < 1 );
    return if ( $ssl !~ m/^SSLv[23]/ );
    _warn("$err $ssl does not support SNI; cipher checks are done without SNI");
    return;
}

sub _vprint {
    my @txt = @_;
    return if ( 0 >= _is_ARGV('(?:--v$)') );
    printf( "%s%s\n", $OText::STR{'INFO'} || '**INFO: ', join( " ", @txt ) );
    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 (OSAFT_MAKE exists)");
    }
    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/ } @{ $OCfg::dbx{'files'} } );

    eval { require $fil; } or _warn("101: 'require $fil' failed");
    $err = $@;
    chomp $err;
    if ( "" eq $err ) {
        $fil = $INC{$fil};
        $txt = "$txt done";
    }
    else {
        $txt = "$txt failed";
    }
    push( @{ $OCfg::dbx{'files'} }, $fil );
    _vprint_read( $fil, $txt );
    return $err;
}

sub _is_cfg_intern($);

my $arg  = "";
my @argv = ();

our %ciphers    = %Ciphers::ciphers;
our %cfg        = %OCfg::cfg;
our %checks     = %OData::checks;
our %data       = %OData::data;
our %data0      = %OData::data0;
our %info       = %OData::info;
our %shorttexts = %OData::shorttexts;
our %check_cert = %OData::check_cert;
our %check_conn = %OData::check_conn;
our %check_dest = %OData::check_dest;
our %check_http = %OData::check_http;
our %check_size = %OData::check_size;

$cfg{'time0'} = $time0;
OCfg::set_user_agent("$cfg{'me'}/3.15");
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,
);

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 ( 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)') ) {

    if ( open( my $rc, '<:encoding(UTF-8)', "$cfg{'RC-FILE'}" ) ) {
        push( @{ $OCfg::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);
        _warn("052: option with trailing spaces '$_'") foreach ( grep { m/\s+$/ } @rc_argv );
        push( @argv, @rc_argv );

        if ( _is_trace() ) {
            my @cfgs;
            _tprint("$cfg{'RC-FILE'}");
            _tprint("!!Hint: use  --trace  to see complete settings");
            _tprint("#------------------------------------------------- RC-FILE {");
            foreach my $val (@rc_argv) {
                $val =~ s/(--cfg[^=]*=[^=]*).*/$1/ if ( 0 >= _is_argv('(?:--trace)') );
                _tprint("     $val");
                if ( $val =~ m/--cfg[^=]*=[^=]*/ ) {
                    $val =~ s/--cfg[^=]*=([^=]*).*/+$1/;
                    push( @cfgs, $val );
                }
            }
            _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");

push( @argv, @ARGV );
push( @ARGV, "--no-header" ) if ( ( grep { /--no-?header/ } @argv ) );

my $err = "";
my @dbx = grep { /--(?:trace|v$|exitcode.?v$|tests?|yeast)/ } @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 file" );
    if ( $err ne "" ) {
        die $OText::STR{ERROR}, "012: $err" unless ( -e $arg );
    }
}
else {
    sub trace              { }
    sub trace_             { }
    sub trace1             { }
    sub trace2             { }
    sub trace_arg          { }
    sub trace_args         { }
    sub trace_test         { }
    sub trace_init         { }
    sub trace_exit         { }
    sub trace_ciphers_list { }
}
if ( exists $INC{'lib/OTrace.pm'} ) {
    no warnings 'redefine';
    *trace              = \&OTrace::trace;
    *trace_             = \&OTrace::trace_;
    *trace1             = \&OTrace::trace1;
    *trace2             = \&OTrace::trace2;
    *trace_arg          = \&OTrace::trace_arg;
    *trace_args         = \&OTrace::trace_args;
    *trace_init         = \&OTrace::trace_init;
    *trace_exit         = \&OTrace::trace_exit;
    *trace_test         = \&OTrace::trace_test;
    *trace_ciphers_list = \&OTrace::trace_ciphers_list;
    $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 file" );
    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 $host    = "";
my $port    = "";
my $legacy  = "";
my $verbose = 0;

my $test    = "";
my $info    = 0;
my $check   = 0;
my $quick   = 0;
my $cmdsni  = 0;
my $sniname = undef;

$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";
}
foreach my $ssl ( keys %OCfg::prot ) {
    my $key = lc($ssl);
    $shorttexts{$key} = "Default $OCfg::prot{$ssl}->{txt} cipher";
}

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",
);

our %cmd = (
    'timeout'    => "timeout",
    'openssl'    => "openssl",
    'openssl3'   => "openssl",
    'libs'       => [],
    'path'       => [],
    'extopenssl' => 1,
    'extsclient' => 1,
    'extciphers' => 0,
    'envlibvar'  => "LD_LIBRARY_PATH",
    'envlibvar3' => "LD_LIBRARY_PATH",
    'call'       => [],

    'version' => "",
);

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");

our @ciphers_keys;

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_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",
    },

);

$cmd{'extopenssl'} = 0 if ( $^O =~ m/MSWin32/ );
$cmd{'extsclient'} = 0 if ( $^O =~ m/MSWin32/ );

$cmd{'openssl'} = $cfg{'openssl_env'} if ( defined $cfg{'openssl_env'} );
if ( defined $ENV{'LIBPATH'} ) {
    _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($$);

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( @{ $OCfg::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;
    }
    _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'}/ ) {
        _warn("071: configuration key unknown '$typ'; setting ignored");
        goto FIN;
    }
    if ( ( $arg =~ m|^[a-zA-Z0-9,._+#()\/-]+| ) and ( -f "$arg" ) ) {

        if ( $cgi == 1 ) {

            _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{'cmd-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' );
                _warn("073: configuration: please use '+cipher-selected' instead of '+$key'; setting ignored");
                next;
            }
            _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 );
                    _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)$/ ) {
            _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 );
    _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 { _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 module'" );
        if ( "" ne $_err ) {
            warn $OText::STR{ERROR}, "007: $_err";
            _warn("111: option '--mx disabled");
            $cfg{'use'}->{'mx'} = 0;
        }
    }
    if ( 0 < $cfg{'need_timelocal'} ) {
        $_err = _load_file( "Time/Local.pm", "Time module" );
        if ( "" ne $_err ) {
            warn $OText::STR{ERROR}, "008: $_err";
            _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", "O-Saft module" );
    if ( "" ne $_err ) {
        die $OText::STR{ERROR}, "010: $_err" 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", "O-Saft module" );
    if ( "" ne $_err ) {
        die $OText::STR{ERROR}, "011: $_err" 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 version openssl 0x%x", $version_openssl );
    my $txs = "ancient version Net::SSLeay $version_ssleay";
    my $txt = "improper Net::SSLeay version;";

    if ( $cfg{'ssleay'}->{'openssl'} == 0 ) {
        warn $OText::STR{WARN}, "122: ancient Net::SSLeay $version_ssleay 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 ( $cmd{'extciphers'} == 0 ) ) {
            $cfg{'use'}->{'sni'} = 0;
            my $txt_buggysni = "does not support SNI or is known to be buggy; SNI disabled;";
            if ( $version_iosocket < 1.90 ) {
                warn $OText::STR{WARN}, "124: ancient version IO::Socket::SSL $version_iosocket < 1.90; $txt_buggysni";
            }
            if ( $version_openssl < 0x01000000 ) {
                warn $OText::STR{WARN}, "125: $txo < 1.0.0; $txt_buggysni";
            }
            _hint("use '--force-openssl' to disable this 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_ssleay < 1.56 ) {
                warn $OText::STR{WARN}, "127: $txs < 1.56" if ( $cfg{'verbose'} > 1 );
            }
            if ( $version_openssl < 0x10002000 ) {
                warn $OText::STR{WARN}, "128: $txo < 1.0.2" if ( $cfg{'verbose'} > 1 );
            }
            _hint("use '--no-alpn' to disable this 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_ssleay < 1.46 ) {
                warn $OText::STR{WARN}, "130: $txs < 1.46" if ( $cfg{'verbose'} > 1 );
            }
            if ( $version_openssl < 0x10001000 ) {
                warn $OText::STR{WARN}, "132: $txo < 1.0.1" if ( $cfg{'verbose'} > 1 );
            }
            _hint("use '--no-npn' to disable this 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";
        }
    }
    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 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|ssleay') ) {
        @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'}/ ) {
            _warn("141: SSL version '$ssl': not supported; not checked");
            next;
        }
        if ( _is_cfg_use('ssl_lazy') or _is_cfg_ciphermode('openssl|ssleay') ) {
            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 {
            _warn("143: SSL version '$ssl': not supported by Net::SSLeay; not checked");
            _hint("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 ( $cmd{'extciphers'} > 0 ) {
                _warn("144: 'openssl s_client' does not support '$opt'; $txt") if ( $txt ne "" );
            }
        }
        else {
            _warn("145: 'openssl s_client' does not support '$opt'; $txt") if ( $txt ne "" );
        }
        if ( $opt eq '-tlsextdebug' ) {
            _warn(
"146: 'openssl -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 );
    }
    trace("_enable_sclient() }");
    return;
}

sub _reset_openssl {
    $cmd{'openssl'}    = "";
    $cmd{'extopenssl'} = 0;
    $cmd{'extsclient'} = 0;
    $cmd{'extciphers'} = 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() ) {
        _warn("147: '$cmd{'openssl'}' not available; all openssl functionality disabled");
        _hint("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 ( $val eq '<<openssl>>' );
        $cfg{'openssl'}->{$opt}[0] = $val;
        next if ( $cfg{'openssl'}->{$opt}[1] eq "<<NOT YET USED>>" );
        _enable_sclient($opt);
        my $ssl;
        if ( grep { $ssl = $_ if $opt eq ( $OCfg::prot{$_}{'opt'} || "" ); } keys %OCfg::prot ) {
            $cfg{$ssl} = $val if 0 < $cfg{$ssl};
        }
    }
    $cmd{'version'} = OCfg::get_openssl_version( $cmd{'openssl'} );
    if ( $cmd{'version'} lt "1.0.2" ) {
        _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|ssleay') ) {
            _hint( $cfg{'hints'}->{'openssl3'} );
            _hint( $cfg{'hints'}->{'openssl3c'} );
        }
    }
    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 = "";
        _warn("149: no executable for '$cmd{'openssl'}' found; all openssl functionality disabled");
        _hint("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 {
            _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;
    _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" ) {
                _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();

    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]";
        _warn("060: no PEM file for CA found; using '--ca-file=$cfg{'ca_file'}'");
        _warn("060: if default file does not exist, some certificate checks may fail");
        _hint("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 = "";
    $checks{$_}->{val} = $notxt foreach ( keys %checks );
    $checks{'heartbeat'}->{val} = $text{'undef'};
    foreach my $key (qw(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/$cfg{'regex'}->{'cmd-sizes'}/ );
        $checks{$key}->{val} = 0 if ( $key =~ m/$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 ( @{ $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} = "";
    }
    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 %check_cert ) {
            $checks{$key}->{val} = $text{'na_cert'} if ( _is_hashkey( $key, \%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();
    _init_checks_val();
    $cfg{'hints'}->{$_} = $text{'hints'}->{$_} foreach ( keys %{ $text{'hints'} } );
    _tprint("_init_all() }") if _is_trace();
    return;
}
_init_all();

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 _prot_cipher { my @txt = @_; return " " . join( ":", @txt ); }

sub _prot_cipher_or_empty {
    my $p1 = shift;
    my $p2 = shift;
    return "" if ( ( "" eq $p1 ) or ( "" eq $p2 ) );
    return _prot_cipher( $p1, $p2 );
}

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 __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_ssl_bleed {
    my ( $host, $port ) = @_;
    my $heartbeats = 1;
    trace("_is_ssl_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 {
            _warn("321: _is_ssl_bleed: failed to connect: '$!'");
            trace("_is_ssl_bleed: fatal exit in IO::Socket::INET->new");
            return "failed to connect";
        };
    }
    else {
        trace("_is_ssl_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 ($@);
            _warn("322: _is_ssl_bleed (with openTcpSSLconnection): $@\n");
            trace("_is_ssl_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 {
            _warn("323: heartbleed: no reply: '$!'");
            _hint("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_ssl_bleed()\t= $ret }");
    return $ret;
}

sub _is_ssl_beast {
    my ( $ssl, $cipher ) = @_;
    return ""      if ( $ssl    !~ /(?:SSL|TLSv1$)/ );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'BEAST'}/ );
    return "";
}

sub _is_ssl_breach {
    return "";
}

sub _is_ssl_ccs {
    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 {
        _warn("331: _is_ssl_ccs: 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 {
            _warn("332: _is_ssl_ccs: 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_ssl_crime {
    my ( $val, $protocols ) = @_;
    my $ret = ( $val =~ /$cfg{'regex'}->{'nocompression'}/ ) ? "" : $val . " ";
    $ret .= ( $protocols =~ /$cfg{'regex'}->{'isSPDY3'}/ ) ? "SPDY/3 " : "";
    return $ret;
}

sub _is_ssl_fips {
    my ( $ssl, $cipher ) = @_;
    return $cipher if ( $ssl ne "TLSv1" );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'notFIPS-140'}/ );
    return $cipher if ( $cipher !~ /$cfg{'regex'}->{'FIPS-140'}/ );
    return "";
}

sub _is_ssl_freak {
    my ( $ssl, $cipher ) = @_;
    return ""      if ( $ssl    !~ /(?:SSLv3)/ );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'FREAK'}/ );
    return "";
}

sub _is_ssl_logjam {
    my ( $ssl, $cipher ) = @_;
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'Logjam'}/ );
    return "";
}
sub _is_ssl_lucky { my $val = shift; return ( $val =~ /$cfg{'regex'}->{'Lucky13'}/ ) ? $val : ""; }

sub _is_ssl_nsab {
}

sub _is_ssl_pci {
    my ( $ssl, $cipher ) = @_;
    return $cipher if ( $ssl eq "SSLv2" );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'notPCI'}/ );
    return "";
}
sub _is_ssl_pfs { my ( $ssl, $c ) = @_; return ( "$ssl-$c" =~ /$cfg{'regex'}->{'PFS'}/ ) ? $c : ""; }
sub _is_ssl_rc4 { my $val = shift; return ( $val =~ /$cfg{'regex'}->{'RC4'}/ ) ? $val . " " : ""; }

sub _is_ssl_robot {
    my ( $ssl, $cipher ) = @_;
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'ROBOT'}/ );
    return "";
}

sub _is_ssl_sloth {
    my ( $ssl, $cipher ) = @_;
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'SLOTH'}/ );
    return "";
}

sub _is_ssl_sweet {
    my ( $ssl, $cipher ) = @_;
    return ""      if ( $cipher =~ /$cfg{'regex'}->{'notSweet32'}/ );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'Sweet32'}/ );
    return "";
}
sub _is_ssl_time { return 0; }

sub _is_tls12only {
    my ( $host, $port ) = @_;
    my @ret;
    foreach my $ssl (qw(SSLv2 SSLv3 TLSv1 TLSv11)) {
        if ( $OCfg::prot{$ssl}->{'cnt'} > 0 ) {
            push( @ret, $ssl );
        }
        if ( $cfg{$ssl} == 0 ) {
            push( @ret, _get_text( 'disabled', "--no-$ssl" ) );
        }
    }
    return join( " ", @ret );
}

sub _is_tr02102 {
    my ( $ssl, $cipher ) = @_;
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'EXPORT'}/ );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'notTR-02102'}/ );
    return $cipher if ( $cipher !~ /$cfg{'regex'}->{'TR-02102'}/ );
    return "";
}

sub _is_tr02102_strict {
    my ( $ssl, $cipher ) = @_;
    my $val = _is_tr02102( $ssl, $cipher );
    if ( $val eq "" ) {
        return $cipher if ( $cipher !~ /$cfg{'regex'}->{'AES-GCM'}/ );
        return $cipher if ( $cipher =~ /$cfg{'regex'}->{'notTR-02102'}/ );
    }
    return $val;
}

sub _is_tr02102_lazy {
    my ( $ssl, $cipher ) = @_;
    my $val = _is_tr02102( $ssl, $cipher );
    return $val;
}

sub _is_tr03116_strict {
    my ( $ssl, $cipher ) = @_;
    return $cipher if ( $ssl ne "TLSv12" );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'EXPORT'}/ );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'notTR-03116'}/ );
    return $cipher if ( $cipher !~ /$cfg{'regex'}->{'TR-03116+'}/ );
    return "";
}

sub _is_tr03116_lazy {
    my ( $ssl, $cipher ) = @_;
    return $cipher if ( $ssl ne "TLSv12" );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'EXPORT'}/ );
    return $cipher if ( $cipher !~ /$cfg{'regex'}->{'TR-03116-'}/ );
    return "";
}

sub _is_rfc7525 {
    my ( $ssl, $cipher ) = @_;
    my $bit = Ciphers::get_bits( Ciphers::get_key($cipher) );
    return $cipher if ( $cipher !~ /$cfg{'regex'}->{'RFC7525'}/ );
    return $cipher if ( $cipher =~ /NULL/ );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'EXPORT'}/ );
    return $cipher if ( $cipher =~ /$cfg{'regex'}->{'RC4orARC4'}/ );
    return ""      if ( $bit    =~ m/^\s*$/ );
    return $cipher if ( $bit < 128 );
    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 _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'} ) {
        _warn("301: $txt after $cfg{'sslerror'}->{'total'} total errors");
        _hint("use '--no-ssl-error' or '--ssl-error-max=' to continue connecting");
        return 1;
    }
    if ( $cfg{'done'}->{'ssl_failed'} > $cfg{'sslerror'}->{'max'} ) {
        _warn("302: $txt after $cfg{'sslerror'}->{'max'} max errors");
        _hint("use '--no-ssl-error' or '--ssl-error-max=' to continue connecting");
        return 1;
    }
    return 0;
}

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 ) ) {
        _warn("303: SSL version '$ssl': not supported by Net::SSLeay");
        return "";
    }
    if ( ( $ssl eq "SSLv3" ) && ( not defined &Net::SSLeay::CTX_v3_new ) ) {
        _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 ($@);
                    _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 ) = @_;
    my $msg = $cfg{'openssl_msg'};
    my $sni = ( not _is_cfg_use('sni') ) ? "" : "-servername $host";
    $ciphers = ( $ciphers eq "" ) ? "" : "-cipher $ciphers";
    my $curves = "-curves " . join( ":", $cfg{'ciphercurves'} );
    trace1("_useopenssl($ssl, $host, $port, $ciphers)");
    $ssl = ( $cfg{'openssl_option_map'}->{$ssl} || '' );
    my $data = SSLinfo::do_openssl( "s_client $ssl $sni $msg $ciphers ", $host, $port, '' );
    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;
    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*$# ) {
        _warn("311: SSL version '$ssl': empty result from openssl");
    }
    else {
        _warn("312: SSL version '$ssl': unknown result from openssl or '$cipher'");
        _warn("312: result from openssl: '$data'") if _is_trace();
    }
    trace2("_useopenssl: #{ $data }");
    if ( $cfg{'verbose'} < 1 ) {
        _hint("use '--v' or '--trace'");
    }
    else {
        trace1("_useopenssl: SSLinfo::do_openssl() #{\n$data\n#}");
    }

    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 {
        _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 );
        $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 );
            $data0{$key}->{val} = $data{$key}->{val}( $host, $port );
        }
    }
    else {
        _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() ) ) {
        _warn("203: connection without SNI succeded with errors; errors ignored");
        if ( _is_cfg_verbose() or ( 1 < $cfg{'trace'} ) ) {
            _warn("206: $_") foreach SSLinfo::errors();
        }
        else {
            _hint("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('intern|dump') ) {
            foreach my $name ( split( ":", $pattern ) ) {
                push( @ciphers, Ciphers::find_names($name) );
            }
        }
        else {

            $pattern = $cfg{'cipherpattern'} if $pattern =~ m/^ *$/;
            if ( $cmd{'extciphers'} == 1 ) {
                trace(" get list openssl  = $pattern");
                push( @ciphers, SSLinfo::cipher_openssl($pattern) );
            }
            else {
                trace(" get list sslleay  = $pattern");
                push( @ciphers, SSLinfo::cipher_list($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";
            }
        }
    }
    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 ) {
            _warn("063: given pattern '$pattern' did not return cipher list");
        }
    }
    @ciphers = sort grep { !/^\s*$/ } @ciphers;
    if ( 'names' eq $mode ) {
        for my $i ( 0 .. $#ciphers ) {
            my $c = $ciphers[$i];
            $ciphers[$i] = Ciphers::get_name($c) || "" if _is_cipher_key($c);
        }
    }
    if ( 'keys' eq $mode ) {
        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 ( 0 == $cmd{'extciphers'} ) {
        ( $version, $cipher ) = _usesocket( $ssl, $host, $port, $cipher_list );
    }
    else {
        ( $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 $OCfg::prot{$ssl}->{opt};
        my $anf = time();
        $OCfg::prot{$ssl}->{'cipher_strong'} = _get_cipher_default( $ssl, $host, $port, 'strong' );
        $OCfg::prot{$ssl}->{'cipher_weak'}   = _get_cipher_default( $ssl, $host, $port, 'weak' );
        $OCfg::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 = $OCfg::prot{$ssl}->{'cipher_strong'};
        $OCfg::prot{$ssl}->{'cipher_pfs'} = $cipher if ( "" ne _is_ssl_pfs( $ssl, $cipher ) );
    }
    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 ( 0 == $cmd{'extciphers'} ) {
            if ( 0 >= $cfg{'cipher_md5'} ) {
                _vprint("  check cipher (MD5): $ssl:$c\n") if ( 1 < $cfg{'verbose'} );
                next                                       if ( ( $ssl ne "SSLv2" ) && ( $c =~ m/MD5/ ) );
            }
            ( $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();
                _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();
            _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 $__openssl = ( $cmd{'extciphers'} == 0 ) ? 'socket' : 'openssl';
        my $usesni    = $cfg{'use'}->{'sni'};
        _vprint("  test $cnt ciphers for $ssl ... ($__openssl) ");
        trace("  test $cnt ciphers for $ssl ... ($__openssl) ");
        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*$/ ) {
            _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};
            $OCfg::prot{$ssl}->{'cipher_strong'} = $cipher;
            $OCfg::prot{$ssl}->{'default'}       = $cipher;
            $OCfg::prot{$ssl}->{'cipher_pfs'}    = $cipher if ( "" ne _is_ssl_pfs( $ssl, $cipher ) );
            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;
        got 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 = $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:
    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 ) {
            _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' ) {
                _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 ) {
        $info{'alpns'} = join( ",", check_nextproto( $host, $port, 'ALPN', 'single' ) );
        $info{'alpn'}  = join( ",", check_nextproto( $host, $port, 'ALPN', 'all' ) );
    }
    if ( $cfg{'ssleay'}->{'get_npn'} > 0 ) {
        $info{'npns'} = join( ",", check_nextproto( $host, $port, 'NPN', 'single' ) );
        $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 = $OCfg::prot{$ssl}->{'cipher_strong'};
        my $weak   = $OCfg::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);
    trace("checkcipher($host, $port) {");
    $checks{'cipher_null'}->{val} .= _prot_cipher( $ssl, $c ) if ( $c =~ /NULL/ );
    $checks{'cipher_adh'}->{val}  .= _prot_cipher( $ssl, $c ) if ( $c =~ /$cfg{'regex'}->{'ADHorDHA'}/ );
    $checks{'cipher_exp'}->{val}  .= _prot_cipher( $ssl, $c ) if ( $c =~ /$cfg{'regex'}->{'EXPORT'}/ );
    $checks{'cipher_cbc'}->{val}  .= _prot_cipher( $ssl, $c ) if ( $c =~ /CBC/ );
    $checks{'cipher_des'}->{val}  .= _prot_cipher( $ssl, $c ) if ( $c =~ /DES/ );
    $checks{'cipher_rc4'}->{val}  .= _prot_cipher( $ssl, $c ) if ( $c =~ /$cfg{'regex'}->{'RC4orARC4'}/ );
    $checks{'cipher_edh'}->{val}  .= _prot_cipher( $ssl, $c ) if ( $c =~ /$cfg{'regex'}->{'DHEorEDH'}/ );
    $checks{'ism'}->{val}       .= _prot_cipher( $ssl, $c ) if ( $c =~ /$cfg{'regex'}->{'notISM'}/ );
    $checks{'pci'}->{val}       .= _prot_cipher_or_empty( $ssl, _is_ssl_pci( $ssl, $c ) );
    $checks{'fips'}->{val}      .= _prot_cipher_or_empty( $ssl, _is_ssl_fips( $ssl, $c ) );
    $checks{'rfc_7525'}->{val}  .= _prot_cipher_or_empty( $ssl, _is_rfc7525( $ssl, $c ) );
    $checks{'tr_02102+'}->{val} .= _prot_cipher_or_empty( $ssl, _is_tr02102_strict( $ssl, $c ) );
    $checks{'tr_02102-'}->{val} .= _prot_cipher_or_empty( $ssl, _is_tr02102_lazy( $ssl, $c ) );
    $checks{'tr_03116+'}->{val} .= _prot_cipher_or_empty( $ssl, _is_tr03116_strict( $ssl, $c ) );
    $checks{'tr_03116-'}->{val} .= _prot_cipher_or_empty( $ssl, _is_tr03116_lazy( $ssl, $c ) );
    $checks{'rc4'}->{val} = $checks{'cipher_rc4'}->{val};
    $checks{'beast'}->{val}   .= _prot_cipher_or_empty( $ssl, _is_ssl_beast( $ssl, $c ) );
    $checks{'breach'}->{val}  .= _prot_cipher_or_empty( $ssl, _is_ssl_breach($c) );
    $checks{'freak'}->{val}   .= _prot_cipher_or_empty( $ssl, _is_ssl_freak( $ssl, $c ) );
    $checks{'lucky13'}->{val} .= _prot_cipher_or_empty( $ssl, _is_ssl_lucky($c) );
    $checks{'robot'}->{val}   .= _prot_cipher_or_empty( $ssl, _is_ssl_robot( $ssl, $c ) );
    $checks{'sloth'}->{val}   .= _prot_cipher_or_empty( $ssl, _is_ssl_sloth( $ssl, $c ) );
    $checks{'sweet32'}->{val} .= _prot_cipher_or_empty( $ssl, _is_ssl_sweet( $ssl, $c ) );
    $OCfg::prot{$ssl}->{'-?-'}++    if ( $risk =~ /-\?-/ );
    $OCfg::prot{$ssl}->{'WEAK'}++   if ( $risk =~ /WEAK/i );
    $OCfg::prot{$ssl}->{'LOW'}++    if ( $risk =~ /LOW/i );
    $OCfg::prot{$ssl}->{'MEDIUM'}++ if ( $risk =~ /MEDIUM/i );
    $OCfg::prot{$ssl}->{'HIGH'}++   if ( $risk =~ /HIGH/i );
    $risk = OCfg::get_cipher_owasp($c);
    $OCfg::prot{$ssl}->{'OWASP_miss'}++ if ( $risk eq 'miss' );
    $OCfg::prot{$ssl}->{'OWASP_NA'}++   if ( $risk eq '-?-' );
    $OCfg::prot{$ssl}->{'OWASP_D'}++    if ( $risk eq 'D' );
    $OCfg::prot{$ssl}->{'OWASP_C'}++    if ( $risk eq 'C' );
    $OCfg::prot{$ssl}->{'OWASP_B'}++    if ( $risk eq 'B' );
    $OCfg::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 = $OCfg::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_ssl_pfs( $ssl, $cipher ) ) ? $cipher : "";
    }
    else {
        _warn( "631: protocol '" . join( ';', @prots ) . "' multiple protocols with selected cipher available" );
        $checks{'cipher_pfs'}->{val} .= "$ssl}:" . $OCfg::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;
    $OCfg::prot{'cipher_selected'} = "";
    foreach my $ssl ( reverse( @{ $cfg{'version'} } ) ) {
        $cnt_all += $OCfg::prot{$ssl}->{'cnt'};
        $cnt_pfs += scalar( @{ $OCfg::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'}/ );
                }
            }
            @{ $OCfg::prot{$ssl}->{'ciphers_pfs'} } = _get_text( 'miss_cipher', "" );
        }
        my $cipher = $OCfg::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/ } $OCfg::prot{'cipher_selected'} ) {
            $OCfg::prot{'cipher_selected'} .= " $ssl:$cipher";
        }
        $OCfg::prot{'cipher_selected'} =~ s/^\s*//;
    }

    my %hasecdsa;
    my %hasrsa;
    foreach my $ssl ( keys %$results ) {
        next if '_admin' eq $ssl;
        next if not $results->{$ssl};
        foreach my $key ( 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*$/ ) ) {
                _warn("420: empty value for $key => '$cipher: [$yesno]'; check ignored");
                next;
            }
            if ( $yesno =~ m/yes/i ) {
                $OCfg::prot{$ssl}->{'cnt'}++;
                checkcipher( $ssl, $key );
                $checks{'logjam'}->{val} .= _prot_cipher_or_empty( $ssl, _is_ssl_logjam( $ssl, $cipher ) );
            }
            $hasrsa{$ssl}   = 1 if ( $cipher =~ /$cfg{'regex'}->{'EC-RSA'}/ );
            $hasecdsa{$ssl} = 1 if ( $cipher =~ /$cfg{'regex'}->{'EC-DSA'}/ );
            push( @{ $OCfg::prot{$ssl}->{'ciphers_pfs'} }, $cipher ) if ( "" ne _is_ssl_pfs( $ssl, $cipher ) );
        }
    }

    my $beastskipped = _is_beast_skipped( $host, $port );
    $checks{'beast'}->{val} .= " " . ${beastskipped} if "" ne $beastskipped;
    $checks{'breach'}->{val} = "<<NOT YET IMPLEMENTED>>";

    foreach my $ssl ( @{ $cfg{'version'} } ) {
        $cnt_all += $OCfg::prot{$ssl}->{'cnt'};
        $cnt_pfs += scalar( @{ $OCfg::prot{$ssl}->{'ciphers_pfs'} } );
        $hasrsa{$ssl}   = 0 if not defined $hasrsa{$ssl};
        $hasecdsa{$ssl} = 0 if not defined $hasecdsa{$ssl};

        if ( $OCfg::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} += $OCfg::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 {
        _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_ssl_bleed( $host, $port );
    if ( $cfg{'ignorenoreply'} > 0 ) {
        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} );
    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                        .= ( $OCfg::prot{'SSLv2'}->{'cnt'} > 0 ) ? _get_text( 'insecure', "protocol SSLv2" ) : "";
    $val                        .= ( $OCfg::prot{'SSLv3'}->{'cnt'} > 0 ) ? _get_text( 'insecure', "protocol SSLv3" ) : "";
    $val                        .= ( $OCfg::prot{'TLSv1'}->{'cnt'} > 0 ) ? _get_text( 'insecure', "protocol TLSv1" ) : "";
    $checks{'tr_02102-'}->{val} .= $val;
    $val                        .= ( $OCfg::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;

    $checks{'tr_02102+'}->{val} .= $checks{'crime'}->{val};
    $checks{'tr_02102-'}->{val} .= $checks{'crime'}->{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;

    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 .= ( $OCfg::prot{'SSLv2'}->{'cnt'} > 0 )  ? _get_text( 'insecure', "protocol SSLv2" )  : "";
    $txt .= ( $OCfg::prot{'SSLv3'}->{'cnt'} > 0 )  ? _get_text( 'insecure', "protocol SSLv3" )  : "";
    $txt .= ( $OCfg::prot{'TLSv1'}->{'cnt'} > 0 )  ? _get_text( 'insecure', "protocol TLSv1" )  : "";
    $txt .= ( $OCfg::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};

    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;

    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 ( $OCfg::prot{'SSLv2'}->{'cnt'} > 0 );
    $val .= " SSLv3"  if ( $OCfg::prot{'SSLv3'}->{'cnt'} > 0 );
    $val .= " TLSv1"  if ( ( $OCfg::prot{'TLSv11'}->{'cnt'} + $OCfg::prot{'TLSv12'}->{'cnt'} ) > 0 );
    $val .= " TLSv11" if ( ( $OCfg::prot{'TLSv11'}->{'cnt'} > 0 ) and ( $OCfg::prot{'TLSv12'}->{'cnt'} > 0 ) );

    $val .= " DTLSv1"  if ( $OCfg::prot{'DTLSv1'}->{'cnt'} > 0 );
    $val .= " DTLSv11" if ( $OCfg::prot{'DTLSv11'}->{'cnt'} > 0 );

    $val .= " DTLSv11" if ( $OCfg::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};

    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) {");

    if ( _is_cfg_ssl('SSLv2') ) {
        my $notxt = ( 0 < $OCfg::prot{'SSLv2'}->{'cnt'} ) ? " " : "";
        $checks{'hassslv2'}->{val} = ( _is_cfg_use('nullssl2') ) ? $notxt : "";
        $checks{'drown'}->{val} = $notxt;
    }
    if ( _is_cfg_ssl('SSLv3') ) {
        my $notxt = ( 0 < $OCfg::prot{'SSLv3'}->{'cnt'} ) ? " " : "";
        $checks{'hassslv3'}->{val} = $notxt;
        $checks{'poodle'}->{val}   = ( 0 < $OCfg::prot{'SSLv3'}->{'cnt'} ) ? "SSLv3" : "";

    }
    if ( _is_cfg_ssl('TLSv1') ) {
        $checks{'hastls10_old'}->{val} = " " if ( $OCfg::prot{'TLSv1'}->{'cnt'} <= 0 );
    }
    if ( _is_cfg_ssl('TLSv11') ) {
        $checks{'hastls11_old'}->{val} = " " if ( $OCfg::prot{'TLSv11'}->{'cnt'} <= 0 );
    }
    if ( 0 >= $OCfg::prot{'TLSv13'}->{'cnt'} ) {
        _hint("TLSv1.3 did not return ciphers, consider using '+hastls10_old' and '+hastls10_old'");
    }
    if ( _is_cfg_ssl('TLSv1') ) {
        my $notxt = ( 0 < $OCfg::prot{'TLSv1'}->{'cnt'} ) ? " " : "";
        $checks{'hastls10'}->{val} = $notxt;
    }
    if ( _is_cfg_ssl('TLSv11') ) {
        my $notxt = ( 0 < $OCfg::prot{'TLSv11'}->{'cnt'} ) ? " " : "";
        $checks{'hastls11'}->{val} = $notxt;
    }
    if ( _is_cfg_ssl('TLSv12') ) {
        $checks{'hastls12'}->{val} = " " if ( $OCfg::prot{'TLSv12'}->{'cnt'} <= 0 );
    }
    if ( _is_cfg_ssl('TLSv13') ) {
        $checks{'hastls13'}->{val} = " " if ( $OCfg::prot{'TLSv13'}->{'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 $data0{$key}->{val} ) {
        $checks{'session_random'}->{val} = $value if ( $value eq $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_ssl_crime( $value, $data{'next_protocols'}->{val}($host) );
    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 =~ /^<</ ) {
        _warn("641: HTTPS response failed, some information and checks are missing");
        _hint("consider using '--proto-alpn=,' also") if ( $https_body =~ /bad client magic byte string/ );
    }

    $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>>" if ( $response =~ m/^\s*-1/ );
    %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 = $OCfg::prot{$ssl}->{'cnt'} - $#{ $OCfg::prot{$ssl}->{'ciphers_pfs'} };
        $cnt_pfs = 0 if ( 0 >= $OCfg::prot{$ssl}->{'cnt'} );
        $exitcode += $cnt_pfs if ( _is_cfg_out('exitcode_pfs') );
        $cnt_ciph = 0;
        $cnt_ciph += $OCfg::prot{$ssl}->{'MEDIUM'} if ( _is_cfg_out('exitcode_medium') );
        $cnt_ciph += $OCfg::prot{$ssl}->{'WEAK'}   if ( _is_cfg_out('exitcode_weak') );
        $cnt_ciph += $OCfg::prot{$ssl}->{'LOW'}    if ( _is_cfg_out('exitcode_low') );
        $exitcode += $cnt_ciph;
        _vprint(
            sprintf(
                "%-12s\t%3s %3s %3s %3s %3s\t%s",
                $ssl, $OCfg::prot{$ssl}->{'HIGH'}, $OCfg::prot{$ssl}->{'MEDIUM'}, $OCfg::prot{$ssl}->{'LOW'},
                $OCfg::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 ( keys %data ) {
        next if ( _is_cfg_intern($key) > 0 );
        _printdump( $data{$key}->{txt}, $data{$key}->{val}($host) );
    }
    print '######################################################################## %check';
    foreach my $key ( 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 ) {
        _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 ( $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{'cmd-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 = "    ";
        }
        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 bits  %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", $OCfg::prot{$ssl}->{'WEAK'} );
        printf( "Intermediate: %s\n", $OCfg::prot{$ssl}->{'MEDIUM'} );
        printf( "Strong:       %s\n", $OCfg::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}, $OCfg::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]", $OCfg::prot{$ssl}->{hex} ) if ( $legacy eq 'key' );
        printf( "%-7s\t%-31s\t%s\n", $key, $OCfg::prot{$ssl}->{'cipher_strong'}, $OCfg::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 ) );
        next if ( $ssl =~ m/^SSLv2/ );
        my $cnt = scalar( @{ $OCfg::prot{$ssl}->{'ciphers_pfs'} } );
        my $key = $ssl . $text{'separator'};
        $key = sprintf( "[0x%x]", $OCfg::prot{$ssl}->{hex} ) if ( $legacy eq 'key' );
        my $cipher_strong = $OCfg::prot{$ssl}->{'cipher_strong'};
        my $cipher_pfs    = $OCfg::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 (    ( @{ $OCfg::prot{$ssl}->{'ciphers_pfs'} } )
            and ( ${ $OCfg::prot{$ssl}->{'ciphers_pfs'} }[0] =~ m/^\s*<</ ) )
        {

            $cipher_strong = ${ $OCfg::prot{$ssl}->{'ciphers_pfs'} }[0];
            $cnt           = 0;
        }
        print_line( '_cipher', $host, $port, $ssl, $ssl, "" );
        if ( 'owasp' eq $legacy ) {
            printf(
                "%-7s\t%3s %3s %3s %3s %3s %3s %-31s %s\n",
                $key,                           $OCfg::prot{$ssl}->{'OWASP_A'}, $OCfg::prot{$ssl}->{'OWASP_B'},
                $OCfg::prot{$ssl}->{'OWASP_C'}, $OCfg::prot{$ssl}->{'OWASP_D'}, $cnt,
                $OCfg::prot{$ssl}->{'cnt'},     $cipher_strong,                 $cipher_pfs
            );
        }
        else {
            printf(
                "%-7s\t%3s %3s %3s %3s %3s %3s %-31s %s\n",
                $key,                       $OCfg::prot{$ssl}->{'HIGH'}, $OCfg::prot{$ssl}->{'MEDIUM'},
                $OCfg::prot{$ssl}->{'LOW'}, $OCfg::prot{$ssl}->{'WEAK'}, $cnt,
                $OCfg::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|ssleay') ) {
        print_line( $legacy, $host, $port, 'cipher_selected', $data{'cipher_selected'}->{txt}, $OCfg::prot{'cipher_selected'} );
    }
    _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') ) {
        _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|ssleay') ) {
            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'} }[4];
        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{'cmd-NL'} } ) ) {
            if ( _is_cfg_do('info') and not _is_cfg_verbose() ) {
                _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'} );
    _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') ) {
        _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 version 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 ) {
        _warn("841: used openssl version '$o' differs from compiled Net::SSLeay '$s'; ignored");
    }
    return;
}

sub printversion {
    trace("printversion() {");
    local $\ = "\n";
    if ( _is_cfg_verbose() ) {
        print "# perl $^V";
        print '# @INC = ' . join( " ", @INC ) . "\n";
    }
    if ( defined $ENV{PWD} ) {
        print("=== started in: $ENV{PWD} ===");
    }

    my $version_openssl = Net::SSLeay::OPENSSL_VERSION_NUMBER() || $OText::STR{UNDEF};
    my $me              = $cfg{'me'};
    print( "=== $0 " . _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 ) {
        _warn("851: ancient version 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 ) {
                    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 "= $me +cipher --ciphermode=openssl or --ciphermode=ssleay =";
    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();
    _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 "    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 =";
    print '    @INC                 ', "@INC";
    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 Time::Local Net::DNS Net::SSLeay OCfg OData Ciphers SSLinfo SSLhello OMan OText OTrace OUsr)) {
        no strict 'refs';

        ( $d = $m ) =~ s#::#/#g;
        $d .= '.pm';
        $v = $m . "::VERSION";
        if ( defined $$v ) {
            $v = $$v;
        }
        else {
            $v = qx(lib/$m.pm +VERSION);
            chomp $v;
            $v = " " if ( $v =~ m/^\s*$/ );
        }
        printf( "    %-22s %-9s%s\n", $m, $v, ( $INC{$d} || $INC{"lib/$d"} || "<<not loaded>>" ) );
    }
    _hint("use '--v' to get list of all modules") if not _is_cfg_verbose();
    if ( _is_cfg_verbose() ) {
        print "\n= Loaded Modules =";
        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*$/ );
            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();

_vprint("read command line arguments");
my $typ = 'HOST';
push( @argv, "" );
while ( $#argv >= 0 ) {
    $arg = shift @argv;
    trace_arg("cli_arg= $arg");
    push( @{ $OCfg::dbx{argv} }, $arg ) if ( ( $arg !~ m/^--cfg[_-]/ ) && ( ( $arg =~ m/^[+-]/ ) || ( $typ ne "HOST" ) ) );
    push( @{ $OCfg::dbx{cfg} },  $arg ) if ( $arg =~ m/^--cfg[_-]/ );

    if ( $typ ne 'HOST' ) {

        trace_arg("argument? $arg, typ= $typ");
        push( @{ $OCfg::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 '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-]+$/ ) {

                    _warn("062: given pattern '$arg' for cipher unknown; setting ignored");
                    $arg = "";
                }
            }
            push( @{ $cfg{'cipher'} }, $arg ) if ( $arg !~ m/^\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 {
                _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 {
                _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 {
                _warn("055: option with unknown format '$arg'; setting ignored") if ( $arg !~ /^\s*$/ );
            }
        }
        if ( $typ eq 'CIPHER_RANGE' ) {
            if ( 1 == ( grep { /^$arg$/i } keys %{ $cfg{'cipherranges'} } ) ) {
                $cfg{'cipherrange'} = $arg;
            }
            else {
                _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 {
                _warn("057: option with unknown cipher mode '$arg'; setting ignored") if ( $arg !~ /^\s*$/ );
            }
        }
        if ( $typ eq 'CIPHER_CURVES' ) {
            $arg = lc($arg);
            $cfg{'ciphercurves'} = [""] if ( $arg =~ /^[,:][,:]$/ );
            if ( $arg =~ /^[,:]$/ ) {
                $cfg{'ciphercurves'} = [];
            }
            else {
                push( @{ $cfg{'ciphercurves'} }, split( /,/, $arg ) );
            }
        }

        if ( $typ eq 'CIPHER_ALPN' ) {
            $arg = lc($arg);
            $cfg{'cipher_alpns'} = [""] if ( $arg =~ /^[,:][,:]$/ );
            if ( $arg =~ /^[,:]$/ ) {
                $cfg{'cipher_alpns'} = [];
            }
            else {
                push( @{ $cfg{'cipher_alpns'} }, split( /,/, $arg ) );
            }
        }
        if ( $typ eq 'CIPHER_NPN' ) {
            $cfg{'cipher_npns'} = [""] if ( $arg =~ /^[,:][,:]$/ );
            if ( $arg =~ /^[,:]$/ ) {
                $cfg{'cipher_npns'} = [];
            }
            else {
                push( @{ $cfg{'cipher_npns'} }, split( /,/, $arg ) );
            }
        }
        if ( $typ eq 'PROTO_ALPN' ) {
            $arg = lc($arg);
            $cfg{'protos_alpn'} = [""] if ( $arg =~ /^[,:][,:]$/ );
            if ( $arg =~ /^[,:]$/ ) {
                $cfg{'protos_alpn'} = [];
            }
            else {
                push( @{ $cfg{'protos_alpn'} }, split( /,/, $arg ) );
            }
        }
        if ( $typ eq 'PROTO_NPN' ) {
            $arg = lc($arg);
            $cfg{'protos_npn'} = [""] if ( $arg =~ /^[,:][,:]$/ );
            if ( $arg =~ /^[,:]$/ ) {
                $cfg{'protos_npn'} = [];
            }
            else {
                push( @{ $cfg{'protos_npn'} }, split( /,/, $arg ) );
            }
        }
        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'}/ ) {
            _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 );
        _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=(.*)/ ) {
        _warn("021: old (pre 13.12.11) syntax '--set-score=*' obsolete, please use '--cfg-score=*'; option ignored");
        next;
    }
    if ( $arg =~ /^--legacy=key/ ) {
        _warn("023: old (pre 19.01.14) syntax '--legacy=key' obsolete, please use '--label=key'; option ignored");
        next;
    }
    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 = "--help=NAME"; }
    if ( $arg =~ /^[+,](abbr|abk|glossar|todo)$/i ) { $arg = "--help=$1"; }

    if ( $arg =~ /^(?:--|\+|,)help=?(.*)?$/ ) {
        if ( defined $1 ) {
            $arg = $1 if ( $1 !~ /^\s*$/ );
        }
        trace_arg("handle --help= ...");
        my $_err = _load_file( 'lib/OMan.pm', "help file" );
        warn $OText::STR{ERROR}, "009: $_err" if ( "" ne $_err );
        OMan::man_printhelp($arg);
        exit 0;
    }

    trace_arg("optmisc? $arg");
    if ( $arg eq '--trace--' )        { _set_cfg_out( 'traceARG', 1 ); next; }
    if ( $arg =~ /^--trace.?CLI$/ )   { 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 =~ /^--cmd=\+?(.*)/ )     { $arg = '+' . $1; }
    if ( $arg =~ /^--rc/ )              { next; }
    if ( $arg eq '+VERSION' )           { _version_exit(); exit 0; }
    if ( $arg eq '--yeast' )            { $arg = '--test-data'; }
    if ( $arg =~ /^--yeast[_.-]?(.*)/ ) { $arg = "--test-$1"; }

    if ( $arg eq '--openssl' ) { $arg = '--extopenssl'; }

    if ( $arg =~ m/(^-[^=]*)=(.*)/ ) {
        $arg = $1;
        unshift( @argv, $2 );
    }

    $arg =~ s/([a-zA-Z0-9])(?:[_.-])/$1/g if ( $arg =~ /^-/ );

    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 =~ /^--showkeys?/i )           { $arg = '--traceKEY'; }
    if ( $arg eq '--version' )               { $arg = '+version'; }
    if ( $arg eq '--forceopenssl' )          { $arg = '--opensslciphers'; }
    if ( $arg eq '--cipheropenssl' )         { $arg = '--opensslciphers'; }
    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 '--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 =~ /^--tests?/ )             { $test = $arg; }
    if ( $arg =~ /^[+,]tests?/ )           { $test = $arg; next; }

    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 '--opensslciphers' )      { $cmd{'extciphers'} = 1; }
    if ( $arg eq '--noopensslciphers' )    { $cmd{'extciphers'} = 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 =~ /^--notcp/i ) { $cfg{$_} = 0 foreach (qw(SSLv2 SSLv3 TLSv1 TLSv11 TLSv12 TLSv13)); }
    if ( $arg =~ /^--tcp/i )   { $cfg{$_} = 1 foreach (qw(SSLv2 SSLv3 TLSv1 TLSv11 TLSv12 TLSv13)); }
    if ( $arg =~ /^--noudp/i ) { $cfg{$_} = 0 foreach (qw(DTLSv09 DTLSv1 DTLSv11 DTLSv12 DTLSv13)); }
    if ( $arg =~ /^--udp/i )   { $cfg{$_} = 1 foreach (qw(DTLSv09 DTLSv1 DTLSv11 DTLSv12 DTLSv13)); }
    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_ALPN'; }
    if ( $arg =~ /^--ciphernpns?/ )   { $typ               = 'CIPHER_NPN'; }
    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?$/ )         { _set_cfg_out( 'hint_info', 1 ); _set_cfg_out( 'hint_check', 1 ); }
    if ( $arg =~ /^--nohints?$/ )       { _set_cfg_out( 'hint_info', 0 ); _set_cfg_out( 'hint_check', 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?cipher/ )    { _set_cfg_out( 'hint_cipher', 1 ); }
    if ( $arg =~ /^--nohints?cipher/ )  { _set_cfg_out( 'hint_cipher', 0 ); }
    if ( $arg =~ /^--showhosts?/i )     { _set_cfg_out( 'hostname',    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 = 'PROTO_ALPN'; }
    if ( $arg =~ /^--protonpns?/ )                          { $typ = 'PROTO_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 =~ /^-/ );

    my $p = qr/[._-]/;

    trace_arg("command? $arg");
    $arg =~ s/^,/+/;

    if ( $arg =~ /^\+targets?$/ ) { $arg = '+host'; }
    if ( $arg =~ /^\+host$p/ )    { $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'; }

    if ( $arg =~ /^\+ciphers?$p?adh$/i )       { $arg = '+cipher_adh'; }
    if ( $arg =~ /^\+ciphers?$p?cbc$/i )       { $arg = '+cipher_cbc'; }
    if ( $arg =~ /^\+ciphers?$p?des$/i )       { $arg = '+cipher_des'; }
    if ( $arg =~ /^\+ciphers?$p?edh$/i )       { $arg = '+cipher_edh'; }
    if ( $arg =~ /^\+ciphers?$p?exp$/i )       { $arg = '+cipher_exp'; }
    if ( $arg =~ /^\+ciphers?$p?export$/i )    { $arg = '+cipher_exp'; }
    if ( $arg =~ /^\+ciphers?$p?null$/i )      { $arg = '+cipher_null'; }
    if ( $arg =~ /^\+ciphers?$p?weak$/i )      { $arg = '+cipher_weak'; }
    if ( $arg =~ /^\+ciphers?$p?order$/i )     { $arg = '+cipher_order'; }
    if ( $arg =~ /^\+ciphers?$p?strong/i )     { $arg = '+cipher_strong'; }
    if ( $arg =~ /^\+ciphers?$p?pfs$/i )       { $arg = '+cipher_pfs'; }
    if ( $arg =~ /^\+ciphers?$p?pfsall$/i )    { $arg = '+cipher_pfsall'; }
    if ( $arg =~ /^\+ciphers?$p?selected/i )   { $arg = '+cipher_selected'; }
    if ( $arg =~ /^\+ciphers$p?openssl$/i )    { $arg = '+ciphers_local'; }
    if ( $arg =~ /^\+ciphers$p?local$/i )      { $arg = '+ciphers_local'; }
    if ( $arg =~ /^\+ciphers?$p?preferr?ed/i ) { $arg = '+cipher_default'; }
    if ( $arg =~ /^\+ciphers?$p?defaults?$/i ) { $arg = '+cipher_default'; }
    if ( $arg =~ /^\+ciphers?$p?dh$/i )        { $arg = '+cipher_dh'; }
    if ( $arg =~ /^\+cipher--?v$/ )            { $arg = '+cipher'; $cfg{'v_cipher'}++; }
    if ( $arg =~ /^\+adh$p?ciphers?/i )        { $arg = '+cipher_adh'; }
    if ( $arg =~ /^\+cbc$p?ciphers?/i )        { $arg = '+cipher_cbc'; }
    if ( $arg =~ /^\+des$p?ciphers?/i )        { $arg = '+cipher_des'; }
    if ( $arg =~ /^\+edh$p?ciphers?/i )        { $arg = '+cipher_edh'; }
    if ( $arg =~ /^\+exp$p?ciphers?/i )        { $arg = '+cipher_exp'; }
    if ( $arg =~ /^\+export$p?ciphers?/i )     { $arg = '+cipher_exp'; }
    if ( $arg =~ /^\+null$p?ciphers?/i )       { $arg = '+cipher_null'; }
    if ( $arg =~ /^\+weak$p?ciphers?/i )       { $arg = '+cipher_weak'; }
    if ( $arg =~ /^\+order$p?ciphers?/i )      { $arg = '+cipher_order'; }
    if ( $arg =~ /^\+strong$p?ciphers?/i )     { $arg = '+cipher_strong'; }
    if ( $arg =~ /^\+selected$p?ciphers?/i )   { $arg = '+cipher_selected'; }
    if ( $arg =~ /^\+session$p?ciphers?/i )    { $arg = '+cipher_selected'; }
    if ( $arg eq '+selected' )                 { $arg = '+cipher_selected'; }
    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.?2015.?0204/i ) { $arg = '+freak'; }
    if ( $arg =~ /^[+]cve.?2016.?0703/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 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 '+sts' )                    { $arg = '+hsts'; }
    if ( $arg eq '+sigkey' )                 { $arg = '+sigdump'; }
    if ( $arg =~ /^\+sigkey$p?algorithm/i )  { $arg = '+signame'; }
    if ( $arg eq '+protocol' )               { $arg = '+session_protocol'; }
    if ( $arg =~ /^\+selected$p?protocol/i ) { $arg = '+session_protocol'; }
    if ( $arg =~ /^\+rfc$p?2818$/i )         { $arg = '+rfc_2818_names'; }
    if ( $arg =~ /^\+rfc$p?2818$p?names/i )  { $arg = '+rfc_2818_names'; }
    if ( $arg =~ /^\+rfc$p?6125$/i )         { $arg = '+rfc_6125_names'; }
    if ( $arg =~ /^\+rfc$p?6125$p?names/i )  { $arg = '+rfc_6125_names'; }
    if ( $arg =~ /^\+rfc$p?6797$/i )         { $arg = '+hsts'; }
    if ( $arg =~ /^\+rfc$p?7525$/i )         { $arg = '+rfc_7525'; }

    if ( $arg =~ /^\+fingerprint$p?(.{2,})$/ )            { $arg = '+fingerprint_' . $1; }
    if ( $arg =~ /^\+fingerprint$p?sha$/i )               { $arg = '+fingerprint_sha1'; }
    if ( $arg =~ /^\+subject$p?altnames?/i )              { $arg = '+altname'; }
    if ( $arg =~ /^\+modulus$p?exponent$p?1$/ )           { $arg = '+modulus_exp_1'; }
    if ( $arg =~ /^\+modulus$p?exponent$p?65537$/ )       { $arg = '+modulus_exp_65537'; }
    if ( $arg =~ /^\+modulus$p?exponent$p?size$/ )        { $arg = '+modulus_exp_oldssl'; }
    if ( $arg =~ /^\+pubkey$p?enc(?:ryption)?$/ )         { $arg = '+pub_encryption'; }
    if ( $arg =~ /^\+public$p?enc(?:ryption)?$/ )         { $arg = '+pub_encryption'; }
    if ( $arg =~ /^\+pubkey$p?enc(?:ryption)?$p?known/ )  { $arg = '+pub_enc_known'; }
    if ( $arg =~ /^\+public$p?enc(?:ryption)?$p?known/ )  { $arg = '+pub_enc_known'; }
    if ( $arg =~ /^\+ocsp$p?public$p?hash$/ )             { $arg = '+ocsp_public_hash'; }
    if ( $arg =~ /^\+ocsp$p?subject$p?hash$/ )            { $arg = '+ocsp_subject_hash'; }
    if ( $arg =~ /^\+sig(key)?$p?enc(?:ryption)?$/ )      { $arg = '+sig_encryption'; }
    if ( $arg =~ /^\+sig(key)?$p?enc(?:ryption)?_known/ ) { $arg = '+sig_enc_known'; }
    if ( $arg =~ /^\+server$p?(?:temp)?$p?key$/ )         { $arg = '+dh_parameter'; }
    if ( $arg =~ /^\+master$p?secret$/ )                  { $arg = '+master_secret'; }
    if ( $arg =~ /^\+extended$p?master$p?secret$/ )       { $arg = '+master_secret'; }
    if ( $arg =~ /^\+reneg/ )                             { $arg = '+renegotiation'; }
    if ( $arg =~ /^\+resum/ )                             { $arg = '+resumption'; }
    if ( $arg =~ /^\+reused?$/i )                         { $arg = '+resumption'; }
    if ( $arg =~ /^\+commonName$/i )                      { $arg = '+cn'; }
    if ( $arg =~ /^\+cert(?:ificate)?$/i )                { $arg = '+pem'; }
    if ( $arg =~ /^\+issuer$p?X509$/i )                   { $arg = '+issuer'; }
    if ( $arg =~ /^\+subject$p?X509$/i )                  { $arg = '+subject'; }
    if ( $arg =~ /^\+sha2sig(?:nature)?$/ )               { $arg = '+sha2signature'; }
    if ( $arg =~ /^\+sni$p?check$/ )                      { $arg = '+check_sni'; }
    if ( $arg =~ /^\+check$p?sni$/ )                      { $arg = '+check_sni'; }
    if ( $arg =~ /^\+ext$p?aia$/i )                       { $arg = '+ext_authority'; }
    if ( $arg =~ /^\+vulnerabilit(y|ies)/ )               { $arg = '+vulns'; }
    if ( $arg =~ /^\+hpkp$/i )                            { $arg = '+https_pins'; }
    if ( $arg =~ /^\+pkp$p?pins$/i )                      { $arg = '+https_pins'; }
    if ( $arg =~ /^\+https?${p}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; }

    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$p?02102/ ) { push( @{ $cfg{'do'} }, qw(tr_02102+ tr_02102-) );  next; }
        if ( $val =~ /tr$p?03116/ ) { push( @{ $cfg{'do'} }, qw(tr_03116+ tr_03116-) );  next; }

        if ( _is_member( $val, \@{ $cfg{'commands_usr'} } ) == 1 ) {
            trace_arg("cmdsusr= $val");
            push( @{ $cfg{'do'} }, @{ $cfg{"cmd-$val"} } );
            next;
        }
        if ( _is_member( $val, \@{ $cfg{'commands_notyet'} } ) > 0 ) {
            _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 {
            _warn("049: command '$val' unknown; command ignored");
            _hint( $cfg{'hints'}->{'cipher'} ) if ( $val =~ m/^cipher(?:all|raw)/ );
        }
        next;
    }

    if ( $arg =~ /(?:ciphers|s_client|version)/ ) {
        _warn("041: host-like argument '$arg'; treated as command '+$arg'");
        _hint("please use '+$arg' instead");
        push( @{ $cfg{'do'} }, $arg );
        next;
    }

    trace_arg("host?    $arg");
    if ( $typ eq 'HOST' ) {
        if ( $arg !~ m/^[a-zA-Z0-9.-]+/ ) {
            _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*$/ ) ) {
            _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 = "'";
    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;
    _hint( $cfg{'hints'}->{'cipher'} );
}

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 ( 0 < $cmd{'extciphers'} ) {
    if ( not _is_cfg_ciphermode('openssl') ) {
        $cfg{'ciphermode'} = 'openssl';
        _warn("061: force setting '--ciphermode=openssl' to use ciphers from OpenSSL");
    }
}

if ( _is_cfg_do('cipher_default') ) {
    if ( not _is_cfg_ciphermode('openssl|ssleay') ) {
        _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)') );

if ( 2 == @{ $cfg{'targets'} } ) {
    my $_host = OCfg::get_target_host(1);
    if ( defined $cfg{'port'} ) {
        _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};
    _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 ) {

    $cmd{'extciphers'}    = 0;
    $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() 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_ssleay');

_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");
_check_openssl() if ( 0 < $do_checks );

_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') ) {

    _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'}   = $shorttexts{$key}; }
    foreach my $key ( keys %checks ) { $checks{$key}->{'txt'} = $shorttexts{$key}; }
}

_init_checks_val();

_trace_info("CONF9   - runtime configuration end");

_vprint("check for no connection commands");
trace(" --test= $test");
$test =~ s/^(?:[+]|--)(test.*)/--$1/;
if ( $test =~ m/testciphersregex/ ) { _vprint("  test regex "); OCfg::test_cipher_regex(); exit 0; }
if ( $test =~ m/testciphers.+/ )    { _vprint("  test cipher"); trace_test($test);         exit 0; }
if ( $test !~ m/^\s*$/ )            { _vprint("  test any   "); trace_test($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");
trace_ciphers_list( ( _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_ssleay') ) {
    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 ) ) {
    _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'} } ) ) {
            _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 ) {
    _warn("066: $fail data and check outputs are disbaled due to use of '--no-out':");
    if ( _is_cfg_verbose() ) {
        _warn( "067:  disabled:  +" . join( " +", @{ $cfg{'ignore-out'} } ) );
        _warn( "068:  given:  +" . join( " +", @{ $cfg{'do'} } ) );
    }
    else {
        _hint("use '--v' for more information");
    }
    _hint("do not use '--ignore-out=*' or '--no-out=*'");
}
else {
    foreach my $cmd ( @{ $cfg{'do'} } ) {
        if ( _is_member( $cmd, \@{ $cfg{'commands_hint'} } ) ) {
            _hint("+$cmd : please see '$cfg{'me'} --help=CHECKS' for more information");
        }
    }
}
_trace_info("TARGETS0 - start");

OUsr::pre_host();

_vprint("check all targets ...");

$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'} ) {
                _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'} ) {
            _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/ ) {
                _warn("202: Can't do DNS reverse lookup: for '$host': $fail; ignored");
                _hint("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;
        _warn("325: HTTP disabled, using '--no-http'");
    }
    next if _trace_next("  CONN9   - end");

    if ( _is_cfg_do('cipher_dh') ) {
        if ( 0 >= $cmd{'extopenssl'} ) {
            _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() ) {
        _warn("209: No SSL versions for '+cipher' available") if ( $#{ $cfg{'version'} } < 0 );
        $cipher_results = {};
        if ( _is_cfg_ciphermode('intern|dump') ) {
            trace(" use SSLhello +cipher$typ ...");
            SSLhello::printParameters() if ( $cfg{'trace'} > 1 );
            $cipher_results = ciphers_scan_intern( $host, $port );
        }
        if ( _is_cfg_ciphermode('openssl|ssleay') ) {
            trace(" use socket ...")  if ( 0 == $cmd{'extciphers'} );
            trace(" use openssl ...") if ( 1 == $cmd{'extciphers'} );
            $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 ( 0 == $cmd{'extciphers'} ) {
            ( $version, $supported ) = _usesocket( '', $host, $port, '' );
        }
        else {
            ( $version, $supported, $dh ) = _useopenssl( '', $host, $port, '' );
        }
        $OCfg::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 ) );
                _warn("205: Can't make a connection to '$host:$port'; target ignored");
                _hint("use '--v' to show more information");
                _hint("use '--socket-reuse' it may help in some cases");
                _hint("use '--ignore-no-conn' to disable this check");
                _hint("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 ) ) {
            _warn("207: Errors occoured when using '$cmd{'openssl'}', some results may be wrong; errors ignored");
            _hint("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 ( _need_checkalpn() > 0 ) {
        checkalpn( $host, $port );
        _trace_time("  checkalpn.");
    }
    checkdates( $host, $port );
    _trace_time("  checkdates.");
    if ( _need_checkhttp() > 0 ) {
        checkhttp( $host, $port );
        _trace_time("  checkhttp.");
    }
    checksni( $host, $port );
    _trace_time("  checksni.");
    checksizes( $host, $port );
    _trace_time("  checksizes.");
    if ( $info == 0 ) {
        checkdv( $host, $port );
        _trace_time("  checkdv.");
    }
    if ( _need_checkprot() > 0 ) {
        checkprot( $host, $port );
        _trace_time("  checkprot.");
    }
    if ( _need_checkdest() > 0 ) {
        checkdest( $host, $port );
        _trace_time("  checkdest.");
    }
    if ( _need_checkbleed() > 0 ) {
        checkbleed( $host, $port );
        _trace_time("  checkbleed.");
    }
    if ( _need_checkssl() > 0 ) {
        _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 ) {
        _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 column must not be prepended by white spaces.  It must begin
#   iwith 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
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	MEDIUM	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	HIGH	SSLv3	RSA	RSA	AES	128	SHA1	5246	AES128-SHA	RSA_WITH_AES_128_CBC_SHA,RSA_WITH_AES_128_SHA	-
0x03000030	HIGH	medium	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	medium	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	HIGH	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	HIGH	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	HIGH	SSLv3	RSA	RSA	AES	256	SHA1	5246	AES256-SHA	RSA_WITH_AES_256_SHA,RSA_WITH_AES_256_CBC_SHA	-
0x03000036	HIGH	medium	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	medium	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	HIGH	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	HIGH	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	HIGH	TLSv12	RSA	RSA	AES	128	SHA256	5246	AES128-SHA256	RSA_WITH_AES_128_SHA256,RSA_WITH_AES_128_CBC_SHA256	L
0x0300003D	HIGH	HIGH	TLSv12	RSA	RSA	AES	256	SHA256	5246	AES256-SHA256	RSA_WITH_AES_256_SHA256,RSA_WITH_AES_256_CBC_SHA256	L
0x0300003E	HIGH	HIGH	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	HIGH	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	HIGH	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	HIGH	TLSv1	RSA	RSA	CAMELLIA	128	SHA1	4132,5932	CAMELLIA128-SHA	RSA_WITH_CAMELLIA_128_CBC_SHA	-
0x03000042	HIGH	HIGH	TLSv1	DH	DSS	CAMELLIA	128	SHA1	4132,5932	DH-DSS-CAMELLIA128-SHA	DH_DSS_WITH_CAMELLIA_128_CBC_SHA	-
0x03000043	HIGH	HIGH	TLSv1	DH	RSA	CAMELLIA	128	SHA1	4132,5932	DH-RSA-CAMELLIA128-SHA	DH_RSA_WITH_CAMELLIA_128_CBC_SHA	-
0x03000044	HIGH	HIGH	TLSv1	DH	DSS	CAMELLIA	128	SHA1	4132,5932	DHE-DSS-CAMELLIA128-SHA	DHE_DSS_WITH_CAMELLIA_128_CBC_SHA	-
0x03000045	HIGH	HIGH	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	EXPORT
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	HIGH	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	HIGH	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	HIGH	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	HIGH	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	HIGH	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	HIGH	TLSv1	RSA	RSA	CAMELLIA	256	SHA1	4132,5932	CAMELLIA256-SHA	RSA_WITH_CAMELLIA_256_CBC_SHA	-
0x03000085	HIGH	HIGH	TLSv1	DSS	DH	CAMELLIA	256	SHA1	4132,5932	DH-DSS-CAMELLIA256-SHA	DH_DSS_WITH_CAMELLIA_256_CBC_SHA	-
0x03000085-c	HIGH	HIGH	TLSv1	DH	DH	CAMELLIA	256	SHA1	-?-	DH-DSS-CAMELLIA256-SHA	-?-	C
0x03000086	HIGH	HIGH	TLSv1	RSA	DH	CAMELLIA	256	SHA1	4132,5932	DH-RSA-CAMELLIA256-SHA	DH_RSA_WITH_CAMELLIA_256_CBC_SHA	-
0x03000086-c	HIGH	HIGH	TLSv1	DH	DH	CAMELLIA	256	SHA1	-?-	DH-RSA-CAMELLIA256-SHA	-?-	C
0x03000087	HIGH	HIGH	TLSv1	DH	DSS	CAMELLIA	256	SHA1	4132,5932	DHE-DSS-CAMELLIA256-SHA	DHE_DSS_WITH_CAMELLIA_256_CBC_SHA	-
0x03000088	HIGH	HIGH	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	-
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	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	-
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	MEDIUM	TLSv1	RSA	RSA	SEED	128	SHA1	4162	SEED-SHA	RSA_WITH_SEED_SHA,RSA_WITH_SEED_CBC_SHA	OSX
0x03000097	MEDIUM	medium	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	medium	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	MEDIUM	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	MEDIUM	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_SHA256	L
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	HIGH	TLSv12	RSA	RSA	CAMELLIA	128	SHA256	5932	CAMELLIA128-SHA256,RSA-CAMELLIA128-SHA256	RSA_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000BB	HIGH	HIGH	TLSv12	DH	DSS	CAMELLIA	128	SHA256	5932	DH-DSS-CAMELLIA128-SHA256	DH_DSS_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000BC	HIGH	HIGH	TLSv12	DH	RSA	CAMELLIA	128	SHA256	5932	DH-RSA-CAMELLIA128-SHA256	DH_RSA_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000BD	HIGH	HIGH	TLSv12	DH	DSS	CAMELLIA	128	SHA256	5932	DHE-DSS-CAMELLIA128-SHA256	DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256	Q
0x030000BE	HIGH	HIGH	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	HIGH	TLSv12	RSA	RSA	CAMELLIA	256	SHA256	5932	CAMELLIA256-SHA256,RSA-CAMELLIA256-SHA256	RSA_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C1	HIGH	HIGH	TLSv12	DSS	DH	CAMELLIA	256	SHA256	5932	DH-DSS-CAMELLIA256-SHA256	DH_DSS_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C1-c	HIGH	HIGH	TLSv12	DH	DH	CAMELLIA	256	SHA256	-	DH-DSS-CAMELLIA256-SHA256	DH_DSS_WITH_CAMELLIA_256_CBC_SHA256	C
0x030000C2	HIGH	HIGH	TLSv12	RSA	DH	CAMELLIA	256	SHA256	5932	DH-RSA-CAMELLIA256-SHA256	DH_RSA_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C2-c	HIGH	HIGH	TLSv12	DH	DH	CAMELLIA	256	SHA256	-	DH-RSA-CAMELLIA256-SHA256	DH_RSA_WITH_CAMELLIA_256_CBC_SHA256	C
0x030000C3	HIGH	HIGH	TLSv12	DH	DSS	CAMELLIA	256	SHA256	5932	DHE-DSS-CAMELLIA256-SHA256	DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256	Q
0x030000C4	HIGH	HIGH	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	D,E,F
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	D,E,F
0x03001303	HIGH	HIGH	TLSv13	any	any	ChaCha20-Poly1305	256	AEAD	8446	TLS13-CHACHA20-POLY1305-SHA256,TLS_CHACHA20_POLY1305_SHA256	CHACHA20_POLY1305_SHA256	F
0x03001304	-?-	high	TLSv13	any	any	AESCCM	128	AEAD	8446	TLS13-AES-128-CCM-SHA256,TLS13-AES128-CCM-SHA256	AES_128_CCM_SHA256	F
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	F
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,ECDH_ECDSA_WITH_RC4_128_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	HIGH	SSLv3	ECDH/ECDSA	ECDH	AES	128	SHA1	4492	ECDH-ECDSA-AES128-SHA	ECDH_ECDSA_WITH_AES_128_CBC_SHA	-
0x0300C005	HIGH	HIGH	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	HIGH	SSLv3	ECDH	ECDSA	AES	128	SHA1	8422	ECDHE-ECDSA-AES128-SHA	ECDHE_ECDSA_WITH_AES_128_CBC_SHA	-
0x0300C00A	HIGH	HIGH	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	HIGH	SSLv3	ECDH/RSA	ECDH	AES	128	SHA1	4492	ECDH-RSA-AES128-SHA	ECDH_RSA_WITH_AES_128_CBC_SHA	-
0x0300C00F	HIGH	HIGH	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	HIGH	SSLv3	ECDH	RSA	AES	128	SHA1	8422	ECDHE-RSA-AES128-SHA	ECDHE_RSA_WITH_AES_128_CBC_SHA	-
0x0300C014	HIGH	HIGH	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	-
0x0300C016	MEDIUM	weak	SSLv3	ECDH	None	RC4	128	SHA1	4492,6347	AECDH-RC4-SHA	ECDH_anon_WITH_RC4_128_SHA	-
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	HIGH	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	HIGH	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	HIGH	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	HIGH	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	HIGH	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	HIGH	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	HIGH	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	HIGH	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	-?-	-?-	TLSv12	RSA	RSA	ARIA	128	SHA256	6209	RSA-ARIA128-SHA256	RSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C03D	-?-	-?-	TLSv12	RSA	RSA	ARIA	256	SHA384	6209	RSA-ARIA256-SHA384	RSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C03E	-?-	-?-	TLSv12	DH	DSS	ARIA	128	SHA256	6209	DH-DSS-ARIA128-SHA256	DH_DSS_WITH_ARIA_128_CBC_SHA256	-
0x0300C03F	-?-	-?-	TLSv12	DH	DSS	ARIA	256	SHA384	6209	DH-DSS-ARIA256-SHA384	DH_DSS_WITH_ARIA_256_CBC_SHA384	-
0x0300C040	-?-	-?-	TLSv12	DH	RSA	ARIA	128	SHA256	6209	DH-RSA-ARIA128-SHA256	DH_RSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C041	-?-	-?-	TLSv12	DH	RSA	ARIA	256	SHA384	6209	DH-RSA-ARIA256-SHA384	DH_RSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C042	-?-	-?-	TLSv12	DHE	DSS	ARIA	128	SHA256	6209	DHE-DSS-ARIA128-SHA256	DHE_DSS_WITH_ARIA_128_CBC_SHA256	-
0x0300C043	-?-	-?-	TLSv12	DHE	DSS	ARIA	256	SHA384	6209	DHE-DSS-ARIA256-SHA384	DHE_DSS_WITH_ARIA_256_CBC_SHA384	-
0x0300C044	-?-	-?-	TLSv12	DHE	RSA	ARIA	128	SHA256	6209	DHE-RSA-ARIA128-SHA256,DHE-RSA-ARIA256-SHA256	DHE_RSA_WITH_ARIA_256_CBC_SHA256	I
0x0300C045	-?-	-?-	TLSv12	DHE	RSA	ARIA	256	SHA384	6209	DHE-RSA-ARIA256-SHA384	DHE_RSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C046	-?-	-?-	TLSv12	DH	None	ARIA	128	SHA256	6209	ADH-ARIA128-SHA256	DH_anon_WITH_ARIA_128_CBC_SHA256	-
0x0300C047	-?-	-?-	TLSv12	DH	None	ARIA	256	SHA384	6209	ADH-ARIA256-SHA384	DH_anon_WITH_ARIA_256_CBC_SHA384	-
0x0300C048	-?-	-?-	TLSv12	ECDHE	ECDSA	ARIA	128	SHA256	6209	ECDHE-ECDSA-ARIA128-SHA256	ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C049	-?-	-?-	TLSv12	ECDHE	ECDSA	ARIA	256	SHA384	6209	ECDHE-ECDSA-ARIA256-SHA384	ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C04A	-?-	-?-	TLSv12	ECDH	ECDSA	ARIA	128	SHA256	6209	ECDH-ECDSA-ARIA128-SHA256	ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C04B	-?-	-?-	TLSv12	ECDH	ECDSA	ARIA	256	SHA384	6209	ECDH-ECDSA-ARIA256-SHA384	ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C04C	-?-	-?-	TLSv12	ECDHE	RSA	ARIA	128	SHA256	6209	ECDHE-RSA-ARIA128-SHA256	ECDHE_RSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C04D	-?-	-?-	TLSv12	ECDHE	RSA	ARIA	256	SHA384	6209	ECDHE-RSA-ARIA256-SHA384	ECDHE_RSA_WITH_ARIA_256_CBC_SHA384	-
0x0300C04E	-?-	-?-	TLSv12	ECDH	RSA	ARIA	128	SHA256	6209	ECDH-RSA-ARIA128-SHA256	ECDH_RSA_WITH_ARIA_128_CBC_SHA256	-
0x0300C04F	-?-	-?-	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	medium	TLSv12	ECDH	ECDSA	CAMELLIA	128	SHA256	6367	ECDHE-ECDSA-CAMELLIA128-SHA256	ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C073	HIGH	medium	TLSv12	ECDH	ECDSA	CAMELLIA	256	SHA384	6367	ECDHE-ECDSA-CAMELLIA256-SHA384	ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C074	HIGH	medium	TLSv12	ECDH/ECDSA	ECDH	CAMELLIA	128	SHA256	6367	ECDH-ECDSA-CAMELLIA128-SHA256	ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C075	HIGH	medium	TLSv12	ECDH/ECDSA	ECDH	CAMELLIA	256	SHA384	6367	ECDH-ECDSA-CAMELLIA256-SHA384	ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C076	HIGH	medium	TLSv12	ECDH	RSA	CAMELLIA	128	SHA256	6367	ECDHE-RSA-CAMELLIA128-SHA256	ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C077	HIGH	medium	TLSv12	ECDH	RSA	CAMELLIA	256	SHA384	6367	ECDHE-RSA-CAMELLIA256-SHA384	ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384	-
0x0300C078	HIGH	medium	TLSv12	ECDH/RSA	ECDH	CAMELLIA	128	SHA256	6367	ECDH-RSA-CAMELLIA128-SHA256	ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256	-
0x0300C079	HIGH	medium	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-SHA384	ECCPWD_WITH_AES_128_GCM_SHA384	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-SHA384	ECCPWD_WITH_AES_128_CCM_SHA384	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	SHA256_SHA256	-
0x0300C0B5	-?-	-?-	TLSv13	-?-	-?-	-?-	-?-	SHA384	9150	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-c	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-c	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-c	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 }

