<?php

$odbc_driver = "MySQL ODBC 8.0 Driver";
$server   = "ssltest-linux";
$output_file = false;

if (isset($argv[1])) $server      = $argv[1];
if (isset($argv[2])) $odbc_driver = $argv[2];
if (isset($argv[3])) $output_file = $argv[3];

$port     = "3306";
$database = "mysql";

$user     = "ssl";
$password = "Secret23!";

$ssl_options = "SSLCA=/vagrant/ssl/ca-cert.pem";

$dsn_base = "Driver={".$odbc_driver."};SERVER=$server;USER=$user;PASSWORD=$password;DATABASE=$database;PORT=$port;$ssl_options";

$con = @odbc_connect($dsn_base, $user, $password) or die(odbc_errormsg());

function get_mariadb_var($con, $varname, $status=false)
{
    $table = $status ? "status" : "variables";
    
    $query = "SELECT variable_value FROM information_schema.global_$table WHERE variable_name = '$varname'";

    $res = odbc_exec($con, $query);

    odbc_fetch_row($res);

    $value = odbc_result($res, "variable_value");

    odbc_free_result($res);
    
    return $value;
}

function get_status_var($con, $varname)
{
    return get_mariadb_var($con, $varname, true);
}


function get_cipher_list($con)
{
    $ciphers = get_status_var($con, "SSL_CIPHER_LIST");

    return explode(":", $ciphers);
}

function get_tls_versions($con)
{
    $tls_versions = get_mariadb_var($con, "TLS_VERSION");

    return explode(",", $tls_versions);
}

$versions["server_version"]         = get_mariadb_var($con, "VERSION");
$versions["server_version_comment"] = get_mariadb_var($con, "VERSION_COMMENT");
$versions["server_ssl_version"]     = get_mariadb_var($con, "VERSION_SSL_LIBRARY");

$cipher_list  = get_cipher_list($con);

$tls_versions = get_tls_versions($con);

odbc_close($con);

function check_tls_ciphers($dsn_base, $user, $password, $tls_versions, $cipher_list, $versions) {
    $result = [];
    
    foreach ($cipher_list as $cipher) {
        $result[$cipher] = [];
        
        foreach ($tls_versions as $tls_version) {
            $dsn = "$dsn_base";
            if ($tls_version) {
                if (false !== strpos($dsn_base, "MariaDB")) {
                    $dsn = "$dsn;TLSVERSION=$tls_version";
                } else {
                    switch($tls_version) {
                    case "TLSv1.0":
                        $dsn = "$dsn;NO_TLS_1_1;NO_TLS_1_2;NO_TLS_1_3";
                        break;
                    case "TLSv1.1":
                        $dsn = "$dsn;NO_TLS_1_0;NO_TLS_1_2;NO_TLS_1_3";
                        break;
                    case "TLSv1.2":
                        $dsn = "$dsn;NO_TLS_1_0;NO_TLS_1_1;NO_TLS_1_3";
                        break;
                    case "TLSv1.3":
                        $dsn = "$dsn;NO_TLS_1_0;NO_TLS_1_1;NO_TLS_1_2";
                        break;
                    default:
                        break;
                    }
                }
            }
            if ($cipher) {
                $dsn = "$dsn;SSLCIPHER=$cipher";
            }
            
            $con = @odbc_connect($dsn, $user, $password);
            $errors = [];
            if ($con) {
                $chosen_cipher = get_status_var($con, "SSL_CIPHER");
                $chosen_tls_version = get_status_var($con, "SSL_VERSION");
                if ($cipher && ($cipher != $chosen_cipher)) {
                    $errors[] = "got $chosen_cipher instead of $cipher";
                }
                if ($tls_version && ($tls_version != $chosen_tls_version)) {
                    $errors[] = "got $chosen_tls_version instead of $tls_version";
                }
                odbc_close($con);
            } else {
                $chosen_cipher = false;
                $chosen_tls_version = false;
                $errors[] = odbc_errormsg();
            }

            $result[$cipher][$tls_version] = [
                "dsn"         => $dsn,
                "cipher"      => $chosen_cipher,
                "tls_version" => $chosen_tls_version,
                "errors"      => $errors
            ];
  
        } 
    }

    return $result;
}

function html_result_page($client, $results, $versions, $tls_versions, $cipher_list) {
?>
<html>
<head>
<title>TLS Versions and Ciphers</title>
</head>
<body>
    <h1>Sever and Client versions</h1>
<table>
    <tr><td><b>Server version:</b></td><td><?php echo $versions["server_version_comment"]." ".$versions["server_version"]; ?></td></tr>
    <tr><td><b>Server SSL lib:</b></td><td><?php echo $versions["server_ssl_version"]; ?></td></tr>
    <tr><td><b>Client connector:</b></td><td><?php echo $client; ?></td></tr>
</table>  
<?php
    echo "<h1>Working Ciphers</h1>\n";
    $working = [];
    foreach ($results as $subresults) {
        foreach ($subresults as $result) {
            if (count($result["errors"]) == 0) {
                $working[$result["cipher"]] = $result["cipher"];
            }
        }
    }
    ksort($working);
    echo "<ul>\n";
    foreach ($working as $cipher) {
        echo "  <li>$cipher</li>\n";
    }
    echo "</ul>\n";
    
    echo "<h1>Result Details</h1>\n";
    
    echo "<table>\n";

    echo "<tr><th>Cipher</th>";
    foreach ($tls_versions as $tls) {
        echo "<th>$tls</th>";
    }
    echo "</tr>\n";

    
    ksort($results);
    foreach ($results as $cipher => $cipher_results) {
        echo "<tr><td>$cipher</td>\n";
        foreach ($cipher_results as $tls => $result) {
            if (count($result["errors"]) == 0) {
                $color = "green";
            } else if ($result["cipher"] && $result["tls_version"]) {
                $color = "yellow";
            } else {
                $color = "red";
            }
            echo "<td bgcolor='$color' title='$result[dsn]'>".join("<br/>", $result["errors"])."</td>\n";
        }
        echo "</tr>\n";
    }
    
    echo "</table>\n";
    
    echo "</body></html>\n";
}

$results = check_tls_ciphers($dsn_base, $user, $password, $tls_versions, $cipher_list, $versions);

if (!empty($output_file)) {
    ob_start();
}
$client = $odbc_driver . " (" . PHP_OS . ")";
html_result_page($client, $results, $versions, $tls_versions, $cipher_list);
if (!empty($output_file)) {
    file_put_contents($output_file, ob_get_clean());
}
