/* Copyright (C) 2007-2022 Open Information Security Foundation
 *
 * You can copy, redistribute or modify this Program under the terms of
 * the GNU General Public License version 2 as published by the Free
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

/**
 * \file
 *
 * \author Anoop Saldanha <anoopsaldanha@gmail.com>
 * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
 * \author Mats Klepsland <mats.klepsland@gmail.com>
 *
 */

#include "suricata-common.h"
#include "decode.h"
#include "threads.h"

#include "stream-tcp-private.h"
#include "stream-tcp-reassemble.h"
#include "stream-tcp.h"
#include "stream.h"

#include "app-layer.h"
#include "app-layer-detect-proto.h"
#include "app-layer-protos.h"
#include "app-layer-parser.h"
#include "app-layer-frames.h"
#include "app-layer-ssl.h"

#include "decode-events.h"
#include "conf.h"

#include "feature.h"

#include "util-spm.h"
#include "util-unittest.h"
#include "util-debug.h"
#include "util-print.h"
#include "util-pool.h"
#include "util-byte.h"
#include "util-ja3.h"
#include "util-enum.h"
#include "flow-util.h"
#include "flow-private.h"
#include "util-validate.h"

SCEnumCharMap tls_frame_table[] = {
    {
            "pdu",
            TLS_FRAME_PDU,
    },
    {
            "hdr",
            TLS_FRAME_HDR,
    },
    {
            "data",
            TLS_FRAME_DATA,
    },
    {
            "alert",
            TLS_FRAME_ALERT_DATA,
    },
    {
            "heartbeat",
            TLS_FRAME_HB_DATA,
    },
    {
            "ssl2.hdr",
            TLS_FRAME_SSLV2_HDR,
    },
    {
            "ssl2.pdu",
            TLS_FRAME_SSLV2_PDU,
    },
    { NULL, -1 },
};

SCEnumCharMap tls_decoder_event_table[] = {
    /* TLS protocol messages */
    { "INVALID_SSLV2_HEADER", TLS_DECODER_EVENT_INVALID_SSLV2_HEADER },
    { "INVALID_TLS_HEADER", TLS_DECODER_EVENT_INVALID_TLS_HEADER },
    { "INVALID_RECORD_VERSION", TLS_DECODER_EVENT_INVALID_RECORD_VERSION },
    { "INVALID_RECORD_TYPE", TLS_DECODER_EVENT_INVALID_RECORD_TYPE },
    { "INVALID_RECORD_LENGTH", TLS_DECODER_EVENT_INVALID_RECORD_LENGTH },
    { "INVALID_HANDSHAKE_MESSAGE", TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE },
    { "HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_HEARTBEAT },
    { "INVALID_HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_INVALID_HEARTBEAT },
    { "OVERFLOW_HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT },
    { "DATALEAK_HEARTBEAT_MISMATCH", TLS_DECODER_EVENT_DATALEAK_HEARTBEAT_MISMATCH },
    { "HANDSHAKE_INVALID_LENGTH", TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH },
    { "MULTIPLE_SNI_EXTENSIONS", TLS_DECODER_EVENT_MULTIPLE_SNI_EXTENSIONS },
    { "INVALID_SNI_TYPE", TLS_DECODER_EVENT_INVALID_SNI_TYPE },
    { "INVALID_SNI_LENGTH", TLS_DECODER_EVENT_INVALID_SNI_LENGTH },
    { "TOO_MANY_RECORDS_IN_PACKET", TLS_DECODER_EVENT_TOO_MANY_RECORDS_IN_PACKET },
    /* certificate decoding messages */
    { "INVALID_CERTIFICATE", TLS_DECODER_EVENT_INVALID_CERTIFICATE },
    { "CERTIFICATE_INVALID_LENGTH", TLS_DECODER_EVENT_CERTIFICATE_INVALID_LENGTH },
    { "CERTIFICATE_INVALID_VERSION", TLS_DECODER_EVENT_CERTIFICATE_INVALID_VERSION },
    { "CERTIFICATE_INVALID_SERIAL", TLS_DECODER_EVENT_CERTIFICATE_INVALID_SERIAL },
    { "CERTIFICATE_INVALID_ALGORITHMIDENTIFIER",
            TLS_DECODER_EVENT_CERTIFICATE_INVALID_ALGORITHMIDENTIFIER },
    { "CERTIFICATE_INVALID_X509NAME", TLS_DECODER_EVENT_CERTIFICATE_INVALID_X509NAME },
    { "CERTIFICATE_INVALID_DATE", TLS_DECODER_EVENT_CERTIFICATE_INVALID_DATE },
    { "CERTIFICATE_INVALID_EXTENSIONS", TLS_DECODER_EVENT_CERTIFICATE_INVALID_EXTENSIONS },
    { "CERTIFICATE_INVALID_DER", TLS_DECODER_EVENT_CERTIFICATE_INVALID_DER },
    { "CERTIFICATE_INVALID_SUBJECT", TLS_DECODER_EVENT_CERTIFICATE_INVALID_SUBJECT },
    { "CERTIFICATE_INVALID_ISSUER", TLS_DECODER_EVENT_CERTIFICATE_INVALID_ISSUER },
    { "CERTIFICATE_INVALID_VALIDITY", TLS_DECODER_EVENT_CERTIFICATE_INVALID_VALIDITY },
    { "ERROR_MESSAGE_ENCOUNTERED", TLS_DECODER_EVENT_ERROR_MSG_ENCOUNTERED },
    /* used as a generic error event */
    { "INVALID_SSL_RECORD", TLS_DECODER_EVENT_INVALID_SSL_RECORD },
    { NULL, -1 },
};

enum {
    /* X.509 error codes, returned by decoder
     * THESE CONSTANTS MUST MATCH rust/src/x509/mod.rs ! */
    ERR_INVALID_CERTIFICATE=1,
    ERR_INVALID_LENGTH,
    ERR_INVALID_VERSION,
    ERR_INVALID_SERIAL,
    ERR_INVALID_ALGORITHMIDENTIFIER,
    ERR_INVALID_X509NAME,
    ERR_INVALID_DATE,
    ERR_INVALID_EXTENSIONS,
    ERR_INVALID_DER,

    /* error getting data */
    ERR_EXTRACT_SUBJECT,
    ERR_EXTRACT_ISSUER,
    ERR_EXTRACT_VALIDITY,
};

/* JA3 and JA4 fingerprints are disabled by default */
#define SSL_CONFIG_DEFAULT_JA3 0
#define SSL_CONFIG_DEFAULT_JA4 0

enum SslConfigEncryptHandling {
    SSL_CNF_ENC_HANDLE_DEFAULT = 0, /**< disable raw content, continue tracking */
    SSL_CNF_ENC_HANDLE_BYPASS = 1,  /**< skip processing of flow, bypass if possible */
    SSL_CNF_ENC_HANDLE_FULL = 2,    /**< handle fully like any other proto */
};

typedef struct SslConfig_ {
    enum SslConfigEncryptHandling encrypt_mode;
    /** dynamic setting for ja3 and ja4: can be enabled on demand if not
     *  explicitly disabled. */
    SC_ATOMIC_DECLARE(int, enable_ja3);
    bool disable_ja3; /**< ja3 explicitly disabled. Don't enable on demand. */
    SC_ATOMIC_DECLARE(int, enable_ja4);
    bool disable_ja4; /**< ja4 explicitly disabled. Don't enable on demand. */
} SslConfig;

SslConfig ssl_config;

/* SSLv3 record types */
#define SSLV3_CHANGE_CIPHER_SPEC       20
#define SSLV3_ALERT_PROTOCOL           21
#define SSLV3_HANDSHAKE_PROTOCOL       22
#define SSLV3_APPLICATION_PROTOCOL     23
#define SSLV3_HEARTBEAT_PROTOCOL       24

/* SSLv3 handshake protocol types */
#define SSLV3_HS_HELLO_REQUEST          0
#define SSLV3_HS_CLIENT_HELLO           1
#define SSLV3_HS_SERVER_HELLO           2
#define SSLV3_HS_NEW_SESSION_TICKET     4
#define SSLV3_HS_CERTIFICATE           11
#define SSLV3_HS_SERVER_KEY_EXCHANGE   12
#define SSLV3_HS_CERTIFICATE_REQUEST   13
#define SSLV3_HS_SERVER_HELLO_DONE     14
#define SSLV3_HS_CERTIFICATE_VERIFY    15
#define SSLV3_HS_CLIENT_KEY_EXCHANGE   16
#define SSLV3_HS_FINISHED              20
#define SSLV3_HS_CERTIFICATE_URL       21
#define SSLV3_HS_CERTIFICATE_STATUS    22

/* SSLv2 protocol message types */
#define SSLV2_MT_ERROR                  0
#define SSLV2_MT_CLIENT_HELLO           1
#define SSLV2_MT_CLIENT_MASTER_KEY      2
#define SSLV2_MT_CLIENT_FINISHED        3
#define SSLV2_MT_SERVER_HELLO           4
#define SSLV2_MT_SERVER_VERIFY          5
#define SSLV2_MT_SERVER_FINISHED        6
#define SSLV2_MT_REQUEST_CERTIFICATE    7
#define SSLV2_MT_CLIENT_CERTIFICATE     8

#define SSLV3_RECORD_HDR_LEN            5
#define SSLV3_MESSAGE_HDR_LEN           4
/** max length according to RFC 5246 6.2.2 is 2^14 + 1024 */
#define SSLV3_RECORD_MAX_LEN ((1 << 14) + 1024)

#define SSLV3_CLIENT_HELLO_VERSION_LEN  2
#define SSLV3_CLIENT_HELLO_RANDOM_LEN  32

/* TLS heartbeat protocol types */
#define TLS_HB_REQUEST                  1
#define TLS_HB_RESPONSE                 2

#define SSL_RECORD_MINIMUM_LENGTH       6

#define SHA1_STRING_LENGTH             60

#define HAS_SPACE(n) ((uint64_t)(input - initial_input) + (uint64_t)(n) <= (uint64_t)(input_len))

struct SSLDecoderResult {
    int retval;      // nr bytes consumed from input, or < 0 on error
    uint32_t needed; // more bytes needed
};
#define SSL_DECODER_ERROR(e)                                                                       \
    (struct SSLDecoderResult)                                                                      \
    {                                                                                              \
        (e), 0                                                                                     \
    }
#define SSL_DECODER_OK(c)                                                                          \
    (struct SSLDecoderResult)                                                                      \
    {                                                                                              \
        (c), 0                                                                                     \
    }
#define SSL_DECODER_INCOMPLETE(c, n)                                                               \
    (struct SSLDecoderResult)                                                                      \
    {                                                                                              \
        (c), (n)                                                                                   \
    }

static inline int SafeMemcpy(void *dst, size_t dst_offset, size_t dst_size,
        const void *src, size_t src_offset, size_t src_size, size_t src_tocopy) WARN_UNUSED;

static inline int SafeMemcpy(void *dst, size_t dst_offset, size_t dst_size,
        const void *src, size_t src_offset, size_t src_size, size_t src_tocopy)
{
    DEBUG_VALIDATE_BUG_ON(dst_offset >= dst_size);
    DEBUG_VALIDATE_BUG_ON(src_offset >= src_size);
    DEBUG_VALIDATE_BUG_ON(src_tocopy > (src_size - src_offset));
    DEBUG_VALIDATE_BUG_ON(src_tocopy > (dst_size - dst_offset));

    if (dst_offset < dst_size && src_offset < src_size &&
        src_tocopy <= (src_size - src_offset) &&
        src_tocopy <= (dst_size - dst_offset)) {
        memcpy(dst + dst_offset, src + src_offset, src_tocopy);
        return 0;
    }
    return -1;
}

#ifdef DEBUG_VALIDATION
#define ValidateRecordState(connp)                                              \
    do {                                                                        \
        DEBUG_VALIDATE_BUG_ON(((connp)->record_length + SSLV3_RECORD_HDR_LEN) < \
                (connp)->bytes_processed);                                      \
    } while(0);
#else
#define ValidateRecordState(...)
#endif

#define SSLParserHSReset(connp)                                                                    \
    do {                                                                                           \
        (connp)->handshake_type = 0;                                                               \
        (connp)->message_length = 0;                                                               \
    } while (0)

#define SSLParserReset(state)                       \
    do {                                            \
        SCLogDebug("resetting state");              \
        (state)->curr_connp->bytes_processed = 0;   \
        SSLParserHSReset((state)->curr_connp);      \
    } while(0)

#define SSLSetEvent(ssl_state, event)                                                              \
    do {                                                                                           \
        SCLogDebug("setting event %u", (event));                                                   \
        if ((ssl_state) == NULL) {                                                                 \
            SCLogDebug("could not set decoder event %u", event);                                   \
        } else {                                                                                   \
            AppLayerDecoderEventsSetEventRaw(&(ssl_state)->tx_data.events, (event));               \
            (ssl_state)->events++;                                                                 \
        }                                                                                          \
    } while (0)

static void *SSLGetTx(void *state, uint64_t tx_id)
{
    SSLState *ssl_state = (SSLState *)state;
    return ssl_state;
}

static uint64_t SSLGetTxCnt(void *state)
{
    /* single tx */
    return 1;
}

static int SSLGetAlstateProgress(void *tx, uint8_t direction)
{
    SSLState *ssl_state = (SSLState *)tx;

    /* we don't care about direction, only that app-layer parser is done
       and have sent an EOF */
    if (ssl_state->flags & SSL_AL_FLAG_STATE_FINISHED) {
        return TLS_STATE_FINISHED;
    }

    /* we want the logger to log when the handshake is done, even if the
       state is not finished */
    if (ssl_state->flags & SSL_AL_FLAG_HANDSHAKE_DONE) {
        return TLS_HANDSHAKE_DONE;
    }

    if (direction == STREAM_TOSERVER &&
        (ssl_state->server_connp.cert0_subject != NULL ||
         ssl_state->server_connp.cert0_issuerdn != NULL))
    {
        return TLS_STATE_CERT_READY;
    }

    return TLS_STATE_IN_PROGRESS;
}

static AppLayerTxData *SSLGetTxData(void *vtx)
{
    SSLState *ssl_state = (SSLState *)vtx;
    return &ssl_state->tx_data;
}

static AppLayerStateData *SSLGetStateData(void *vstate)
{
    SSLState *ssl_state = (SSLState *)vstate;
    return &ssl_state->state_data;
}

void SSLVersionToString(uint16_t version, char *buffer)
{
    buffer[0] = '\0';

    switch (version) {
        case TLS_VERSION_UNKNOWN:
            strlcat(buffer, "UNDETERMINED", 13);
            break;
        case SSL_VERSION_2:
            strlcat(buffer, "SSLv2", 6);
            break;
        case SSL_VERSION_3:
            strlcat(buffer, "SSLv3", 6);
            break;
        case TLS_VERSION_10:
            strlcat(buffer, "TLSv1", 6);
            break;
        case TLS_VERSION_11:
            strlcat(buffer, "TLS 1.1", 8);
            break;
        case TLS_VERSION_12:
            strlcat(buffer, "TLS 1.2", 8);
            break;
        case TLS_VERSION_13:
            strlcat(buffer, "TLS 1.3", 8);
            break;
        case TLS_VERSION_13_DRAFT28:
            strlcat(buffer, "TLS 1.3 draft-28", 17);
            break;
        case TLS_VERSION_13_DRAFT27:
            strlcat(buffer, "TLS 1.3 draft-27", 17);
            break;
        case TLS_VERSION_13_DRAFT26:
            strlcat(buffer, "TLS 1.3 draft-26", 17);
            break;
        case TLS_VERSION_13_DRAFT25:
            strlcat(buffer, "TLS 1.3 draft-25", 17);
            break;
        case TLS_VERSION_13_DRAFT24:
            strlcat(buffer, "TLS 1.3 draft-24", 17);
            break;
        case TLS_VERSION_13_DRAFT23:
            strlcat(buffer, "TLS 1.3 draft-23", 17);
            break;
        case TLS_VERSION_13_DRAFT22:
            strlcat(buffer, "TLS 1.3 draft-22", 17);
            break;
        case TLS_VERSION_13_DRAFT21:
            strlcat(buffer, "TLS 1.3 draft-21", 17);
            break;
        case TLS_VERSION_13_DRAFT20:
            strlcat(buffer, "TLS 1.3 draft-20", 17);
            break;
        case TLS_VERSION_13_DRAFT19:
            strlcat(buffer, "TLS 1.3 draft-19", 17);
            break;
        case TLS_VERSION_13_DRAFT18:
            strlcat(buffer, "TLS 1.3 draft-18", 17);
            break;
        case TLS_VERSION_13_DRAFT17:
            strlcat(buffer, "TLS 1.3 draft-17", 17);
            break;
        case TLS_VERSION_13_DRAFT16:
            strlcat(buffer, "TLS 1.3 draft-16", 17);
            break;
        case TLS_VERSION_13_PRE_DRAFT16:
            strlcat(buffer, "TLS 1.3 draft-<16", 18);
            break;
        case TLS_VERSION_13_DRAFT20_FB:
            strlcat(buffer, "TLS 1.3 draft-20-fb", 20);
            break;
        case TLS_VERSION_13_DRAFT21_FB:
            strlcat(buffer, "TLS 1.3 draft-21-fb", 20);
            break;
        case TLS_VERSION_13_DRAFT22_FB:
            strlcat(buffer, "TLS 1.3 draft-22-fb", 20);
            break;
        case TLS_VERSION_13_DRAFT23_FB:
            strlcat(buffer, "TLS 1.3 draft-23-fb", 20);
            break;
        case TLS_VERSION_13_DRAFT26_FB:
            strlcat(buffer, "TLS 1.3 draft-26-fb", 20);
            break;
        default:
            snprintf(buffer, 7, "0x%04x", version);
            break;
    }
}

static void TlsDecodeHSCertificateErrSetEvent(SSLState *ssl_state, uint32_t err)
{
    switch(err) {
        case ERR_EXTRACT_VALIDITY:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_VALIDITY);
            break;
        case ERR_EXTRACT_ISSUER:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_ISSUER);
            break;
        case ERR_EXTRACT_SUBJECT:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_SUBJECT);
            break;
        case ERR_INVALID_DER:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_DER);
            break;
        case ERR_INVALID_EXTENSIONS:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_EXTENSIONS);
            break;
        case ERR_INVALID_DATE:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_DATE);
            break;
        case ERR_INVALID_X509NAME:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_X509NAME);
            break;
        case ERR_INVALID_ALGORITHMIDENTIFIER:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_ALGORITHMIDENTIFIER);
            break;
        case ERR_INVALID_SERIAL:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_SERIAL);
            break;
        case ERR_INVALID_VERSION:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_VERSION);
            break;
        case ERR_INVALID_LENGTH:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_LENGTH);
            break;
        case ERR_INVALID_CERTIFICATE:
        default:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_CERTIFICATE);
            break;
    }
}

static inline int TlsDecodeHSCertificateFingerprint(
        SSLStateConnp *connp, const uint8_t *input, uint32_t cert_len)
{
    if (unlikely(connp->cert0_fingerprint != NULL))
        return 0;

    connp->cert0_fingerprint = SCCalloc(1, SHA1_STRING_LENGTH);
    if (connp->cert0_fingerprint == NULL)
        return -1;

    uint8_t hash[SC_SHA1_LEN];
    if (SCSha1HashBuffer(input, cert_len, hash, sizeof(hash)) == 1) {
        rs_to_hex_sep(
                (uint8_t *)connp->cert0_fingerprint, SHA1_STRING_LENGTH, ':', hash, SC_SHA1_LEN);
    }
    return 0;
}

static inline int TlsDecodeHSCertificateAddCertToChain(
        SSLStateConnp *connp, const uint8_t *input, uint32_t cert_len)
{
    SSLCertsChain *cert = SCCalloc(1, sizeof(SSLCertsChain));
    if (cert == NULL)
        return -1;

    cert->cert_data = (uint8_t *)input;
    cert->cert_len = cert_len;
    TAILQ_INSERT_TAIL(&connp->certs, cert, next);

    return 0;
}

static int TlsDecodeHSCertificate(SSLState *ssl_state, SSLStateConnp *connp,
        const uint8_t *const initial_input, const uint32_t input_len, const int certn)
{
    const uint8_t *input = (uint8_t *)initial_input;
    uint32_t err_code = 0;
    X509 *x509 = NULL;
    int rc = 0;

    if (!(HAS_SPACE(3)))
        goto invalid_cert;

    uint32_t cert_len = *input << 16 | *(input + 1) << 8 | *(input + 2);
    input += 3;

    if (!(HAS_SPACE(cert_len)))
        goto invalid_cert;

    /* only store fields from the first certificate in the chain */
    if (certn == 0 && connp->cert0_subject == NULL && connp->cert0_issuerdn == NULL &&
            connp->cert0_serial == NULL) {
        x509 = rs_x509_decode(input, cert_len, &err_code);
        if (x509 == NULL) {
            TlsDecodeHSCertificateErrSetEvent(ssl_state, err_code);
            goto next;
        }

        char *str = rs_x509_get_subject(x509);
        if (str == NULL) {
            err_code = ERR_EXTRACT_SUBJECT;
            goto error;
        }
        connp->cert0_subject = str;

        str = rs_x509_get_issuer(x509);
        if (str == NULL) {
            err_code = ERR_EXTRACT_ISSUER;
            goto error;
        }
        connp->cert0_issuerdn = str;

        str = rs_x509_get_serial(x509);
        if (str == NULL) {
            err_code = ERR_INVALID_SERIAL;
            goto error;
        }
        connp->cert0_serial = str;

        rc = rs_x509_get_validity(x509, &connp->cert0_not_before, &connp->cert0_not_after);
        if (rc != 0) {
            err_code = ERR_EXTRACT_VALIDITY;
            goto error;
        }

        rs_x509_free(x509);
        x509 = NULL;

        rc = TlsDecodeHSCertificateFingerprint(connp, input, cert_len);
        if (rc != 0) {
            SCLogDebug("TlsDecodeHSCertificateFingerprint failed with %d", rc);
            goto error;
        }
    }

    rc = TlsDecodeHSCertificateAddCertToChain(connp, input, cert_len);
    if (rc != 0) {
        SCLogDebug("TlsDecodeHSCertificateAddCertToChain failed with %d", rc);
        goto error;
    }

next:
    input += cert_len;
    return (input - initial_input);

error:
    if (err_code != 0)
        TlsDecodeHSCertificateErrSetEvent(ssl_state, err_code);
    if (x509 != NULL)
        rs_x509_free(x509);
    return -1;

invalid_cert:
    SCLogDebug("TLS invalid certificate");
    SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_CERTIFICATE);
    return -1;
}

/** \internal
 * \brief parse cert data in a certificate handshake message
 *        will be called with all data.
 * \retval consumed bytes consumed or -1 on error
 */
static int TlsDecodeHSCertificates(SSLState *ssl_state, SSLStateConnp *connp,
        const uint8_t *const initial_input, const uint32_t input_len)
{
    const uint8_t *input = (uint8_t *)initial_input;

    if (!(HAS_SPACE(3)))
        return -1;

    const uint32_t cert_chain_len = *input << 16 | *(input + 1) << 8 | *(input + 2);
    input += 3;

    if (!(HAS_SPACE(cert_chain_len)))
        return -1;

    if (connp->certs_buffer != NULL) {
        // TODO should we set an event here?
        return -1;
    }

    connp->certs_buffer = SCCalloc(1, cert_chain_len);
    if (connp->certs_buffer == NULL) {
        return -1;
    }
    connp->certs_buffer_size = cert_chain_len;
    memcpy(connp->certs_buffer, input, cert_chain_len);

    int cert_cnt = 0;
    uint32_t processed_len = 0;
    /* coverity[tainted_data] */
    while (processed_len < cert_chain_len) {
        int rc = TlsDecodeHSCertificate(ssl_state, connp, connp->certs_buffer + processed_len,
                connp->certs_buffer_size - processed_len, cert_cnt);
        if (rc <= 0) { // 0 should be impossible, but lets be defensive
            return -1;
        }
        DEBUG_VALIDATE_BUG_ON(processed_len + (uint32_t)rc > cert_chain_len);
        if (processed_len + (uint32_t)rc > cert_chain_len) {
            return -1;
        }

        processed_len += (uint32_t)rc;
    }

    return processed_len + 3;
}

/**
 * \inline
 * \brief Check if value is GREASE.
 *
 * http://tools.ietf.org/html/draft-davidben-tls-grease-00
 *
 * \param value Value to check.
 *
 * \retval 1 if is GREASE.
 * \retval 0 if not is GREASE.
 */
static inline int TLSDecodeValueIsGREASE(const uint16_t value)
{
    switch (value)
    {
        case 0x0a0a:
        case 0x1a1a:
        case 0x2a2a:
        case 0x3a3a:
        case 0x4a4a:
        case 0x5a5a:
        case 0x6a6a:
        case 0x7a7a:
        case 0x8a8a:
        case 0x9a9a:
        case 0xaaaa:
        case 0xbaba:
        case 0xcaca:
        case 0xdada:
        case 0xeaea:
        case 0xfafa:
            return 1;
        default:
            return 0;
    }
}

static inline int TLSDecodeHSHelloVersion(SSLState *ssl_state,
                                          const uint8_t * const initial_input,
                                          const uint32_t input_len)
{
    uint8_t *input = (uint8_t *)initial_input;

    if (!(HAS_SPACE(SSLV3_CLIENT_HELLO_VERSION_LEN))) {
        SCLogDebug("TLS handshake invalid length");
        SSLSetEvent(ssl_state,
                    TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
        return -1;
    }

    uint16_t version = (uint16_t)(*input << 8) | *(input + 1);
    ssl_state->curr_connp->version = version;

    if (ssl_state->curr_connp->ja4 != NULL &&
            ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
        SCJA4SetTLSVersion(ssl_state->curr_connp->ja4, version);
    }

    /* TLSv1.3 draft1 to draft21 use the version field as earlier TLS
       versions, instead of using the supported versions extension. */
    if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
            ((ssl_state->curr_connp->version == TLS_VERSION_13) ||
            (((ssl_state->curr_connp->version >> 8) & 0xff) == 0x7f))) {
        ssl_state->flags |= SSL_AL_FLAG_LOG_WITHOUT_CERT;
    }

    /* Catch some early TLSv1.3 draft implementations that does not conform
       to the draft version. */
    if ((ssl_state->curr_connp->version >= 0x7f01) &&
            (ssl_state->curr_connp->version < 0x7f10)) {
        ssl_state->curr_connp->version = TLS_VERSION_13_PRE_DRAFT16;
    }

    /* TLSv1.3 drafts from draft1 to draft15 use 0x0304 (TLSv1.3) as the
       version number, which makes it hard to accurately pinpoint the
       exact draft version. */
    else if (ssl_state->curr_connp->version == TLS_VERSION_13) {
        ssl_state->curr_connp->version = TLS_VERSION_13_PRE_DRAFT16;
    }

    if (SC_ATOMIC_GET(ssl_config.enable_ja3) && ssl_state->curr_connp->ja3_str == NULL) {
        ssl_state->curr_connp->ja3_str = Ja3BufferInit();
        if (ssl_state->curr_connp->ja3_str == NULL)
            return -1;

        int rc = Ja3BufferAddValue(&ssl_state->curr_connp->ja3_str, version);
        if (rc != 0)
            return -1;
    }

    input += SSLV3_CLIENT_HELLO_VERSION_LEN;

    return (input - initial_input);
}

static inline int TLSDecodeHSHelloRandom(SSLState *ssl_state,
                                         const uint8_t * const initial_input,
                                         const uint32_t input_len)
{
    uint8_t *input = (uint8_t *)initial_input;

    if (!(HAS_SPACE(SSLV3_CLIENT_HELLO_RANDOM_LEN))) {
        SCLogDebug("TLS handshake invalid length");
        SSLSetEvent(ssl_state,
                    TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
        return -1;
    }

    if (ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) {
        memcpy(ssl_state->server_connp.random, input, TLS_RANDOM_LEN);
        ssl_state->flags |= TLS_TS_RANDOM_SET;
    } else if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
        memcpy(ssl_state->client_connp.random, input, TLS_RANDOM_LEN);
        ssl_state->flags |= TLS_TC_RANDOM_SET;
    }

    /* Skip random */
    input += SSLV3_CLIENT_HELLO_RANDOM_LEN;

    return (input - initial_input);
}

static inline int TLSDecodeHSHelloSessionID(SSLState *ssl_state,
                                            const uint8_t * const initial_input,
                                            const uint32_t input_len)
{
    uint8_t *input = (uint8_t *)initial_input;

    if (!(HAS_SPACE(1)))
        goto invalid_length;

    uint8_t session_id_length = *input;
    input += 1;

    if (!(HAS_SPACE(session_id_length)))
        goto invalid_length;

    if (session_id_length != 0 && ssl_state->curr_connp->session_id == NULL) {
        ssl_state->curr_connp->session_id = SCMalloc(session_id_length);

        if (unlikely(ssl_state->curr_connp->session_id == NULL)) {
            return -1;
        }

        if (SafeMemcpy(ssl_state->curr_connp->session_id, 0, session_id_length,
                    input, 0, input_len, session_id_length) != 0) {
            return -1;
        }
        ssl_state->curr_connp->session_id_length = session_id_length;

        if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
                ssl_state->client_connp.session_id != NULL &&
                ssl_state->server_connp.session_id != NULL) {
            if ((ssl_state->client_connp.session_id_length ==
                    ssl_state->server_connp.session_id_length) &&
                    (memcmp(ssl_state->server_connp.session_id,
                    ssl_state->client_connp.session_id, session_id_length) == 0)) {
                ssl_state->flags |= SSL_AL_FLAG_SESSION_RESUMED;
            }
        }
    }

    input += session_id_length;

    return (input - initial_input);

invalid_length:
    SCLogDebug("TLS handshake invalid length");
    SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
    return -1;
}

static inline int TLSDecodeHSHelloCipherSuites(SSLState *ssl_state,
                                           const uint8_t * const initial_input,
                                           const uint32_t input_len)
{
    const uint8_t *input = initial_input;

    if (!(HAS_SPACE(2)))
        goto invalid_length;

    uint16_t cipher_suites_length;

    if (ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) {
        cipher_suites_length = 2;
    } else if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
        cipher_suites_length = (uint16_t)(*input << 8) | *(input + 1);
        input += 2;
    } else {
        return -1;
    }

    if (!(HAS_SPACE(cipher_suites_length)))
        goto invalid_length;

    /* Cipher suites length should always be divisible by 2 */
    if ((cipher_suites_length % 2) != 0) {
        goto invalid_length;
    }

    const bool enable_ja3 =
            SC_ATOMIC_GET(ssl_config.enable_ja3) && ssl_state->curr_connp->ja3_hash == NULL;

    if (enable_ja3 || SC_ATOMIC_GET(ssl_config.enable_ja4)) {
        JA3Buffer *ja3_cipher_suites = NULL;

        if (enable_ja3) {
            ja3_cipher_suites = Ja3BufferInit();
            if (ja3_cipher_suites == NULL)
                return -1;
        }

        uint16_t processed_len = 0;
        /* coverity[tainted_data] */
        while (processed_len < cipher_suites_length)
        {
            if (!(HAS_SPACE(2))) {
                if (enable_ja3) {
                    Ja3BufferFree(&ja3_cipher_suites);
                }
                goto invalid_length;
            }

            uint16_t cipher_suite = (uint16_t)(*input << 8) | *(input + 1);
            input += 2;

            if (TLSDecodeValueIsGREASE(cipher_suite) != 1) {
                if (ssl_state->curr_connp->ja4 != NULL &&
                        ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
                    SCJA4AddCipher(ssl_state->curr_connp->ja4, cipher_suite);
                }
                if (enable_ja3) {
                    int rc = Ja3BufferAddValue(&ja3_cipher_suites, cipher_suite);
                    if (rc != 0) {
                        return -1;
                    }
                }
            }
            processed_len += 2;
        }

        if (enable_ja3) {
            int rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str, &ja3_cipher_suites);
            if (rc == -1) {
                return -1;
            }
        }

    } else {
        /* Skip cipher suites */
        input += cipher_suites_length;
    }

    return (input - initial_input);

invalid_length:
    SCLogDebug("TLS handshake invalid length");
    SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
    return -1;
}

static inline int TLSDecodeHSHelloCompressionMethods(SSLState *ssl_state,
                                           const uint8_t * const initial_input,
                                           const uint32_t input_len)
{
    const uint8_t *input = initial_input;

    if (!(HAS_SPACE(1)))
        goto invalid_length;

    /* Skip compression methods */
    if (ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) {
        input += 1;
    } else {
        uint8_t compression_methods_length = *input;
        input += 1;

        if (!(HAS_SPACE(compression_methods_length)))
            goto invalid_length;

        input += compression_methods_length;
    }

    return (input - initial_input);

invalid_length:
    SCLogDebug("TLS handshake invalid_length");
    SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
    return -1;
}

static inline int TLSDecodeHSHelloExtensionSni(SSLState *ssl_state,
                                           const uint8_t * const initial_input,
                                           const uint32_t input_len)
{
    uint8_t *input = (uint8_t *)initial_input;

    /* Empty extension */
    if (input_len == 0)
        return 0;

    if (!(HAS_SPACE(2)))
        goto invalid_length;

    /* Skip sni_list_length */
    input += 2;

    if (!(HAS_SPACE(1)))
        goto invalid_length;

    uint8_t sni_type = *input;
    input += 1;

    /* Currently the only type allowed is host_name
       (RFC6066 section 3). */
    if (sni_type != SSL_SNI_TYPE_HOST_NAME) {
        SCLogDebug("Unknown SNI type");
        SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_INVALID_SNI_TYPE);
        return -1;
    }

    if (!(HAS_SPACE(2)))
        goto invalid_length;

    uint16_t sni_len = (uint16_t)(*input << 8) | *(input + 1);
    input += 2;

    /* host_name contains the fully qualified domain name,
       and should therefore be limited by the maximum domain
       name length. */
    if (!(HAS_SPACE(sni_len)) || sni_len > 255 || sni_len == 0) {
        SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_INVALID_SNI_LENGTH);
        return -1;
    }

    /* There must not be more than one extension of the same
       type (RFC5246 section 7.4.1.4). */
    if (ssl_state->curr_connp->sni) {
        SCLogDebug("Multiple SNI extensions");
        SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_MULTIPLE_SNI_EXTENSIONS);
        input += sni_len;
        return (input - initial_input);
    }

    const size_t sni_strlen = sni_len + 1;
    ssl_state->curr_connp->sni = SCMalloc(sni_strlen);
    if (unlikely(ssl_state->curr_connp->sni == NULL))
        return -1;

    const size_t consumed = input - initial_input;
    if (SafeMemcpy(ssl_state->curr_connp->sni, 0, sni_strlen,
                initial_input, consumed, input_len, sni_len) != 0) {
        SCFree(ssl_state->curr_connp->sni);
        ssl_state->curr_connp->sni = NULL;
        return -1;
    }
    ssl_state->curr_connp->sni[sni_strlen-1] = 0;

    input += sni_len;

    return (input - initial_input);

invalid_length:
    SCLogDebug("TLS handshake invalid length");
    SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);


    return -1;
}

static inline int TLSDecodeHSHelloExtensionSupportedVersions(SSLState *ssl_state,
                                             const uint8_t * const initial_input,
                                             const uint32_t input_len)
{
    const uint8_t *input = initial_input;

    /* Empty extension */
    if (input_len == 0)
        return 0;

    if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
        if (!(HAS_SPACE(1)))
            goto invalid_length;

        uint8_t supported_ver_len = *input;
        input += 1;

        if (supported_ver_len < 2)
            goto invalid_length;

        if (!(HAS_SPACE(supported_ver_len)))
            goto invalid_length;

        /* Use the first (and prefered) valid version as client version,
         * skip over GREASE and other possible noise. */
        uint16_t i = 0;
        while (i + 1 < (uint16_t)supported_ver_len) {
            uint16_t ver = (uint16_t)(input[i] << 8) | input[i + 1];
            if (TLSVersionValid(ver)) {
                ssl_state->curr_connp->version = ver;
                if (ssl_state->curr_connp->ja4 != NULL &&
                        ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
                    SCJA4SetTLSVersion(ssl_state->curr_connp->ja4, ver);
                }
                break;
            }
            i += 2;
        }

        /* Set a flag to indicate that we have seen this extension */
        ssl_state->flags |= SSL_AL_FLAG_CH_VERSION_EXTENSION;

        input += supported_ver_len;
    }
    else if (ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) {
        if (!(HAS_SPACE(2)))
            goto invalid_length;

        uint16_t ver = (uint16_t)(*input << 8) | *(input + 1);

        if ((ssl_state->flags & SSL_AL_FLAG_CH_VERSION_EXTENSION) &&
                (ver > TLS_VERSION_12)) {
            ssl_state->flags |= SSL_AL_FLAG_LOG_WITHOUT_CERT;
        }

        ssl_state->curr_connp->version = ver;
        input += 2;
    }

    return (input - initial_input);

invalid_length:
    SCLogDebug("TLS handshake invalid length");
    SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);

    return -1;
}

static inline int TLSDecodeHSHelloExtensionEllipticCurves(SSLState *ssl_state,
                                          const uint8_t * const initial_input,
                                          const uint32_t input_len,
                                          JA3Buffer *ja3_elliptic_curves)
{
    const uint8_t *input = initial_input;

    /* Empty extension */
    if (input_len == 0)
        return 0;

    if (!(HAS_SPACE(2)))
        goto invalid_length;

    uint16_t elliptic_curves_len = (uint16_t)(*input << 8) | *(input + 1);
    input += 2;

    if (!(HAS_SPACE(elliptic_curves_len)))
        goto invalid_length;

    if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) && ja3_elliptic_curves) {
        uint16_t ec_processed_len = 0;
        /* coverity[tainted_data] */
        while (ec_processed_len < elliptic_curves_len)
        {
            if (!(HAS_SPACE(2)))
                goto invalid_length;

            uint16_t elliptic_curve = (uint16_t)(*input << 8) | *(input + 1);
            input += 2;

            if (TLSDecodeValueIsGREASE(elliptic_curve) != 1) {
                int rc = Ja3BufferAddValue(&ja3_elliptic_curves,
                                           elliptic_curve);
                if (rc != 0)
                    return -1;
            }

            ec_processed_len += 2;
        }

    } else {
        /* Skip elliptic curves */
        input += elliptic_curves_len;
    }

    return (input - initial_input);

invalid_length:
    SCLogDebug("TLS handshake invalid length");
    SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);

    return -1;
}

static inline int TLSDecodeHSHelloExtensionEllipticCurvePF(SSLState *ssl_state,
                                            const uint8_t * const initial_input,
                                            const uint32_t input_len,
                                            JA3Buffer *ja3_elliptic_curves_pf)
{
    const uint8_t *input = initial_input;

    /* Empty extension */
    if (input_len == 0)
        return 0;

    if (!(HAS_SPACE(1)))
        goto invalid_length;

    uint8_t ec_pf_len = *input;
    input += 1;

    if (!(HAS_SPACE(ec_pf_len)))
        goto invalid_length;

    if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) && ja3_elliptic_curves_pf) {
        uint8_t ec_pf_processed_len = 0;
        /* coverity[tainted_data] */
        while (ec_pf_processed_len < ec_pf_len)
        {
            uint8_t elliptic_curve_pf = *input;
            input += 1;

            if (TLSDecodeValueIsGREASE(elliptic_curve_pf) != 1) {
                int rc = Ja3BufferAddValue(&ja3_elliptic_curves_pf,
                                           elliptic_curve_pf);
                if (rc != 0)
                    return -1;
            }

            ec_pf_processed_len += 1;
        }

    } else {
        /* Skip elliptic curve point formats */
        input += ec_pf_len;
    }

    return (input - initial_input);

invalid_length:
    SCLogDebug("TLS handshake invalid length");
    SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);

    return -1;
}

static inline int TLSDecodeHSHelloExtensionSigAlgorithms(
        SSLState *ssl_state, const uint8_t *const initial_input, const uint32_t input_len)
{
    const uint8_t *input = initial_input;

    /* Empty extension */
    if (input_len == 0)
        return 0;

    if (!(HAS_SPACE(2)))
        goto invalid_length;

    uint16_t sigalgo_len = (uint16_t)(*input << 8) | *(input + 1);
    input += 2;

    /* Signature algorithms length should always be divisible by 2 */
    if ((sigalgo_len % 2) != 0) {
        goto invalid_length;
    }

    if (!(HAS_SPACE(sigalgo_len)))
        goto invalid_length;

    if (ssl_state->curr_connp->ja4 != NULL &&
            ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
        uint16_t sigalgo_processed_len = 0;
        while (sigalgo_processed_len < sigalgo_len) {
            uint16_t sigalgo = (uint16_t)(*input << 8) | *(input + 1);
            input += 2;
            sigalgo_processed_len += 2;

            SCJA4AddSigAlgo(ssl_state->curr_connp->ja4, sigalgo);
        }
    } else {
        /* Skip signature algorithms */
        input += sigalgo_len;
    }

    return (input - initial_input);

invalid_length:
    SCLogDebug("Signature algorithm list invalid length");
    SSLSetEvent(ssl_state, TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);

    return -1;
}

static inline int TLSDecodeHSHelloExtensionALPN(
        SSLState *ssl_state, const uint8_t *const initial_input, const uint32_t input_len)
{
    const uint8_t *input = initial_input;

    /* Empty extension */
    if (input_len == 0)
        return 0;

    if (!(HAS_SPACE(2)))
        goto invalid_length;

    uint16_t alpn_len = (uint16_t)(*input << 8) | *(input + 1);
    input += 2;

    if (!(HAS_SPACE(alpn_len)))
        goto invalid_length;

    if (ssl_state->curr_connp->ja4 != NULL &&
            ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
        /* We use 32 bits here to avoid potentially overflowing a value that
           needs to be compared to an unsigned 16-bit value. */
        uint32_t alpn_processed_len = 0;
        while (alpn_processed_len < alpn_len) {
            uint8_t protolen = *input;
            input += 1;
            alpn_processed_len += 1;

            if (!(HAS_SPACE(protolen)))
                goto invalid_length;

            /* Check if reading another protolen bytes would exceed the
               overall ALPN length; if so, skip and continue */
            if (alpn_processed_len + protolen > ((uint32_t)alpn_len)) {
                input += alpn_len - alpn_processed_len;
                break;
            }

            /* Only record the first value for JA4 */
            if (alpn_processed_len == 1) {
                SCJA4SetALPN(ssl_state->curr_connp->ja4, (const char *)input, protolen);
            }

            alpn_processed_len += protolen;
            input += protolen;
        }
    } else {
        /* Skip ALPN protocols */
        input += alpn_len;
    }

    return (input - initial_input);

invalid_length:
    SCLogDebug("ALPN list invalid length");
    SSLSetEvent(ssl_state, TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);

    return -1;
}

static inline int TLSDecodeHSHelloExtensions(SSLState *ssl_state,
                                         const uint8_t * const initial_input,
                                         const uint32_t input_len)
{
    const uint8_t *input = initial_input;

    int ret;
    int rc;
    // if ja3_hash is already computed, do not use new hello to augment ja3_str
    const bool ja3 =
            (SC_ATOMIC_GET(ssl_config.enable_ja3) == 1) && ssl_state->curr_connp->ja3_hash == NULL;

    JA3Buffer *ja3_extensions = NULL;
    JA3Buffer *ja3_elliptic_curves = NULL;
    JA3Buffer *ja3_elliptic_curves_pf = NULL;

    if (ja3) {
        ja3_extensions = Ja3BufferInit();
        if (ja3_extensions == NULL)
            goto error;

        if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
            ja3_elliptic_curves = Ja3BufferInit();
            if (ja3_elliptic_curves == NULL)
                goto error;

            ja3_elliptic_curves_pf = Ja3BufferInit();
            if (ja3_elliptic_curves_pf == NULL)
                goto error;
        }
    }

    /* Extensions are optional (RFC5246 section 7.4.1.2) */
    if (!(HAS_SPACE(2)))
        goto end;

    uint16_t extensions_len = (uint16_t)(*input << 8) | *(input + 1);
    input += 2;

    if (!(HAS_SPACE(extensions_len)))
        goto invalid_length;

    uint16_t processed_len = 0;
    /* coverity[tainted_data] */
    while (processed_len < extensions_len)
    {
        if (!(HAS_SPACE(2)))
            goto invalid_length;

        uint16_t ext_type = (uint16_t)(*input << 8) | *(input + 1);
        input += 2;

        if (!(HAS_SPACE(2)))
            goto invalid_length;

        uint16_t ext_len = (uint16_t)(*input << 8) | *(input + 1);
        input += 2;

        if (!(HAS_SPACE(ext_len)))
            goto invalid_length;

        switch (ext_type) {
            case SSL_EXTENSION_SNI:
            {
                /* coverity[tainted_data] */
                ret = TLSDecodeHSHelloExtensionSni(ssl_state, input,
                                                   ext_len);
                if (ret < 0)
                    goto end;

                input += ret;

                break;
            }

            case SSL_EXTENSION_ELLIPTIC_CURVES:
            {
                /* coverity[tainted_data] */
                ret = TLSDecodeHSHelloExtensionEllipticCurves(ssl_state, input,
                                                              ext_len,
                                                              ja3_elliptic_curves);
                if (ret < 0)
                    goto end;

                input += ret;

                break;
            }

            case SSL_EXTENSION_EC_POINT_FORMATS:
            {
                /* coverity[tainted_data] */
                ret = TLSDecodeHSHelloExtensionEllipticCurvePF(ssl_state, input,
                                                               ext_len,
                                                               ja3_elliptic_curves_pf);
                if (ret < 0)
                    goto end;

                input += ret;

                break;
            }

            case SSL_EXTENSION_SIGNATURE_ALGORITHMS: {
                /* coverity[tainted_data] */
                ret = TLSDecodeHSHelloExtensionSigAlgorithms(ssl_state, input, ext_len);
                if (ret < 0)
                    goto end;

                input += ret;

                break;
            }

            case SSL_EXTENSION_ALPN: {
                /* coverity[tainted_data] */
                ret = TLSDecodeHSHelloExtensionALPN(ssl_state, input, ext_len);
                if (ret < 0)
                    goto end;

                input += ext_len;

                break;
            }

            case SSL_EXTENSION_EARLY_DATA:
            {
                if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
                    /* Used by 0-RTT to indicate that encrypted data will
                       be sent right after the ClientHello record. */
                    ssl_state->flags |= SSL_AL_FLAG_EARLY_DATA;
                }

                input += ext_len;

                break;
            }

            case SSL_EXTENSION_SUPPORTED_VERSIONS:
            {
                ret = TLSDecodeHSHelloExtensionSupportedVersions(ssl_state, input,
                                                                 ext_len);
                if (ret < 0)
                    goto end;

                input += ret;

                break;
            }

            case SSL_EXTENSION_SESSION_TICKET:
            {
                if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
                    /* This has to be verified later on by checking if a
                       certificate record has been sent by the server. */
                    ssl_state->flags |= SSL_AL_FLAG_SESSION_RESUMED;
                }

                input += ext_len;

                break;
            }

            default:
            {
                input += ext_len;
                break;
            }
        }

        if (ja3) {
            if (TLSDecodeValueIsGREASE(ext_type) != 1) {
                rc = Ja3BufferAddValue(&ja3_extensions, ext_type);
                if (rc != 0)
                    goto error;
            }
        }

        if (ssl_state->curr_connp->ja4 != NULL &&
                ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
            if (TLSDecodeValueIsGREASE(ext_type) != 1) {
                SCJA4AddExtension(ssl_state->curr_connp->ja4, ext_type);
            }
        }

        processed_len += ext_len + 4;
    }

end:
    if (ja3) {
        rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
                                   &ja3_extensions);
        if (rc == -1)
            goto error;

        if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
            rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
                                       &ja3_elliptic_curves);
            if (rc == -1)
                goto error;

            rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
                                       &ja3_elliptic_curves_pf);
            if (rc == -1)
                goto error;
        }
    }

    return (input - initial_input);

invalid_length:
    SCLogDebug("TLS handshake invalid length");
    SSLSetEvent(ssl_state,
                TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);

error:
    if (ja3_extensions != NULL)
        Ja3BufferFree(&ja3_extensions);
    if (ja3_elliptic_curves != NULL)
        Ja3BufferFree(&ja3_elliptic_curves);
    if (ja3_elliptic_curves_pf != NULL)
        Ja3BufferFree(&ja3_elliptic_curves_pf);

    return -1;
}

static int TLSDecodeHandshakeHello(SSLState *ssl_state,
                                   const uint8_t * const input,
                                   const uint32_t input_len)
{
    int ret;
    uint32_t parsed = 0;

    /* Ensure that we have a JA4 state defined by now if we have JA4 enabled,
       we are in a client hello and we don't have such a state yet (to avoid
       leaking memory in case this function is entered more than once). */
    if (SC_ATOMIC_GET(ssl_config.enable_ja4) &&
            ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO &&
            ssl_state->curr_connp->ja4 == NULL) {
        ssl_state->curr_connp->ja4 = SCJA4New();
    }

    ret = TLSDecodeHSHelloVersion(ssl_state, input, input_len);
    if (ret < 0)
        goto end;

    parsed += ret;

    ret = TLSDecodeHSHelloRandom(ssl_state, input + parsed, input_len - parsed);
    if (ret < 0)
        goto end;

    parsed += ret;

    /* The session id field in the server hello record was removed in
       TLSv1.3 draft1, but was readded in draft22. */
    if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) ||
            ((ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
            ((ssl_state->flags & SSL_AL_FLAG_LOG_WITHOUT_CERT) == 0))) {
        ret = TLSDecodeHSHelloSessionID(ssl_state, input + parsed,
                                        input_len - parsed);
        if (ret < 0)
            goto end;

        parsed += ret;
    }

    ret = TLSDecodeHSHelloCipherSuites(ssl_state, input + parsed,
                                       input_len - parsed);
    if (ret < 0)
        goto end;

    parsed += ret;

   /* The compression methods field in the server hello record was
      removed in TLSv1.3 draft1, but was readded in draft22. */
   if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) ||
              ((ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
              ((ssl_state->flags & SSL_AL_FLAG_LOG_WITHOUT_CERT) == 0))) {
        ret = TLSDecodeHSHelloCompressionMethods(ssl_state, input + parsed,
                                                 input_len - parsed);
        if (ret < 0)
            goto end;

        parsed += ret;
    }

    ret = TLSDecodeHSHelloExtensions(ssl_state, input + parsed,
                                     input_len - parsed);
    if (ret < 0)
        goto end;

    if (SC_ATOMIC_GET(ssl_config.enable_ja3) && ssl_state->curr_connp->ja3_hash == NULL) {
        ssl_state->curr_connp->ja3_hash = Ja3GenerateHash(ssl_state->curr_connp->ja3_str);
    }

end:
    return 0;
}

#ifdef DEBUG_VALIDATION
static inline bool
RecordAlreadyProcessed(const SSLStateConnp *curr_connp)
{
    return ((curr_connp->record_length + SSLV3_RECORD_HDR_LEN) <
            curr_connp->bytes_processed);
}
#endif

static inline int SSLv3ParseHandshakeTypeCertificate(SSLState *ssl_state, SSLStateConnp *connp,
        const uint8_t *const initial_input, const uint32_t input_len)
{
    int rc = TlsDecodeHSCertificates(ssl_state, connp, initial_input, input_len);
    SCLogDebug("rc %d", rc);
    if (rc > 0) {
        DEBUG_VALIDATE_BUG_ON(rc > (int)input_len);
        SSLParserHSReset(connp);
    } else if (rc < 0) {
        SCLogDebug("error parsing cert, reset state");
        SSLParserHSReset(connp);
        /* fall through to still consume the cert bytes */
    }
    return input_len;
}

static int SupportedHandshakeType(const uint8_t type)
{
    switch (type) {
        case SSLV3_HS_CLIENT_HELLO:
        case SSLV3_HS_SERVER_HELLO:
        case SSLV3_HS_SERVER_KEY_EXCHANGE:
        case SSLV3_HS_CLIENT_KEY_EXCHANGE:
        case SSLV3_HS_CERTIFICATE:
        case SSLV3_HS_HELLO_REQUEST:
        case SSLV3_HS_CERTIFICATE_REQUEST:
        case SSLV3_HS_CERTIFICATE_VERIFY:
        case SSLV3_HS_FINISHED:
        case SSLV3_HS_CERTIFICATE_URL:
        case SSLV3_HS_CERTIFICATE_STATUS:
        case SSLV3_HS_NEW_SESSION_TICKET:
        case SSLV3_HS_SERVER_HELLO_DONE:
            return true;
            break;

        default:
            return false;
            break;
    }
}

/**
 *  \retval parsed number of consumed bytes
 *  \retval < 0 error
 */
static int SSLv3ParseHandshakeType(SSLState *ssl_state, const uint8_t *input,
                                   uint32_t input_len, uint8_t direction)
{
    const uint8_t *initial_input = input;
    int rc;

    if (input_len == 0) {
        return 0;
    }
    DEBUG_VALIDATE_BUG_ON(RecordAlreadyProcessed(ssl_state->curr_connp));

    switch (ssl_state->curr_connp->handshake_type) {
        case SSLV3_HS_CLIENT_HELLO:
            ssl_state->current_flags = SSL_AL_FLAG_STATE_CLIENT_HELLO;

            rc = TLSDecodeHandshakeHello(ssl_state, input, input_len);
            if (rc < 0)
                return rc;
            break;

        case SSLV3_HS_SERVER_HELLO:
            ssl_state->current_flags = SSL_AL_FLAG_STATE_SERVER_HELLO;

            DEBUG_VALIDATE_BUG_ON(ssl_state->curr_connp->message_length != input_len);
            rc = TLSDecodeHandshakeHello(ssl_state, input, input_len);
            if (rc < 0)
                return rc;
            break;

        case SSLV3_HS_SERVER_KEY_EXCHANGE:
            ssl_state->current_flags = SSL_AL_FLAG_STATE_SERVER_KEYX;
            break;

        case SSLV3_HS_CLIENT_KEY_EXCHANGE:
            ssl_state->current_flags = SSL_AL_FLAG_STATE_CLIENT_KEYX;
            break;

        case SSLV3_HS_CERTIFICATE:

            rc = SSLv3ParseHandshakeTypeCertificate(ssl_state,
                    direction ? &ssl_state->server_connp : &ssl_state->client_connp, initial_input,
                    input_len);
            if (rc < 0)
                return rc;
            break;

        case SSLV3_HS_HELLO_REQUEST:
            break;
        case SSLV3_HS_CERTIFICATE_REQUEST:
            if (direction) {
                ssl_state->current_flags = SSL_AL_FLAG_NEED_CLIENT_CERT;
            }
            break;
        case SSLV3_HS_CERTIFICATE_VERIFY:
        case SSLV3_HS_FINISHED:
        case SSLV3_HS_CERTIFICATE_URL:
        case SSLV3_HS_CERTIFICATE_STATUS:
            break;
        case SSLV3_HS_NEW_SESSION_TICKET:
            SCLogDebug("new session ticket");
            break;
        case SSLV3_HS_SERVER_HELLO_DONE:
            break;
        default:
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
            return -1;
    }

    ssl_state->flags |= ssl_state->current_flags;

    SCLogDebug("message: length %u", ssl_state->curr_connp->message_length);
    SCLogDebug("input_len %u ssl_state->curr_connp->bytes_processed %u", input_len, ssl_state->curr_connp->bytes_processed);

    return input_len;
}

static int SSLv3ParseHandshakeProtocol(SSLState *ssl_state, const uint8_t *input,
                                       uint32_t input_len, uint8_t direction)
{
    const uint8_t *initial_input = input;

    if (input_len == 0 || ssl_state->curr_connp->bytes_processed ==
            (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN)) {
        SCReturnInt(0);
    }

    while (input_len) {
        SCLogDebug("input_len %u", input_len);

        if (ssl_state->curr_connp->hs_buffer != NULL) {
            SCLogDebug("partial handshake record in place");
            const uint32_t need = ssl_state->curr_connp->hs_buffer_message_size -
                                  ssl_state->curr_connp->hs_buffer_offset;
            const uint32_t add = MIN(need, input_len);

            /* grow buffer to next multiple of 4k that fits all data we have */
            if (ssl_state->curr_connp->hs_buffer_offset + add >
                    ssl_state->curr_connp->hs_buffer_size) {
                const uint32_t avail = ssl_state->curr_connp->hs_buffer_offset + add;
                const uint32_t new_size = avail + (4096 - (avail % 4096));
                SCLogDebug("new_size %u, avail %u", new_size, avail);
                void *ptr = SCRealloc(ssl_state->curr_connp->hs_buffer, new_size);
                if (ptr == NULL)
                    return -1;
                ssl_state->curr_connp->hs_buffer = ptr;
                ssl_state->curr_connp->hs_buffer_size = new_size;
            }

            SCLogDebug("ssl_state->curr_connp->hs_buffer_offset %u "
                       "ssl_state->curr_connp->hs_buffer_size %u",
                    ssl_state->curr_connp->hs_buffer_offset, ssl_state->curr_connp->hs_buffer_size);
            SCLogDebug("to add %u total %u", add, ssl_state->curr_connp->hs_buffer_offset + add);

            if (SafeMemcpy(ssl_state->curr_connp->hs_buffer,
                        ssl_state->curr_connp->hs_buffer_offset,
                        ssl_state->curr_connp->hs_buffer_size, input, 0, add, add) != 0) {
                SCLogDebug("copy failed");
                return -1;
            }
            ssl_state->curr_connp->hs_buffer_offset += add;

            if (ssl_state->curr_connp->hs_buffer_message_size <=
                    ssl_state->curr_connp->hs_buffer_offset) {
                DEBUG_VALIDATE_BUG_ON(ssl_state->curr_connp->hs_buffer_message_size !=
                                      ssl_state->curr_connp->hs_buffer_offset);

                ssl_state->curr_connp->handshake_type =
                        ssl_state->curr_connp->hs_buffer_message_type;
                ssl_state->curr_connp->message_length =
                        ssl_state->curr_connp->hs_buffer_message_size;

                SCLogDebug("got all data now: handshake_type %u message_length %u",
                        ssl_state->curr_connp->handshake_type,
                        ssl_state->curr_connp->message_length);

                int retval = SSLv3ParseHandshakeType(ssl_state, ssl_state->curr_connp->hs_buffer,
                        ssl_state->curr_connp->hs_buffer_offset, direction);
                if (retval < 0) {
                    SSLParserHSReset(ssl_state->curr_connp);
                    return (retval);
                }
                SCLogDebug("retval %d", retval);

                /* data processed, reset buffer */
                SCFree(ssl_state->curr_connp->hs_buffer);
                ssl_state->curr_connp->hs_buffer = NULL;
                ssl_state->curr_connp->hs_buffer_size = 0;
                ssl_state->curr_connp->hs_buffer_message_size = 0;
                ssl_state->curr_connp->hs_buffer_message_type = 0;
                ssl_state->curr_connp->hs_buffer_offset = 0;
            } else {
                SCLogDebug("partial data");
            }

            input += add;
            input_len -= add;
            SCLogDebug("input_len %u", input_len);
            SSLParserHSReset(ssl_state->curr_connp);
            continue;
        }

        SCLogDebug("bytes_processed %u", ssl_state->curr_connp->bytes_processed);
        SCLogDebug("input %p input_len %u", input, input_len);

        if (input_len < 4) {
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
            SCReturnInt(-1);
        }

        ssl_state->curr_connp->handshake_type = input[0];
        ssl_state->curr_connp->message_length = input[1] << 16 | input[2] << 8 | input[3];
        SCLogDebug("handshake_type %u message len %u input %p input_len %u",
                ssl_state->curr_connp->handshake_type, ssl_state->curr_connp->message_length, input,
                input_len);
        input += 4;
        input_len -= 4;

        const uint32_t record_len = ssl_state->curr_connp->message_length;
        /* see if we support this type. We check here to not use the fragment
         * handling on things we don't support. */
        const bool supported_type = SupportedHandshakeType(ssl_state->curr_connp->handshake_type);
        SCLogDebug("supported_type %s handshake_type %u/%02x", supported_type ? "true" : "false",
                ssl_state->curr_connp->handshake_type, ssl_state->curr_connp->handshake_type);
        if (!supported_type) {
            uint32_t avail_record_len = MIN(input_len, record_len);
            input += avail_record_len;
            input_len -= avail_record_len;

            SSLParserHSReset(ssl_state->curr_connp);

            if ((direction && (ssl_state->flags & SSL_AL_FLAG_SERVER_CHANGE_CIPHER_SPEC)) ||
                    (!direction && (ssl_state->flags & SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC))) {
                // after Change Cipher Spec we get Encrypted Handshake Messages
            } else {
                SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE);
            }
            continue;
        }

        /* if the message length exceeds our input_len, we have a tls fragment. */
        if (record_len > input_len) {
            const uint32_t avail = input_len;
            const uint32_t size = avail + (4096 - (avail % 4096));
            SCLogDebug("initial buffer size %u, based on input %u", size, avail);
            ssl_state->curr_connp->hs_buffer = SCCalloc(1, size);
            if (ssl_state->curr_connp->hs_buffer == NULL) {
                return -1;
            }
            ssl_state->curr_connp->hs_buffer_size = size;
            ssl_state->curr_connp->hs_buffer_message_size = record_len;
            ssl_state->curr_connp->hs_buffer_message_type = ssl_state->curr_connp->handshake_type;

            if (input_len > 0) {
                if (SafeMemcpy(ssl_state->curr_connp->hs_buffer, 0,
                            ssl_state->curr_connp->hs_buffer_size, input, 0, input_len,
                            input_len) != 0) {
                    return -1;
                }
                ssl_state->curr_connp->hs_buffer_offset = input_len;
            }
            SCLogDebug("opened record buffer %p size %u offset %u type %u msg_size %u",
                    ssl_state->curr_connp->hs_buffer, ssl_state->curr_connp->hs_buffer_size,
                    ssl_state->curr_connp->hs_buffer_offset,
                    ssl_state->curr_connp->hs_buffer_message_type,
                    ssl_state->curr_connp->hs_buffer_message_size);
            input += input_len;
            SSLParserHSReset(ssl_state->curr_connp);
            return (input - initial_input);

        } else {
            /* full record, parse it now */
            int retval = SSLv3ParseHandshakeType(
                    ssl_state, input, ssl_state->curr_connp->message_length, direction);
            if (retval < 0 || retval > (int)input_len) {
                DEBUG_VALIDATE_BUG_ON(retval > (int)input_len);
                return (retval);
            }
            SCLogDebug("retval %d input_len %u", retval, input_len);
            input += retval;
            input_len -= retval;

            SSLParserHSReset(ssl_state->curr_connp);
        }
        SCLogDebug("input_len left %u", input_len);
    }
    return (input - initial_input);
}

/**
 * \internal
 * \brief TLS Heartbeat parser (see RFC 6520)
 *
 * \param sslstate  Pointer to the SSL state.
 * \param input     Pointer to the received input data.
 * \param input_len Length in bytes of the received data.
 * \param direction 1 toclient, 0 toserver
 *
 * \retval The number of bytes parsed on success, 0 if nothing parsed, -1 on failure.
 */
static int SSLv3ParseHeartbeatProtocol(SSLState *ssl_state, const uint8_t *input,
                                       uint32_t input_len, uint8_t direction)
{
    uint8_t hb_type;
    uint16_t payload_len;
    uint32_t padding_len;

    /* expect at least 3 bytes: heartbeat type (1) + length (2) */
    if (input_len < 3) {
        return 0;
    }

    hb_type = *input++;

    if (!(ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC)) {
        if (!(hb_type == TLS_HB_REQUEST || hb_type == TLS_HB_RESPONSE)) {
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
            return -1;
        }
    }

    if ((ssl_state->flags & SSL_AL_FLAG_HB_INFLIGHT) == 0) {
        ssl_state->flags |= SSL_AL_FLAG_HB_INFLIGHT;

        if (direction) {
            SCLogDebug("HeartBeat Record type sent in the toclient direction!");
            ssl_state->flags |= SSL_AL_FLAG_HB_SERVER_INIT;
        } else {
            SCLogDebug("HeartBeat Record type sent in the toserver direction!");
            ssl_state->flags |= SSL_AL_FLAG_HB_CLIENT_INIT;
        }

        /* if we reach this point, then we can assume that the HB request
           is encrypted. If so, let's set the HB record length */
        if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC) {
            ssl_state->hb_record_len = ssl_state->curr_connp->record_length;
            SCLogDebug("Encrypted HeartBeat Request In-flight. Storing len %u",
                       ssl_state->hb_record_len);
            return (ssl_state->curr_connp->record_length - 3);
        }

        payload_len = (uint16_t)(*input << 8) | *(input + 1);

        /* check that the requested payload length is really present in
           the record (CVE-2014-0160) */
        if ((uint32_t)(payload_len+3) > ssl_state->curr_connp->record_length) {
            SCLogDebug("We have a short record in HeartBeat Request");
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT);
            return -1;
        }

        /* check the padding length. It must be at least 16 bytes
           (RFC 6520, section 4) */
        padding_len = ssl_state->curr_connp->record_length - payload_len - 3;
        if (padding_len < 16) {
            SCLogDebug("We have a short record in HeartBeat Request");
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
            return -1;
        }

        /* we don't have the payload */
        if (input_len < payload_len + padding_len) {
            return 0;
        }

    /* OpenSSL still seems to discard multiple in-flight
       heartbeats although some tools send multiple at once */
    } else if (direction == 1 && (ssl_state->flags & SSL_AL_FLAG_HB_INFLIGHT) &&
            (ssl_state->flags & SSL_AL_FLAG_HB_SERVER_INIT)) {
        SCLogDebug("Multiple in-flight server initiated HeartBeats");
        SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
        return -1;

    } else if (direction == 0 && (ssl_state->flags & SSL_AL_FLAG_HB_INFLIGHT) &&
            (ssl_state->flags & SSL_AL_FLAG_HB_CLIENT_INIT)) {
        SCLogDebug("Multiple in-flight client initiated HeartBeats");
        SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
        return -1;

    } else {
        /* we have a HB record in the opposite direction of the request,
           let's reset our flags */
        ssl_state->flags &= ~SSL_AL_FLAG_HB_INFLIGHT;
        ssl_state->flags &= ~SSL_AL_FLAG_HB_SERVER_INIT;
        ssl_state->flags &= ~SSL_AL_FLAG_HB_CLIENT_INIT;

        /* if we reach this point, then we can assume that the HB request
           is encrypted. If so, let's set the HB record length */
        if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC) {
            /* check to see if the encrypted response is longer than the
               encrypted request */
            if (ssl_state->hb_record_len > 0 && ssl_state->hb_record_len <
                    ssl_state->curr_connp->record_length) {
                SCLogDebug("My heart is bleeding.. OpenSSL HeartBleed response (%u)",
                        ssl_state->hb_record_len);
                SSLSetEvent(ssl_state,
                        TLS_DECODER_EVENT_DATALEAK_HEARTBEAT_MISMATCH);
                ssl_state->hb_record_len = 0;
                return -1;
            }
        }

        /* reset the HB record length in case we have a legit HB followed
           by a bad one */
        ssl_state->hb_record_len = 0;
    }

    /* skip the HeartBeat, 3 bytes were already parsed,
       e.g |18 03 02| for TLS 1.2 */
    return (ssl_state->curr_connp->record_length - 3);
}

static int SSLv3ParseRecord(uint8_t direction, SSLState *ssl_state,
                            const uint8_t *input, uint32_t input_len)
{
    const uint8_t *initial_input = input;

    if (input_len == 0) {
        return 0;
    }

    uint8_t skip_version = 0;

    /* Only set SSL/TLS version here if it has not already been set in
       client/server hello. */
    if (direction == 0) {
        if ((ssl_state->flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
                (ssl_state->client_connp.version != TLS_VERSION_UNKNOWN)) {
            skip_version = 1;
        }
    } else {
        if ((ssl_state->flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
                (ssl_state->server_connp.version != TLS_VERSION_UNKNOWN)) {
            skip_version = 1;
        }
    }

    switch (ssl_state->curr_connp->bytes_processed) {
        case 0:
            if (input_len >= 5) {
                ssl_state->curr_connp->content_type = input[0];
                if (!skip_version) {
                    ssl_state->curr_connp->version = (uint16_t)(input[1] << 8) | input[2];
                }
                ssl_state->curr_connp->record_length = input[3] << 8;
                ssl_state->curr_connp->record_length |= input[4];
                ssl_state->curr_connp->bytes_processed += SSLV3_RECORD_HDR_LEN;
                return SSLV3_RECORD_HDR_LEN;
            } else {
                ssl_state->curr_connp->content_type = *(input++);
                if (--input_len == 0)
                    break;
            }

            /* fall through */
        case 1:
            if (!skip_version) {
                ssl_state->curr_connp->version = (uint16_t)(*(input++) << 8);
            } else {
                input++;
            }
            if (--input_len == 0)
                break;

            /* fall through */
        case 2:
            if (!skip_version) {
                ssl_state->curr_connp->version |= *(input++);
            } else {
                input++;
            }
            if (--input_len == 0)
                break;

            /* fall through */
        case 3:
            ssl_state->curr_connp->record_length = *(input++) << 8;
            if (--input_len == 0)
                break;

            /* fall through */
        case 4:
            ssl_state->curr_connp->record_length |= *(input++);
            if (--input_len == 0)
                break;

            /* fall through */
    }

    ssl_state->curr_connp->bytes_processed += (input - initial_input);

    return (input - initial_input);
}

static int SSLv2ParseRecord(uint8_t direction, SSLState *ssl_state,
                            const uint8_t *input, uint32_t input_len)
{
    const uint8_t *initial_input = input;

    if (input_len == 0) {
        return 0;
    }

    if (ssl_state->curr_connp->record_lengths_length == 2) {
        switch (ssl_state->curr_connp->bytes_processed) {
            case 0:
                if (input_len >= ssl_state->curr_connp->record_lengths_length + 1) {
                    ssl_state->curr_connp->record_length = (0x7f & input[0]) << 8 | input[1];
                    ssl_state->curr_connp->content_type = input[2];
                    ssl_state->curr_connp->version = SSL_VERSION_2;
                    ssl_state->curr_connp->bytes_processed += 3;
                    return 3;
                } else {
                    ssl_state->curr_connp->record_length = (0x7f & *(input++)) << 8;
                    if (--input_len == 0)
                        break;
                }

                /* fall through */
            case 1:
                ssl_state->curr_connp->record_length |= *(input++);
                if (--input_len == 0)
                    break;

                /* fall through */
            case 2:
                ssl_state->curr_connp->content_type = *(input++);
                ssl_state->curr_connp->version = SSL_VERSION_2;
                if (--input_len == 0)
                    break;

                /* fall through */
        }

    } else {
        switch (ssl_state->curr_connp->bytes_processed) {
            case 0:
                if (input_len >= ssl_state->curr_connp->record_lengths_length + 1) {
                    ssl_state->curr_connp->record_length = (0x3f & input[0]) << 8 | input[1];
                    ssl_state->curr_connp->content_type = input[3];
                    ssl_state->curr_connp->version = SSL_VERSION_2;
                    ssl_state->curr_connp->bytes_processed += 4;
                    return 4;
                } else {
                    ssl_state->curr_connp->record_length = (0x3f & *(input++)) << 8;
                    if (--input_len == 0)
                        break;
                }

                /* fall through */
            case 1:
                ssl_state->curr_connp->record_length |= *(input++);
                if (--input_len == 0)
                    break;

                /* fall through */
            case 2:
                /* padding */
                input++;
                if (--input_len == 0)
                    break;

                /* fall through */
            case 3:
                ssl_state->curr_connp->content_type = *(input++);
                ssl_state->curr_connp->version = SSL_VERSION_2;
                if (--input_len == 0)
                    break;

                /* fall through */
        }
    }

    ssl_state->curr_connp->bytes_processed += (input - initial_input);

    return (input - initial_input);
}

static struct SSLDecoderResult SSLv2Decode(uint8_t direction, SSLState *ssl_state,
        AppLayerParserState *pstate, const uint8_t *input, uint32_t input_len,
        const StreamSlice stream_slice)
{
    const uint8_t *initial_input = input;

    if (ssl_state->curr_connp->bytes_processed == 0) {
        if (input[0] & 0x80) {
            ssl_state->curr_connp->record_lengths_length = 2;
        } else {
            ssl_state->curr_connp->record_lengths_length = 3;
        }

        SCLogDebug("record start: ssl2.hdr frame");
        AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input,
                ssl_state->curr_connp->record_lengths_length + 1, direction, TLS_FRAME_SSLV2_HDR);
    }

    SCLogDebug("direction %u ssl_state->curr_connp->record_lengths_length + 1 %u, "
               "ssl_state->curr_connp->bytes_processed %u",
            direction, ssl_state->curr_connp->record_lengths_length + 1,
            ssl_state->curr_connp->bytes_processed);
    /* the +1 is because we read one extra byte inside SSLv2ParseRecord
       to read the msg_type */
    if (ssl_state->curr_connp->bytes_processed <
            (ssl_state->curr_connp->record_lengths_length + 1)) {
        const int retval = SSLv2ParseRecord(direction, ssl_state, input, input_len);
        SCLogDebug("retval %d ssl_state->curr_connp->record_length %u", retval,
                ssl_state->curr_connp->record_length);
        if (retval < 0 || retval > (int)input_len) {
            DEBUG_VALIDATE_BUG_ON(retval > (int)input_len);
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSLV2_HEADER);
            return SSL_DECODER_ERROR(-1);
        }

        AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input,
                ssl_state->curr_connp->record_lengths_length + ssl_state->curr_connp->record_length,
                direction, TLS_FRAME_SSLV2_PDU);
        SCLogDebug("record start: ssl2.pdu frame");

        input += retval;
        input_len -= retval;
    }

    /* if we don't have the full record, we return incomplete */
    if (ssl_state->curr_connp->record_lengths_length + ssl_state->curr_connp->record_length >
            input_len + ssl_state->curr_connp->bytes_processed) {
        uint32_t needed = ssl_state->curr_connp->record_length;
        SCLogDebug("record len %u input_len %u parsed %u: need %u bytes more data",
                ssl_state->curr_connp->record_length, input_len, (uint32_t)(input - initial_input),
                needed);
        return SSL_DECODER_INCOMPLETE((input - initial_input), needed);
    }

    if (input_len == 0) {
        return SSL_DECODER_OK((input - initial_input));
    }

    /* record_length should never be zero */
    if (ssl_state->curr_connp->record_length == 0) {
        SCLogDebug("SSLv2 record length is zero");
        SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSLV2_HEADER);
        return SSL_DECODER_ERROR(-1);
    }

    /* record_lengths_length should never be zero */
    if (ssl_state->curr_connp->record_lengths_length == 0) {
        SCLogDebug("SSLv2 record lengths length is zero");
        SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSLV2_HEADER);
        return SSL_DECODER_ERROR(-1);
    }

    switch (ssl_state->curr_connp->content_type) {
        case SSLV2_MT_ERROR:
            SCLogDebug("SSLV2_MT_ERROR msg_type received. Error encountered "
                       "in establishing the sslv2 session, may be version");
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_ERROR_MSG_ENCOUNTERED);

            break;

        case SSLV2_MT_CLIENT_HELLO:
            if (input_len < 6) {
                SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
                return SSL_DECODER_ERROR(-1);
            }

            ssl_state->current_flags = SSL_AL_FLAG_STATE_CLIENT_HELLO;
            ssl_state->current_flags |= SSL_AL_FLAG_SSL_CLIENT_HS;

            const uint16_t version = (uint16_t)(input[0] << 8) | input[1];
            SCLogDebug("SSLv2: version %04x", version);
            ssl_state->curr_connp->version = version;
            uint16_t session_id_length = (input[5]) | (uint16_t)(input[4] << 8);
            input += 6;
            input_len -= 6;
            ssl_state->curr_connp->bytes_processed += 6;
            if (session_id_length == 0) {
                ssl_state->current_flags |= SSL_AL_FLAG_SSL_NO_SESSION_ID;
            }
            break;

        case SSLV2_MT_CLIENT_MASTER_KEY:
            if (!(ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_HS)) {
                SCLogDebug("Client hello is not seen before master key "
                           "message!");
            }
            ssl_state->current_flags = SSL_AL_FLAG_SSL_CLIENT_MASTER_KEY;

            break;

        case SSLV2_MT_CLIENT_CERTIFICATE:
            if (direction == 1) {
                SCLogDebug("Incorrect SSL Record type sent in the toclient "
                           "direction!");
            } else {
                ssl_state->current_flags = SSL_AL_FLAG_STATE_CLIENT_KEYX;
            }

            /* fall through */
        case SSLV2_MT_SERVER_VERIFY:
        case SSLV2_MT_SERVER_FINISHED:
            if (direction == 0 &&
                    !(ssl_state->curr_connp->content_type &
                    SSLV2_MT_CLIENT_CERTIFICATE)) {
                SCLogDebug("Incorrect SSL Record type sent in the toserver "
                           "direction!");
            }

            /* fall through */
        case SSLV2_MT_CLIENT_FINISHED:
        case SSLV2_MT_REQUEST_CERTIFICATE:
            /* both client hello and server hello must be seen */
            if ((ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_HS) &&
                    (ssl_state->flags & SSL_AL_FLAG_SSL_SERVER_HS)) {

                if (direction == 0) {
                    if (ssl_state->flags & SSL_AL_FLAG_SSL_NO_SESSION_ID) {
                        ssl_state->current_flags |= SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED;
                        SCLogDebug("SSLv2 client side has started the encryption");
                    } else if (ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_MASTER_KEY) {
                        ssl_state->current_flags = SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED;
                        SCLogDebug("SSLv2 client side has started the encryption");
                    }
                } else {
                    ssl_state->current_flags = SSL_AL_FLAG_SSL_SERVER_SSN_ENCRYPTED;
                    SCLogDebug("SSLv2 Server side has started the encryption");
                }

                if ((ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED) &&
                    (ssl_state->flags & SSL_AL_FLAG_SSL_SERVER_SSN_ENCRYPTED))
                {
                    if (ssl_config.encrypt_mode != SSL_CNF_ENC_HANDLE_FULL) {
                        AppLayerParserStateSetFlag(pstate,
                                APP_LAYER_PARSER_NO_INSPECTION);
                    }

                    if (ssl_config.encrypt_mode == SSL_CNF_ENC_HANDLE_BYPASS) {
                        AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY);
                        AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_BYPASS_READY);
                    }
                    SCLogDebug("SSLv2 No reassembly & inspection has been set");
                }
            }

            break;

        case SSLV2_MT_SERVER_HELLO:
            ssl_state->current_flags = SSL_AL_FLAG_STATE_SERVER_HELLO;
            ssl_state->current_flags |= SSL_AL_FLAG_SSL_SERVER_HS;

            break;
    }

    ssl_state->flags |= ssl_state->current_flags;

    if (input_len + ssl_state->curr_connp->bytes_processed >=
            (ssl_state->curr_connp->record_length +
            ssl_state->curr_connp->record_lengths_length)) {

        /* looks like we have another record after this */
        uint32_t diff = ssl_state->curr_connp->record_length +
                ssl_state->curr_connp->record_lengths_length + -
                ssl_state->curr_connp->bytes_processed;
        input += diff;
        SSLParserReset(ssl_state);

        /* we still don't have the entire record for the one we are
           currently parsing */
    } else {
        input += input_len;
        ssl_state->curr_connp->bytes_processed += input_len;
    }
    return SSL_DECODER_OK((input - initial_input));
}

static struct SSLDecoderResult SSLv3Decode(uint8_t direction, SSLState *ssl_state,
        AppLayerParserState *pstate, const uint8_t *input, const uint32_t input_len,
        const StreamSlice stream_slice)
{
    uint32_t parsed = 0;
    uint32_t record_len; /* slice of input_len for the current record */
    const bool first_call = (ssl_state->curr_connp->bytes_processed == 0);

    if (ssl_state->curr_connp->bytes_processed < SSLV3_RECORD_HDR_LEN) {
        const uint16_t prev_version = ssl_state->curr_connp->version;

        int retval = SSLv3ParseRecord(direction, ssl_state, input, input_len);
        if (retval < 0 || retval > (int)input_len) {
            DEBUG_VALIDATE_BUG_ON(retval > (int)input_len);
            SCLogDebug("SSLv3ParseRecord returned %d", retval);
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_TLS_HEADER);
            return SSL_DECODER_ERROR(-1);
        }
        parsed = retval;

        SCLogDebug("%s input %p record_length %u", (direction == 0) ? "toserver" : "toclient",
                input, ssl_state->curr_connp->record_length);

        /* first the hdr frame at our first chance */
        if (first_call) {
            AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input, SSLV3_RECORD_HDR_LEN,
                    direction, TLS_FRAME_HDR);
        }

        /* parser is streaming for the initial header, then switches to incomplete
         * API: so if we don't have the hdr yet, return consumed bytes and wait
         * until we are called again with new data. */
        if (ssl_state->curr_connp->bytes_processed < SSLV3_RECORD_HDR_LEN) {
            SCLogDebug(
                    "incomplete header, return %u bytes consumed and wait for more data", parsed);
            return SSL_DECODER_OK(parsed);
        }

        /* pdu frame needs record length, so only create it when hdr fully parsed. */
        AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input,
                ssl_state->curr_connp->record_length + retval, direction, TLS_FRAME_PDU);
        record_len = MIN(input_len - parsed, ssl_state->curr_connp->record_length);
        SCLogDebug(
                "record_len %u (input_len %u, parsed %u, ssl_state->curr_connp->record_length %u)",
                record_len, input_len, parsed, ssl_state->curr_connp->record_length);

        bool unknown_record = false;
        switch (ssl_state->curr_connp->content_type) {
            case SSLV3_CHANGE_CIPHER_SPEC:
            case SSLV3_ALERT_PROTOCOL:
            case SSLV3_HANDSHAKE_PROTOCOL:
            case SSLV3_APPLICATION_PROTOCOL:
            case SSLV3_HEARTBEAT_PROTOCOL:
                break;
            default:
                unknown_record = true;
                break;
        }

        /* unknown record type. For TLS 1.0, 1.1 and 1.2 this is ok. For the rest it is fatal. Based
         * on Wireshark logic. */
        if (prev_version == TLS_VERSION_10 || prev_version == TLS_VERSION_11) {
            if (unknown_record) {
                SCLogDebug("unknown record, ignore it");
                SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_TYPE);

                ssl_state->curr_connp->bytes_processed = 0; // TODO review this reset logic
                ssl_state->curr_connp->content_type = 0;
                ssl_state->curr_connp->record_length = 0;
                // restore last good version
                ssl_state->curr_connp->version = prev_version;
                return SSL_DECODER_OK(input_len); // consume everything
            }
        } else {
            if (unknown_record) {
                SCLogDebug("unknown record, fatal");
                SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_TYPE);
                return SSL_DECODER_ERROR(-1);
            }
        }

        /* record_length should never be zero */
        if (ssl_state->curr_connp->record_length == 0) {
            SCLogDebug("SSLv3 Record length is 0");
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_LENGTH);
            return SSL_DECODER_ERROR(-1);
        }

        if (!TLSVersionValid(ssl_state->curr_connp->version)) {
            SCLogDebug("ssl_state->curr_connp->version %04x", ssl_state->curr_connp->version);
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_VERSION);
            return SSL_DECODER_ERROR(-1);
        }

        if (ssl_state->curr_connp->bytes_processed == SSLV3_RECORD_HDR_LEN &&
                ssl_state->curr_connp->record_length > SSLV3_RECORD_MAX_LEN) {
            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_LENGTH);
            return SSL_DECODER_ERROR(-1);
        }
        DEBUG_VALIDATE_BUG_ON(ssl_state->curr_connp->bytes_processed > SSLV3_RECORD_HDR_LEN);
    } else {
        ValidateRecordState(ssl_state->curr_connp);

        record_len = (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN)- ssl_state->curr_connp->bytes_processed;
        record_len = MIN(input_len, record_len);
    }
    SCLogDebug("record length %u processed %u got %u",
            ssl_state->curr_connp->record_length, ssl_state->curr_connp->bytes_processed, record_len);

    /* if we don't have the full record, we return incomplete */
    if (ssl_state->curr_connp->record_length > input_len - parsed) {
        /* no need to use incomplete api buffering for application
         * records that we'll not use anyway. */
        if (ssl_state->curr_connp->content_type == SSLV3_APPLICATION_PROTOCOL) {
            SCLogDebug("application record");
        } else {
            uint32_t needed = ssl_state->curr_connp->record_length;
            SCLogDebug("record len %u input_len %u parsed %u: need %u bytes more data",
                    ssl_state->curr_connp->record_length, input_len, parsed, needed);
            DEBUG_VALIDATE_BUG_ON(needed > SSLV3_RECORD_MAX_LEN);
            return SSL_DECODER_INCOMPLETE(parsed, needed);
        }
    }

    if (record_len == 0) {
        return SSL_DECODER_OK(parsed);
    }

    AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed,
            ssl_state->curr_connp->record_length, direction, TLS_FRAME_DATA);

    switch (ssl_state->curr_connp->content_type) {
        /* we don't need any data from these types */
        case SSLV3_CHANGE_CIPHER_SPEC:
            ssl_state->flags |= SSL_AL_FLAG_CHANGE_CIPHER_SPEC;

            if (direction) {
                ssl_state->flags |= SSL_AL_FLAG_SERVER_CHANGE_CIPHER_SPEC;
            } else {
                ssl_state->flags |= SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC;
            }
            break;

        case SSLV3_ALERT_PROTOCOL:
            AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed,
                    ssl_state->curr_connp->record_length, direction, TLS_FRAME_ALERT_DATA);
            break;

        case SSLV3_APPLICATION_PROTOCOL:
            /* In TLSv1.3 early data (0-RTT) could be sent before the
               handshake is complete (rfc8446, section 2.3). We should
               therefore not mark the handshake as done before we have
               seen the ServerHello record. */
            if ((ssl_state->flags & SSL_AL_FLAG_EARLY_DATA) &&
                    ((ssl_state->flags & SSL_AL_FLAG_STATE_SERVER_HELLO) == 0))
                break;

            /* if we see (encrypted) application data, then this means the
               handshake must be done */
            ssl_state->flags |= SSL_AL_FLAG_HANDSHAKE_DONE;

            if (ssl_config.encrypt_mode != SSL_CNF_ENC_HANDLE_FULL) {
                SCLogDebug("setting APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD");
                AppLayerParserStateSetFlag(pstate,
                        APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD);
            }

            /* Encrypted data, reassembly not asked, bypass asked, let's sacrifice
             * heartbeat lke inspection to be able to be able to bypass the flow */
            if (ssl_config.encrypt_mode == SSL_CNF_ENC_HANDLE_BYPASS) {
                SCLogDebug("setting APP_LAYER_PARSER_NO_REASSEMBLY");
                AppLayerParserStateSetFlag(pstate,
                        APP_LAYER_PARSER_NO_REASSEMBLY);
                AppLayerParserStateSetFlag(pstate,
                        APP_LAYER_PARSER_NO_INSPECTION);
                AppLayerParserStateSetFlag(pstate,
                        APP_LAYER_PARSER_BYPASS_READY);
            }
            break;

        case SSLV3_HANDSHAKE_PROTOCOL: {
            if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC) {
                /* In TLSv1.3, ChangeCipherSpec is only used for middlebox
                   compatibility (rfc8446, appendix D.4). */
                // Client hello flags is needed to have a valid version
                if ((ssl_state->flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
                        (ssl_state->client_connp.version > TLS_VERSION_12) &&
                        ((ssl_state->flags & SSL_AL_FLAG_STATE_SERVER_HELLO) == 0)) {
                    /* do nothing */
                } else {
                    // if we started parsing this, we must stop
                    break;
                }
            }

            if (ssl_state->curr_connp->record_length < 4) {
                SSLParserReset(ssl_state);
                SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
                SCLogDebug("record len < 4 => %u", ssl_state->curr_connp->record_length);
                return SSL_DECODER_ERROR(-1);
            }

            int retval = SSLv3ParseHandshakeProtocol(ssl_state, input + parsed,
                                                     record_len, direction);
            SCLogDebug("retval %d", retval);
            if (retval < 0 || retval > (int)record_len) {
                DEBUG_VALIDATE_BUG_ON(retval > (int)record_len);
                SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE);
                SCLogDebug("SSLv3ParseHandshakeProtocol returned %d", retval);
                return SSL_DECODER_ERROR(-1);
            }
            ValidateRecordState(ssl_state->curr_connp);
            break;
        }
        case SSLV3_HEARTBEAT_PROTOCOL: {
            AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed,
                    ssl_state->curr_connp->record_length, direction, TLS_FRAME_HB_DATA);
            int retval = SSLv3ParseHeartbeatProtocol(ssl_state, input + parsed,
                                                 record_len, direction);
            if (retval < 0) {
                SCLogDebug("SSLv3ParseHeartbeatProtocol returned %d", retval);
                return SSL_DECODER_ERROR(-1);
            }
            break;
        }
        default:
            // should be unreachable now that we check after header parsing
            DEBUG_VALIDATE_BUG_ON(1);
            SCLogDebug("unsupported record type");
            return SSL_DECODER_ERROR(-1);
    }

    parsed += record_len;
    ssl_state->curr_connp->bytes_processed += record_len;

    if (ssl_state->curr_connp->bytes_processed >=
            ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN) {
        SCLogDebug("record complete, trigger RAW");
        AppLayerParserTriggerRawStreamReassembly(
                ssl_state->f, direction == 0 ? STREAM_TOSERVER : STREAM_TOCLIENT);
        SSLParserReset(ssl_state);
        ValidateRecordState(ssl_state->curr_connp);
        return SSL_DECODER_OK(parsed);

    } else {
        /* we still don't have the entire record for the one we are
           currently parsing */
        ValidateRecordState(ssl_state->curr_connp);
        return SSL_DECODER_OK(parsed);
    }
}

/**
 * \internal
 * \brief SSLv2, SSLv23, SSLv3, TLSv1.1, TLSv1.2, TLSv1.3 parser.
 *
 *        On parsing error, this should be the only function that should reset
 *        the parser state, to avoid multiple functions in the chain resetting
 *        the parser state.
 *
 * \param direction 0 for toserver, 1 for toclient.
 * \param alstate   Pointer to the state.
 * \param pstate    Application layer parser state for this session.
 * \param output    Pointer to the list of parsed output elements.
 *
 * \todo On reaching an inconsistent state, check if the input has
 *  another new record, instead of just returning after the reset
 *
 * \retval >=0 On success.
 */
static AppLayerResult SSLDecode(Flow *f, uint8_t direction, void *alstate,
        AppLayerParserState *pstate, StreamSlice stream_slice)
{
    SSLState *ssl_state = (SSLState *)alstate;
    ssl_state->tx_data.updated_tc = true;
    ssl_state->tx_data.updated_ts = true;
    uint32_t counter = 0;
    ssl_state->f = f;
    const uint8_t *input = StreamSliceGetData(&stream_slice);
    const uint8_t *init_input = input;
    int32_t input_len = (int32_t)StreamSliceGetDataLen(&stream_slice);

    if ((input == NULL || input_len == 0) &&
            ((direction == 0 && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS)) ||
                    (direction == 1 &&
                            AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC)))) {
        /* flag session as finished if APP_LAYER_PARSER_EOF is set */
        ssl_state->flags |= SSL_AL_FLAG_STATE_FINISHED;
        SCReturnStruct(APP_LAYER_OK);
    } else if (input == NULL || input_len == 0) {
        SCReturnStruct(APP_LAYER_ERROR);
    }

    if (direction == 0)
        ssl_state->curr_connp = &ssl_state->client_connp;
    else
        ssl_state->curr_connp = &ssl_state->server_connp;

    /* If entering on a new record, reset the current flags. */
    if (ssl_state->curr_connp->bytes_processed == 0) {
        ssl_state->current_flags = 0;
    }

    /* if we have more than one record */
    uint32_t max_records = MAX((input_len / SSL_RECORD_MINIMUM_LENGTH),1);
    while (input_len > 0) {
        if (counter > max_records) {
            SCLogDebug("Looks like we have looped quite a bit. Reset state "
                       "and get out of here");
            SSLParserReset(ssl_state);
            SSLSetEvent(ssl_state,
                        TLS_DECODER_EVENT_TOO_MANY_RECORDS_IN_PACKET);
            return APP_LAYER_ERROR;
        }

        /* ssl_state->bytes_processed is zero for a fresh record or
           positive to indicate a record currently being parsed */

        if (ssl_state->curr_connp->bytes_processed == 0) {
            if ((input[0] & 0x80) || (input[0] & 0x40)) {
                /* only SSLv2, has one of the top 2 bits set */
                ssl_state->curr_connp->version = SSL_VERSION_2;
                SCLogDebug("SSLv2 detected");
            } else if (ssl_state->curr_connp->version == SSL_VERSION_2) {
                ssl_state->curr_connp->version = TLS_VERSION_UNKNOWN;
                SCLogDebug("SSL/TLS version reset");
            }
        }
        SCLogDebug("record %u: bytes_processed %u, version %02X, input_len %u", counter,
                ssl_state->curr_connp->bytes_processed, ssl_state->curr_connp->version, input_len);

        if (ssl_state->curr_connp->version == SSL_VERSION_2) {
            if (ssl_state->curr_connp->bytes_processed == 0) {
                SCLogDebug("New SSLv2 record parsing");
            } else {
                SCLogDebug("Continuing parsing SSLv2 record");
            }
            struct SSLDecoderResult r =
                    SSLv2Decode(direction, ssl_state, pstate, input, input_len, stream_slice);
            if (r.retval < 0 || r.retval > input_len) {
                DEBUG_VALIDATE_BUG_ON(r.retval > input_len);
                SCLogDebug("Error parsing SSLv2. Resetting parser "
                           "state. Let's get outta here");
                SSLParserReset(ssl_state);
                SSLSetEvent(ssl_state,
                        TLS_DECODER_EVENT_INVALID_SSL_RECORD);
                return APP_LAYER_ERROR;
            } else if (r.needed) {
                input += r.retval;
                SCLogDebug("returning consumed %" PRIuMAX " needed %u",
                        (uintmax_t)(input - init_input), r.needed);
                SCReturnStruct(APP_LAYER_INCOMPLETE(input - init_input, r.needed));
            }
            input_len -= r.retval;
            input += r.retval;
            SCLogDebug("SSLv2 decoder consumed %d bytes: %u left", r.retval, input_len);
        } else {
            if (ssl_state->curr_connp->bytes_processed == 0) {
                SCLogDebug("New TLS record: record_length %u",
                        ssl_state->curr_connp->record_length);
            } else {
                SCLogDebug("Continuing parsing TLS record: record_length %u, bytes_processed %u",
                        ssl_state->curr_connp->record_length, ssl_state->curr_connp->bytes_processed);
            }
            struct SSLDecoderResult r =
                    SSLv3Decode(direction, ssl_state, pstate, input, input_len, stream_slice);
            if (r.retval < 0 || r.retval > input_len) {
                DEBUG_VALIDATE_BUG_ON(r.retval > input_len);
                SCLogDebug("Error parsing TLS. Resetting parser "
                           "state.  Let's get outta here");
                SSLParserReset(ssl_state);
                return APP_LAYER_ERROR;
            } else if (r.needed) {
                input += r.retval;
                SCLogDebug("returning consumed %" PRIuMAX " needed %u",
                        (uintmax_t)(input - init_input), r.needed);
                SCReturnStruct(APP_LAYER_INCOMPLETE(input - init_input, r.needed));
            }
            input_len -= r.retval;
            input += r.retval;
            SCLogDebug("TLS decoder consumed %d bytes: %u left", r.retval, input_len);

            if (ssl_state->curr_connp->bytes_processed == SSLV3_RECORD_HDR_LEN
                    && ssl_state->curr_connp->record_length == 0) {
                SCLogDebug("TLS empty record");
                /* empty record */
                SSLParserReset(ssl_state);
            }
        }
        counter++;
    } /* while (input_len) */

    /* mark handshake as done if we have subject and issuer */
    if ((ssl_state->flags & SSL_AL_FLAG_NEED_CLIENT_CERT) &&
            ssl_state->client_connp.cert0_subject && ssl_state->client_connp.cert0_issuerdn) {
        SCLogDebug("SSL_AL_FLAG_HANDSHAKE_DONE");
        ssl_state->flags |= SSL_AL_FLAG_HANDSHAKE_DONE;
    } else if ((ssl_state->flags & SSL_AL_FLAG_NEED_CLIENT_CERT) == 0 &&
               ssl_state->server_connp.cert0_subject && ssl_state->server_connp.cert0_issuerdn) {
        SCLogDebug("SSL_AL_FLAG_HANDSHAKE_DONE");
        ssl_state->flags |= SSL_AL_FLAG_HANDSHAKE_DONE;
    }

    /* flag session as finished if APP_LAYER_PARSER_EOF is set */
    if (AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) &&
        AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC)) {
        SCLogDebug("SSL_AL_FLAG_STATE_FINISHED");
        ssl_state->flags |= SSL_AL_FLAG_STATE_FINISHED;
    }

    return APP_LAYER_OK;
}

static AppLayerResult SSLParseClientRecord(Flow *f, void *alstate, AppLayerParserState *pstate,
        StreamSlice stream_slice, void *local_data)
{
    return SSLDecode(f, 0 /* toserver */, alstate, pstate, stream_slice);
}

static AppLayerResult SSLParseServerRecord(Flow *f, void *alstate, AppLayerParserState *pstate,
        StreamSlice stream_slice, void *local_data)
{
    return SSLDecode(f, 1 /* toclient */, alstate, pstate, stream_slice);
}

/**
 * \internal
 * \brief Function to allocate the SSL state memory.
 */
static void *SSLStateAlloc(void *orig_state, AppProto proto_orig)
{
    SSLState *ssl_state = SCMalloc(sizeof(SSLState));
    if (unlikely(ssl_state == NULL))
        return NULL;
    memset(ssl_state, 0, sizeof(SSLState));
    ssl_state->client_connp.cert_log_flag = 0;
    ssl_state->server_connp.cert_log_flag = 0;
    memset(ssl_state->client_connp.random, 0, TLS_RANDOM_LEN);
    memset(ssl_state->server_connp.random, 0, TLS_RANDOM_LEN);
    TAILQ_INIT(&ssl_state->server_connp.certs);
    TAILQ_INIT(&ssl_state->client_connp.certs);

    return (void *)ssl_state;
}

/**
 * \internal
 * \brief Function to free the SSL state memory.
 */
static void SSLStateFree(void *p)
{
    SSLState *ssl_state = (SSLState *)p;
    SSLCertsChain *item;

    if (ssl_state->client_connp.cert0_subject)
        rs_cstring_free(ssl_state->client_connp.cert0_subject);
    if (ssl_state->client_connp.cert0_issuerdn)
        rs_cstring_free(ssl_state->client_connp.cert0_issuerdn);
    if (ssl_state->client_connp.cert0_serial)
        rs_cstring_free(ssl_state->client_connp.cert0_serial);
    if (ssl_state->client_connp.cert0_fingerprint)
        SCFree(ssl_state->client_connp.cert0_fingerprint);
    if (ssl_state->client_connp.sni)
        SCFree(ssl_state->client_connp.sni);
    if (ssl_state->client_connp.session_id)
        SCFree(ssl_state->client_connp.session_id);
    if (ssl_state->client_connp.hs_buffer)
        SCFree(ssl_state->client_connp.hs_buffer);

    if (ssl_state->server_connp.cert0_subject)
        rs_cstring_free(ssl_state->server_connp.cert0_subject);
    if (ssl_state->server_connp.cert0_issuerdn)
        rs_cstring_free(ssl_state->server_connp.cert0_issuerdn);
    if (ssl_state->server_connp.cert0_serial)
        rs_cstring_free(ssl_state->server_connp.cert0_serial);
    if (ssl_state->server_connp.cert0_fingerprint)
        SCFree(ssl_state->server_connp.cert0_fingerprint);
    if (ssl_state->server_connp.sni)
        SCFree(ssl_state->server_connp.sni);
    if (ssl_state->server_connp.session_id)
        SCFree(ssl_state->server_connp.session_id);

    if (ssl_state->client_connp.ja4)
        SCJA4Free(ssl_state->client_connp.ja4);
    if (ssl_state->client_connp.ja3_str)
        Ja3BufferFree(&ssl_state->client_connp.ja3_str);
    if (ssl_state->client_connp.ja3_hash)
        SCFree(ssl_state->client_connp.ja3_hash);
    if (ssl_state->server_connp.ja3_str)
        Ja3BufferFree(&ssl_state->server_connp.ja3_str);
    if (ssl_state->server_connp.ja3_hash)
        SCFree(ssl_state->server_connp.ja3_hash);
    if (ssl_state->server_connp.hs_buffer)
        SCFree(ssl_state->server_connp.hs_buffer);

    AppLayerDecoderEventsFreeEvents(&ssl_state->tx_data.events);

    if (ssl_state->tx_data.de_state != NULL) {
        DetectEngineStateFree(ssl_state->tx_data.de_state);
    }

    /* Free certificate chain */
    if (ssl_state->server_connp.certs_buffer)
        SCFree(ssl_state->server_connp.certs_buffer);
    while ((item = TAILQ_FIRST(&ssl_state->server_connp.certs))) {
        TAILQ_REMOVE(&ssl_state->server_connp.certs, item, next);
        SCFree(item);
    }
    TAILQ_INIT(&ssl_state->server_connp.certs);
    /* Free certificate chain */
    if (ssl_state->client_connp.certs_buffer)
        SCFree(ssl_state->client_connp.certs_buffer);
    while ((item = TAILQ_FIRST(&ssl_state->client_connp.certs))) {
        TAILQ_REMOVE(&ssl_state->client_connp.certs, item, next);
        SCFree(item);
    }
    TAILQ_INIT(&ssl_state->client_connp.certs);

    SCFree(ssl_state);

    return;
}

static void SSLStateTransactionFree(void *state, uint64_t tx_id)
{
    /* do nothing */
}

static AppProto SSLProbingParser(Flow *f, uint8_t direction,
        const uint8_t *input, uint32_t ilen, uint8_t *rdir)
{
    /* probably a rst/fin sending an eof */
    if (ilen < 3)
        return ALPROTO_UNKNOWN;

    /* for now just the 3 byte header ones */
    /* \todo Detect the 2 byte ones */
    if ((input[0] & 0x80) && (input[2] == 0x01)) {
        return ALPROTO_TLS;
    }

    return ALPROTO_FAILED;
}

static int SSLStateGetFrameIdByName(const char *frame_name)
{
    int id = SCMapEnumNameToValue(frame_name, tls_frame_table);
    if (id < 0) {
        return -1;
    }
    return id;
}

static const char *SSLStateGetFrameNameById(const uint8_t frame_id)
{
    const char *name = SCMapEnumValueToName(frame_id, tls_frame_table);
    return name;
}

static int SSLStateGetEventInfo(const char *event_name,
                         int *event_id, AppLayerEventType *event_type)
{
    *event_id = SCMapEnumNameToValue(event_name, tls_decoder_event_table);
    if (*event_id == -1) {
        SCLogError("event \"%s\" not present in "
                   "ssl's enum map table.",
                event_name);
        /* yes this is fatal */
        return -1;
    }

    *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;

    return 0;
}

static int SSLStateGetEventInfoById(int event_id, const char **event_name,
                                    AppLayerEventType *event_type)
{
    *event_name = SCMapEnumValueToName(event_id, tls_decoder_event_table);
    if (*event_name == NULL) {
        SCLogError("event \"%d\" not present in "
                   "ssl's enum map table.",
                event_id);
        /* yes this is fatal */
        return -1;
    }

    *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;

    return 0;
}

static int SSLRegisterPatternsForProtocolDetection(void)
{
    if (AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_TLS, "|01 00 02|", 5, 2,
                STREAM_TOSERVER, SSLProbingParser, 0, 3) < 0) {
        return -1;
    }

    /** SSLv3 */
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|01 03 00|", 3, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|16 03 00|", 3, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }

    /** TLSv1 */
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|01 03 01|", 3, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|16 03 01|", 3, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }

    /** TLSv1.1 */
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|01 03 02|", 3, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|16 03 02|", 3, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }

    /** TLSv1.2 */
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|01 03 03|", 3, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|16 03 03|", 3, 0, STREAM_TOSERVER) < 0)
    {
        return -1;
    }

    /***** toclient direction *****/

    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|15 03 00|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|16 03 00|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|17 03 00|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }

    /** TLSv1 */
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|15 03 01|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|16 03 01|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|17 03 01|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }

    /** TLSv1.1 */
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|15 03 02|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|16 03 02|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|17 03 02|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }

    /** TLSv1.2 */
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|15 03 03|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|16 03 03|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|17 03 03|", 3, 0, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }

    /* Subsection - SSLv2 style record by client, but informing the server
     * the max version it supports.
     * Updated by Anoop Saldanha.  Disabled it for now.  We'll get back to
     * it after some tests */
#if 0
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|01 03 00|", 5, 2, STREAM_TOSERVER) < 0)
    {
        return -1;
    }
    if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
                                               "|00 02|", 7, 5, STREAM_TOCLIENT) < 0)
    {
        return -1;
    }
#endif

    return 0;
}

#ifdef HAVE_JA3
static void CheckJA3Enabled(void)
{
    const char *strval = NULL;
    /* Check if we should generate JA3 fingerprints */
    int enable_ja3 = SSL_CONFIG_DEFAULT_JA3;
    if (ConfGet("app-layer.protocols.tls.ja3-fingerprints", &strval) != 1) {
        enable_ja3 = SSL_CONFIG_DEFAULT_JA3;
    } else if (strcmp(strval, "auto") == 0) {
        enable_ja3 = SSL_CONFIG_DEFAULT_JA3;
    } else if (ConfValIsFalse(strval)) {
        enable_ja3 = 0;
        ssl_config.disable_ja3 = true;
    } else if (ConfValIsTrue(strval)) {
        enable_ja3 = true;
    }
    SC_ATOMIC_SET(ssl_config.enable_ja3, enable_ja3);
    if (!ssl_config.disable_ja3 && !g_disable_hashing) {
        /* The feature is available, i.e. _could_ be activated by a rule or
            even is enabled in the configuration. */
        ProvidesFeature(FEATURE_JA3);
    }
}
#endif /* HAVE_JA3 */

#ifdef HAVE_JA4
static void CheckJA4Enabled(void)
{
    const char *strval = NULL;
    /* Check if we should generate JA4 fingerprints */
    int enable_ja4 = SSL_CONFIG_DEFAULT_JA4;
    if (ConfGet("app-layer.protocols.tls.ja4-fingerprints", &strval) != 1) {
        enable_ja4 = SSL_CONFIG_DEFAULT_JA4;
    } else if (strcmp(strval, "auto") == 0) {
        enable_ja4 = SSL_CONFIG_DEFAULT_JA4;
    } else if (ConfValIsFalse(strval)) {
        enable_ja4 = 0;
        ssl_config.disable_ja4 = true;
    } else if (ConfValIsTrue(strval)) {
        enable_ja4 = true;
    }
    SC_ATOMIC_SET(ssl_config.enable_ja4, enable_ja4);
    if (!ssl_config.disable_ja4 && !g_disable_hashing) {
        /* The feature is available, i.e. _could_ be activated by a rule or
            even is enabled in the configuration. */
        ProvidesFeature(FEATURE_JA4);
    }
}
#endif /* HAVE_JA4 */

/**
 * \brief Function to register the SSL protocol parser and other functions
 */
void RegisterSSLParsers(void)
{
    const char *proto_name = "tls";

    SC_ATOMIC_INIT(ssl_config.enable_ja3);

    /** SSLv2  and SSLv23*/
    if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
        AppLayerProtoDetectRegisterProtocol(ALPROTO_TLS, proto_name);

        if (SSLRegisterPatternsForProtocolDetection() < 0)
            return;

        if (RunmodeIsUnittests()) {
            AppLayerProtoDetectPPRegister(IPPROTO_TCP,
                                          "443",
                                          ALPROTO_TLS,
                                          0, 3,
                                          STREAM_TOSERVER,
                                          SSLProbingParser, NULL);
        } else {
            if (AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
                                                    proto_name, ALPROTO_TLS,
                                                    0, 3,
                                                    SSLProbingParser, NULL) == 0) {
                SCLogConfig("no TLS config found, "
                            "enabling TLS detection on port 443.");
                AppLayerProtoDetectPPRegister(IPPROTO_TCP,
                                              "443",
                                              ALPROTO_TLS,
                                              0, 3,
                                              STREAM_TOSERVER,
                                              SSLProbingParser, NULL);
            }
        }
    } else {
        SCLogConfig("Protocol detection and parser disabled for %s protocol",
                  proto_name);
        return;
    }

    if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
        AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOSERVER,
                                     SSLParseClientRecord);

        AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOCLIENT,
                                     SSLParseServerRecord);

        AppLayerParserRegisterGetFrameFuncs(
                IPPROTO_TCP, ALPROTO_TLS, SSLStateGetFrameIdByName, SSLStateGetFrameNameById);
        AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetEventInfo);
        AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetEventInfoById);

        AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TLS, SSLStateAlloc, SSLStateFree);

        AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOSERVER);

        AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_TLS, SSLStateTransactionFree);

        AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_TLS, SSLGetTx);
        AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_TLS, SSLGetTxData);
        AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_TLS, SSLGetStateData);

        AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_TLS, SSLGetTxCnt);

        AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_TLS, SSLGetAlstateProgress);

        AppLayerParserRegisterStateProgressCompletionStatus(
                ALPROTO_TLS, TLS_STATE_FINISHED, TLS_STATE_FINISHED);

        ConfNode *enc_handle = ConfGetNode("app-layer.protocols.tls.encryption-handling");
        if (enc_handle != NULL && enc_handle->val != NULL) {
            SCLogDebug("have app-layer.protocols.tls.encryption-handling = %s", enc_handle->val);
            if (strcmp(enc_handle->val, "full") == 0) {
                ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_FULL;
            } else if (strcmp(enc_handle->val, "bypass") == 0) {
                ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_BYPASS;
            } else if (strcmp(enc_handle->val, "default") == 0) {
                ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_DEFAULT;
            } else {
                ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_DEFAULT;
            }
        } else {
            /* Get the value of no reassembly option from the config file */
            if (ConfGetNode("app-layer.protocols.tls.no-reassemble") == NULL) {
                int value = 0;
                if (ConfGetBool("tls.no-reassemble", &value) == 1 && value == 1)
                    ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_BYPASS;
            } else {
                int value = 0;
                if (ConfGetBool("app-layer.protocols.tls.no-reassemble", &value) == 1 && value == 1)
                    ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_BYPASS;
            }
        }
        SCLogDebug("ssl_config.encrypt_mode %u", ssl_config.encrypt_mode);

#ifdef HAVE_JA3
        CheckJA3Enabled();
#endif /* HAVE_JA3 */
#ifdef HAVE_JA4
        CheckJA4Enabled();
#endif /* HAVE_JA4 */

        if (g_disable_hashing) {
            if (SC_ATOMIC_GET(ssl_config.enable_ja3)) {
                SCLogWarning("MD5 calculation has been disabled, disabling JA3");
                SC_ATOMIC_SET(ssl_config.enable_ja3, 0);
            }
            if (SC_ATOMIC_GET(ssl_config.enable_ja4)) {
                SCLogWarning("Hashing has been disabled, disabling JA4");
                SC_ATOMIC_SET(ssl_config.enable_ja4, 0);
            }
        } else {
            if (RunmodeIsUnittests()) {
#ifdef HAVE_JA3
                SC_ATOMIC_SET(ssl_config.enable_ja3, 1);
#endif /* HAVE_JA3 */
#ifdef HAVE_JA4
                SC_ATOMIC_SET(ssl_config.enable_ja4, 1);
#endif /* HAVE_JA4 */
            }
        }
    } else {
        SCLogConfig("Parsed disabled for %s protocol. Protocol detection"
                  "still on.", proto_name);
    }

    return;
}

/**
 * \brief if not explicitly disabled in config, enable ja3 support
 *
 * Implemented using atomic to allow rule reloads to do this at
 * runtime.
 */
void SSLEnableJA3(void)
{
    if (g_disable_hashing || ssl_config.disable_ja3) {
        return;
    }
    if (SC_ATOMIC_GET(ssl_config.enable_ja3)) {
        return;
    }
    SC_ATOMIC_SET(ssl_config.enable_ja3, 1);
}

/**
 * \brief if not explicitly disabled in config, enable ja4 support
 *
 * Implemented using atomic to allow rule reloads to do this at
 * runtime.
 */
void SSLEnableJA4(void)
{
    if (g_disable_hashing || ssl_config.disable_ja4) {
        return;
    }
    if (SC_ATOMIC_GET(ssl_config.enable_ja4)) {
        return;
    }
    SC_ATOMIC_SET(ssl_config.enable_ja4, 1);
}

/**
 * \brief return whether ja3 is effectively enabled
 *
 * This means that it either has been enabled explicitly or has been
 * enabled by having loaded a rule while not being explicitly disabled.
 *
 * \retval true if enabled, false otherwise
 */
bool SSLJA3IsEnabled(void)
{
    return SC_ATOMIC_GET(ssl_config.enable_ja3);
}

/**
 * \brief return whether ja4 is effectively enabled
 *
 * This means that it either has been enabled explicitly or has been
 * enabled by having loaded a rule while not being explicitly disabled.
 *
 * \retval true if enabled, false otherwise
 */
bool SSLJA4IsEnabled(void)
{
    return SC_ATOMIC_GET(ssl_config.enable_ja4);
}
