/*
 * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#ifndef OSSL_QUIC_LOCAL_H
#define OSSL_QUIC_LOCAL_H

#include <openssl/ssl.h>
#include "internal/quic_ssl.h" /* QUIC_CONNECTION */
#include "internal/quic_txp.h"
#include "internal/quic_statm.h"
#include "internal/quic_demux.h"
#include "internal/quic_record_rx.h"
#include "internal/quic_tls.h"
#include "internal/quic_fc.h"
#include "internal/quic_stream.h"
#include "internal/quic_channel.h"
#include "internal/quic_reactor.h"
#include "internal/quic_thread_assist.h"
#include "../ssl_local.h"

#ifndef OPENSSL_NO_QUIC

/*
 * QUIC stream SSL object (QSSO) type. This implements the API personality layer
 * for QSSO objects, wrapping the QUIC-native QUIC_STREAM object and tracking
 * state required by the libssl API personality.
 */
struct quic_xso_st {
    /* SSL object common header. */
    struct ssl_st ssl;

    /* The connection this stream is associated with. Always non-NULL. */
    QUIC_CONNECTION *conn;

    /* The stream object. Always non-NULL for as long as the XSO exists. */
    QUIC_STREAM *stream;

    /*
     * Has this stream been logically configured into blocking mode? Only
     * meaningful if desires_blocking_set is 1. Ignored if blocking is not
     * currently possible given QUIC_CONNECTION configuration.
     */
    unsigned int desires_blocking : 1;

    /*
     * Has SSL_set_blocking_mode been called on this stream? If not set, we
     * inherit from the QUIC_CONNECTION blocking state.
     */
    unsigned int desires_blocking_set : 1;

    /* The application has retired a FIN (i.e. SSL_ERROR_ZERO_RETURN). */
    unsigned int retired_fin : 1;

    /*
     * The application has requested a reset. Not set for reflexive
     * STREAM_RESETs caused by peer STOP_SENDING.
     */
    unsigned int requested_reset : 1;

    /*
     * This state tracks SSL_write all-or-nothing (AON) write semantics
     * emulation.
     *
     * Example chronology:
     *
     *   t=0:  aon_write_in_progress=0
     *   t=1:  SSL_write(ssl, b1, l1) called;
     *         too big to enqueue into sstream at once, SSL_ERROR_WANT_WRITE;
     *         aon_write_in_progress=1; aon_buf_base=b1; aon_buf_len=l1;
     *         aon_buf_pos < l1 (depends on how much room was in sstream);
     *   t=2:  SSL_write(ssl, b2, l2);
     *         b2 must equal b1 (validated unless ACCEPT_MOVING_WRITE_BUFFER)
     *         l2 must equal l1 (always validated)
     *         append into sstream from [b2 + aon_buf_pos, b2 + aon_buf_len)
     *         if done, aon_write_in_progress=0
     *
     */
    /* Is an AON write in progress? */
    unsigned int aon_write_in_progress : 1;

    /* Event handling mode. One of SSL_QUIC_VALUE_EVENT_HANDLING. */
    unsigned int event_handling_mode : 2;

    /*
     * The base buffer pointer the caller passed us for the initial AON write
     * call. We use this for validation purposes unless
     * ACCEPT_MOVING_WRITE_BUFFER is enabled.
     *
     * NOTE: We never dereference this, as the caller might pass a different
     * (but identical) buffer if using ACCEPT_MOVING_WRITE_BUFFER. It is for
     * validation by pointer comparison only.
     */
    const unsigned char *aon_buf_base;
    /* The total length of the AON buffer being sent, in bytes. */
    size_t aon_buf_len;
    /*
     * The position in the AON buffer up to which we have successfully sent data
     * so far.
     */
    size_t aon_buf_pos;

    /* SSL_set_mode */
    uint32_t ssl_mode;

    /* SSL_set_options */
    uint64_t ssl_options;

    /*
     * Last 'normal' error during an app-level I/O operation, used by
     * SSL_get_error(); used to track data-path errors like SSL_ERROR_WANT_READ
     * and SSL_ERROR_WANT_WRITE.
     */
    int last_error;
};

struct quic_conn_st {
    /*
     * ssl_st is a common header for ordinary SSL objects, QUIC connection
     * objects and QUIC stream objects, allowing objects of these different
     * types to be disambiguated at runtime and providing some common fields.
     *
     * Note: This must come first in the QUIC_CONNECTION structure.
     */
    struct ssl_st ssl;

    SSL *tls;

    /* The QUIC engine representing the QUIC event domain. */
    QUIC_ENGINE *engine;

    /* The QUIC port representing the QUIC listener and socket. */
    QUIC_PORT *port;

    /*
     * The QUIC channel providing the core QUIC connection implementation. Note
     * that this is not instantiated until we actually start trying to do the
     * handshake. This is to allow us to gather information like whether we are
     * going to be in client or server mode before committing to instantiating
     * the channel, since we want to determine the channel arguments based on
     * that.
     *
     * The channel remains available after connection termination until the SSL
     * object is freed, thus (ch != NULL) iff (started == 1).
     */
    QUIC_CHANNEL *ch;

    /*
     * The mutex used to synchronise access to the QUIC_CHANNEL. We own this but
     * provide it to the channel.
     */
    CRYPTO_MUTEX *mutex;

    /*
     * If we have a default stream attached, this is the internal XSO
     * object. If there is no default stream, this is NULL.
     */
    QUIC_XSO *default_xso;

    /* The network read and write BIOs. */
    BIO *net_rbio, *net_wbio;

    /* Initial peer L4 address. */
    BIO_ADDR init_peer_addr;

#ifndef OPENSSL_NO_QUIC_THREAD_ASSIST
    /* Manages thread for QUIC thread assisted mode. */
    QUIC_THREAD_ASSIST thread_assist;
#endif

    /* If non-NULL, used instead of ossl_time_now(). Used for testing. */
    OSSL_TIME (*override_now_cb)(void *arg);
    void *override_now_cb_arg;

    /* Number of XSOs allocated. Includes the default XSO, if any. */
    size_t num_xso;

    /* Have we started? */
    unsigned int started : 1;

    /*
     * This is 1 if we were instantiated using a QUIC server method
     * (for future use).
     */
    unsigned int as_server : 1;

    /*
     * Has the application called SSL_set_accept_state? We require this to be
     * congruent with the value of as_server.
     */
    unsigned int as_server_state : 1;

    /* Are we using thread assisted mode? Never changes after init. */
    unsigned int is_thread_assisted : 1;

    /* Do connection-level operations (e.g. handshakes) run in blocking mode? */
    unsigned int blocking : 1;

    /* Does the application want blocking mode? */
    unsigned int desires_blocking : 1;

    /* Have we created a default XSO yet? */
    unsigned int default_xso_created : 1;

    /*
     * Pre-TERMINATING shutdown phase in which we are flushing streams.
     * Monotonically transitions to 1.
     * New streams cannot be created in this state.
     */
    unsigned int shutting_down : 1;

    /* Have we probed the BIOs for addressing support? */
    unsigned int addressing_probe_done : 1;

    /* Are we using addressed mode (BIO_sendmmsg with non-NULL peer)? */
    unsigned int addressed_mode_w : 1;
    unsigned int addressed_mode_r : 1;

    /* Event handling mode. One of SSL_QUIC_VALUE_EVENT_HANDLING. */
    unsigned int event_handling_mode : 2;

    /* Default stream type. Defaults to SSL_DEFAULT_STREAM_MODE_AUTO_BIDI. */
    uint32_t default_stream_mode;

    /* SSL_set_mode. This is not used directly but inherited by new XSOs. */
    uint32_t default_ssl_mode;

    /* SSL_set_options. This is not used directly but inherited by new XSOs. */
    uint64_t default_ssl_options;

    /* SSL_set_incoming_stream_policy. */
    int incoming_stream_policy;
    uint64_t incoming_stream_aec;

    /*
     * Last 'normal' error during an app-level I/O operation, used by
     * SSL_get_error(); used to track data-path errors like SSL_ERROR_WANT_READ
     * and SSL_ERROR_WANT_WRITE.
     */
    int last_error;
};

/* Internal calls to the QUIC CSM which come from various places. */
int ossl_quic_conn_on_handshake_confirmed(QUIC_CONNECTION *qc);

/*
 * To be called when a protocol violation occurs. The connection is torn down
 * with the given error code, which should be a OSSL_QUIC_ERR_* value. Reason
 * string is optional and copied if provided. frame_type should be 0 if not
 * applicable.
 */
void ossl_quic_conn_raise_protocol_error(QUIC_CONNECTION *qc,
    uint64_t error_code,
    uint64_t frame_type,
    const char *reason);

void ossl_quic_conn_on_remote_conn_close(QUIC_CONNECTION *qc,
    OSSL_QUIC_FRAME_CONN_CLOSE *f);

int ossl_quic_trace(int write_p, int version, int content_type,
    const void *buf, size_t msglen, SSL *ssl, void *arg);

#define OSSL_QUIC_ANY_VERSION 0xFFFFF
#define IS_QUIC_METHOD(m) \
    ((m) == OSSL_QUIC_client_method() || (m) == OSSL_QUIC_client_thread_method())
#define IS_QUIC_CTX(ctx) IS_QUIC_METHOD((ctx)->method)

#define QUIC_CONNECTION_FROM_SSL_int(ssl, c)                  \
    ((ssl) == NULL ? NULL                                     \
                   : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION \
                             ? (c QUIC_CONNECTION *)(ssl)     \
                             : NULL))

#define QUIC_XSO_FROM_SSL_int(ssl, c)                                                   \
    ((ssl) == NULL                                                                      \
            ? NULL                                                                      \
            : (((ssl)->type == SSL_TYPE_QUIC_XSO                                        \
                      ? (c QUIC_XSO *)(ssl)                                             \
                      : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION                        \
                                ? (c QUIC_XSO *)((QUIC_CONNECTION *)(ssl))->default_xso \
                                : NULL))))

#define SSL_CONNECTION_FROM_QUIC_SSL_int(ssl, c)                                     \
    ((ssl) == NULL ? NULL                                                            \
                   : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION                        \
                             ? (c SSL_CONNECTION *)((c QUIC_CONNECTION *)(ssl))->tls \
                             : NULL))

#define IS_QUIC(ssl) ((ssl) != NULL             \
    && ((ssl)->type == SSL_TYPE_QUIC_CONNECTION \
        || (ssl)->type == SSL_TYPE_QUIC_XSO))
#else
#define QUIC_CONNECTION_FROM_SSL_int(ssl, c) NULL
#define QUIC_XSO_FROM_SSL_int(ssl, c) NULL
#define SSL_CONNECTION_FROM_QUIC_SSL_int(ssl, c) NULL
#define IS_QUIC(ssl) 0
#define IS_QUIC_CTX(ctx) 0
#define IS_QUIC_METHOD(m) 0
#endif

#define QUIC_CONNECTION_FROM_SSL(ssl) \
    QUIC_CONNECTION_FROM_SSL_int(ssl, SSL_CONNECTION_NO_CONST)
#define QUIC_CONNECTION_FROM_CONST_SSL(ssl) \
    QUIC_CONNECTION_FROM_SSL_int(ssl, const)
#define QUIC_XSO_FROM_SSL(ssl) \
    QUIC_XSO_FROM_SSL_int(ssl, SSL_CONNECTION_NO_CONST)
#define QUIC_XSO_FROM_CONST_SSL(ssl) \
    QUIC_XSO_FROM_SSL_int(ssl, const)
#define SSL_CONNECTION_FROM_QUIC_SSL(ssl) \
    SSL_CONNECTION_FROM_QUIC_SSL_int(ssl, SSL_CONNECTION_NO_CONST)
#define SSL_CONNECTION_FROM_CONST_QUIC_SSL(ssl) \
    SSL_CONNECTION_FROM_CONST_QUIC_SSL_int(ssl, const)

#define IMPLEMENT_quic_meth_func(version, func_name, q_accept, \
    q_connect, enc_data)                                       \
    const SSL_METHOD *func_name(void)                          \
    {                                                          \
        static const SSL_METHOD func_name##_data = {           \
            version,                                           \
            0,                                                 \
            0,                                                 \
            ossl_quic_new,                                     \
            ossl_quic_free,                                    \
            ossl_quic_reset,                                   \
            ossl_quic_init,                                    \
            NULL /* clear */,                                  \
            ossl_quic_deinit,                                  \
            q_accept,                                          \
            q_connect,                                         \
            ossl_quic_read,                                    \
            ossl_quic_peek,                                    \
            ossl_quic_write,                                   \
            NULL /* shutdown */,                               \
            NULL /* renegotiate */,                            \
            ossl_quic_renegotiate_check,                       \
            NULL /* read_bytes */,                             \
            NULL /* write_bytes */,                            \
            NULL /* dispatch_alert */,                         \
            ossl_quic_ctrl,                                    \
            ossl_quic_ctx_ctrl,                                \
            ossl_quic_get_cipher_by_char,                      \
            NULL /* put_cipher_by_char */,                     \
            ossl_quic_pending,                                 \
            ossl_quic_num_ciphers,                             \
            ossl_quic_get_cipher,                              \
            tls1_default_timeout,                              \
            &enc_data,                                         \
            ssl_undefined_void_function,                       \
            ossl_quic_callback_ctrl,                           \
            ossl_quic_ctx_callback_ctrl,                       \
        };                                                     \
        return &func_name##_data;                              \
    }

#endif
