Skip to main content
  • Home
  • login
  • Browse the archive

    swh mirror partner logo
swh logo
SoftwareHeritage
Software
Heritage
Mirror
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

Revision 85eb4f303f4fc9eb8edfd9be0f6f67d435af9972 authored by ha1215 on 23 April 2024, 01:54:36 UTC, committed by Tomas Mraz on 09 September 2024, 07:23:38 UTC
Minor WINDOWS.md cleanups
The possessive form of "Windows" has been updated from "Windows's"
to "Windows'".

The function call "a poll(2) call" has been specified as
"a poll(2) system call" for clarity.

The phrase "and supposed" has been corrected to "and was supposed" to
improve sentence structure.

The phrase "However Microsoft has" now includes a comma, revised to
"However, Microsoft has" to enhance readability.

The statement "Supporting these is a pain" has been adjusted to
"Supporting these can be a pain" to better convey potential variability
in user experience.

CLA: trivial

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Yang <kaishen.yy@antfin.com>
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/24242)
1 parent a5cd06f
  • Files
  • Changes
  • cc018ae
  • /
  • apps
  • /
  • s_client.c
Raw File
Cook and download a directory from the Software Heritage Vault

You have requested the cooking of the directory with identifier None into a standard tar.gz archive.

Are you sure you want to continue ?

Download a directory from the Software Heritage Vault

You have requested the download of the directory with identifier None as a standard tar.gz archive.

Are you sure you want to continue ?

Cook and download a revision from the Software Heritage Vault

You have requested the cooking of the history heading to revision with identifier swh:1:rev:85eb4f303f4fc9eb8edfd9be0f6f67d435af9972 into a bare git archive.

Are you sure you want to continue ?

Download a revision from the Software Heritage Vault

You have requested the download of the history heading to revision with identifier swh:1:rev:85eb4f303f4fc9eb8edfd9be0f6f67d435af9972 as a bare git archive.

Are you sure you want to continue ?

Invalid Email !

The provided email is not well-formed.

Download link has expired

The requested archive is no longer available for download from the Software Heritage Vault.

Do you want to cook it again ?

Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • revision
  • content
revision badge
swh:1:rev:85eb4f303f4fc9eb8edfd9be0f6f67d435af9972
content badge Iframe embedding
swh:1:cnt:c922653ee773e767356bdac658566553654a0437
s_client.c
/*
 * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
 * Copyright 2005 Nokia. All rights reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#include "internal/e_os.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <openssl/e_os2.h>
#include "internal/nelem.h"
#include "internal/sockets.h" /* for openssl_fdset() */

#ifndef OPENSSL_NO_SOCK

/*
 * With IPv6, it looks like Digital has mixed up the proper order of
 * recursive header file inclusion, resulting in the compiler complaining
 * that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which is
 * needed to have fileno() declared correctly...  So let's define u_int
 */
#if defined(OPENSSL_SYS_VMS_DECC) && !defined(__U_INT)
# define __U_INT
typedef unsigned int u_int;
#endif

#include "apps.h"
#include "progs.h"
#include <openssl/x509.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/ocsp.h>
#include <openssl/bn.h>
#include <openssl/trace.h>
#include <openssl/async.h>
#ifndef OPENSSL_NO_CT
# include <openssl/ct.h>
#endif
#include "s_apps.h"
#include "timeouts.h"
#include "internal/sockets.h"

#if defined(__has_feature)
# if __has_feature(memory_sanitizer)
#  include <sanitizer/msan_interface.h>
# endif
#endif

#undef BUFSIZZ
#define BUFSIZZ 1024*16
#define S_CLIENT_IRC_READ_TIMEOUT 8

#define USER_DATA_MODE_NONE     0
#define USER_DATA_MODE_BASIC    1
#define USER_DATA_MODE_ADVANCED 2

#define USER_DATA_PROCESS_BAD_ARGUMENT 0
#define USER_DATA_PROCESS_SHUT         1
#define USER_DATA_PROCESS_RESTART      2
#define USER_DATA_PROCESS_NO_DATA      3
#define USER_DATA_PROCESS_CONTINUE     4

struct user_data_st {
    /* SSL connection we are processing commands for */
    SSL *con;

    /* Buffer where we are storing data supplied by the user */
    char *buf;

    /* Allocated size of the buffer */
    size_t bufmax;

    /* Amount of the buffer actually used */
    size_t buflen;

    /* Current location in the buffer where we will read from next*/
    size_t bufoff;

    /* The mode we are using for processing commands */
    int mode;

    /* Whether FIN has ben sent on the stream */
    int isfin;
};

static void user_data_init(struct user_data_st *user_data, SSL *con, char *buf,
                           size_t bufmax, int mode);
static int user_data_add(struct user_data_st *user_data, size_t i);
static int user_data_process(struct user_data_st *user_data, size_t *len,
                             size_t *off);
static int user_data_has_data(struct user_data_st *user_data);

static char *prog;
static int c_debug = 0;
static int c_showcerts = 0;
static char *keymatexportlabel = NULL;
static int keymatexportlen = 20;
static BIO *bio_c_out = NULL;
static int c_quiet = 0;
static char *sess_out = NULL;
static SSL_SESSION *psksess = NULL;

static void print_stuff(BIO *berr, SSL *con, int full);
#ifndef OPENSSL_NO_OCSP
static int ocsp_resp_cb(SSL *s, void *arg);
#endif
static int ldap_ExtendedResponse_parse(const char *buf, long rem);
static int is_dNS_name(const char *host);

static const unsigned char cert_type_rpk[] = { TLSEXT_cert_type_rpk, TLSEXT_cert_type_x509 };
static int enable_server_rpk = 0;

static int saved_errno;

static void save_errno(void)
{
    saved_errno = errno;
    errno = 0;
}

static int restore_errno(void)
{
    int ret = errno;
    errno = saved_errno;
    return ret;
}

/* Default PSK identity and key */
static char *psk_identity = "Client_identity";

#ifndef OPENSSL_NO_PSK
static unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity,
                                  unsigned int max_identity_len,
                                  unsigned char *psk,
                                  unsigned int max_psk_len)
{
    int ret;
    long key_len;
    unsigned char *key;

    if (c_debug)
        BIO_printf(bio_c_out, "psk_client_cb\n");
    if (!hint) {
        /* no ServerKeyExchange message */
        if (c_debug)
            BIO_printf(bio_c_out,
                       "NULL received PSK identity hint, continuing anyway\n");
    } else if (c_debug) {
        BIO_printf(bio_c_out, "Received PSK identity hint '%s'\n", hint);
    }

    /*
     * lookup PSK identity and PSK key based on the given identity hint here
     */
    ret = BIO_snprintf(identity, max_identity_len, "%s", psk_identity);
    if (ret < 0 || (unsigned int)ret > max_identity_len)
        goto out_err;
    if (c_debug)
        BIO_printf(bio_c_out, "created identity '%s' len=%d\n", identity,
                   ret);

    /* convert the PSK key to binary */
    key = OPENSSL_hexstr2buf(psk_key, &key_len);
    if (key == NULL) {
        BIO_printf(bio_err, "Could not convert PSK key '%s' to buffer\n",
                   psk_key);
        return 0;
    }
    if (max_psk_len > INT_MAX || key_len > (long)max_psk_len) {
        BIO_printf(bio_err,
                   "psk buffer of callback is too small (%d) for key (%ld)\n",
                   max_psk_len, key_len);
        OPENSSL_free(key);
        return 0;
    }

    memcpy(psk, key, key_len);
    OPENSSL_free(key);

    if (c_debug)
        BIO_printf(bio_c_out, "created PSK len=%ld\n", key_len);

    return key_len;
 out_err:
    if (c_debug)
        BIO_printf(bio_err, "Error in PSK client callback\n");
    return 0;
}
#endif

const unsigned char tls13_aes128gcmsha256_id[] = { 0x13, 0x01 };
const unsigned char tls13_aes256gcmsha384_id[] = { 0x13, 0x02 };

static int psk_use_session_cb(SSL *s, const EVP_MD *md,
                              const unsigned char **id, size_t *idlen,
                              SSL_SESSION **sess)
{
    SSL_SESSION *usesess = NULL;
    const SSL_CIPHER *cipher = NULL;

    if (psksess != NULL) {
        SSL_SESSION_up_ref(psksess);
        usesess = psksess;
    } else {
        long key_len;
        unsigned char *key = OPENSSL_hexstr2buf(psk_key, &key_len);

        if (key == NULL) {
            BIO_printf(bio_err, "Could not convert PSK key '%s' to buffer\n",
                       psk_key);
            return 0;
        }

        /* We default to SHA-256 */
        cipher = SSL_CIPHER_find(s, tls13_aes128gcmsha256_id);
        if (cipher == NULL) {
            BIO_printf(bio_err, "Error finding suitable ciphersuite\n");
            OPENSSL_free(key);
            return 0;
        }

        usesess = SSL_SESSION_new();
        if (usesess == NULL
                || !SSL_SESSION_set1_master_key(usesess, key, key_len)
                || !SSL_SESSION_set_cipher(usesess, cipher)
                || !SSL_SESSION_set_protocol_version(usesess, TLS1_3_VERSION)) {
            OPENSSL_free(key);
            goto err;
        }
        OPENSSL_free(key);
    }

    cipher = SSL_SESSION_get0_cipher(usesess);
    if (cipher == NULL)
        goto err;

    if (md != NULL && SSL_CIPHER_get_handshake_digest(cipher) != md) {
        /* PSK not usable, ignore it */
        *id = NULL;
        *idlen = 0;
        *sess = NULL;
        SSL_SESSION_free(usesess);
    } else {
        *sess = usesess;
        *id = (unsigned char *)psk_identity;
        *idlen = strlen(psk_identity);
    }

    return 1;

 err:
    SSL_SESSION_free(usesess);
    return 0;
}

/* This is a context that we pass to callbacks */
typedef struct tlsextctx_st {
    BIO *biodebug;
    int ack;
} tlsextctx;

static int ssl_servername_cb(SSL *s, int *ad, void *arg)
{
    tlsextctx *p = (tlsextctx *) arg;
    const char *hn = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
    if (SSL_get_servername_type(s) != -1)
        p->ack = !SSL_session_reused(s) && hn != NULL;
    else
        BIO_printf(bio_err, "Can't use SSL_get_servername\n");

    return SSL_TLSEXT_ERR_OK;
}

#ifndef OPENSSL_NO_NEXTPROTONEG
/* This the context that we pass to next_proto_cb */
typedef struct tlsextnextprotoctx_st {
    unsigned char *data;
    size_t len;
    int status;
} tlsextnextprotoctx;

static tlsextnextprotoctx next_proto;

static int next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen,
                         const unsigned char *in, unsigned int inlen,
                         void *arg)
{
    tlsextnextprotoctx *ctx = arg;

    if (!c_quiet) {
        /* We can assume that |in| is syntactically valid. */
        unsigned i;
        BIO_printf(bio_c_out, "Protocols advertised by server: ");
        for (i = 0; i < inlen;) {
            if (i)
                BIO_write(bio_c_out, ", ", 2);
            BIO_write(bio_c_out, &in[i + 1], in[i]);
            i += in[i] + 1;
        }
        BIO_write(bio_c_out, "\n", 1);
    }

    ctx->status =
        SSL_select_next_proto(out, outlen, in, inlen, ctx->data, ctx->len);
    return SSL_TLSEXT_ERR_OK;
}
#endif                         /* ndef OPENSSL_NO_NEXTPROTONEG */

static int serverinfo_cli_parse_cb(SSL *s, unsigned int ext_type,
                                   const unsigned char *in, size_t inlen,
                                   int *al, void *arg)
{
    char pem_name[100];
    unsigned char ext_buf[4 + 65536];

    /* Reconstruct the type/len fields prior to extension data */
    inlen &= 0xffff; /* for formal memcmpy correctness */
    ext_buf[0] = (unsigned char)(ext_type >> 8);
    ext_buf[1] = (unsigned char)(ext_type);
    ext_buf[2] = (unsigned char)(inlen >> 8);
    ext_buf[3] = (unsigned char)(inlen);
    memcpy(ext_buf + 4, in, inlen);

    BIO_snprintf(pem_name, sizeof(pem_name), "SERVERINFO FOR EXTENSION %d",
                 ext_type);
    PEM_write_bio(bio_c_out, pem_name, "", ext_buf, 4 + inlen);
    return 1;
}

/*
 * Hex decoder that tolerates optional whitespace.  Returns number of bytes
 * produced, advances inptr to end of input string.
 */
static ossl_ssize_t hexdecode(const char **inptr, void *result)
{
    unsigned char **out = (unsigned char **)result;
    const char *in = *inptr;
    unsigned char *ret = app_malloc(strlen(in) / 2, "hexdecode");
    unsigned char *cp = ret;
    uint8_t byte;
    int nibble = 0;

    if (ret == NULL)
        return -1;

    for (byte = 0; *in; ++in) {
        int x;

        if (isspace(_UC(*in)))
            continue;
        x = OPENSSL_hexchar2int(*in);
        if (x < 0) {
            OPENSSL_free(ret);
            return 0;
        }
        byte |= (char)x;
        if ((nibble ^= 1) == 0) {
            *cp++ = byte;
            byte = 0;
        } else {
            byte <<= 4;
        }
    }
    if (nibble != 0) {
        OPENSSL_free(ret);
        return 0;
    }
    *inptr = in;

    return cp - (*out = ret);
}

/*
 * Decode unsigned 0..255, returns 1 on success, <= 0 on failure. Advances
 * inptr to next field skipping leading whitespace.
 */
static ossl_ssize_t checked_uint8(const char **inptr, void *out)
{
    uint8_t *result = (uint8_t *)out;
    const char *in = *inptr;
    char *endp;
    long v;
    int e;

    save_errno();
    v = strtol(in, &endp, 10);
    e = restore_errno();

    if (((v == LONG_MIN || v == LONG_MAX) && e == ERANGE) ||
        endp == in || !isspace(_UC(*endp)) ||
        v != (*result = (uint8_t) v)) {
        return -1;
    }
    for (in = endp; isspace(_UC(*in)); ++in)
        continue;

    *inptr = in;
    return 1;
}

struct tlsa_field {
    void *var;
    const char *name;
    ossl_ssize_t (*parser)(const char **, void *);
};

static int tlsa_import_rr(SSL *con, const char *rrdata)
{
    /* Not necessary to re-init these values; the "parsers" do that. */
    static uint8_t usage;
    static uint8_t selector;
    static uint8_t mtype;
    static unsigned char *data;
    static struct tlsa_field tlsa_fields[] = {
        { &usage, "usage", checked_uint8 },
        { &selector, "selector", checked_uint8 },
        { &mtype, "mtype", checked_uint8 },
        { &data, "data", hexdecode },
        { NULL, }
    };
    struct tlsa_field *f;
    int ret;
    const char *cp = rrdata;
    ossl_ssize_t len = 0;

    for (f = tlsa_fields; f->var; ++f) {
        /* Returns number of bytes produced, advances cp to next field */
        if ((len = f->parser(&cp, f->var)) <= 0) {
            BIO_printf(bio_err, "%s: warning: bad TLSA %s field in: %s\n",
                       prog, f->name, rrdata);
            return 0;
        }
    }
    /* The data field is last, so len is its length */
    ret = SSL_dane_tlsa_add(con, usage, selector, mtype, data, len);
    OPENSSL_free(data);

    if (ret == 0) {
        ERR_print_errors(bio_err);
        BIO_printf(bio_err, "%s: warning: unusable TLSA rrdata: %s\n",
                   prog, rrdata);
        return 0;
    }
    if (ret < 0) {
        ERR_print_errors(bio_err);
        BIO_printf(bio_err, "%s: warning: error loading TLSA rrdata: %s\n",
                   prog, rrdata);
        return 0;
    }
    return ret;
}

static int tlsa_import_rrset(SSL *con, STACK_OF(OPENSSL_STRING) *rrset)
{
    int num = sk_OPENSSL_STRING_num(rrset);
    int count = 0;
    int i;

    for (i = 0; i < num; ++i) {
        char *rrdata = sk_OPENSSL_STRING_value(rrset, i);
        if (tlsa_import_rr(con, rrdata) > 0)
            ++count;
    }
    return count > 0;
}

typedef enum OPTION_choice {
    OPT_COMMON,
    OPT_4, OPT_6, OPT_HOST, OPT_PORT, OPT_CONNECT, OPT_BIND, OPT_UNIX,
    OPT_XMPPHOST, OPT_VERIFY, OPT_NAMEOPT,
    OPT_CERT, OPT_CRL, OPT_CRL_DOWNLOAD, OPT_SESS_OUT, OPT_SESS_IN,
    OPT_CERTFORM, OPT_CRLFORM, OPT_VERIFY_RET_ERROR, OPT_VERIFY_QUIET,
    OPT_BRIEF, OPT_PREXIT, OPT_NO_INTERACTIVE, OPT_CRLF, OPT_QUIET, OPT_NBIO,
    OPT_SSL_CLIENT_ENGINE, OPT_IGN_EOF, OPT_NO_IGN_EOF,
    OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_WDEBUG,
    OPT_MSG, OPT_MSGFILE, OPT_ENGINE, OPT_TRACE, OPT_SECURITY_DEBUG,
    OPT_SECURITY_DEBUG_VERBOSE, OPT_SHOWCERTS, OPT_NBIO_TEST, OPT_STATE,
    OPT_PSK_IDENTITY, OPT_PSK, OPT_PSK_SESS,
#ifndef OPENSSL_NO_SRP
    OPT_SRPUSER, OPT_SRPPASS, OPT_SRP_STRENGTH, OPT_SRP_LATEUSER,
    OPT_SRP_MOREGROUPS,
#endif
    OPT_SSL3, OPT_SSL_CONFIG,
    OPT_TLS1_3, OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1,
    OPT_DTLS1_2, OPT_QUIC, OPT_SCTP, OPT_TIMEOUT, OPT_MTU, OPT_KEYFORM,
    OPT_PASS, OPT_CERT_CHAIN, OPT_KEY, OPT_RECONNECT, OPT_BUILD_CHAIN,
    OPT_NEXTPROTONEG, OPT_ALPN,
    OPT_CAPATH, OPT_NOCAPATH, OPT_CHAINCAPATH, OPT_VERIFYCAPATH,
    OPT_CAFILE, OPT_NOCAFILE, OPT_CHAINCAFILE, OPT_VERIFYCAFILE,
    OPT_CASTORE, OPT_NOCASTORE, OPT_CHAINCASTORE, OPT_VERIFYCASTORE,
    OPT_SERVERINFO, OPT_STARTTLS, OPT_SERVERNAME, OPT_NOSERVERNAME, OPT_ASYNC,
    OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_PROTOHOST,
    OPT_MAXFRAGLEN, OPT_MAX_SEND_FRAG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES,
    OPT_READ_BUF, OPT_KEYLOG_FILE, OPT_EARLY_DATA, OPT_REQCAFILE,
    OPT_TFO,
    OPT_V_ENUM,
    OPT_X_ENUM,
    OPT_S_ENUM, OPT_IGNORE_UNEXPECTED_EOF,
    OPT_FALLBACKSCSV, OPT_NOCMDS, OPT_ADV, OPT_PROXY, OPT_PROXY_USER,
    OPT_PROXY_PASS, OPT_DANE_TLSA_DOMAIN,
#ifndef OPENSSL_NO_CT
    OPT_CT, OPT_NOCT, OPT_CTLOG_FILE,
#endif
    OPT_DANE_TLSA_RRDATA, OPT_DANE_EE_NO_NAME,
    OPT_ENABLE_PHA,
    OPT_ENABLE_SERVER_RPK,
    OPT_ENABLE_CLIENT_RPK,
    OPT_SCTP_LABEL_BUG,
    OPT_KTLS,
    OPT_R_ENUM, OPT_PROV_ENUM
} OPTION_CHOICE;

const OPTIONS s_client_options[] = {
    {OPT_HELP_STR, 1, '-', "Usage: %s [options] [host:port]\n"},

    OPT_SECTION("General"),
    {"help", OPT_HELP, '-', "Display this summary"},
#ifndef OPENSSL_NO_ENGINE
    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
    {"ssl_client_engine", OPT_SSL_CLIENT_ENGINE, 's',
     "Specify engine to be used for client certificate operations"},
#endif
    {"ssl_config", OPT_SSL_CONFIG, 's', "Use specified section for SSL_CTX configuration"},
#ifndef OPENSSL_NO_CT
    {"ct", OPT_CT, '-', "Request and parse SCTs (also enables OCSP stapling)"},
    {"noct", OPT_NOCT, '-', "Do not request or parse SCTs (default)"},
    {"ctlogfile", OPT_CTLOG_FILE, '<', "CT log list CONF file"},
#endif

    OPT_SECTION("Network"),
    {"host", OPT_HOST, 's', "Use -connect instead"},
    {"port", OPT_PORT, 'p', "Use -connect instead"},
    {"connect", OPT_CONNECT, 's',
     "TCP/IP where to connect; default: " PORT ")"},
    {"bind", OPT_BIND, 's', "bind local address for connection"},
    {"proxy", OPT_PROXY, 's',
     "Connect to via specified proxy to the real server"},
    {"proxy_user", OPT_PROXY_USER, 's', "UserID for proxy authentication"},
    {"proxy_pass", OPT_PROXY_PASS, 's', "Proxy authentication password source"},
#ifdef AF_UNIX
    {"unix", OPT_UNIX, 's', "Connect over the specified Unix-domain socket"},
#endif
    {"4", OPT_4, '-', "Use IPv4 only"},
#ifdef AF_INET6
    {"6", OPT_6, '-', "Use IPv6 only"},
#endif
    {"maxfraglen", OPT_MAXFRAGLEN, 'p',
     "Enable Maximum Fragment Length Negotiation (len values: 512, 1024, 2048 and 4096)"},
    {"max_send_frag", OPT_MAX_SEND_FRAG, 'p', "Maximum Size of send frames "},
    {"split_send_frag", OPT_SPLIT_SEND_FRAG, 'p',
     "Size used to split data for encrypt pipelines"},
    {"max_pipelines", OPT_MAX_PIPELINES, 'p',
     "Maximum number of encrypt/decrypt pipelines to be used"},
    {"read_buf", OPT_READ_BUF, 'p',
     "Default read buffer size to be used for connections"},
    {"fallback_scsv", OPT_FALLBACKSCSV, '-', "Send the fallback SCSV"},

    OPT_SECTION("Identity"),
    {"cert", OPT_CERT, '<', "Client certificate file to use"},
    {"certform", OPT_CERTFORM, 'F',
     "Client certificate file format (PEM/DER/P12); has no effect"},
    {"cert_chain", OPT_CERT_CHAIN, '<',
     "Client certificate chain file (in PEM format)"},
    {"build_chain", OPT_BUILD_CHAIN, '-', "Build client certificate chain"},
    {"key", OPT_KEY, 's', "Private key file to use; default: -cert file"},
    {"keyform", OPT_KEYFORM, 'E', "Key format (ENGINE, other values ignored)"},
    {"pass", OPT_PASS, 's', "Private key and cert file pass phrase source"},
    {"verify", OPT_VERIFY, 'p', "Turn on peer certificate verification"},
    {"nameopt", OPT_NAMEOPT, 's', "Certificate subject/issuer name printing options"},
    {"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"},
    {"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"},
    {"CAstore", OPT_CASTORE, ':', "URI to store of CA's"},
    {"no-CAfile", OPT_NOCAFILE, '-',
     "Do not load the default certificates file"},
    {"no-CApath", OPT_NOCAPATH, '-',
     "Do not load certificates from the default certificates directory"},
    {"no-CAstore", OPT_NOCASTORE, '-',
     "Do not load certificates from the default certificates store"},
    {"requestCAfile", OPT_REQCAFILE, '<',
      "PEM format file of CA names to send to the server"},
#if defined(TCP_FASTOPEN) && !defined(OPENSSL_NO_TFO)
    {"tfo", OPT_TFO, '-', "Connect using TCP Fast Open"},
#endif
    {"dane_tlsa_domain", OPT_DANE_TLSA_DOMAIN, 's', "DANE TLSA base domain"},
    {"dane_tlsa_rrdata", OPT_DANE_TLSA_RRDATA, 's',
     "DANE TLSA rrdata presentation form"},
    {"dane_ee_no_namechecks", OPT_DANE_EE_NO_NAME, '-',
     "Disable name checks when matching DANE-EE(3) TLSA records"},
    {"psk_identity", OPT_PSK_IDENTITY, 's', "PSK identity"},
    {"psk", OPT_PSK, 's', "PSK in hex (without 0x)"},
    {"psk_session", OPT_PSK_SESS, '<', "File to read PSK SSL session from"},
    {"name", OPT_PROTOHOST, 's',
     "Hostname to use for \"-starttls lmtp\", \"-starttls smtp\" or \"-starttls xmpp[-server]\""},

    OPT_SECTION("Session"),
    {"reconnect", OPT_RECONNECT, '-',
     "Drop and re-make the connection with the same Session-ID"},
    {"sess_out", OPT_SESS_OUT, '>', "File to write SSL session to"},
    {"sess_in", OPT_SESS_IN, '<', "File to read SSL session from"},

    OPT_SECTION("Input/Output"),
    {"crlf", OPT_CRLF, '-', "Convert LF from terminal into CRLF"},
    {"quiet", OPT_QUIET, '-', "No s_client output"},
    {"ign_eof", OPT_IGN_EOF, '-', "Ignore input eof (default when -quiet)"},
    {"no_ign_eof", OPT_NO_IGN_EOF, '-', "Don't ignore input eof"},
    {"starttls", OPT_STARTTLS, 's',
     "Use the appropriate STARTTLS command before starting TLS"},
    {"xmpphost", OPT_XMPPHOST, 's',
     "Alias of -name option for \"-starttls xmpp[-server]\""},
    {"brief", OPT_BRIEF, '-',
     "Restrict output to brief summary of connection parameters"},
    {"prexit", OPT_PREXIT, '-',
     "Print session information when the program exits"},
    {"no-interactive", OPT_NO_INTERACTIVE, '-',
     "Don't run the client in the interactive mode"},

    OPT_SECTION("Debug"),
    {"showcerts", OPT_SHOWCERTS, '-',
     "Show all certificates sent by the server"},
    {"debug", OPT_DEBUG, '-', "Extra output"},
    {"msg", OPT_MSG, '-', "Show protocol messages"},
    {"msgfile", OPT_MSGFILE, '>',
     "File to send output of -msg or -trace, instead of stdout"},
    {"nbio_test", OPT_NBIO_TEST, '-', "More ssl protocol testing"},
    {"state", OPT_STATE, '-', "Print the ssl states"},
    {"keymatexport", OPT_KEYMATEXPORT, 's',
     "Export keying material using label"},
    {"keymatexportlen", OPT_KEYMATEXPORTLEN, 'p',
     "Export len bytes of keying material; default 20"},
    {"security_debug", OPT_SECURITY_DEBUG, '-',
     "Enable security debug messages"},
    {"security_debug_verbose", OPT_SECURITY_DEBUG_VERBOSE, '-',
     "Output more security debug output"},
#ifndef OPENSSL_NO_SSL_TRACE
    {"trace", OPT_TRACE, '-', "Show trace output of protocol messages"},
#endif
#ifdef WATT32
    {"wdebug", OPT_WDEBUG, '-', "WATT-32 tcp debugging"},
#endif
    {"keylogfile", OPT_KEYLOG_FILE, '>', "Write TLS secrets to file"},
    {"nocommands", OPT_NOCMDS, '-', "Do not use interactive command letters"},
    {"adv", OPT_ADV, '-', "Advanced command mode"},
    {"servername", OPT_SERVERNAME, 's',
     "Set TLS extension servername (SNI) in ClientHello (default)"},
    {"noservername", OPT_NOSERVERNAME, '-',
     "Do not send the server name (SNI) extension in the ClientHello"},
    {"tlsextdebug", OPT_TLSEXTDEBUG, '-',
     "Hex dump of all TLS extensions received"},
    {"ignore_unexpected_eof", OPT_IGNORE_UNEXPECTED_EOF, '-',
     "Do not treat lack of close_notify from a peer as an error"},
#ifndef OPENSSL_NO_OCSP
    {"status", OPT_STATUS, '-', "Request certificate status from server"},
#endif
    {"serverinfo", OPT_SERVERINFO, 's',
     "types  Send empty ClientHello extensions (comma-separated numbers)"},
    {"alpn", OPT_ALPN, 's',
     "Enable ALPN extension, considering named protocols supported (comma-separated list)"},
    {"async", OPT_ASYNC, '-', "Support asynchronous operation"},
    {"nbio", OPT_NBIO, '-', "Use non-blocking IO"},

    OPT_SECTION("Protocol and version"),
#ifndef OPENSSL_NO_SSL3
    {"ssl3", OPT_SSL3, '-', "Just use SSLv3"},
#endif
#ifndef OPENSSL_NO_TLS1
    {"tls1", OPT_TLS1, '-', "Just use TLSv1"},
#endif
#ifndef OPENSSL_NO_TLS1_1
    {"tls1_1", OPT_TLS1_1, '-', "Just use TLSv1.1"},
#endif
#ifndef OPENSSL_NO_TLS1_2
    {"tls1_2", OPT_TLS1_2, '-', "Just use TLSv1.2"},
#endif
#ifndef OPENSSL_NO_TLS1_3
    {"tls1_3", OPT_TLS1_3, '-', "Just use TLSv1.3"},
#endif
#ifndef OPENSSL_NO_DTLS
    {"dtls", OPT_DTLS, '-', "Use any version of DTLS"},
    {"quic", OPT_QUIC, '-', "Use QUIC"},
    {"timeout", OPT_TIMEOUT, '-',
     "Enable send/receive timeout on DTLS connections"},
    {"mtu", OPT_MTU, 'p', "Set the link layer MTU"},
#endif
#ifndef OPENSSL_NO_DTLS1
    {"dtls1", OPT_DTLS1, '-', "Just use DTLSv1"},
#endif
#ifndef OPENSSL_NO_DTLS1_2
    {"dtls1_2", OPT_DTLS1_2, '-', "Just use DTLSv1.2"},
#endif
#ifndef OPENSSL_NO_SCTP
    {"sctp", OPT_SCTP, '-', "Use SCTP"},
    {"sctp_label_bug", OPT_SCTP_LABEL_BUG, '-', "Enable SCTP label length bug"},
#endif
#ifndef OPENSSL_NO_NEXTPROTONEG
    {"nextprotoneg", OPT_NEXTPROTONEG, 's',
     "Enable NPN extension, considering named protocols supported (comma-separated list)"},
#endif
    {"early_data", OPT_EARLY_DATA, '<', "File to send as early data"},
    {"enable_pha", OPT_ENABLE_PHA, '-', "Enable post-handshake-authentication"},
    {"enable_server_rpk", OPT_ENABLE_SERVER_RPK, '-', "Enable raw public keys (RFC7250) from the server"},
    {"enable_client_rpk", OPT_ENABLE_CLIENT_RPK, '-', "Enable raw public keys (RFC7250) from the client"},
#ifndef OPENSSL_NO_SRTP
    {"use_srtp", OPT_USE_SRTP, 's',
     "Offer SRTP key management with a colon-separated profile list"},
#endif
#ifndef OPENSSL_NO_SRP
    {"srpuser", OPT_SRPUSER, 's', "(deprecated) SRP authentication for 'user'"},
    {"srppass", OPT_SRPPASS, 's', "(deprecated) Password for 'user'"},
    {"srp_lateuser", OPT_SRP_LATEUSER, '-',
     "(deprecated) SRP username into second ClientHello message"},
    {"srp_moregroups", OPT_SRP_MOREGROUPS, '-',
     "(deprecated) Tolerate other than the known g N values."},
    {"srp_strength", OPT_SRP_STRENGTH, 'p',
     "(deprecated) Minimal length in bits for N"},
#endif
#ifndef OPENSSL_NO_KTLS
    {"ktls", OPT_KTLS, '-', "Enable Kernel TLS for sending and receiving"},
#endif

    OPT_R_OPTIONS,
    OPT_S_OPTIONS,
    OPT_V_OPTIONS,
    {"CRL", OPT_CRL, '<', "CRL file to use"},
    {"crl_download", OPT_CRL_DOWNLOAD, '-', "Download CRL from distribution points"},
    {"CRLform", OPT_CRLFORM, 'F', "CRL format (PEM or DER); default PEM"},
    {"verify_return_error", OPT_VERIFY_RET_ERROR, '-',
     "Close connection on verification error"},
    {"verify_quiet", OPT_VERIFY_QUIET, '-', "Restrict verify output to errors"},
    {"chainCAfile", OPT_CHAINCAFILE, '<',
     "CA file for certificate chain (PEM format)"},
    {"chainCApath", OPT_CHAINCAPATH, '/',
     "Use dir as certificate store path to build CA certificate chain"},
    {"chainCAstore", OPT_CHAINCASTORE, ':',
     "CA store URI for certificate chain"},
    {"verifyCAfile", OPT_VERIFYCAFILE, '<',
     "CA file for certificate verification (PEM format)"},
    {"verifyCApath", OPT_VERIFYCAPATH, '/',
     "Use dir as certificate store path to verify CA certificate"},
    {"verifyCAstore", OPT_VERIFYCASTORE, ':',
     "CA store URI for certificate verification"},
    OPT_X_OPTIONS,
    OPT_PROV_OPTIONS,

    OPT_PARAMETERS(),
    {"host:port", 0, 0, "Where to connect; same as -connect option"},
    {NULL}
};

typedef enum PROTOCOL_choice {
    PROTO_OFF,
    PROTO_SMTP,
    PROTO_POP3,
    PROTO_IMAP,
    PROTO_FTP,
    PROTO_TELNET,
    PROTO_XMPP,
    PROTO_XMPP_SERVER,
    PROTO_IRC,
    PROTO_MYSQL,
    PROTO_POSTGRES,
    PROTO_LMTP,
    PROTO_NNTP,
    PROTO_SIEVE,
    PROTO_LDAP
} PROTOCOL_CHOICE;

static const OPT_PAIR services[] = {
    {"smtp", PROTO_SMTP},
    {"pop3", PROTO_POP3},
    {"imap", PROTO_IMAP},
    {"ftp", PROTO_FTP},
    {"xmpp", PROTO_XMPP},
    {"xmpp-server", PROTO_XMPP_SERVER},
    {"telnet", PROTO_TELNET},
    {"irc", PROTO_IRC},
    {"mysql", PROTO_MYSQL},
    {"postgres", PROTO_POSTGRES},
    {"lmtp", PROTO_LMTP},
    {"nntp", PROTO_NNTP},
    {"sieve", PROTO_SIEVE},
    {"ldap", PROTO_LDAP},
    {NULL, 0}
};

#define IS_INET_FLAG(o) \
 (o == OPT_4 || o == OPT_6 || o == OPT_HOST || o == OPT_PORT || o == OPT_CONNECT)
#define IS_UNIX_FLAG(o) (o == OPT_UNIX)

#define IS_PROT_FLAG(o) \
 (o == OPT_SSL3 || o == OPT_TLS1 || o == OPT_TLS1_1 || o == OPT_TLS1_2 \
  || o == OPT_TLS1_3 || o == OPT_DTLS || o == OPT_DTLS1 || o == OPT_DTLS1_2 \
  || o == OPT_QUIC)

/* Free |*dest| and optionally set it to a copy of |source|. */
static void freeandcopy(char **dest, const char *source)
{
    OPENSSL_free(*dest);
    *dest = NULL;
    if (source != NULL)
        *dest = OPENSSL_strdup(source);
}

static int new_session_cb(SSL *s, SSL_SESSION *sess)
{

    if (sess_out != NULL) {
        BIO *stmp = BIO_new_file(sess_out, "w");

        if (stmp == NULL) {
            BIO_printf(bio_err, "Error writing session file %s\n", sess_out);
        } else {
            PEM_write_bio_SSL_SESSION(stmp, sess);
            BIO_free(stmp);
        }
    }

    /*
     * Session data gets dumped on connection for TLSv1.2 and below, and on
     * arrival of the NewSessionTicket for TLSv1.3.
     */
    if (SSL_version(s) == TLS1_3_VERSION) {
        BIO_printf(bio_c_out,
                   "---\nPost-Handshake New Session Ticket arrived:\n");
        SSL_SESSION_print(bio_c_out, sess);
        BIO_printf(bio_c_out, "---\n");
    }

    /*
     * We always return a "fail" response so that the session gets freed again
     * because we haven't used the reference.
     */
    return 0;
}

int s_client_main(int argc, char **argv)
{
    BIO *sbio;
    EVP_PKEY *key = NULL;
    SSL *con = NULL;
    SSL_CTX *ctx = NULL;
    STACK_OF(X509) *chain = NULL;
    X509 *cert = NULL;
    X509_VERIFY_PARAM *vpm = NULL;
    SSL_EXCERT *exc = NULL;
    SSL_CONF_CTX *cctx = NULL;
    STACK_OF(OPENSSL_STRING) *ssl_args = NULL;
    char *dane_tlsa_domain = NULL;
    STACK_OF(OPENSSL_STRING) *dane_tlsa_rrset = NULL;
    int dane_ee_no_name = 0;
    STACK_OF(X509_CRL) *crls = NULL;
    const SSL_METHOD *meth = TLS_client_method();
    const char *CApath = NULL, *CAfile = NULL, *CAstore = NULL;
    char *cbuf = NULL, *sbuf = NULL, *mbuf = NULL;
    char *proxystr = NULL, *proxyuser = NULL;
    char *proxypassarg = NULL, *proxypass = NULL;
    char *connectstr = NULL, *bindstr = NULL;
    char *cert_file = NULL, *key_file = NULL, *chain_file = NULL;
    char *chCApath = NULL, *chCAfile = NULL, *chCAstore = NULL, *host = NULL;
    char *thost = NULL, *tport = NULL;
    char *port = NULL;
    char *bindhost = NULL, *bindport = NULL;
    char *passarg = NULL, *pass = NULL;
    char *vfyCApath = NULL, *vfyCAfile = NULL, *vfyCAstore = NULL;
    char *ReqCAfile = NULL;
    char *sess_in = NULL, *crl_file = NULL, *p;
    const char *protohost = NULL;
    struct timeval timeout, *timeoutp;
    fd_set readfds, writefds;
    int noCApath = 0, noCAfile = 0, noCAstore = 0;
    int build_chain = 0, cert_format = FORMAT_UNDEF;
    size_t cbuf_len, cbuf_off;
    int key_format = FORMAT_UNDEF, crlf = 0, full_log = 1, mbuf_len = 0;
    int prexit = 0;
    int nointeractive = 0;
    int sdebug = 0;
    int reconnect = 0, verify = SSL_VERIFY_NONE, vpmtouched = 0;
    int ret = 1, in_init = 1, i, nbio_test = 0, sock = -1, k, width, state = 0;
    int sbuf_len, sbuf_off, cmdmode = USER_DATA_MODE_BASIC;
    int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM, protocol = 0;
    int starttls_proto = PROTO_OFF, crl_format = FORMAT_UNDEF, crl_download = 0;
    int write_tty, read_tty, write_ssl, read_ssl, tty_on, ssl_pending;
    int first_loop;
#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS)
    int at_eof = 0;
#endif
    int read_buf_len = 0;
    int fallback_scsv = 0;
    OPTION_CHOICE o;
#ifndef OPENSSL_NO_DTLS
    int enable_timeouts = 0;
    long socket_mtu = 0;
#endif
#ifndef OPENSSL_NO_ENGINE
    ENGINE *ssl_client_engine = NULL;
#endif
    ENGINE *e = NULL;
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS)
    struct timeval tv;
#endif
    const char *servername = NULL;
    char *sname_alloc = NULL;
    int noservername = 0;
    const char *alpn_in = NULL;
    tlsextctx tlsextcbp = { NULL, 0 };
    const char *ssl_config = NULL;
#define MAX_SI_TYPES 100
    unsigned short serverinfo_types[MAX_SI_TYPES];
    int serverinfo_count = 0, start = 0, len;
#ifndef OPENSSL_NO_NEXTPROTONEG
    const char *next_proto_neg_in = NULL;
#endif
#ifndef OPENSSL_NO_SRP
    char *srppass = NULL;
    int srp_lateuser = 0;
    SRP_ARG srp_arg = { NULL, NULL, 0, 0, 0, 1024 };
#endif
#ifndef OPENSSL_NO_SRTP
    char *srtp_profiles = NULL;
#endif
#ifndef OPENSSL_NO_CT
    char *ctlog_file = NULL;
    int ct_validation = 0;
#endif
    int min_version = 0, max_version = 0, prot_opt = 0, no_prot_opt = 0;
    int async = 0;
    unsigned int max_send_fragment = 0;
    unsigned int split_send_fragment = 0, max_pipelines = 0;
    enum { use_inet, use_unix, use_unknown } connect_type = use_unknown;
    int count4or6 = 0;
    uint8_t maxfraglen = 0;
    int c_nbio = 0, c_msg = 0, c_ign_eof = 0, c_brief = 0;
    int c_tlsextdebug = 0;
#ifndef OPENSSL_NO_OCSP
    int c_status_req = 0;
#endif
    BIO *bio_c_msg = NULL;
    const char *keylog_file = NULL, *early_data_file = NULL;
    int isdtls = 0, isquic = 0;
    char *psksessf = NULL;
    int enable_pha = 0;
    int enable_client_rpk = 0;
#ifndef OPENSSL_NO_SCTP
    int sctp_label_bug = 0;
#endif
    int ignore_unexpected_eof = 0;
#ifndef OPENSSL_NO_KTLS
    int enable_ktls = 0;
#endif
    int tfo = 0;
    int is_infinite;
    BIO_ADDR *peer_addr = NULL;
    struct user_data_st user_data;

    FD_ZERO(&readfds);
    FD_ZERO(&writefds);
/* Known false-positive of MemorySanitizer. */
#if defined(__has_feature)
# if __has_feature(memory_sanitizer)
    __msan_unpoison(&readfds, sizeof(readfds));
    __msan_unpoison(&writefds, sizeof(writefds));
# endif
#endif

    c_quiet = 0;
    c_debug = 0;
    c_showcerts = 0;
    c_nbio = 0;
    port = OPENSSL_strdup(PORT);
    vpm = X509_VERIFY_PARAM_new();
    cctx = SSL_CONF_CTX_new();

    if (port == NULL || vpm == NULL || cctx == NULL) {
        BIO_printf(bio_err, "%s: out of memory\n", opt_getprog());
        goto end;
    }

    cbuf = app_malloc(BUFSIZZ, "cbuf");
    sbuf = app_malloc(BUFSIZZ, "sbuf");
    mbuf = app_malloc(BUFSIZZ, "mbuf");

    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT | SSL_CONF_FLAG_CMDLINE);

    prog = opt_init(argc, argv, s_client_options);
    while ((o = opt_next()) != OPT_EOF) {
        /* Check for intermixing flags. */
        if (connect_type == use_unix && IS_INET_FLAG(o)) {
            BIO_printf(bio_err,
                       "%s: Intermixed protocol flags (unix and internet domains)\n",
                       prog);
            goto end;
        }
        if (connect_type == use_inet && IS_UNIX_FLAG(o)) {
            BIO_printf(bio_err,
                       "%s: Intermixed protocol flags (internet and unix domains)\n",
                       prog);
            goto end;
        }

        if (IS_PROT_FLAG(o) && ++prot_opt > 1) {
            BIO_printf(bio_err, "Cannot supply multiple protocol flags\n");
            goto end;
        }
        if (IS_NO_PROT_FLAG(o))
            no_prot_opt++;
        if (prot_opt == 1 && no_prot_opt) {
            BIO_printf(bio_err,
                       "Cannot supply both a protocol flag and '-no_<prot>'\n");
            goto end;
        }

        switch (o) {
        case OPT_EOF:
        case OPT_ERR:
 opthelp:
            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
            goto end;
        case OPT_HELP:
            opt_help(s_client_options);
            ret = 0;
            goto end;
        case OPT_4:
            connect_type = use_inet;
            socket_family = AF_INET;
            count4or6++;
            break;
#ifdef AF_INET6
        case OPT_6:
            connect_type = use_inet;
            socket_family = AF_INET6;
            count4or6++;
            break;
#endif
        case OPT_HOST:
            connect_type = use_inet;
            freeandcopy(&host, opt_arg());
            break;
        case OPT_PORT:
            connect_type = use_inet;
            freeandcopy(&port, opt_arg());
            break;
        case OPT_CONNECT:
            connect_type = use_inet;
            freeandcopy(&connectstr, opt_arg());
            break;
        case OPT_BIND:
            freeandcopy(&bindstr, opt_arg());
            break;
        case OPT_PROXY:
            proxystr = opt_arg();
            break;
        case OPT_PROXY_USER:
            proxyuser = opt_arg();
            break;
        case OPT_PROXY_PASS:
            proxypassarg = opt_arg();
            break;
#ifdef AF_UNIX
        case OPT_UNIX:
            connect_type = use_unix;
            socket_family = AF_UNIX;
            freeandcopy(&host, opt_arg());
            break;
#endif
        case OPT_XMPPHOST:
            /* fall through, since this is an alias */
        case OPT_PROTOHOST:
            protohost = opt_arg();
            break;
        case OPT_VERIFY:
            verify = SSL_VERIFY_PEER;
            verify_args.depth = atoi(opt_arg());
            if (!c_quiet)
                BIO_printf(bio_err, "verify depth is %d\n", verify_args.depth);
            break;
        case OPT_CERT:
            cert_file = opt_arg();
            break;
        case OPT_NAMEOPT:
            if (!set_nameopt(opt_arg()))
                goto end;
            break;
        case OPT_CRL:
            crl_file = opt_arg();
            break;
        case OPT_CRL_DOWNLOAD:
            crl_download = 1;
            break;
        case OPT_SESS_OUT:
            sess_out = opt_arg();
            break;
        case OPT_SESS_IN:
            sess_in = opt_arg();
            break;
        case OPT_CERTFORM:
            if (!opt_format(opt_arg(), OPT_FMT_ANY, &cert_format))
                goto opthelp;
            break;
        case OPT_CRLFORM:
            if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &crl_format))
                goto opthelp;
            break;
        case OPT_VERIFY_RET_ERROR:
            verify = SSL_VERIFY_PEER;
            verify_args.return_error = 1;
            break;
        case OPT_VERIFY_QUIET:
            verify_args.quiet = 1;
            break;
        case OPT_BRIEF:
            c_brief = verify_args.quiet = c_quiet = 1;
            break;
        case OPT_S_CASES:
            if (ssl_args == NULL)
                ssl_args = sk_OPENSSL_STRING_new_null();
            if (ssl_args == NULL
                || !sk_OPENSSL_STRING_push(ssl_args, opt_flag())
                || !sk_OPENSSL_STRING_push(ssl_args, opt_arg())) {
                BIO_printf(bio_err, "%s: Memory allocation failure\n", prog);
                goto end;
            }
            break;
        case OPT_V_CASES:
            if (!opt_verify(o, vpm))
                goto end;
            vpmtouched++;
            break;
        case OPT_X_CASES:
            if (!args_excert(o, &exc))
                goto end;
            break;
        case OPT_IGNORE_UNEXPECTED_EOF:
            ignore_unexpected_eof = 1;
            break;
        case OPT_PREXIT:
            prexit = 1;
            break;
        case OPT_NO_INTERACTIVE:
            nointeractive = 1;
            break;
        case OPT_CRLF:
            crlf = 1;
            break;
        case OPT_QUIET:
            c_quiet = c_ign_eof = 1;
            break;
        case OPT_NBIO:
            c_nbio = 1;
            break;
        case OPT_NOCMDS:
            cmdmode = USER_DATA_MODE_NONE;
            break;
        case OPT_ADV:
            cmdmode = USER_DATA_MODE_ADVANCED;
            break;
        case OPT_ENGINE:
            e = setup_engine(opt_arg(), 1);
            break;
        case OPT_SSL_CLIENT_ENGINE:
#ifndef OPENSSL_NO_ENGINE
            ssl_client_engine = setup_engine(opt_arg(), 0);
            if (ssl_client_engine == NULL) {
                BIO_printf(bio_err, "Error getting client auth engine\n");
                goto opthelp;
            }
#endif
            break;
        case OPT_R_CASES:
            if (!opt_rand(o))
                goto end;
            break;
        case OPT_PROV_CASES:
            if (!opt_provider(o))
                goto end;
            break;
        case OPT_IGN_EOF:
            c_ign_eof = 1;
            break;
        case OPT_NO_IGN_EOF:
            c_ign_eof = 0;
            break;
        case OPT_DEBUG:
            c_debug = 1;
            break;
        case OPT_TLSEXTDEBUG:
            c_tlsextdebug = 1;
            break;
        case OPT_STATUS:
#ifndef OPENSSL_NO_OCSP
            c_status_req = 1;
#endif
            break;
        case OPT_WDEBUG:
#ifdef WATT32
            dbug_init();
#endif
            break;
        case OPT_MSG:
            c_msg = 1;
            break;
        case OPT_MSGFILE:
            bio_c_msg = BIO_new_file(opt_arg(), "w");
            if (bio_c_msg == NULL) {
                BIO_printf(bio_err, "Error writing file %s\n", opt_arg());
                goto end;
            }
            break;
        case OPT_TRACE:
#ifndef OPENSSL_NO_SSL_TRACE
            c_msg = 2;
#endif
            break;
        case OPT_SECURITY_DEBUG:
            sdebug = 1;
            break;
        case OPT_SECURITY_DEBUG_VERBOSE:
            sdebug = 2;
            break;
        case OPT_SHOWCERTS:
            c_showcerts = 1;
            break;
        case OPT_NBIO_TEST:
            nbio_test = 1;
            break;
        case OPT_STATE:
            state = 1;
            break;
        case OPT_PSK_IDENTITY:
            psk_identity = opt_arg();
            break;
        case OPT_PSK:
            for (p = psk_key = opt_arg(); *p; p++) {
                if (isxdigit(_UC(*p)))
                    continue;
                BIO_printf(bio_err, "Not a hex number '%s'\n", psk_key);
                goto end;
            }
            break;
        case OPT_PSK_SESS:
            psksessf = opt_arg();
            break;
#ifndef OPENSSL_NO_SRP
        case OPT_SRPUSER:
            srp_arg.srplogin = opt_arg();
            if (min_version < TLS1_VERSION)
                min_version = TLS1_VERSION;
            break;
        case OPT_SRPPASS:
            srppass = opt_arg();
            if (min_version < TLS1_VERSION)
                min_version = TLS1_VERSION;
            break;
        case OPT_SRP_STRENGTH:
            srp_arg.strength = atoi(opt_arg());
            BIO_printf(bio_err, "SRP minimal length for N is %d\n",
                       srp_arg.strength);
            if (min_version < TLS1_VERSION)
                min_version = TLS1_VERSION;
            break;
        case OPT_SRP_LATEUSER:
            srp_lateuser = 1;
            if (min_version < TLS1_VERSION)
                min_version = TLS1_VERSION;
            break;
        case OPT_SRP_MOREGROUPS:
            srp_arg.amp = 1;
            if (min_version < TLS1_VERSION)
                min_version = TLS1_VERSION;
            break;
#endif
        case OPT_SSL_CONFIG:
            ssl_config = opt_arg();
            break;
        case OPT_SSL3:
            min_version = SSL3_VERSION;
            max_version = SSL3_VERSION;
            socket_type = SOCK_STREAM;
#ifndef OPENSSL_NO_DTLS
            isdtls = 0;
#endif
            isquic = 0;
            break;
        case OPT_TLS1_3:
            min_version = TLS1_3_VERSION;
            max_version = TLS1_3_VERSION;
            socket_type = SOCK_STREAM;
#ifndef OPENSSL_NO_DTLS
            isdtls = 0;
#endif
            isquic = 0;
            break;
        case OPT_TLS1_2:
            min_version = TLS1_2_VERSION;
            max_version = TLS1_2_VERSION;
            socket_type = SOCK_STREAM;
#ifndef OPENSSL_NO_DTLS
            isdtls = 0;
#endif
            isquic = 0;
            break;
        case OPT_TLS1_1:
            min_version = TLS1_1_VERSION;
            max_version = TLS1_1_VERSION;
            socket_type = SOCK_STREAM;
#ifndef OPENSSL_NO_DTLS
            isdtls = 0;
#endif
            isquic = 0;
            break;
        case OPT_TLS1:
            min_version = TLS1_VERSION;
            max_version = TLS1_VERSION;
            socket_type = SOCK_STREAM;
#ifndef OPENSSL_NO_DTLS
            isdtls = 0;
#endif
            isquic = 0;
            break;
        case OPT_DTLS:
#ifndef OPENSSL_NO_DTLS
            meth = DTLS_client_method();
            socket_type = SOCK_DGRAM;
            isdtls = 1;
            isquic = 0;
#endif
            break;
        case OPT_DTLS1:
#ifndef OPENSSL_NO_DTLS1
            meth = DTLS_client_method();
            min_version = DTLS1_VERSION;
            max_version = DTLS1_VERSION;
            socket_type = SOCK_DGRAM;
            isdtls = 1;
            isquic = 0;
#endif
            break;
        case OPT_DTLS1_2:
#ifndef OPENSSL_NO_DTLS1_2
            meth = DTLS_client_method();
            min_version = DTLS1_2_VERSION;
            max_version = DTLS1_2_VERSION;
            socket_type = SOCK_DGRAM;
            isdtls = 1;
            isquic = 0;
#endif
            break;
        case OPT_QUIC:
#ifndef OPENSSL_NO_QUIC
            meth = OSSL_QUIC_client_method();
            min_version = 0;
            max_version = 0;
            socket_type = SOCK_DGRAM;
# ifndef OPENSSL_NO_DTLS
            isdtls = 0;
# endif
            isquic = 1;
#endif
            break;
        case OPT_SCTP:
#ifndef OPENSSL_NO_SCTP
            protocol = IPPROTO_SCTP;
#endif
            break;
        case OPT_SCTP_LABEL_BUG:
#ifndef OPENSSL_NO_SCTP
            sctp_label_bug = 1;
#endif
            break;
        case OPT_TIMEOUT:
#ifndef OPENSSL_NO_DTLS
            enable_timeouts = 1;
#endif
            break;
        case OPT_MTU:
#ifndef OPENSSL_NO_DTLS
            socket_mtu = atol(opt_arg());
#endif
            break;
        case OPT_FALLBACKSCSV:
            fallback_scsv = 1;
            break;
        case OPT_KEYFORM:
            if (!opt_format(opt_arg(), OPT_FMT_ANY, &key_format))
                goto opthelp;
            break;
        case OPT_PASS:
            passarg = opt_arg();
            break;
        case OPT_CERT_CHAIN:
            chain_file = opt_arg();
            break;
        case OPT_KEY:
            key_file = opt_arg();
            break;
        case OPT_RECONNECT:
            reconnect = 5;
            break;
        case OPT_CAPATH:
            CApath = opt_arg();
            break;
        case OPT_NOCAPATH:
            noCApath = 1;
            break;
        case OPT_CHAINCAPATH:
            chCApath = opt_arg();
            break;
        case OPT_VERIFYCAPATH:
            vfyCApath = opt_arg();
            break;
        case OPT_BUILD_CHAIN:
            build_chain = 1;
            break;
        case OPT_REQCAFILE:
            ReqCAfile = opt_arg();
            break;
        case OPT_CAFILE:
            CAfile = opt_arg();
            break;
        case OPT_NOCAFILE:
            noCAfile = 1;
            break;
#ifndef OPENSSL_NO_CT
        case OPT_NOCT:
            ct_validation = 0;
            break;
        case OPT_CT:
            ct_validation = 1;
            break;
        case OPT_CTLOG_FILE:
            ctlog_file = opt_arg();
            break;
#endif
        case OPT_CHAINCAFILE:
            chCAfile = opt_arg();
            break;
        case OPT_VERIFYCAFILE:
            vfyCAfile = opt_arg();
            break;
        case OPT_CASTORE:
            CAstore = opt_arg();
            break;
        case OPT_NOCASTORE:
            noCAstore = 1;
            break;
        case OPT_CHAINCASTORE:
            chCAstore = opt_arg();
            break;
        case OPT_VERIFYCASTORE:
            vfyCAstore = opt_arg();
            break;
        case OPT_DANE_TLSA_DOMAIN:
            dane_tlsa_domain = opt_arg();
            break;
        case OPT_DANE_TLSA_RRDATA:
            if (dane_tlsa_rrset == NULL)
                dane_tlsa_rrset = sk_OPENSSL_STRING_new_null();
            if (dane_tlsa_rrset == NULL ||
                !sk_OPENSSL_STRING_push(dane_tlsa_rrset, opt_arg())) {
                BIO_printf(bio_err, "%s: Memory allocation failure\n", prog);
                goto end;
            }
            break;
        case OPT_DANE_EE_NO_NAME:
            dane_ee_no_name = 1;
            break;
        case OPT_NEXTPROTONEG:
#ifndef OPENSSL_NO_NEXTPROTONEG
            next_proto_neg_in = opt_arg();
#endif
            break;
        case OPT_ALPN:
            alpn_in = opt_arg();
            break;
        case OPT_SERVERINFO:
            p = opt_arg();
            len = strlen(p);
            for (start = 0, i = 0; i <= len; ++i) {
                if (i == len || p[i] == ',') {
                    serverinfo_types[serverinfo_count] = atoi(p + start);
                    if (++serverinfo_count == MAX_SI_TYPES)
                        break;
                    start = i + 1;
                }
            }
            break;
        case OPT_STARTTLS:
            if (!opt_pair(opt_arg(), services, &starttls_proto))
                goto end;
            break;
        case OPT_TFO:
            tfo = 1;
            break;
        case OPT_SERVERNAME:
            servername = opt_arg();
            break;
        case OPT_NOSERVERNAME:
            noservername = 1;
            break;
        case OPT_USE_SRTP:
#ifndef OPENSSL_NO_SRTP
            srtp_profiles = opt_arg();
#endif
            break;
        case OPT_KEYMATEXPORT:
            keymatexportlabel = opt_arg();
            break;
        case OPT_KEYMATEXPORTLEN:
            keymatexportlen = atoi(opt_arg());
            break;
        case OPT_ASYNC:
            async = 1;
            break;
        case OPT_MAXFRAGLEN:
            len = atoi(opt_arg());
            switch (len) {
            case 512:
                maxfraglen = TLSEXT_max_fragment_length_512;
                break;
            case 1024:
                maxfraglen = TLSEXT_max_fragment_length_1024;
                break;
            case 2048:
                maxfraglen = TLSEXT_max_fragment_length_2048;
                break;
            case 4096:
                maxfraglen = TLSEXT_max_fragment_length_4096;
                break;
            default:
                BIO_printf(bio_err,
                           "%s: Max Fragment Len %u is out of permitted values",
                           prog, len);
                goto opthelp;
            }
            break;
        case OPT_MAX_SEND_FRAG:
            max_send_fragment = atoi(opt_arg());
            break;
        case OPT_SPLIT_SEND_FRAG:
            split_send_fragment = atoi(opt_arg());
            break;
        case OPT_MAX_PIPELINES:
            max_pipelines = atoi(opt_arg());
            break;
        case OPT_READ_BUF:
            read_buf_len = atoi(opt_arg());
            break;
        case OPT_KEYLOG_FILE:
            keylog_file = opt_arg();
            break;
        case OPT_EARLY_DATA:
            early_data_file = opt_arg();
            break;
        case OPT_ENABLE_PHA:
            enable_pha = 1;
            break;
        case OPT_KTLS:
#ifndef OPENSSL_NO_KTLS
            enable_ktls = 1;
#endif
            break;
        case OPT_ENABLE_SERVER_RPK:
            enable_server_rpk = 1;
            break;
        case OPT_ENABLE_CLIENT_RPK:
            enable_client_rpk = 1;
            break;
        }
    }

    /* Optional argument is connect string if -connect not used. */
    if (opt_num_rest() == 1) {
        /* Don't allow -connect and a separate argument. */
        if (connectstr != NULL) {
            BIO_printf(bio_err,
                       "%s: cannot provide both -connect option and target parameter\n",
                       prog);
            goto opthelp;
        }
        connect_type = use_inet;
        freeandcopy(&connectstr, *opt_rest());
    } else if (!opt_check_rest_arg(NULL)) {
        goto opthelp;
    }
    if (!app_RAND_load())
        goto end;

    if (c_ign_eof)
        cmdmode = USER_DATA_MODE_NONE;

    if (count4or6 >= 2) {
        BIO_printf(bio_err, "%s: Can't use both -4 and -6\n", prog);
        goto opthelp;
    }
    if (noservername) {
        if (servername != NULL) {
            BIO_printf(bio_err,
                       "%s: Can't use -servername and -noservername together\n",
                       prog);
            goto opthelp;
        }
        if (dane_tlsa_domain != NULL) {
            BIO_printf(bio_err,
               "%s: Can't use -dane_tlsa_domain and -noservername together\n",
               prog);
            goto opthelp;
        }
    }

#ifndef OPENSSL_NO_NEXTPROTONEG
    if (min_version == TLS1_3_VERSION && next_proto_neg_in != NULL) {
        BIO_printf(bio_err, "Cannot supply -nextprotoneg with TLSv1.3\n");
        goto opthelp;
    }
#endif

    if (connectstr != NULL) {
        int res;
        char *tmp_host = host, *tmp_port = port;

        res = BIO_parse_hostserv(connectstr, &host, &port, BIO_PARSE_PRIO_HOST);
        if (tmp_host != host)
            OPENSSL_free(tmp_host);
        if (tmp_port != port)
            OPENSSL_free(tmp_port);
        if (!res) {
            BIO_printf(bio_err,
                       "%s: -connect argument or target parameter malformed or ambiguous\n",
                       prog);
            goto end;
        }
    }

    if (proxystr != NULL) {
#ifndef OPENSSL_NO_HTTP
        int res;
        char *tmp_host = host, *tmp_port = port;

        if (host == NULL || port == NULL) {
            BIO_printf(bio_err, "%s: -proxy requires use of -connect or target parameter\n", prog);
            goto opthelp;
        }

        if (servername == NULL && !noservername) {
            servername = sname_alloc = OPENSSL_strdup(host);
            if (sname_alloc == NULL) {
                BIO_printf(bio_err, "%s: out of memory\n", prog);
                goto end;
            }
        }

        /* Retain the original target host:port for use in the HTTP proxy connect string */
        thost = OPENSSL_strdup(host);
        tport = OPENSSL_strdup(port);
        if (thost == NULL || tport == NULL) {
            BIO_printf(bio_err, "%s: out of memory\n", prog);
            goto end;
        }

        res = BIO_parse_hostserv(proxystr, &host, &port, BIO_PARSE_PRIO_HOST);
        if (tmp_host != host)
            OPENSSL_free(tmp_host);
        if (tmp_port != port)
            OPENSSL_free(tmp_port);
        if (!res) {
            BIO_printf(bio_err,
                       "%s: -proxy argument malformed or ambiguous\n", prog);
            goto end;
        }
#else
        BIO_printf(bio_err,
                   "%s: -proxy not supported in no-http build\n", prog);
	goto end;
#endif
    }


    if (bindstr != NULL) {
        int res;
        res = BIO_parse_hostserv(bindstr, &bindhost, &bindport,
                                 BIO_PARSE_PRIO_HOST);
        if (!res) {
            BIO_printf(bio_err,
                       "%s: -bind argument parameter malformed or ambiguous\n",
                       prog);
            goto end;
        }
    }

#ifdef AF_UNIX
    if (socket_family == AF_UNIX && socket_type != SOCK_STREAM) {
        BIO_printf(bio_err,
                   "Can't use unix sockets and datagrams together\n");
        goto end;
    }
#endif

#ifndef OPENSSL_NO_SCTP
    if (protocol == IPPROTO_SCTP) {
        if (socket_type != SOCK_DGRAM) {
            BIO_printf(bio_err, "Can't use -sctp without DTLS\n");
            goto end;
        }
        /* SCTP is unusual. It uses DTLS over a SOCK_STREAM protocol */
        socket_type = SOCK_STREAM;
    }
#endif

#if !defined(OPENSSL_NO_NEXTPROTONEG)
    next_proto.status = -1;
    if (next_proto_neg_in) {
        next_proto.data =
            next_protos_parse(&next_proto.len, next_proto_neg_in);
        if (next_proto.data == NULL) {
            BIO_printf(bio_err, "Error parsing -nextprotoneg argument\n");
            goto end;
        }
    } else
        next_proto.data = NULL;
#endif

    if (!app_passwd(passarg, NULL, &pass, NULL)) {
        BIO_printf(bio_err, "Error getting private key password\n");
        goto end;
    }

    if (!app_passwd(proxypassarg, NULL, &proxypass, NULL)) {
        BIO_printf(bio_err, "Error getting proxy password\n");
        goto end;
    }

    if (proxypass != NULL && proxyuser == NULL) {
        BIO_printf(bio_err, "Error: Must specify proxy_user with proxy_pass\n");
        goto end;
    }

    if (key_file == NULL)
        key_file = cert_file;

    if (key_file != NULL) {
        key = load_key(key_file, key_format, 0, pass, e,
                       "client certificate private key");
        if (key == NULL)
            goto end;
    }

    if (cert_file != NULL) {
        cert = load_cert_pass(cert_file, cert_format, 1, pass,
                              "client certificate");
        if (cert == NULL)
            goto end;
    }

    if (chain_file != NULL) {
        if (!load_certs(chain_file, 0, &chain, pass, "client certificate chain"))
            goto end;
    }

    if (crl_file != NULL) {
        X509_CRL *crl;
        crl = load_crl(crl_file, crl_format, 0, "CRL");
        if (crl == NULL)
            goto end;
        crls = sk_X509_CRL_new_null();
        if (crls == NULL || !sk_X509_CRL_push(crls, crl)) {
            BIO_puts(bio_err, "Error adding CRL\n");
            ERR_print_errors(bio_err);
            X509_CRL_free(crl);
            goto end;
        }
    }

    if (!load_excert(&exc))
        goto end;

    if (bio_c_out == NULL) {
        if (c_quiet && !c_debug) {
            bio_c_out = BIO_new(BIO_s_null());
            if (c_msg && bio_c_msg == NULL) {
                bio_c_msg = dup_bio_out(FORMAT_TEXT);
                if (bio_c_msg == NULL) {
                    BIO_printf(bio_err, "Out of memory\n");
                    goto end;
                }
            }
        } else {
            bio_c_out = dup_bio_out(FORMAT_TEXT);
        }

        if (bio_c_out == NULL) {
            BIO_printf(bio_err, "Unable to create BIO\n");
            goto end;
        }
    }
#ifndef OPENSSL_NO_SRP
    if (!app_passwd(srppass, NULL, &srp_arg.srppassin, NULL)) {
        BIO_printf(bio_err, "Error getting password\n");
        goto end;
    }
#endif

    ctx = SSL_CTX_new_ex(app_get0_libctx(), app_get0_propq(), meth);
    if (ctx == NULL) {
        ERR_print_errors(bio_err);
        goto end;
    }

    SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);

    if (sdebug)
        ssl_ctx_security_debug(ctx, sdebug);

    if (!config_ctx(cctx, ssl_args, ctx))
        goto end;

    if (ssl_config != NULL) {
        if (SSL_CTX_config(ctx, ssl_config) == 0) {
            BIO_printf(bio_err, "Error using configuration \"%s\"\n",
                       ssl_config);
            ERR_print_errors(bio_err);
            goto end;
        }
    }

#ifndef OPENSSL_NO_SCTP
    if (protocol == IPPROTO_SCTP && sctp_label_bug == 1)
        SSL_CTX_set_mode(ctx, SSL_MODE_DTLS_SCTP_LABEL_LENGTH_BUG);
#endif

    if (min_version != 0
        && SSL_CTX_set_min_proto_version(ctx, min_version) == 0)
        goto end;
    if (max_version != 0
        && SSL_CTX_set_max_proto_version(ctx, max_version) == 0)
        goto end;

    if (ignore_unexpected_eof)
        SSL_CTX_set_options(ctx, SSL_OP_IGNORE_UNEXPECTED_EOF);
#ifndef OPENSSL_NO_KTLS
    if (enable_ktls)
        SSL_CTX_set_options(ctx, SSL_OP_ENABLE_KTLS);
#endif

    if (vpmtouched && !SSL_CTX_set1_param(ctx, vpm)) {
        BIO_printf(bio_err, "Error setting verify params\n");
        ERR_print_errors(bio_err);
        goto end;
    }

    if (async) {
        SSL_CTX_set_mode(ctx, SSL_MODE_ASYNC);
    }

    if (max_send_fragment > 0
        && !SSL_CTX_set_max_send_fragment(ctx, max_send_fragment)) {
        BIO_printf(bio_err, "%s: Max send fragment size %u is out of permitted range\n",
                   prog, max_send_fragment);
        goto end;
    }

    if (split_send_fragment > 0
        && !SSL_CTX_set_split_send_fragment(ctx, split_send_fragment)) {
        BIO_printf(bio_err, "%s: Split send fragment size %u is out of permitted range\n",
                   prog, split_send_fragment);
        goto end;
    }

    if (max_pipelines > 0
        && !SSL_CTX_set_max_pipelines(ctx, max_pipelines)) {
        BIO_printf(bio_err, "%s: Max pipelines %u is out of permitted range\n",
                   prog, max_pipelines);
        goto end;
    }

    if (read_buf_len > 0) {
        SSL_CTX_set_default_read_buffer_len(ctx, read_buf_len);
    }

    if (maxfraglen > 0
            && !SSL_CTX_set_tlsext_max_fragment_length(ctx, maxfraglen)) {
        BIO_printf(bio_err,
                   "%s: Max Fragment Length code %u is out of permitted values"
                   "\n", prog, maxfraglen);
        goto end;
    }

    if (!ssl_load_stores(ctx,
                         vfyCApath, vfyCAfile, vfyCAstore,
                         chCApath, chCAfile, chCAstore,
                         crls, crl_download)) {
        BIO_printf(bio_err, "Error loading store locations\n");
        ERR_print_errors(bio_err);
        goto end;
    }
    if (ReqCAfile != NULL) {
        STACK_OF(X509_NAME) *nm = sk_X509_NAME_new_null();

        if (nm == NULL || !SSL_add_file_cert_subjects_to_stack(nm, ReqCAfile)) {
            sk_X509_NAME_pop_free(nm, X509_NAME_free);
            BIO_printf(bio_err, "Error loading CA names\n");
            ERR_print_errors(bio_err);
            goto end;
        }
        SSL_CTX_set0_CA_list(ctx, nm);
    }
#ifndef OPENSSL_NO_ENGINE
    if (ssl_client_engine) {
        if (!SSL_CTX_set_client_cert_engine(ctx, ssl_client_engine)) {
            BIO_puts(bio_err, "Error setting client auth engine\n");
            ERR_print_errors(bio_err);
            release_engine(ssl_client_engine);
            goto end;
        }
        release_engine(ssl_client_engine);
    }
#endif

#ifndef OPENSSL_NO_PSK
    if (psk_key != NULL) {
        if (c_debug)
            BIO_printf(bio_c_out, "PSK key given, setting client callback\n");
        SSL_CTX_set_psk_client_callback(ctx, psk_client_cb);
    }
#endif
    if (psksessf != NULL) {
        BIO *stmp = BIO_new_file(psksessf, "r");

        if (stmp == NULL) {
            BIO_printf(bio_err, "Can't open PSK session file %s\n", psksessf);
            ERR_print_errors(bio_err);
            goto end;
        }
        psksess = PEM_read_bio_SSL_SESSION(stmp, NULL, 0, NULL);
        BIO_free(stmp);
        if (psksess == NULL) {
            BIO_printf(bio_err, "Can't read PSK session file %s\n", psksessf);
            ERR_print_errors(bio_err);
            goto end;
        }
    }
    if (psk_key != NULL || psksess != NULL)
        SSL_CTX_set_psk_use_session_callback(ctx, psk_use_session_cb);

#ifndef OPENSSL_NO_SRTP
    if (srtp_profiles != NULL) {
        /* Returns 0 on success! */
        if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_profiles) != 0) {
            BIO_printf(bio_err, "Error setting SRTP profile\n");
            ERR_print_errors(bio_err);
            goto end;
        }
    }
#endif

    if (exc != NULL)
        ssl_ctx_set_excert(ctx, exc);

#if !defined(OPENSSL_NO_NEXTPROTONEG)
    if (next_proto.data != NULL)
        SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto);
#endif
    if (alpn_in) {
        size_t alpn_len;
        unsigned char *alpn = next_protos_parse(&alpn_len, alpn_in);

        if (alpn == NULL) {
            BIO_printf(bio_err, "Error parsing -alpn argument\n");
            goto end;
        }
        /* Returns 0 on success! */
        if (SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len) != 0) {
            BIO_printf(bio_err, "Error setting ALPN\n");
            goto end;
        }
        OPENSSL_free(alpn);
    }

    for (i = 0; i < serverinfo_count; i++) {
        if (!SSL_CTX_add_client_custom_ext(ctx,
                                           serverinfo_types[i],
                                           NULL, NULL, NULL,
                                           serverinfo_cli_parse_cb, NULL)) {
            BIO_printf(bio_err,
                       "Warning: Unable to add custom extension %u, skipping\n",
                       serverinfo_types[i]);
        }
    }

    if (state)
        SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback);

#ifndef OPENSSL_NO_CT
    /* Enable SCT processing, without early connection termination */
    if (ct_validation &&
        !SSL_CTX_enable_ct(ctx, SSL_CT_VALIDATION_PERMISSIVE)) {
        ERR_print_errors(bio_err);
        goto end;
    }

    if (!ctx_set_ctlog_list_file(ctx, ctlog_file)) {
        if (ct_validation) {
            ERR_print_errors(bio_err);
            goto end;
        }

        /*
         * If CT validation is not enabled, the log list isn't needed so don't
         * show errors or abort. We try to load it regardless because then we
         * can show the names of the logs any SCTs came from (SCTs may be seen
         * even with validation disabled).
         */
        ERR_clear_error();
    }
#endif

    SSL_CTX_set_verify(ctx, verify, verify_callback);

    if (!ctx_set_verify_locations(ctx, CAfile, noCAfile, CApath, noCApath,
                                  CAstore, noCAstore)) {
        ERR_print_errors(bio_err);
        goto end;
    }

    ssl_ctx_add_crls(ctx, crls, crl_download);

    if (!set_cert_key_stuff(ctx, cert, key, chain, build_chain))
        goto end;

    if (!noservername) {
        tlsextcbp.biodebug = bio_err;
        SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
        SSL_CTX_set_tlsext_servername_arg(ctx, &tlsextcbp);
    }
#ifndef OPENSSL_NO_SRP
    if (srp_arg.srplogin != NULL
            && !set_up_srp_arg(ctx, &srp_arg, srp_lateuser, c_msg, c_debug))
        goto end;
# endif

    if (dane_tlsa_domain != NULL) {
        if (SSL_CTX_dane_enable(ctx) <= 0) {
            BIO_printf(bio_err,
                       "%s: Error enabling DANE TLSA authentication.\n",
                       prog);
            ERR_print_errors(bio_err);
            goto end;
        }
    }

    /*
     * In TLSv1.3 NewSessionTicket messages arrive after the handshake and can
     * come at any time. Therefore, we use a callback to write out the session
     * when we know about it. This approach works for < TLSv1.3 as well.
     */
    SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT
                                        | SSL_SESS_CACHE_NO_INTERNAL_STORE);
    SSL_CTX_sess_set_new_cb(ctx, new_session_cb);

    if (set_keylog_file(ctx, keylog_file))
        goto end;

    con = SSL_new(ctx);
    if (con == NULL)
        goto end;

    if (enable_pha)
        SSL_set_post_handshake_auth(con, 1);

    if (enable_client_rpk)
        if (!SSL_set1_client_cert_type(con, cert_type_rpk, sizeof(cert_type_rpk))) {
            BIO_printf(bio_err, "Error setting client certificate types\n");
            goto end;
        }
    if (enable_server_rpk) {
        if (!SSL_set1_server_cert_type(con, cert_type_rpk, sizeof(cert_type_rpk))) {
            BIO_printf(bio_err, "Error setting server certificate types\n");
            goto end;
        }
    }

    if (sess_in != NULL) {
        SSL_SESSION *sess;
        BIO *stmp = BIO_new_file(sess_in, "r");
        if (stmp == NULL) {
            BIO_printf(bio_err, "Can't open session file %s\n", sess_in);
            ERR_print_errors(bio_err);
            goto end;
        }
        sess = PEM_read_bio_SSL_SESSION(stmp, NULL, 0, NULL);
        BIO_free(stmp);
        if (sess == NULL) {
            BIO_printf(bio_err, "Can't open session file %s\n", sess_in);
            ERR_print_errors(bio_err);
            goto end;
        }
        if (!SSL_set_session(con, sess)) {
            BIO_printf(bio_err, "Can't set session\n");
            ERR_print_errors(bio_err);
            goto end;
        }

        SSL_SESSION_free(sess);
    }

    if (fallback_scsv)
        SSL_set_mode(con, SSL_MODE_SEND_FALLBACK_SCSV);

    if (!noservername && (servername != NULL || dane_tlsa_domain == NULL)) {
        if (servername == NULL) {
            if (host == NULL || is_dNS_name(host))
                servername = (host == NULL) ? "localhost" : host;
        }
        if (servername != NULL && !SSL_set_tlsext_host_name(con, servername)) {
            BIO_printf(bio_err, "Unable to set TLS servername extension.\n");
            ERR_print_errors(bio_err);
            goto end;
        }
    }

    if (dane_tlsa_domain != NULL) {
        if (SSL_dane_enable(con, dane_tlsa_domain) <= 0) {
            BIO_printf(bio_err, "%s: Error enabling DANE TLSA "
                       "authentication.\n", prog);
            ERR_print_errors(bio_err);
            goto end;
        }
        if (dane_tlsa_rrset == NULL) {
            BIO_printf(bio_err, "%s: DANE TLSA authentication requires at "
                       "least one -dane_tlsa_rrdata option.\n", prog);
            goto end;
        }
        if (tlsa_import_rrset(con, dane_tlsa_rrset) <= 0) {
            BIO_printf(bio_err, "%s: Failed to import any TLSA "
                       "records.\n", prog);
            goto end;
        }
        if (dane_ee_no_name)
            SSL_dane_set_flags(con, DANE_FLAG_NO_DANE_EE_NAMECHECKS);
    } else if (dane_tlsa_rrset != NULL) {
        BIO_printf(bio_err, "%s: DANE TLSA authentication requires the "
                   "-dane_tlsa_domain option.\n", prog);
        goto end;
    }
#ifndef OPENSSL_NO_DTLS
    if (isdtls && tfo) {
        BIO_printf(bio_err, "%s: DTLS does not support the -tfo option\n", prog);
        goto end;
    }
#endif
#ifndef OPENSSL_NO_QUIC
    if (isquic && tfo) {
        BIO_printf(bio_err, "%s: QUIC does not support the -tfo option\n", prog);
        goto end;
    }
    if (isquic && alpn_in == NULL) {
        BIO_printf(bio_err, "%s: QUIC requires ALPN to be specified (e.g. \"h3\" for HTTP/3) via the -alpn option\n", prog);
        goto end;
    }
#endif

    if (tfo)
        BIO_printf(bio_c_out, "Connecting via TFO\n");
 re_start:
    /* peer_addr might be set from previous connections */
    BIO_ADDR_free(peer_addr);
    peer_addr = NULL;
    if (init_client(&sock, host, port, bindhost, bindport, socket_family,
                    socket_type, protocol, tfo, !isquic, &peer_addr) == 0) {
        BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error());
        BIO_closesocket(sock);
        goto end;
    }
    BIO_printf(bio_c_out, "CONNECTED(%08X)\n", sock);

    /*
     * QUIC always uses a non-blocking socket - and we have to switch on
     * non-blocking mode at the SSL level
     */
    if (c_nbio || isquic) {
        if (!BIO_socket_nbio(sock, 1)) {
            ERR_print_errors(bio_err);
            goto end;
        }
        if (c_nbio) {
            if (isquic && !SSL_set_blocking_mode(con, 0))
                goto end;
            BIO_printf(bio_c_out, "Turned on non blocking io\n");
        }
    }
#ifndef OPENSSL_NO_DTLS
    if (isdtls) {
        union BIO_sock_info_u peer_info;

#ifndef OPENSSL_NO_SCTP
        if (protocol == IPPROTO_SCTP)
            sbio = BIO_new_dgram_sctp(sock, BIO_NOCLOSE);
        else
#endif
            sbio = BIO_new_dgram(sock, BIO_NOCLOSE);

        if (sbio == NULL || (peer_info.addr = BIO_ADDR_new()) == NULL) {
            BIO_printf(bio_err, "memory allocation failure\n");
            BIO_free(sbio);
            BIO_closesocket(sock);
            goto end;
        }
        if (!BIO_sock_info(sock, BIO_SOCK_INFO_ADDRESS, &peer_info)) {
            BIO_printf(bio_err, "getsockname:errno=%d\n",
                       get_last_socket_error());
            BIO_free(sbio);
            BIO_ADDR_free(peer_info.addr);
            BIO_closesocket(sock);
            goto end;
        }

        (void)BIO_ctrl_set_connected(sbio, peer_info.addr);
        BIO_ADDR_free(peer_info.addr);
        peer_info.addr = NULL;

        if (enable_timeouts) {
            timeout.tv_sec = 0;
            timeout.tv_usec = DGRAM_RCV_TIMEOUT;
            BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);

            timeout.tv_sec = 0;
            timeout.tv_usec = DGRAM_SND_TIMEOUT;
            BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout);
        }

        if (socket_mtu) {
            if (socket_mtu < DTLS_get_link_min_mtu(con)) {
                BIO_printf(bio_err, "MTU too small. Must be at least %ld\n",
                           DTLS_get_link_min_mtu(con));
                BIO_free(sbio);
                goto shut;
            }
            SSL_set_options(con, SSL_OP_NO_QUERY_MTU);
            if (!DTLS_set_link_mtu(con, socket_mtu)) {
                BIO_printf(bio_err, "Failed to set MTU\n");
                BIO_free(sbio);
                goto shut;
            }
        } else {
            /* want to do MTU discovery */
            BIO_ctrl(sbio, BIO_CTRL_DGRAM_MTU_DISCOVER, 0, NULL);
        }
    } else
#endif /* OPENSSL_NO_DTLS */
#ifndef OPENSSL_NO_QUIC
    if (isquic) {
        sbio = BIO_new_dgram(sock, BIO_NOCLOSE);
        if (!SSL_set1_initial_peer_addr(con, peer_addr)) {
            BIO_printf(bio_err, "Failed to set the initial peer address\n");
            goto shut;
        }
    } else
#endif
        sbio = BIO_new_socket(sock, BIO_NOCLOSE);

    if (sbio == NULL) {
        BIO_printf(bio_err, "Unable to create BIO\n");
        ERR_print_errors(bio_err);
        BIO_closesocket(sock);
        goto end;
    }

    /* Now that we're using a BIO... */
    if (tfo) {
        (void)BIO_set_conn_address(sbio, peer_addr);
        (void)BIO_set_tfo(sbio, 1);
    }

    if (nbio_test) {
        BIO *test;

        test = BIO_new(BIO_f_nbio_test());
        if (test == NULL) {
            BIO_printf(bio_err, "Unable to create BIO\n");
            BIO_free(sbio);
            goto shut;
        }
        sbio = BIO_push(test, sbio);
    }

    if (c_debug) {
        BIO_set_callback_ex(sbio, bio_dump_callback);
        BIO_set_callback_arg(sbio, (char *)bio_c_out);
    }
    if (c_msg) {
#ifndef OPENSSL_NO_SSL_TRACE
        if (c_msg == 2)
            SSL_set_msg_callback(con, SSL_trace);
        else
#endif
            SSL_set_msg_callback(con, msg_cb);
        SSL_set_msg_callback_arg(con, bio_c_msg ? bio_c_msg : bio_c_out);
    }

    if (c_tlsextdebug) {
        SSL_set_tlsext_debug_callback(con, tlsext_cb);
        SSL_set_tlsext_debug_arg(con, bio_c_out);
    }
#ifndef OPENSSL_NO_OCSP
    if (c_status_req) {
        SSL_set_tlsext_status_type(con, TLSEXT_STATUSTYPE_ocsp);
        SSL_CTX_set_tlsext_status_cb(ctx, ocsp_resp_cb);
        SSL_CTX_set_tlsext_status_arg(ctx, bio_c_out);
    }
#endif

    SSL_set_bio(con, sbio, sbio);
    SSL_set_connect_state(con);

    /* ok, lets connect */
    if (fileno_stdin() > SSL_get_fd(con))
        width = fileno_stdin() + 1;
    else
        width = SSL_get_fd(con) + 1;

    read_tty = 1;
    write_tty = 0;
    tty_on = 0;
    read_ssl = 1;
    write_ssl = 1;
    first_loop = 1;

    cbuf_len = 0;
    cbuf_off = 0;
    sbuf_len = 0;
    sbuf_off = 0;

#ifndef OPENSSL_NO_HTTP
    if (proxystr != NULL) {
        /* Here we must use the connect string target host & port */
        if (!OSSL_HTTP_proxy_connect(sbio, thost, tport, proxyuser, proxypass,
                                     0 /* no timeout */, bio_err, prog))
            goto shut;
    }
#endif

    switch ((PROTOCOL_CHOICE) starttls_proto) {
    case PROTO_OFF:
        break;
    case PROTO_LMTP:
    case PROTO_SMTP:
        {
            /*
             * This is an ugly hack that does a lot of assumptions. We do
             * have to handle multi-line responses which may come in a single
             * packet or not. We therefore have to use BIO_gets() which does
             * need a buffering BIO. So during the initial chitchat we do
             * push a buffering BIO into the chain that is removed again
             * later on to not disturb the rest of the s_client operation.
             */
            int foundit = 0;
            BIO *fbio = BIO_new(BIO_f_buffer());

            if (fbio == NULL) {
                BIO_printf(bio_err, "Unable to create BIO\n");
                goto shut;
            }
            BIO_push(fbio, sbio);
            /* Wait for multi-line response to end from LMTP or SMTP */
            do {
                mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
            } while (mbuf_len > 3 && mbuf[3] == '-');
            if (protohost == NULL)
                protohost = "mail.example.com";
            if (starttls_proto == (int)PROTO_LMTP)
                BIO_printf(fbio, "LHLO %s\r\n", protohost);
            else
                BIO_printf(fbio, "EHLO %s\r\n", protohost);
            (void)BIO_flush(fbio);
            /*
             * Wait for multi-line response to end LHLO LMTP or EHLO SMTP
             * response.
             */
            do {
                mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
                if (strstr(mbuf, "STARTTLS"))
                    foundit = 1;
            } while (mbuf_len > 3 && mbuf[3] == '-');
            (void)BIO_flush(fbio);
            BIO_pop(fbio);
            BIO_free(fbio);
            if (!foundit)
                BIO_printf(bio_err,
                           "Didn't find STARTTLS in server response,"
                           " trying anyway...\n");
            BIO_printf(sbio, "STARTTLS\r\n");
            BIO_read(sbio, sbuf, BUFSIZZ);
        }
        break;
    case PROTO_POP3:
        {
            BIO_read(sbio, mbuf, BUFSIZZ);
            BIO_printf(sbio, "STLS\r\n");
            mbuf_len = BIO_read(sbio, sbuf, BUFSIZZ);
            if (mbuf_len < 0) {
                BIO_printf(bio_err, "BIO_read failed\n");
                goto end;
            }
        }
        break;
    case PROTO_IMAP:
        {
            int foundit = 0;
            BIO *fbio = BIO_new(BIO_f_buffer());

            if (fbio == NULL) {
                BIO_printf(bio_err, "Unable to create BIO\n");
                goto shut;
            }
            BIO_push(fbio, sbio);
            BIO_gets(fbio, mbuf, BUFSIZZ);
            /* STARTTLS command requires CAPABILITY... */
            BIO_printf(fbio, ". CAPABILITY\r\n");
            (void)BIO_flush(fbio);
            /* wait for multi-line CAPABILITY response */
            do {
                mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
                if (strstr(mbuf, "STARTTLS"))
                    foundit = 1;
            }
            while (mbuf_len > 3 && mbuf[0] != '.');
            (void)BIO_flush(fbio);
            BIO_pop(fbio);
            BIO_free(fbio);
            if (!foundit)
                BIO_printf(bio_err,
                           "Didn't find STARTTLS in server response,"
                           " trying anyway...\n");
            BIO_printf(sbio, ". STARTTLS\r\n");
            BIO_read(sbio, sbuf, BUFSIZZ);
        }
        break;
    case PROTO_FTP:
        {
            BIO *fbio = BIO_new(BIO_f_buffer());

            if (fbio == NULL) {
                BIO_printf(bio_err, "Unable to create BIO\n");
                goto shut;
            }
            BIO_push(fbio, sbio);
            /* wait for multi-line response to end from FTP */
            do {
                mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
            }
            while (mbuf_len > 3 && (!isdigit((unsigned char)mbuf[0]) || !isdigit((unsigned char)mbuf[1]) || !isdigit((unsigned char)mbuf[2]) || mbuf[3] != ' '));
            (void)BIO_flush(fbio);
            BIO_pop(fbio);
            BIO_free(fbio);
            BIO_printf(sbio, "AUTH TLS\r\n");
            BIO_read(sbio, sbuf, BUFSIZZ);
        }
        break;
    case PROTO_XMPP:
    case PROTO_XMPP_SERVER:
        {
            int seen = 0;
            BIO_printf(sbio, "<stream:stream "
                       "xmlns:stream='http://etherx.jabber.org/streams' "
                       "xmlns='jabber:%s' to='%s' version='1.0'>",
                       starttls_proto == PROTO_XMPP ? "client" : "server",
                       protohost ? protohost : host);
            seen = BIO_read(sbio, mbuf, BUFSIZZ);
            if (seen < 0) {
                BIO_printf(bio_err, "BIO_read failed\n");
                goto end;
            }
            mbuf[seen] = '\0';
            while (!strstr
                   (mbuf, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'")
                   && !strstr(mbuf,
                              "<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\""))
            {
                seen = BIO_read(sbio, mbuf, BUFSIZZ);

                if (seen <= 0)
                    goto shut;

                mbuf[seen] = '\0';
            }
            BIO_printf(sbio,
                       "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
            seen = BIO_read(sbio, sbuf, BUFSIZZ);
            if (seen < 0) {
                BIO_printf(bio_err, "BIO_read failed\n");
                goto shut;
            }
            sbuf[seen] = '\0';
            if (!strstr(sbuf, "<proceed"))
                goto shut;
            mbuf[0] = '\0';
        }
        break;
    case PROTO_TELNET:
        {
            static const unsigned char tls_do[] = {
                /* IAC    DO   START_TLS */
                   255,   253, 46
            };
            static const unsigned char tls_will[] = {
                /* IAC  WILL START_TLS */
                   255, 251, 46
            };
            static const unsigned char tls_follows[] = {
                /* IAC  SB   START_TLS FOLLOWS IAC  SE */
                   255, 250, 46,       1,      255, 240
            };
            int bytes;

            /* Telnet server should demand we issue START_TLS */
            bytes = BIO_read(sbio, mbuf, BUFSIZZ);
            if (bytes != 3 || memcmp(mbuf, tls_do, 3) != 0)
                goto shut;
            /* Agree to issue START_TLS and send the FOLLOWS sub-command */
            BIO_write(sbio, tls_will, 3);
            BIO_write(sbio, tls_follows, 6);
            (void)BIO_flush(sbio);
            /* Telnet server also sent the FOLLOWS sub-command */
            bytes = BIO_read(sbio, mbuf, BUFSIZZ);
            if (bytes != 6 || memcmp(mbuf, tls_follows, 6) != 0)
                goto shut;
        }
        break;
    case PROTO_IRC:
        {
            int numeric;
            BIO *fbio = BIO_new(BIO_f_buffer());

            if (fbio == NULL) {
                BIO_printf(bio_err, "Unable to create BIO\n");
                goto end;
            }
            BIO_push(fbio, sbio);
            BIO_printf(fbio, "STARTTLS\r\n");
            (void)BIO_flush(fbio);
            width = SSL_get_fd(con) + 1;

            do {
                numeric = 0;

                FD_ZERO(&readfds);
                openssl_fdset(SSL_get_fd(con), &readfds);
                timeout.tv_sec = S_CLIENT_IRC_READ_TIMEOUT;
                timeout.tv_usec = 0;
                /*
                 * If the IRCd doesn't respond within
                 * S_CLIENT_IRC_READ_TIMEOUT seconds, assume
                 * it doesn't support STARTTLS. Many IRCds
                 * will not give _any_ sort of response to a
                 * STARTTLS command when it's not supported.
                 */
                if (!BIO_get_buffer_num_lines(fbio)
                    && !BIO_pending(fbio)
                    && !BIO_pending(sbio)
                    && select(width, (void *)&readfds, NULL, NULL,
                              &timeout) < 1) {
                    BIO_printf(bio_err,
                               "Timeout waiting for response (%d seconds).\n",
                               S_CLIENT_IRC_READ_TIMEOUT);
                    break;
                }

                mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
                if (mbuf_len < 1 || sscanf(mbuf, "%*s %d", &numeric) != 1)
                    break;
                /* :example.net 451 STARTTLS :You have not registered */
                /* :example.net 421 STARTTLS :Unknown command */
                if ((numeric == 451 || numeric == 421)
                    && strstr(mbuf, "STARTTLS") != NULL) {
                    BIO_printf(bio_err, "STARTTLS not supported: %s", mbuf);
                    break;
                }
                if (numeric == 691) {
                    BIO_printf(bio_err, "STARTTLS negotiation failed: ");
                    ERR_print_errors(bio_err);
                    break;
                }
            } while (numeric != 670);

            (void)BIO_flush(fbio);
            BIO_pop(fbio);
            BIO_free(fbio);
            if (numeric != 670) {
                BIO_printf(bio_err, "Server does not support STARTTLS.\n");
                ret = 1;
                goto shut;
            }
        }
        break;
    case PROTO_MYSQL:
        {
            /* SSL request packet */
            static const unsigned char ssl_req[] = {
                /* payload_length,   sequence_id */
                   0x20, 0x00, 0x00, 0x01,
                /* payload */
                /* capability flags, CLIENT_SSL always set */
                   0x85, 0xae, 0x7f, 0x00,
                /* max-packet size */
                   0x00, 0x00, 0x00, 0x01,
                /* character set */
                   0x21,
                /* string[23] reserved (all [0]) */
                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
            };
            int bytes = 0;
            int ssl_flg = 0x800;
            int pos;
            const unsigned char *packet = (const unsigned char *)sbuf;

            /* Receiving Initial Handshake packet. */
            bytes = BIO_read(sbio, (void *)packet, BUFSIZZ);
            if (bytes < 0) {
                BIO_printf(bio_err, "BIO_read failed\n");
                goto shut;
            /* Packet length[3], Packet number[1] + minimum payload[17] */
            } else if (bytes < 21) {
                BIO_printf(bio_err, "MySQL packet too short.\n");
                goto shut;
            } else if (bytes != (4 + packet[0] +
                                 (packet[1] << 8) +
                                 (packet[2] << 16))) {
                BIO_printf(bio_err, "MySQL packet length does not match.\n");
                goto shut;
            /* protocol version[1] */
            } else if (packet[4] != 0xA) {
                BIO_printf(bio_err,
                           "Only MySQL protocol version 10 is supported.\n");
                goto shut;
            }

            pos = 5;
            /* server version[string+NULL] */
            for (;;) {
                if (pos >= bytes) {
                    BIO_printf(bio_err, "Cannot confirm server version. ");
                    goto shut;
                } else if (packet[pos++] == '\0') {
                    break;
                }
            }

            /* make sure we have at least 15 bytes left in the packet */
            if (pos + 15 > bytes) {
                BIO_printf(bio_err,
                           "MySQL server handshake packet is broken.\n");
                goto shut;
            }

            pos += 12; /* skip over conn id[4] + SALT[8] */
            if (packet[pos++] != '\0') { /* verify filler */
                BIO_printf(bio_err,
                           "MySQL packet is broken.\n");
                goto shut;
            }

            /* capability flags[2] */
            if (!((packet[pos] + (packet[pos + 1] << 8)) & ssl_flg)) {
                BIO_printf(bio_err, "MySQL server does not support SSL.\n");
                goto shut;
            }

            /* Sending SSL Handshake packet. */
            BIO_write(sbio, ssl_req, sizeof(ssl_req));
            (void)BIO_flush(sbio);
        }
        break;
    case PROTO_POSTGRES:
        {
            static const unsigned char ssl_request[] = {
                /* Length        SSLRequest */
                   0, 0, 0, 8,   4, 210, 22, 47
            };
            int bytes;

            /* Send SSLRequest packet */
            BIO_write(sbio, ssl_request, 8);
            (void)BIO_flush(sbio);

            /* Reply will be a single S if SSL is enabled */
            bytes = BIO_read(sbio, sbuf, BUFSIZZ);
            if (bytes != 1 || sbuf[0] != 'S')
                goto shut;
        }
        break;
    case PROTO_NNTP:
        {
            int foundit = 0;
            BIO *fbio = BIO_new(BIO_f_buffer());

            if (fbio == NULL) {
                BIO_printf(bio_err, "Unable to create BIO\n");
                goto end;
            }
            BIO_push(fbio, sbio);
            BIO_gets(fbio, mbuf, BUFSIZZ);
            /* STARTTLS command requires CAPABILITIES... */
            BIO_printf(fbio, "CAPABILITIES\r\n");
            (void)BIO_flush(fbio);
            BIO_gets(fbio, mbuf, BUFSIZZ);
            /* no point in trying to parse the CAPABILITIES response if there is none */
            if (strstr(mbuf, "101") != NULL) {
                /* wait for multi-line CAPABILITIES response */
                do {
                    mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
                    if (strstr(mbuf, "STARTTLS"))
                        foundit = 1;
                } while (mbuf_len > 1 && mbuf[0] != '.');
            }
            (void)BIO_flush(fbio);
            BIO_pop(fbio);
            BIO_free(fbio);
            if (!foundit)
                BIO_printf(bio_err,
                           "Didn't find STARTTLS in server response,"
                           " trying anyway...\n");
            BIO_printf(sbio, "STARTTLS\r\n");
            mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ);
            if (mbuf_len < 0) {
                BIO_printf(bio_err, "BIO_read failed\n");
                goto end;
            }
            mbuf[mbuf_len] = '\0';
            if (strstr(mbuf, "382") == NULL) {
                BIO_printf(bio_err, "STARTTLS failed: %s", mbuf);
                goto shut;
            }
        }
        break;
    case PROTO_SIEVE:
        {
            int foundit = 0;
            BIO *fbio = BIO_new(BIO_f_buffer());

            if (fbio == NULL) {
                BIO_printf(bio_err, "Unable to create BIO\n");
                goto end;
            }
            BIO_push(fbio, sbio);
            /* wait for multi-line response to end from Sieve */
            do {
                mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
                /*
                 * According to RFC 5804 § 1.7, capability
                 * is case-insensitive, make it uppercase
                 */
                if (mbuf_len > 1 && mbuf[0] == '"') {
                    make_uppercase(mbuf);
                    if (HAS_PREFIX(mbuf, "\"STARTTLS\""))
                        foundit = 1;
                }
            } while (mbuf_len > 1 && mbuf[0] == '"');
            (void)BIO_flush(fbio);
            BIO_pop(fbio);
            BIO_free(fbio);
            if (!foundit)
                BIO_printf(bio_err,
                           "Didn't find STARTTLS in server response,"
                           " trying anyway...\n");
            BIO_printf(sbio, "STARTTLS\r\n");
            mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ);
            if (mbuf_len < 0) {
                BIO_printf(bio_err, "BIO_read failed\n");
                goto end;
            }
            mbuf[mbuf_len] = '\0';
            if (mbuf_len < 2) {
                BIO_printf(bio_err, "STARTTLS failed: %s", mbuf);
                goto shut;
            }
            /*
             * According to RFC 5804 § 2.2, response codes are case-
             * insensitive, make it uppercase but preserve the response.
             */
            strncpy(sbuf, mbuf, 2);
            make_uppercase(sbuf);
            if (!HAS_PREFIX(sbuf, "OK")) {
                BIO_printf(bio_err, "STARTTLS not supported: %s", mbuf);
                goto shut;
            }
        }
        break;
    case PROTO_LDAP:
        {
            /* StartTLS Operation according to RFC 4511 */
            static char ldap_tls_genconf[] = "asn1=SEQUENCE:LDAPMessage\n"
                "[LDAPMessage]\n"
                "messageID=INTEGER:1\n"
                "extendedReq=EXPLICIT:23A,IMPLICIT:0C,"
                "FORMAT:ASCII,OCT:1.3.6.1.4.1.1466.20037\n";
            long errline = -1;
            char *genstr = NULL;
            int result = -1;
            ASN1_TYPE *atyp = NULL;
            BIO *ldapbio = BIO_new(BIO_s_mem());
            CONF *cnf = NCONF_new(NULL);

            if (ldapbio == NULL || cnf == NULL) {
                BIO_free(ldapbio);
                NCONF_free(cnf);
                goto end;
            }
            BIO_puts(ldapbio, ldap_tls_genconf);
            if (NCONF_load_bio(cnf, ldapbio, &errline) <= 0) {
                BIO_free(ldapbio);
                NCONF_free(cnf);
                if (errline <= 0) {
                    BIO_printf(bio_err, "NCONF_load_bio failed\n");
                    goto end;
                } else {
                    BIO_printf(bio_err, "Error on line %ld\n", errline);
                    goto end;
                }
            }
            BIO_free(ldapbio);
            genstr = NCONF_get_string(cnf, "default", "asn1");
            if (genstr == NULL) {
                NCONF_free(cnf);
                BIO_printf(bio_err, "NCONF_get_string failed\n");
                goto end;
            }
            atyp = ASN1_generate_nconf(genstr, cnf);
            if (atyp == NULL) {
                NCONF_free(cnf);
                BIO_printf(bio_err, "ASN1_generate_nconf failed\n");
                goto end;
            }
            NCONF_free(cnf);

            /* Send SSLRequest packet */
            BIO_write(sbio, atyp->value.sequence->data,
                      atyp->value.sequence->length);
            (void)BIO_flush(sbio);
            ASN1_TYPE_free(atyp);

            mbuf_len = BIO_read(sbio, mbuf, BUFSIZZ);
            if (mbuf_len < 0) {
                BIO_printf(bio_err, "BIO_read failed\n");
                goto end;
            }
            result = ldap_ExtendedResponse_parse(mbuf, mbuf_len);
            if (result < 0) {
                BIO_printf(bio_err, "ldap_ExtendedResponse_parse failed\n");
                goto shut;
            } else if (result > 0) {
                BIO_printf(bio_err, "STARTTLS failed, LDAP Result Code: %i\n",
                           result);
                goto shut;
            }
            mbuf_len = 0;
        }
        break;
    }

    if (early_data_file != NULL
            && ((SSL_get0_session(con) != NULL
                 && SSL_SESSION_get_max_early_data(SSL_get0_session(con)) > 0)
                || (psksess != NULL
                    && SSL_SESSION_get_max_early_data(psksess) > 0))) {
        BIO *edfile = BIO_new_file(early_data_file, "r");
        size_t readbytes, writtenbytes;
        int finish = 0;

        if (edfile == NULL) {
            BIO_printf(bio_err, "Cannot open early data file\n");
            goto shut;
        }

        while (!finish) {
            if (!BIO_read_ex(edfile, cbuf, BUFSIZZ, &readbytes))
                finish = 1;

            while (!SSL_write_early_data(con, cbuf, readbytes, &writtenbytes)) {
                switch (SSL_get_error(con, 0)) {
                case SSL_ERROR_WANT_WRITE:
                case SSL_ERROR_WANT_ASYNC:
                case SSL_ERROR_WANT_READ:
                    /* Just keep trying - busy waiting */
                    continue;
                default:
                    BIO_printf(bio_err, "Error writing early data\n");
                    BIO_free(edfile);
                    ERR_print_errors(bio_err);
                    goto shut;
                }
            }
        }

        BIO_free(edfile);
    }

    user_data_init(&user_data, con, cbuf, BUFSIZZ, cmdmode);
    for (;;) {
        FD_ZERO(&readfds);
        FD_ZERO(&writefds);

        if ((isdtls || isquic)
            && SSL_get_event_timeout(con, &timeout, &is_infinite)
            && !is_infinite)
            timeoutp = &timeout;
        else
            timeoutp = NULL;

        if (!SSL_is_init_finished(con) && SSL_total_renegotiations(con) == 0
                && SSL_get_key_update_type(con) == SSL_KEY_UPDATE_NONE) {
            in_init = 1;
            tty_on = 0;
        } else {
            tty_on = 1;
            if (in_init) {
                in_init = 0;
                if (c_brief) {
                    BIO_puts(bio_err, "CONNECTION ESTABLISHED\n");
                    print_ssl_summary(con);
                }

                print_stuff(bio_c_out, con, full_log);
                if (full_log > 0)
                    full_log--;

                if (starttls_proto) {
                    BIO_write(bio_err, mbuf, mbuf_len);
                    /* We don't need to know any more */
                    if (!reconnect)
                        starttls_proto = PROTO_OFF;
                }

                if (reconnect) {
                    reconnect--;
                    BIO_printf(bio_c_out,
                               "drop connection and then reconnect\n");
                    do_ssl_shutdown(con);
                    SSL_set_connect_state(con);
                    BIO_closesocket(SSL_get_fd(con));
                    goto re_start;
                }
            }
        }

        if (!write_ssl) {
            do {
                switch (user_data_process(&user_data, &cbuf_len, &cbuf_off)) {
                default:
                    BIO_printf(bio_err, "ERROR\n");
                    /* fall through */
                case USER_DATA_PROCESS_SHUT:
                    ret = 0;
                    goto shut;

                case USER_DATA_PROCESS_RESTART:
                    goto re_start;

                case USER_DATA_PROCESS_NO_DATA:
                    break;

                case USER_DATA_PROCESS_CONTINUE:
                    write_ssl = 1;
                    break;
                }
            } while (!write_ssl
                     && cbuf_len == 0
                     && user_data_has_data(&user_data));
            if (cbuf_len > 0) {
                read_tty = 0;
                timeout.tv_sec = 0;
                timeout.tv_usec = 0;
            } else {
                read_tty = 1;
            }
        }

        ssl_pending = read_ssl && SSL_has_pending(con);

        if (!ssl_pending) {
#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS)
            if (tty_on) {
                /*
                 * Note that select() returns when read _would not block_,
                 * and EOF satisfies that.  To avoid a CPU-hogging loop,
                 * set the flag so we exit.
                 */
                if (read_tty && !at_eof)
                    openssl_fdset(fileno_stdin(), &readfds);
#if !defined(OPENSSL_SYS_VMS)
                if (write_tty)
                    openssl_fdset(fileno_stdout(), &writefds);
#endif
            }

            /*
             * Note that for QUIC we never actually check FD_ISSET() for the
             * underlying network fds. We just rely on select waking up when
             * they become readable/writeable and then SSL_handle_events() doing
             * the right thing.
             */
            if ((!isquic && read_ssl)
                    || (isquic && SSL_net_read_desired(con)))
                openssl_fdset(SSL_get_fd(con), &readfds);
            if ((!isquic && write_ssl)
                    || (isquic && (first_loop || SSL_net_write_desired(con))))
                openssl_fdset(SSL_get_fd(con), &writefds);
#else
            if (!tty_on || !write_tty) {
                if ((!isquic && read_ssl)
                        || (isquic && SSL_net_read_desired(con)))
                    openssl_fdset(SSL_get_fd(con), &readfds);
                if ((!isquic && write_ssl)
                        || (isquic && (first_loop || SSL_net_write_desired(con))))
                    openssl_fdset(SSL_get_fd(con), &writefds);
            }
#endif

            /*
             * Note: under VMS with SOCKETSHR the second parameter is
             * currently of type (int *) whereas under other systems it is
             * (void *) if you don't have a cast it will choke the compiler:
             * if you do have a cast then you can either go for (int *) or
             * (void *).
             */
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS)
            /*
             * Under Windows/DOS we make the assumption that we can always
             * write to the tty: therefore, if we need to write to the tty we
             * just fall through. Otherwise we timeout the select every
             * second and see if there are any keypresses. Note: this is a
             * hack, in a proper Windows application we wouldn't do this.
             */
            i = 0;
            if (!write_tty) {
                if (read_tty) {
                    tv.tv_sec = 1;
                    tv.tv_usec = 0;
                    i = select(width, (void *)&readfds, (void *)&writefds,
                               NULL, &tv);
                    if (!i && (!has_stdin_waiting() || !read_tty))
                        continue;
                } else
                    i = select(width, (void *)&readfds, (void *)&writefds,
                               NULL, timeoutp);
            }
#else
            i = select(width, (void *)&readfds, (void *)&writefds,
                       NULL, timeoutp);
#endif
            if (i < 0) {
                BIO_printf(bio_err, "bad select %d\n",
                           get_last_socket_error());
                goto shut;
            }
        }

        if (timeoutp != NULL) {
            SSL_handle_events(con);
            if (isdtls
                    && !FD_ISSET(SSL_get_fd(con), &readfds)
                    && !FD_ISSET(SSL_get_fd(con), &writefds))
                BIO_printf(bio_err, "TIMEOUT occurred\n");
        }

        if (!ssl_pending
                && ((!isquic && FD_ISSET(SSL_get_fd(con), &writefds))
                    || (isquic && (cbuf_len > 0 || first_loop)))) {
            k = SSL_write(con, &(cbuf[cbuf_off]), (unsigned int)cbuf_len);
            switch (SSL_get_error(con, k)) {
            case SSL_ERROR_NONE:
                cbuf_off += k;
                cbuf_len -= k;
                if (k <= 0)
                    goto end;
                /* we have done a  write(con,NULL,0); */
                if (cbuf_len == 0) {
                    read_tty = 1;
                    write_ssl = 0;
                } else {        /* if (cbuf_len > 0) */

                    read_tty = 0;
                    write_ssl = 1;
                }
                break;
            case SSL_ERROR_WANT_WRITE:
                BIO_printf(bio_c_out, "write W BLOCK\n");
                write_ssl = 1;
                read_tty = 0;
                break;
            case SSL_ERROR_WANT_ASYNC:
                BIO_printf(bio_c_out, "write A BLOCK\n");
                wait_for_async(con);
                write_ssl = 1;
                read_tty = 0;
                break;
            case SSL_ERROR_WANT_READ:
                BIO_printf(bio_c_out, "write R BLOCK\n");
                write_tty = 0;
                read_ssl = 1;
                write_ssl = 0;
                break;
            case SSL_ERROR_WANT_X509_LOOKUP:
                BIO_printf(bio_c_out, "write X BLOCK\n");
                break;
            case SSL_ERROR_ZERO_RETURN:
                if (cbuf_len != 0) {
                    BIO_printf(bio_c_out, "shutdown\n");
                    ret = 0;
                    goto shut;
                } else {
                    read_tty = 1;
                    write_ssl = 0;
                    break;
                }

            case SSL_ERROR_SYSCALL:
                if ((k != 0) || (cbuf_len != 0)) {
                    int sockerr = get_last_socket_error();

                    if (!tfo || sockerr != EISCONN) {
                        BIO_printf(bio_err, "write:errno=%d\n", sockerr);
                        goto shut;
                    }
                } else {
                    read_tty = 1;
                    write_ssl = 0;
                }
                break;
            case SSL_ERROR_WANT_ASYNC_JOB:
                /* This shouldn't ever happen in s_client - treat as an error */
            case SSL_ERROR_SSL:
                ERR_print_errors(bio_err);
                goto shut;
            }
        }
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_VMS)
        /* Assume Windows/DOS/BeOS can always write */
        else if (!ssl_pending && write_tty)
#else
        else if (!ssl_pending && FD_ISSET(fileno_stdout(), &writefds))
#endif
        {
#ifdef CHARSET_EBCDIC
            ascii2ebcdic(&(sbuf[sbuf_off]), &(sbuf[sbuf_off]), sbuf_len);
#endif
            i = raw_write_stdout(&(sbuf[sbuf_off]), sbuf_len);

            if (i <= 0) {
                BIO_printf(bio_c_out, "DONE\n");
                ret = 0;
                goto shut;
            }

            sbuf_len -= i;
            sbuf_off += i;
            if (sbuf_len <= 0) {
                read_ssl = 1;
                write_tty = 0;
            }
        } else if (ssl_pending
                   || (!isquic && FD_ISSET(SSL_get_fd(con), &readfds))) {
#ifdef RENEG
            {
                static int iiii;
                if (++iiii == 52) {
                    SSL_renegotiate(con);
                    iiii = 0;
                }
            }
#endif
            k = SSL_read(con, sbuf, BUFSIZZ);

            switch (SSL_get_error(con, k)) {
            case SSL_ERROR_NONE:
                if (k <= 0)
                    goto end;
                sbuf_off = 0;
                sbuf_len = k;

                read_ssl = 0;
                write_tty = 1;
                break;
            case SSL_ERROR_WANT_ASYNC:
                BIO_printf(bio_c_out, "read A BLOCK\n");
                wait_for_async(con);
                write_tty = 0;
                read_ssl = 1;
                if ((read_tty == 0) && (write_ssl == 0))
                    write_ssl = 1;
                break;
            case SSL_ERROR_WANT_WRITE:
                BIO_printf(bio_c_out, "read W BLOCK\n");
                write_ssl = 1;
                read_tty = 0;
                break;
            case SSL_ERROR_WANT_READ:
                BIO_printf(bio_c_out, "read R BLOCK\n");
                write_tty = 0;
                read_ssl = 1;
                if ((read_tty == 0) && (write_ssl == 0))
                    write_ssl = 1;
                break;
            case SSL_ERROR_WANT_X509_LOOKUP:
                BIO_printf(bio_c_out, "read X BLOCK\n");
                break;
            case SSL_ERROR_SYSCALL:
                ret = get_last_socket_error();
                if (c_brief)
                    BIO_puts(bio_err, "CONNECTION CLOSED BY SERVER\n");
                else
                    BIO_printf(bio_err, "read:errno=%d\n", ret);
                goto shut;
            case SSL_ERROR_ZERO_RETURN:
                BIO_printf(bio_c_out, "closed\n");
                ret = 0;
                goto shut;
            case SSL_ERROR_WANT_ASYNC_JOB:
                /* This shouldn't ever happen in s_client. Treat as an error */
            case SSL_ERROR_SSL:
                ERR_print_errors(bio_err);
                goto shut;
            }
        }

        /* don't wait for client input in the non-interactive mode */
        else if (nointeractive) {
            ret = 0;
            goto shut;
        }

/* OPENSSL_SYS_MSDOS includes OPENSSL_SYS_WINDOWS */
#if defined(OPENSSL_SYS_MSDOS)
        else if (has_stdin_waiting())
#else
        else if (FD_ISSET(fileno_stdin(), &readfds))
#endif
        {
            if (crlf) {
                int j, lf_num;

                i = raw_read_stdin(cbuf, BUFSIZZ / 2);
                lf_num = 0;
                /* both loops are skipped when i <= 0 */
                for (j = 0; j < i; j++)
                    if (cbuf[j] == '\n')
                        lf_num++;
                for (j = i - 1; j >= 0; j--) {
                    cbuf[j + lf_num] = cbuf[j];
                    if (cbuf[j] == '\n') {
                        lf_num--;
                        i++;
                        cbuf[j + lf_num] = '\r';
                    }
                }
                assert(lf_num == 0);
            } else
                i = raw_read_stdin(cbuf, BUFSIZZ);
#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS)
            if (i == 0)
                at_eof = 1;
#endif

            if (!c_ign_eof && i <= 0) {
                BIO_printf(bio_err, "DONE\n");
                ret = 0;
                goto shut;
            }

            if (i > 0 && !user_data_add(&user_data, i)) {
                ret = 0;
                goto shut;
            }
            read_tty = 0;
        }
        first_loop = 0;
    }

 shut:
    if (in_init)
        print_stuff(bio_c_out, con, full_log);
    do_ssl_shutdown(con);

    /*
     * If we ended with an alert being sent, but still with data in the
     * network buffer to be read, then calling BIO_closesocket() will
     * result in a TCP-RST being sent. On some platforms (notably
     * Windows) then this will result in the peer immediately abandoning
     * the connection including any buffered alert data before it has
     * had a chance to be read. Shutting down the sending side first,
     * and then closing the socket sends TCP-FIN first followed by
     * TCP-RST. This seems to allow the peer to read the alert data.
     */
    shutdown(SSL_get_fd(con), 1); /* SHUT_WR */
    /*
     * We just said we have nothing else to say, but it doesn't mean that
     * the other side has nothing. It's even recommended to consume incoming
     * data. [In testing context this ensures that alerts are passed on...]
     */
    timeout.tv_sec = 0;
    timeout.tv_usec = 500000;  /* some extreme round-trip */
    do {
        FD_ZERO(&readfds);
        openssl_fdset(sock, &readfds);
    } while (select(sock + 1, &readfds, NULL, NULL, &timeout) > 0
             && BIO_read(sbio, sbuf, BUFSIZZ) > 0);

    BIO_closesocket(SSL_get_fd(con));
 end:
    if (con != NULL) {
        if (prexit != 0)
            print_stuff(bio_c_out, con, 1);
        SSL_free(con);
    }
    SSL_SESSION_free(psksess);
#if !defined(OPENSSL_NO_NEXTPROTONEG)
    OPENSSL_free(next_proto.data);
#endif
    SSL_CTX_free(ctx);
    set_keylog_file(NULL, NULL);
    X509_free(cert);
    sk_X509_CRL_pop_free(crls, X509_CRL_free);
    EVP_PKEY_free(key);
    OSSL_STACK_OF_X509_free(chain);
    OPENSSL_free(pass);
#ifndef OPENSSL_NO_SRP
    OPENSSL_free(srp_arg.srppassin);
#endif
    OPENSSL_free(sname_alloc);
    BIO_ADDR_free(peer_addr);
    OPENSSL_free(connectstr);
    OPENSSL_free(bindstr);
    OPENSSL_free(bindhost);
    OPENSSL_free(bindport);
    OPENSSL_free(host);
    OPENSSL_free(port);
    OPENSSL_free(thost);
    OPENSSL_free(tport);
    X509_VERIFY_PARAM_free(vpm);
    ssl_excert_free(exc);
    sk_OPENSSL_STRING_free(ssl_args);
    sk_OPENSSL_STRING_free(dane_tlsa_rrset);
    SSL_CONF_CTX_free(cctx);
    OPENSSL_clear_free(cbuf, BUFSIZZ);
    OPENSSL_clear_free(sbuf, BUFSIZZ);
    OPENSSL_clear_free(mbuf, BUFSIZZ);
    clear_free(proxypass);
    release_engine(e);
    BIO_free(bio_c_out);
    bio_c_out = NULL;
    BIO_free(bio_c_msg);
    bio_c_msg = NULL;
    return ret;
}

static void print_stuff(BIO *bio, SSL *s, int full)
{
    X509 *peer = NULL;
    STACK_OF(X509) *sk;
    const SSL_CIPHER *c;
    EVP_PKEY *public_key;
    int i, istls13 = (SSL_version(s) == TLS1_3_VERSION);
    long verify_result;
#ifndef OPENSSL_NO_COMP
    const COMP_METHOD *comp, *expansion;
#endif
    unsigned char *exportedkeymat;
#ifndef OPENSSL_NO_CT
    const SSL_CTX *ctx = SSL_get_SSL_CTX(s);
#endif

    if (full) {
        int got_a_chain = 0;

        sk = SSL_get_peer_cert_chain(s);
        if (sk != NULL) {
            got_a_chain = 1;

            BIO_printf(bio, "---\nCertificate chain\n");
            for (i = 0; i < sk_X509_num(sk); i++) {
                BIO_printf(bio, "%2d s:", i);
                X509_NAME_print_ex(bio, X509_get_subject_name(sk_X509_value(sk, i)), 0, get_nameopt());
                BIO_puts(bio, "\n");
                BIO_printf(bio, "   i:");
                X509_NAME_print_ex(bio, X509_get_issuer_name(sk_X509_value(sk, i)), 0, get_nameopt());
                BIO_puts(bio, "\n");
                public_key = X509_get_pubkey(sk_X509_value(sk, i));
                if (public_key != NULL) {
                    BIO_printf(bio, "   a:PKEY: %s, %d (bit); sigalg: %s\n",
                               OBJ_nid2sn(EVP_PKEY_get_base_id(public_key)),
                               EVP_PKEY_get_bits(public_key),
                               OBJ_nid2sn(X509_get_signature_nid(sk_X509_value(sk, i))));
                    EVP_PKEY_free(public_key);
                }
                BIO_printf(bio, "   v:NotBefore: ");
                ASN1_TIME_print(bio, X509_get0_notBefore(sk_X509_value(sk, i)));
                BIO_printf(bio, "; NotAfter: ");
                ASN1_TIME_print(bio, X509_get0_notAfter(sk_X509_value(sk, i)));
                BIO_puts(bio, "\n");
                if (c_showcerts)
                    PEM_write_bio_X509(bio, sk_X509_value(sk, i));
            }
        }

        BIO_printf(bio, "---\n");
        peer = SSL_get0_peer_certificate(s);
        if (peer != NULL) {
            BIO_printf(bio, "Server certificate\n");

            /* Redundant if we showed the whole chain */
            if (!(c_showcerts && got_a_chain))
                PEM_write_bio_X509(bio, peer);
            dump_cert_text(bio, peer);
        } else {
            BIO_printf(bio, "no peer certificate available\n");
        }

        /* Only display RPK information if configured */
        if (SSL_get_negotiated_client_cert_type(s) == TLSEXT_cert_type_rpk)
            BIO_printf(bio, "Client-to-server raw public key negotiated\n");
        if (SSL_get_negotiated_server_cert_type(s) == TLSEXT_cert_type_rpk)
            BIO_printf(bio, "Server-to-client raw public key negotiated\n");
        if (enable_server_rpk) {
            EVP_PKEY *peer_rpk = SSL_get0_peer_rpk(s);

            if (peer_rpk != NULL) {
                BIO_printf(bio, "Server raw public key\n");
                EVP_PKEY_print_public(bio, peer_rpk, 2, NULL);
            } else {
                BIO_printf(bio, "no peer rpk available\n");
            }
        }

        print_ca_names(bio, s);

        ssl_print_sigalgs(bio, s);
        ssl_print_tmp_key(bio, s);

#ifndef OPENSSL_NO_CT
        /*
         * When the SSL session is anonymous, or resumed via an abbreviated
         * handshake, no SCTs are provided as part of the handshake.  While in
         * a resumed session SCTs may be present in the session's certificate,
         * no callbacks are invoked to revalidate these, and in any case that
         * set of SCTs may be incomplete.  Thus it makes little sense to
         * attempt to display SCTs from a resumed session's certificate, and of
         * course none are associated with an anonymous peer.
         */
        if (peer != NULL && !SSL_session_reused(s) && SSL_ct_is_enabled(s)) {
            const STACK_OF(SCT) *scts = SSL_get0_peer_scts(s);
            int sct_count = scts != NULL ? sk_SCT_num(scts) : 0;

            BIO_printf(bio, "---\nSCTs present (%i)\n", sct_count);
            if (sct_count > 0) {
                const CTLOG_STORE *log_store = SSL_CTX_get0_ctlog_store(ctx);

                BIO_printf(bio, "---\n");
                for (i = 0; i < sct_count; ++i) {
                    SCT *sct = sk_SCT_value(scts, i);

                    BIO_printf(bio, "SCT validation status: %s\n",
                               SCT_validation_status_string(sct));
                    SCT_print(sct, bio, 0, log_store);
                    if (i < sct_count - 1)
                        BIO_printf(bio, "\n---\n");
                }
                BIO_printf(bio, "\n");
            }
        }
#endif

        BIO_printf(bio,
                   "---\nSSL handshake has read %ju bytes "
                   "and written %ju bytes\n",
                   BIO_number_read(SSL_get_rbio(s)),
                   BIO_number_written(SSL_get_wbio(s)));
    }
    print_verify_detail(s, bio);
    BIO_printf(bio, (SSL_session_reused(s) ? "---\nReused, " : "---\nNew, "));
    c = SSL_get_current_cipher(s);
    BIO_printf(bio, "%s, Cipher is %s\n",
               SSL_CIPHER_get_version(c), SSL_CIPHER_get_name(c));
    BIO_printf(bio, "Protocol: %s\n", SSL_get_version(s));
    if (peer != NULL) {
        EVP_PKEY *pktmp;

        pktmp = X509_get0_pubkey(peer);
        BIO_printf(bio, "Server public key is %d bit\n",
                   EVP_PKEY_get_bits(pktmp));
    }

    ssl_print_secure_renegotiation_notes(bio, s);

#ifndef OPENSSL_NO_COMP
    comp = SSL_get_current_compression(s);
    expansion = SSL_get_current_expansion(s);
    BIO_printf(bio, "Compression: %s\n",
               comp ? SSL_COMP_get_name(comp) : "NONE");
    BIO_printf(bio, "Expansion: %s\n",
               expansion ? SSL_COMP_get_name(expansion) : "NONE");
#endif
#ifndef OPENSSL_NO_KTLS
    if (BIO_get_ktls_send(SSL_get_wbio(s)))
        BIO_printf(bio_err, "Using Kernel TLS for sending\n");
    if (BIO_get_ktls_recv(SSL_get_rbio(s)))
        BIO_printf(bio_err, "Using Kernel TLS for receiving\n");
#endif

    if (OSSL_TRACE_ENABLED(TLS)) {
        /* Print out local port of connection: useful for debugging */
        int sock;
        union BIO_sock_info_u info;

        sock = SSL_get_fd(s);
        if ((info.addr = BIO_ADDR_new()) != NULL
            && BIO_sock_info(sock, BIO_SOCK_INFO_ADDRESS, &info)) {
            BIO_printf(bio_c_out, "LOCAL PORT is %u\n",
                       ntohs(BIO_ADDR_rawport(info.addr)));
        }
        BIO_ADDR_free(info.addr);
    }

#if !defined(OPENSSL_NO_NEXTPROTONEG)
    if (next_proto.status != -1) {
        const unsigned char *proto;
        unsigned int proto_len;
        SSL_get0_next_proto_negotiated(s, &proto, &proto_len);
        BIO_printf(bio, "Next protocol: (%d) ", next_proto.status);
        BIO_write(bio, proto, proto_len);
        BIO_write(bio, "\n", 1);
    }
#endif
    {
        const unsigned char *proto;
        unsigned int proto_len;
        SSL_get0_alpn_selected(s, &proto, &proto_len);
        if (proto_len > 0) {
            BIO_printf(bio, "ALPN protocol: ");
            BIO_write(bio, proto, proto_len);
            BIO_write(bio, "\n", 1);
        } else
            BIO_printf(bio, "No ALPN negotiated\n");
    }

#ifndef OPENSSL_NO_SRTP
    {
        SRTP_PROTECTION_PROFILE *srtp_profile =
            SSL_get_selected_srtp_profile(s);

        if (srtp_profile)
            BIO_printf(bio, "SRTP Extension negotiated, profile=%s\n",
                       srtp_profile->name);
    }
#endif

    if (istls13) {
        switch (SSL_get_early_data_status(s)) {
        case SSL_EARLY_DATA_NOT_SENT:
            BIO_printf(bio, "Early data was not sent\n");
            break;

        case SSL_EARLY_DATA_REJECTED:
            BIO_printf(bio, "Early data was rejected\n");
            break;

        case SSL_EARLY_DATA_ACCEPTED:
            BIO_printf(bio, "Early data was accepted\n");
            break;

        }

        /*
         * We also print the verify results when we dump session information,
         * but in TLSv1.3 we may not get that right away (or at all) depending
         * on when we get a NewSessionTicket. Therefore, we print it now as well.
         */
        verify_result = SSL_get_verify_result(s);
        BIO_printf(bio, "Verify return code: %ld (%s)\n", verify_result,
                   X509_verify_cert_error_string(verify_result));
    } else {
        /* In TLSv1.3 we do this on arrival of a NewSessionTicket */
        SSL_SESSION_print(bio, SSL_get_session(s));
    }

    if (SSL_get_session(s) != NULL && keymatexportlabel != NULL) {
        BIO_printf(bio, "Keying material exporter:\n");
        BIO_printf(bio, "    Label: '%s'\n", keymatexportlabel);
        BIO_printf(bio, "    Length: %i bytes\n", keymatexportlen);
        exportedkeymat = app_malloc(keymatexportlen, "export key");
        if (SSL_export_keying_material(s, exportedkeymat,
                                        keymatexportlen,
                                        keymatexportlabel,
                                        strlen(keymatexportlabel),
                                        NULL, 0, 0) <= 0) {
            BIO_printf(bio, "    Error\n");
        } else {
            BIO_printf(bio, "    Keying material: ");
            for (i = 0; i < keymatexportlen; i++)
                BIO_printf(bio, "%02X", exportedkeymat[i]);
            BIO_printf(bio, "\n");
        }
        OPENSSL_free(exportedkeymat);
    }
    BIO_printf(bio, "---\n");
    /* flush, or debugging output gets mixed with http response */
    (void)BIO_flush(bio);
}

# ifndef OPENSSL_NO_OCSP
static int ocsp_resp_cb(SSL *s, void *arg)
{
    const unsigned char *p;
    int len;
    OCSP_RESPONSE *rsp;
    len = SSL_get_tlsext_status_ocsp_resp(s, &p);
    BIO_puts(arg, "OCSP response: ");
    if (p == NULL) {
        BIO_puts(arg, "no response sent\n");
        return 1;
    }
    rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
    if (rsp == NULL) {
        BIO_puts(arg, "response parse error\n");
        BIO_dump_indent(arg, (char *)p, len, 4);
        return 0;
    }
    BIO_puts(arg, "\n======================================\n");
    OCSP_RESPONSE_print(arg, rsp, 0);
    BIO_puts(arg, "======================================\n");
    OCSP_RESPONSE_free(rsp);
    return 1;
}
# endif

static int ldap_ExtendedResponse_parse(const char *buf, long rem)
{
    const unsigned char *cur, *end;
    long len;
    int tag, xclass, inf, ret = -1;

    cur = (const unsigned char *)buf;
    end = cur + rem;

    /*
     * From RFC 4511:
     *
     *    LDAPMessage ::= SEQUENCE {
     *         messageID       MessageID,
     *         protocolOp      CHOICE {
     *              ...
     *              extendedResp          ExtendedResponse,
     *              ... },
     *         controls       [0] Controls OPTIONAL }
     *
     *    ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
     *         COMPONENTS OF LDAPResult,
     *         responseName     [10] LDAPOID OPTIONAL,
     *         responseValue    [11] OCTET STRING OPTIONAL }
     *
     *    LDAPResult ::= SEQUENCE {
     *         resultCode         ENUMERATED {
     *              success                      (0),
     *              ...
     *              other                        (80),
     *              ...  },
     *         matchedDN          LDAPDN,
     *         diagnosticMessage  LDAPString,
     *         referral           [3] Referral OPTIONAL }
     */

    /* pull SEQUENCE */
    inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem);
    if (inf != V_ASN1_CONSTRUCTED || tag != V_ASN1_SEQUENCE ||
        (rem = end - cur, len > rem)) {
        BIO_printf(bio_err, "Unexpected LDAP response\n");
        goto end;
    }

    rem = len;  /* ensure that we don't overstep the SEQUENCE */

    /* pull MessageID */
    inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem);
    if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_INTEGER ||
        (rem = end - cur, len > rem)) {
        BIO_printf(bio_err, "No MessageID\n");
        goto end;
    }

    cur += len; /* shall we check for MessageId match or just skip? */

    /* pull [APPLICATION 24] */
    rem = end - cur;
    inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem);
    if (inf != V_ASN1_CONSTRUCTED || xclass != V_ASN1_APPLICATION ||
        tag != 24) {
        BIO_printf(bio_err, "Not ExtendedResponse\n");
        goto end;
    }

    /* pull resultCode */
    rem = end - cur;
    inf = ASN1_get_object(&cur, &len, &tag, &xclass, rem);
    if (inf != V_ASN1_UNIVERSAL || tag != V_ASN1_ENUMERATED || len == 0 ||
        (rem = end - cur, len > rem)) {
        BIO_printf(bio_err, "Not LDAPResult\n");
        goto end;
    }

    /* len should always be one, but just in case... */
    for (ret = 0, inf = 0; inf < len; inf++) {
        ret <<= 8;
        ret |= cur[inf];
    }
    /* There is more data, but we don't care... */
 end:
    return ret;
}

/*
 * Host dNS Name verifier: used for checking that the hostname is in dNS format
 * before setting it as SNI
 */
static int is_dNS_name(const char *host)
{
    const size_t MAX_LABEL_LENGTH = 63;
    size_t i;
    int isdnsname = 0;
    size_t length = strlen(host);
    size_t label_length = 0;
    int all_numeric = 1;

    /*
     * Deviation from strict DNS name syntax, also check names with '_'
     * Check DNS name syntax, any '-' or '.' must be internal,
     * and on either side of each '.' we can't have a '-' or '.'.
     *
     * If the name has just one label, we don't consider it a DNS name.
     */
    for (i = 0; i < length && label_length < MAX_LABEL_LENGTH; ++i) {
        char c = host[i];

        if ((c >= 'a' && c <= 'z')
            || (c >= 'A' && c <= 'Z')
            || c == '_') {
            label_length += 1;
            all_numeric = 0;
            continue;
        }

        if (c >= '0' && c <= '9') {
            label_length += 1;
            continue;
        }

        /* Dot and hyphen cannot be first or last. */
        if (i > 0 && i < length - 1) {
            if (c == '-') {
                label_length += 1;
                continue;
            }
            /*
             * Next to a dot the preceding and following characters must not be
             * another dot or a hyphen.  Otherwise, record that the name is
             * plausible, since it has two or more labels.
             */
            if (c == '.'
                && host[i + 1] != '.'
                && host[i - 1] != '-'
                && host[i + 1] != '-') {
                label_length = 0;
                isdnsname = 1;
                continue;
            }
        }
        isdnsname = 0;
        break;
    }

    /* dNS name must not be all numeric and labels must be shorter than 64 characters. */
    isdnsname &= !all_numeric && !(label_length == MAX_LABEL_LENGTH);

    return isdnsname;
}

static void user_data_init(struct user_data_st *user_data, SSL *con, char *buf,
                           size_t bufmax, int mode)
{
    user_data->con = con;
    user_data->buf = buf;
    user_data->bufmax = bufmax;
    user_data->buflen = 0;
    user_data->bufoff = 0;
    user_data->mode = mode;
    user_data->isfin = 0;
}

static int user_data_add(struct user_data_st *user_data, size_t i)
{
    if (user_data->buflen != 0 || i > user_data->bufmax)
        return 0;

    user_data->buflen = i;
    user_data->bufoff = 0;

    return 1;
}

#define USER_COMMAND_HELP        0
#define USER_COMMAND_QUIT        1
#define USER_COMMAND_RECONNECT   2
#define USER_COMMAND_RENEGOTIATE 3
#define USER_COMMAND_KEY_UPDATE  4
#define USER_COMMAND_FIN         5

static int user_data_execute(struct user_data_st *user_data, int cmd, char *arg)
{
    switch (cmd) {
    case USER_COMMAND_HELP:
        /* This only ever occurs in advanced mode, so just emit advanced help */
        BIO_printf(bio_err, "Enter text to send to the peer followed by <enter>\n");
        BIO_printf(bio_err, "To issue a command insert {cmd} or {cmd:arg} anywhere in the text\n");
        BIO_printf(bio_err, "Entering {{ will send { to the peer\n");
        BIO_printf(bio_err, "The following commands are available\n");
        BIO_printf(bio_err, "  {help}: Get this help text\n");
        BIO_printf(bio_err, "  {quit}: Close the connection to the peer\n");
        BIO_printf(bio_err, "  {reconnect}: Reconnect to the peer\n");
        if (SSL_is_quic(user_data->con)) {
            BIO_printf(bio_err, "  {fin}: Send FIN on the stream. No further writing is possible\n");
        } else if(SSL_version(user_data->con) == TLS1_3_VERSION) {
            BIO_printf(bio_err, "  {keyup:req|noreq}: Send a Key Update message\n");
            BIO_printf(bio_err, "                     Arguments:\n");
            BIO_printf(bio_err, "                     req   = peer update requested (default)\n");
            BIO_printf(bio_err, "                     noreq = peer update not requested\n");
        } else {
            BIO_printf(bio_err, "  {reneg}: Attempt to renegotiate\n");
        }
        BIO_printf(bio_err, "\n");
        return USER_DATA_PROCESS_NO_DATA;

    case USER_COMMAND_QUIT:
        BIO_printf(bio_err, "DONE\n");
        return USER_DATA_PROCESS_SHUT;

    case USER_COMMAND_RECONNECT:
        BIO_printf(bio_err, "RECONNECTING\n");
        do_ssl_shutdown(user_data->con);
        SSL_set_connect_state(user_data->con);
        BIO_closesocket(SSL_get_fd(user_data->con));
        return USER_DATA_PROCESS_RESTART;

    case USER_COMMAND_RENEGOTIATE:
        BIO_printf(bio_err, "RENEGOTIATING\n");
        if (!SSL_renegotiate(user_data->con))
            break;
        return USER_DATA_PROCESS_CONTINUE;

    case USER_COMMAND_KEY_UPDATE: {
            int updatetype;

            if (OPENSSL_strcasecmp(arg, "req") == 0)
                updatetype = SSL_KEY_UPDATE_REQUESTED;
            else if (OPENSSL_strcasecmp(arg, "noreq") == 0)
                updatetype = SSL_KEY_UPDATE_NOT_REQUESTED;
            else
                return USER_DATA_PROCESS_BAD_ARGUMENT;
            BIO_printf(bio_err, "KEYUPDATE\n");
            if (!SSL_key_update(user_data->con, updatetype))
                break;
            return USER_DATA_PROCESS_CONTINUE;
        }

    case USER_COMMAND_FIN:
        if (!SSL_stream_conclude(user_data->con, 0))
            break;
        user_data->isfin = 1;
        return USER_DATA_PROCESS_NO_DATA;

    default:
        break;
    }

    BIO_printf(bio_err, "ERROR\n");
    ERR_print_errors(bio_err);

    return USER_DATA_PROCESS_SHUT;
}

static int user_data_process(struct user_data_st *user_data, size_t *len,
                             size_t *off)
{
    char *buf_start = user_data->buf + user_data->bufoff;
    size_t outlen = user_data->buflen;

    if (user_data->buflen == 0) {
        *len = 0;
        *off = 0;
        return USER_DATA_PROCESS_NO_DATA;
    }

    if (user_data->mode == USER_DATA_MODE_BASIC) {
        switch (buf_start[0]) {
        case 'Q':
            user_data->buflen = user_data->bufoff = *len = *off = 0;
            return user_data_execute(user_data, USER_COMMAND_QUIT, NULL);

        case 'C':
            user_data->buflen = user_data->bufoff = *len = *off = 0;
            return user_data_execute(user_data, USER_COMMAND_RECONNECT, NULL);

        case 'R':
            user_data->buflen = user_data->bufoff = *len = *off = 0;
            return user_data_execute(user_data, USER_COMMAND_RENEGOTIATE, NULL);

        case 'K':
        case 'k':
            user_data->buflen = user_data->bufoff = *len = *off = 0;
            return user_data_execute(user_data, USER_COMMAND_KEY_UPDATE,
                                     buf_start[0] == 'K' ? "req" : "noreq");
        default:
            break;
        }
    } else if (user_data->mode == USER_DATA_MODE_ADVANCED) {
        char *cmd_start = buf_start;

        cmd_start[outlen] = '\0';
        for (;;) {
            cmd_start = strchr(cmd_start, '{');
            if (cmd_start == buf_start && *(cmd_start + 1) == '{') {
                /* The "{" is escaped, so skip it */
                cmd_start += 2;
                buf_start++;
                user_data->bufoff++;
                user_data->buflen--;
                outlen--;
                continue;
            }
            break;
        }

        if (cmd_start == buf_start) {
            /* Command detected */
            char *cmd_end = strchr(cmd_start, '}');
            char *arg_start;
            int cmd = -1, ret = USER_DATA_PROCESS_NO_DATA;
            size_t oldoff;

            if (cmd_end == NULL) {
                /* Malformed command */
                cmd_start[outlen - 1] = '\0';
                BIO_printf(bio_err,
                           "ERROR PROCESSING COMMAND. REST OF LINE IGNORED: %s\n",
                           cmd_start);
                user_data->buflen = user_data->bufoff = *len = *off = 0;
                return USER_DATA_PROCESS_NO_DATA;
            }
            *cmd_end = '\0';
            arg_start = strchr(cmd_start, ':');
            if (arg_start != NULL) {
                *arg_start = '\0';
                arg_start++;
            }
            /* Skip over the { */
            cmd_start++;
            /*
             * Now we have cmd_start pointing to a NUL terminated string for
             * the command, and arg_start either being NULL or pointing to a
             * NUL terminated string for the argument.
             */
            if (OPENSSL_strcasecmp(cmd_start, "help") == 0) {
                cmd = USER_COMMAND_HELP;
            } else if (OPENSSL_strcasecmp(cmd_start, "quit") == 0) {
                cmd = USER_COMMAND_QUIT;
            } else if (OPENSSL_strcasecmp(cmd_start, "reconnect") == 0) {
                cmd = USER_COMMAND_RECONNECT;
            } else if(SSL_is_quic(user_data->con)) {
                if (OPENSSL_strcasecmp(cmd_start, "fin") == 0)
                    cmd = USER_COMMAND_FIN;
            } if (SSL_version(user_data->con) == TLS1_3_VERSION) {
                if (OPENSSL_strcasecmp(cmd_start, "keyup") == 0) {
                    cmd = USER_COMMAND_KEY_UPDATE;
                    if (arg_start == NULL)
                        arg_start = "req";
                }
            } else {
                /* (D)TLSv1.2 or below */
                if (OPENSSL_strcasecmp(cmd_start, "reneg") == 0)
                    cmd = USER_COMMAND_RENEGOTIATE;
            }
            if (cmd == -1) {
                BIO_printf(bio_err, "UNRECOGNISED COMMAND (IGNORED): %s\n",
                           cmd_start);
            } else {
                ret = user_data_execute(user_data, cmd, arg_start);
                if (ret == USER_DATA_PROCESS_BAD_ARGUMENT) {
                    BIO_printf(bio_err, "BAD ARGUMENT (COMMAND IGNORED): %s\n",
                               arg_start);
                    ret = USER_DATA_PROCESS_NO_DATA;
                }
            }
            oldoff = user_data->bufoff;
            user_data->bufoff = (cmd_end - user_data->buf) + 1;
            user_data->buflen -= user_data->bufoff - oldoff;
            if (user_data->buf + 1 == cmd_start
                    && user_data->buflen == 1
                    && user_data->buf[user_data->bufoff] == '\n') {
                /*
                 * This command was the only thing on the whole line. We
                 * suppress the final `\n`
                 */
                user_data->bufoff = 0;
                user_data->buflen = 0;
            }
            *len = *off = 0;
            return ret;
        } else if (cmd_start != NULL) {
            /*
             * There is a command on this line, but its not at the start. Output
             * the start of the line, and we'll process the command next time
             * we call this function
             */
            outlen = cmd_start - buf_start;
        }
    }

    if (user_data->isfin) {
        user_data->buflen = user_data->bufoff = *len = *off = 0;
        return USER_DATA_PROCESS_NO_DATA;
    }

#ifdef CHARSET_EBCDIC
    ebcdic2ascii(buf_start, buf_start, outlen);
#endif
    *len = outlen;
    *off = user_data->bufoff;
    user_data->buflen -= outlen;
    if (user_data->buflen == 0)
        user_data->bufoff = 0;
    else
        user_data->bufoff += outlen;
    return USER_DATA_PROCESS_CONTINUE;
}

static int user_data_has_data(struct user_data_st *user_data)
{
    return user_data->buflen > 0;
}
#endif                          /* OPENSSL_NO_SOCK */
The diff you're trying to view is too large. Only the first 1000 changed files have been loaded.
Showing with 0 additions and 0 deletions (0 / 0 diffs computed)
swh spinner

Computing file changes ...

ENEA — Copyright (C), ENEA. License: GNU AGPLv3+.
Legal notes  ::  JavaScript license information ::  Web API

back to top