/* Client.java
 *
 * Copyright (C) 2006-2025 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CharacterCodingException;

import com.wolfssl.WolfSSL;
import com.wolfssl.WolfSSLSession;
import com.wolfssl.WolfSSLContext;
import com.wolfssl.WolfSSLCertificate;
import com.wolfssl.WolfSSLException;
import com.wolfssl.WolfSSLJNIException;
import com.wolfssl.WolfSSLIOSendCallback;
import com.wolfssl.WolfSSLIORecvCallback;

/* suppress SSLv3 deprecation warnings, meant for end user not examples */
@SuppressWarnings("deprecation")
public class Client {

    public static Charset charset = Charset.forName("UTF-8");
    public static CharsetEncoder encoder = charset.newEncoder();

    public static void main(String[] args) {
        new Client().run(args);
    }

    public void run(String[] args) {

        int ret = 0, input, err;
        byte[] back = new byte[80];
        String msg  = "hello from jni";
        WolfSSLSession ssl;
        long method = 0;
        Socket sock = null;
        DataOutputStream outstream = null;
        DataInputStream  instream  = null;
        DatagramSocket dsock = null;          /* used with DTLS, if needed */
        InetAddress hostAddr = null;          /* used with DTLS, if needed */

        /* config info */
        boolean useIOCallbacks = false;       /* test I/O callbacks */
        String cipherList = null;             /* default ciphersuite list */
        int sslVersion = 3;                   /* default to TLS 1.2 */
        int verifyPeer = 1;                   /* verify peer by default */
        int benchmark = 0;
        int doDTLS = 0;                       /* don't use DTLS by default */
        int useOcsp = 0;                      /* don't use OCSP by default */
        String ocspUrl = null;                /* OCSP override URL */
        int useAtomic = 0;                    /* atomic record lyr processing */
        int pkCallbacks = 0;                  /* public key callbacks */
        int logCallback = 0;                  /* use test logging callback */
        int usePsk = 0;                       /* use pre shared keys */

        boolean useSecretCallback = false;    /* enable TLS 1.3 secret cb */
        String keyLogFile = "sslkeylog.log";  /* output keylog file */

        long session = 0;                     /* pointer to WOLFSSL_SESSION */
        boolean resumeSession = false;        /* try one session resumption */

        /* cert info */
        String clientCert = "../certs/client-cert.pem";
        String clientKey  = "../certs/client-key.pem";
        String caCert     = "../certs/ca-cert.pem";
        String crlPemDir  = "../certs/crl";

        /* server (peer) info */
        String host = "localhost";
        int port    =  11111;

        try {

            /* load JNI library */
            WolfSSL.loadLibrary();

            /* pull in command line options from user */
            for (int i = 0; i < args.length; i++)
            {
                String arg = args[i];

                if (arg.equals("-?")) {
                    printUsage();

                } else if (arg.equals("-h")) {
                    if (args.length < i+2)
                        printUsage();
                    host = args[++i];

                } else if (arg.equals("-p")) {
                    if (args.length < i+2)
                        printUsage();
                    port = Integer.parseInt(args[++i]);

                } else if (arg.equals("-v")) {
                    if (args.length < i+2)
                        printUsage();
                    sslVersion = Integer.parseInt(args[++i]);
                    if (sslVersion < 0 || sslVersion > 4) {
                        printUsage();
                    }

                } else if (arg.equals("-l")) {
                    if (args.length < i+2)
                        printUsage();
                    cipherList = args[++i];

                } else if (arg.equals("-c")) {
                    if (args.length < i+2)
                        printUsage();
                    clientCert = args[++i];

                } else if (arg.equals("-k")) {
                    if (args.length < i+2)
                        printUsage();
                    clientKey = args[++i];

                } else if (arg.equals("-b")) {
                    if (args.length < i+2)
                      printUsage();
                    benchmark = Integer.parseInt(args[++i]);
                    if (benchmark < 0 || benchmark > 1000000)
                        printUsage();

                } else if (arg.equals("-A")) {
                    if (args.length < i+2)
                        printUsage();
                    caCert = args[++i];

                } else if (arg.equals("-d")) {
                    verifyPeer = 0;

                } else if (arg.equals("-u")) {
                    doDTLS = 1;

                } else if (arg.equals("-s")) {
                    if (WolfSSL.isEnabledPSK() == 0) {
                        System.out.println("PSK support not enabled in " +
                                           "wolfSSL");
                        System.exit(1);
                    }
                    usePsk = 1;

                } else if (arg.equals("-iocb")) {
                    useIOCallbacks = true;

                } else if (arg.equals("-logtest")) {
                    logCallback = 1;

                } else if (arg.equals("-o")) {
                    if (WolfSSL.isEnabledOCSP() == 0) {
                        System.out.println("OCSP support not enabled in " +
                                           "wolfSSL");
                        System.exit(1);
                    }
                    useOcsp = 1;

                } else if (arg.equals("-O")) {
                    if (WolfSSL.isEnabledOCSP() == 0) {
                        System.out.println("OCSP support not enabled in " +
                                           "wolfSSL");
                        System.exit(1);
                    }
                    if (args.length < i+2)
                        printUsage();
                    useOcsp = 1;
                    ocspUrl = args[++i];

                } else if (arg.equals("-U")) {
                    if (WolfSSL.isEnabledAtomicUser() == 0) {
                        System.out.println("Atomic User support not enabled " +
                                           "in wolfSSL");
                        System.exit(1);
                    }
                    useAtomic = 1;

                } else if (arg.equals("-P")) {
                    if (WolfSSL.isEnabledPKCallbacks() == 0) {
                        System.out.println("Public Key callback support not " +
                                           "enabled in wolfSSL");
                        System.exit(1);
                    }
                    pkCallbacks = 1;

                } else if (arg.equals("-r")) {
                    resumeSession = true;

                } else if (arg.equals("-tls13secretcb")) {
                    if (!WolfSSL.secretCallbackEnabled()) {
                        printUsage();
                    }
                    if (args.length < i+2) {
                        printUsage();
                    }
                    useSecretCallback = true;
                    keyLogFile = args[++i];

                } else {
                    printUsage();
                }
            }

            /* sort out DTLS versus TLS versions */
            if (doDTLS == 1) {
                if (sslVersion == 4) {
                    sslVersion = -3;
                }
                else if (sslVersion == 3) {
                    sslVersion = -2;
                }
                else {
                    sslVersion = -1;
                }
            }

            /* init library */
            WolfSSL sslLib = new WolfSSL();

            /* set logging callback */
            if (logCallback == 1) {
                MyLoggingCallback lc = new MyLoggingCallback();
                sslLib.setLoggingCb(lc);
            }

            /* set SSL version method */
            switch (sslVersion) {
                case 0:
                    method = WolfSSL.SSLv3_ClientMethod();
                    break;
                case 1:
                    method = WolfSSL.TLSv1_ClientMethod();
                    break;
                case 2:
                    method = WolfSSL.TLSv1_1_ClientMethod();
                    break;
                case 3:
                    method = WolfSSL.TLSv1_2_ClientMethod();
                    break;
                case 4:
                    method = WolfSSL.TLSv1_3_ClientMethod();
                    break;
                case -1:
                    method = WolfSSL.DTLSv1_ClientMethod();
                    break;
                case -2:
                    method = WolfSSL.DTLSv1_2_ClientMethod();
                    break;
                case -3:
                    method = WolfSSL.DTLSv1_3_ClientMethod();
                    break;
                default:
                    System.err.println("Bad SSL version");
                    System.exit(1);
            }

            /* create context */
            WolfSSLContext sslCtx = new WolfSSLContext(method);

            /* set up PSK, if being used */
            if (usePsk == 1) {

                MyPskClientCallback pskClientCb = new MyPskClientCallback();
                sslCtx.setPskClientCb(pskClientCb);

            } else {

                /* load certificate files */
                ret = sslCtx.useCertificateFile(clientCert,
                        WolfSSL.SSL_FILETYPE_PEM);
                if (ret != WolfSSL.SSL_SUCCESS) {
                    System.out.println("failed to load client certificate!");
                    System.exit(1);
                }

                ret = sslCtx.usePrivateKeyFile(clientKey,
                        WolfSSL.SSL_FILETYPE_PEM);
                if (ret != WolfSSL.SSL_SUCCESS) {
                    System.out.println("failed to load client private key!");
                    System.exit(1);
                }

                /* set verify callback */
                if (verifyPeer == 0) {
                    sslCtx.setVerify(WolfSSL.SSL_VERIFY_NONE, null);
                } else {
                    ret = sslCtx.loadVerifyLocations(caCert, null);
                    if (ret != WolfSSL.SSL_SUCCESS) {
                        System.out.println("failed to load CA certificates!");
                        System.exit(1);
                    }

                    VerifyCallback vc = new VerifyCallback();
                    sslCtx.setVerify(WolfSSL.SSL_VERIFY_PEER, vc);
                }
            }

            /* set cipher list */
            if (cipherList == null) {
                if (usePsk == 1) {
                    ret = sslCtx.setCipherList("DHE-PSK-AES128-GCM-SHA256");
                }
            } else {
                ret = sslCtx.setCipherList(cipherList);
            }

            if (ret != WolfSSL.SSL_SUCCESS) {
                System.out.println("failed to set cipher list, ret = " + ret);
                System.exit(1);
            }

            /* set OCSP options, override URL */
            if (useOcsp == 1) {

                long ocspOptions = WolfSSL.WOLFSSL_OCSP_NO_NONCE;

                if (ocspUrl != null) {
                    ocspOptions = ocspOptions |
                                  WolfSSL.WOLFSSL_OCSP_URL_OVERRIDE;
                }

                if (ocspUrl != null) {
                    ret = sslCtx.setOCSPOverrideUrl(ocspUrl);

                    if (ret != WolfSSL.SSL_SUCCESS) {
                        System.out.println("failed to set OCSP overrideUrl");
                        System.exit(1);
                    }
                }

                ret = sslCtx.enableOCSP(ocspOptions);
                if (ret != WolfSSL.SSL_SUCCESS) {
                    System.out.println("failed to enable OCSP, ret = "
                            + ret);
                    System.exit(1);
                }
            }

            /* register I/O callbacks, I/O ctx setup is later */
            if (useIOCallbacks || (doDTLS == 1)) {
                MyRecvCallback rcb = new MyRecvCallback();
                MySendCallback scb = new MySendCallback();
                sslCtx.setIORecv(rcb);
                sslCtx.setIOSend(scb);
                System.out.println("Registered I/O callbacks");
            }

            /* register atomic record layer callbacks, ctx setup later */
            if (useAtomic == 1) {
                MyMacEncryptCallback mecb = new MyMacEncryptCallback();
                MyDecryptVerifyCallback dvcb = new MyDecryptVerifyCallback();
                sslCtx.setMacEncryptCb(mecb);
                sslCtx.setDecryptVerifyCb(dvcb);

                if (WolfSSL.encryptThenMacEnabled()) {
                    MyVerifyDecryptCallback vdcb =
                        new MyVerifyDecryptCallback();
                    sslCtx.setVerifyDecryptCb(vdcb);
                }
            }

            /* register public key callbacks, ctx setup is later */
            if (pkCallbacks == 1) {

                /* ECC */
                MyEccSignCallback eccSign = new MyEccSignCallback();
                MyEccVerifyCallback eccVerify = new MyEccVerifyCallback();
                MyEccSharedSecretCallback eccSharedSecret =
                    new MyEccSharedSecretCallback();
                sslCtx.setEccSignCb(eccSign);
                sslCtx.setEccVerifyCb(eccVerify);
                sslCtx.setEccSharedSecretCb(eccSharedSecret);

                /* RSA */
                MyRsaSignCallback rsaSign = new MyRsaSignCallback();
                MyRsaVerifyCallback rsaVerify = new MyRsaVerifyCallback();
                MyRsaEncCallback rsaEnc = new MyRsaEncCallback();
                MyRsaDecCallback rsaDec = new MyRsaDecCallback();
                sslCtx.setRsaSignCb(rsaSign);
                sslCtx.setRsaVerifyCb(rsaVerify);
                sslCtx.setRsaEncCb(rsaEnc);
                sslCtx.setRsaDecCb(rsaDec);
            }

            if (benchmark != 0) {
                int times = benchmark;
                int i = 0;

                long start = System.nanoTime();
                long avg;


                for (i = 0; i < times; i++) {
                    sock = new Socket(host, port);
                   /* System.out.println("Connected to " +
                            sock.getInetAddress().getHostAddress() +
                            " on port " +
                            sock.getPort() + "\n"); */

                    outstream = new DataOutputStream(sock.getOutputStream());
                    instream = new DataInputStream(sock.getInputStream());
                    ssl = new WolfSSLSession(sslCtx);
                    ssl.setFd(sock);

                    do {
                        ret = ssl.connect();
                        err = ssl.getError(ret);
                    } while (ret != WolfSSL.SSL_SUCCESS &&
                           (err == WolfSSL.SSL_ERROR_WANT_READ ||
                            err == WolfSSL.SSL_ERROR_WANT_WRITE));

                    if (ret != WolfSSL.SSL_SUCCESS) {
                        System.out.println("ssl.connect failed");
                    }

                    ssl.shutdownSSL();
                    ssl.freeSSL();
                    sock.close();
                }
                avg = System.nanoTime() - start;
                avg /= times;
                avg /= 1000000; /*milliseconds*/
                System.out.println("wolfSSL_connect avg took: " + avg +
                        " milliseconds");

                sslCtx.free();
                System.exit(1);
            }
            /* create SSL object */
            ssl = new WolfSSLSession(sslCtx);

            /* enable/load CRL functionality */
            if (WolfSSL.isEnabledCRL() == 1) {
                ret = ssl.enableCRL(WolfSSL.WOLFSSL_CRL_CHECKALL);
                if (ret != WolfSSL.SSL_SUCCESS) {
                    System.out.println("failed to enable CRL check");
                    System.exit(1);
                }
                ret = ssl.loadCRL(crlPemDir, WolfSSL.SSL_FILETYPE_PEM, 0);
                if (ret != WolfSSL.SSL_SUCCESS) {
                    System.out.println("can't load CRL, check CRL file and " +
                            "date validity");
                    System.exit(1);
                }
                MyMissingCRLCallback crlCb = new MyMissingCRLCallback();
                ret = ssl.setCRLCb(crlCb);
                if (ret != WolfSSL.SSL_SUCCESS) {
                    System.out.println("can't set CRL callback");
                    System.exit(1);
                }
            }

            /* enable use of session tickets if compiled in native wolfSSL */
            if (WolfSSL.sessionTicketEnabled()) {
                ret = ssl.useSessionTicket();
                if (ret != WolfSSL.SSL_SUCCESS) {
                    System.out.println("Session tickets not enabled for " +
                        "this WolfSSLSession: ret = " + ret);
                }
            }

            /* Set TLS 1.3 secret callback if enabled */
            if (useSecretCallback) {
                MyTls13SecretCallback tsc =
                    new MyTls13SecretCallback(keyLogFile);
                ssl.keepArrays();
                ssl.setTls13SecretCb(tsc, null);
                System.out.println("Writing TLS 1.3 secrets to keylog file: " +
                    keyLogFile);
            }

            /* open Socket */
            if (doDTLS == 1) {
                dsock = new DatagramSocket();
                hostAddr = InetAddress.getByName(host);
                InetSocketAddress addr = new InetSocketAddress(hostAddr, port);
                ret = ssl.dtlsSetPeer(addr);
                if (ret != WolfSSL.SSL_SUCCESS) {
                    System.out.println("failed to set DTLS peer");
                    System.exit(1);
                }
            } else {
                sock = new Socket(host, port);
                System.out.println("Connected to " +
                        sock.getInetAddress().getHostAddress() +
                        " on port " +
                        sock.getPort() + "\n");

                outstream = new DataOutputStream(sock.getOutputStream());
                instream = new DataInputStream(sock.getInputStream());
            }

            if (useIOCallbacks || (doDTLS == 1)) {
                /* register I/O callback user context */
                MyIOCtx ioctx = new MyIOCtx(outstream, instream, dsock,
                        hostAddr, port);
                ssl.setIOReadCtx(ioctx);
                ssl.setIOWriteCtx(ioctx);
                System.out.println("Registered I/O callback user contexts");

            } else {

                /* if not using DTLS or I/O callbacks, pass Socket
                 * fd to wolfSSL */
                ret = ssl.setFd(sock);

                if (ret != WolfSSL.SSL_SUCCESS) {
                    System.out.println("Failed to set file descriptor");
                    return;
                }
            }

            if (useAtomic == 1) {
                /* register atomic record layer callback user contexts */
                MyAtomicEncCtx encCtx = new MyAtomicEncCtx();
                MyAtomicDecCtx decCtx = new MyAtomicDecCtx();
                ssl.setMacEncryptCtx(encCtx);
                ssl.setDecryptVerifyCtx(decCtx);

                if (WolfSSL.encryptThenMacEnabled()) {
                    MyAtomicDecCtx vdCtx = new MyAtomicDecCtx();
                    ssl.setVerifyDecryptCtx(vdCtx);
                }
            }

            if (pkCallbacks == 1) {
                /* register public key callback user contexts */

                /* ECC */
                MyEccSignCtx eccSignCtx = new MyEccSignCtx();
                MyEccVerifyCtx eccVerifyCtx = new MyEccVerifyCtx();
                MyEccSharedSecretCtx eccSharedSecretCtx =
                    new MyEccSharedSecretCtx();
                ssl.setEccSignCtx(eccSignCtx);
                ssl.setEccVerifyCtx(eccVerifyCtx);
                ssl.setEccSharedSecretCtx(eccSharedSecretCtx);

                /* RSA */
                MyRsaSignCtx rsaSignCtx = new MyRsaSignCtx();
                MyRsaVerifyCtx rsaVerifyCtx = new MyRsaVerifyCtx();
                MyRsaEncCtx rsaEncCtx = new MyRsaEncCtx();
                MyRsaDecCtx rsaDecCtx = new MyRsaDecCtx();
                ssl.setRsaSignCtx(rsaSignCtx);
                ssl.setRsaVerifyCtx(rsaVerifyCtx);
                ssl.setRsaEncCtx(rsaEncCtx);
                ssl.setRsaDecCtx(rsaDecCtx);
            }

            /* call wolfSSL_connect */
            do {
                ret = ssl.connect();
                err = ssl.getError(ret);
            } while (ret != WolfSSL.SSL_SUCCESS &&
                   (err == WolfSSL.SSL_ERROR_WANT_READ ||
                    err == WolfSSL.SSL_ERROR_WANT_WRITE));

            if (ret != WolfSSL.SSL_SUCCESS) {
                err = ssl.getError(ret);
                String errString = sslLib.getErrorString(err);
                System.out.println("wolfSSL_connect failed. err = " + err +
                        ", " + errString);
                System.exit(1);
            }

            /* show peer info */
            showPeer(ssl);

            /* test write(long, byte[], int) */
            do {
                ret = ssl.write(msg.getBytes(), msg.length());
                err = ssl.getError(0);
            } while (ret < 0 &&
                     (err == WolfSSL.SSL_ERROR_WANT_READ ||
                      err == WolfSSL.SSL_ERROR_WANT_WRITE));

            /* read response */
            do {
                input = ssl.read(back, back.length);
                err = ssl.getError(0);
            } while (input < 0 &&
                     (err == WolfSSL.SSL_ERROR_WANT_READ ||
                      err == WolfSSL.SSL_ERROR_WANT_WRITE));

            if (input > 0) {
                System.out.println("got back: " + new String(back));
            } else {
                System.out.println("read failed");
            }

            /* save session in case resuming */
            session = ssl.getSession();
            if (session == 0) {
                System.out.println("Failed to get native WOLFSSL_SESSION");
            } else {
                System.out.println("Saved native WOLFSSL_SESSION");
            }

            /* shutdown SSL/TLS session */
            ssl.shutdownSSL();

            /* free native WOLFSSL session, also done by garbage collector
             * later if not called explicitly */
            ssl.freeSSL();

            if (resumeSession) {
                System.out.println("\nTrying session resumption...");

                /* create SSL object */
                ssl = new WolfSSLSession(sslCtx);

                /* open Socket */
                if (doDTLS == 1) {
                    dsock = new DatagramSocket();
                    hostAddr = InetAddress.getByName(host);
                    InetSocketAddress addr =
                        new InetSocketAddress(hostAddr, port);
                    ret = ssl.dtlsSetPeer(addr);
                    if (ret != WolfSSL.SSL_SUCCESS) {
                        System.out.println("failed to set DTLS peer");
                        System.exit(1);
                    }
                } else {
                    sock = new Socket(host, port);
                    System.out.println("Connected to " +
                            sock.getInetAddress().getHostAddress() +
                            " on port " +
                            sock.getPort() + "\n");

                    outstream = new DataOutputStream(sock.getOutputStream());
                    instream = new DataInputStream(sock.getInputStream());
                }

                if (useIOCallbacks || (doDTLS == 1)) {
                    /* register I/O callback user context */
                    MyIOCtx ioctx = new MyIOCtx(outstream, instream, dsock,
                            hostAddr, port);
                    ssl.setIOReadCtx(ioctx);
                    ssl.setIOWriteCtx(ioctx);
                    System.out.println("Registered I/O callback user contexts");

                } else {

                    /* if not using DTLS or I/O callbacks, pass Socket
                     * fd to wolfSSL */
                    ret = ssl.setFd(sock);

                    if (ret != WolfSSL.SSL_SUCCESS) {
                        System.out.println("Failed to set file descriptor");
                        return;
                    }
                }

                /* restore saved WOLFSSL_SESSION */
                ssl.setSession(session);

                /* call wolfSSL_connect */
                do {
                    ret = ssl.connect();
                    err = ssl.getError(ret);
                } while (ret != WolfSSL.SSL_SUCCESS &&
                       (err == WolfSSL.SSL_ERROR_WANT_READ ||
                        err == WolfSSL.SSL_ERROR_WANT_WRITE));

                if (ret != WolfSSL.SSL_SUCCESS) {
                    err = ssl.getError(ret);
                    String errString = sslLib.getErrorString(err);
                    System.out.println("wolfSSL_connect failed. err = " + err +
                            ", " + errString);
                    System.exit(1);
                }

                /* show peer info */
                showPeer(ssl);
                System.out.println("Session resumed: " + ssl.sessionReused());

                /* test write(long, byte[], int) */
                do {
                    ret = ssl.write(msg.getBytes(), msg.length());
                    err = ssl.getError(0);
                } while (ret < 0 &&
                         (err == WolfSSL.SSL_ERROR_WANT_READ ||
                          err == WolfSSL.SSL_ERROR_WANT_WRITE));

                /* read response */
                do {
                    input = ssl.read(back, back.length);
                    err = ssl.getError(0);
                } while (input < 0 &&
                         (err == WolfSSL.SSL_ERROR_WANT_READ ||
                          err == WolfSSL.SSL_ERROR_WANT_WRITE));

                if (input > 0) {
                    System.out.println("got back: " + new String(back));
                } else {
                    System.out.println("read failed");
                }

                /* shutdown SSL/TLS session */
                ssl.shutdownSSL();

                /* free native WOLFSSL session, also done by garbage collector
                 * later if not called explicitly */
                ssl.freeSSL();
            }

            /* free resources */
            sslCtx.free();

        } catch (UnsatisfiedLinkError ule) {
            ule.printStackTrace();
        } catch (WolfSSLException wex) {
            wex.printStackTrace();
        } catch (WolfSSLJNIException jex) {
           jex.printStackTrace();
        } catch (CharacterCodingException cce) {
            cce.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    } /* end run() */

    void showPeer(WolfSSLSession ssl) {

        long peerCrtPtr = 0;
        String altname;

        try {
            peerCrtPtr = ssl.getPeerCertificate();

            if (peerCrtPtr != 0) {
                System.out.println("issuer : " +
                        ssl.getPeerX509Issuer(peerCrtPtr));
                System.out.println("subject : " +
                        ssl.getPeerX509Subject(peerCrtPtr));

                while( (altname = ssl.getPeerX509AltName(peerCrtPtr)) != null)
                    System.out.println("altname = " + altname);
            }

            System.out.println("SSL version is " + ssl.getVersion());
            System.out.println("SSL cipher suite is " + ssl.cipherGetName());

        } catch (WolfSSLJNIException e) {
            e.printStackTrace();

        } finally {
            if (WolfSSL.getLibVersionHex() >= 0x05003000) {
                if (peerCrtPtr != 0) {
                    WolfSSLCertificate.freeX509(peerCrtPtr);
                }
            }
        }
    }

    void printUsage() {
        System.out.println("Java example client usage:");
        System.out.println("-?\t\tHelp, print this usage");
        System.out.println("-h <host>\tHost to connect to, default 127.0.0.1");
        System.out.println("-p <num>\tPort to connect to, default 11111");
        System.out.println("-v <num>\tSSL version [0-3], SSLv3(0) - " +
                "TLS1.3(4)), default 3");
        System.out.println("-l <str>\tCipher list");
        System.out.println("-c <file>\tCertificate file,\t\tdefault " +
                "../certs/client-cert.pem");
        System.out.println("-k <file>\tKey file,\t\t\tdefault " +
                "../certs/client-key.pem");
        System.out.println("-A <file>\tCertificate Authority file,\tdefault " +
                "../certs/ca-cert.pem");
        System.out.println("-b <num>\tBenchmark <num> connections and print" +
                " stats");
        System.out.println("-r\t\tResume session");
        if (WolfSSL.isEnabledPSK() == 1)
            System.out.println("-s\t\tUse pre shared keys");
        System.out.println("-d\t\tDisable peer checks");
        if (WolfSSL.isEnabledDTLS() == 1)
            System.out.println("-u\t\tUse UDP DTLS, add -v 2 for DTLSv1 " +
                    "(default), -v 3 for DTLSv1.2, -v 4 for DTLSv1.3");
        System.out.println("-iocb\t\tEnable test I/O callbacks");
        System.out.println("-logtest\tEnable test logging callback");
        if (WolfSSL.isEnabledOCSP() == 1) {
            System.out.println("-o\t\tPerform OCSP lookup on peer certificate");
            System.out.println("-O <url>\tPerform OCSP lookup using <url> " +
                    "as responder");
        }
        if (WolfSSL.isEnabledAtomicUser() == 1)
            System.out.println("-U\t\tEnable Atomic User Record Layer Callbacks");
        if (WolfSSL.isEnabledPKCallbacks() == 1)
            System.out.println("-P\t\tPublic Key Callbacks");
        if (WolfSSL.secretCallbackEnabled())
            System.out.println("-tls13secretcb\tEnable TLS 1.3 secret callback");
        System.exit(1);
    }

} /* end Client */

