/*
 * include/haproxy/quic_conn.h
 *
 * Copyright 2020 HAProxy Technologies, Frederic Lecaille <flecaille@haproxy.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation, version 2.1
 * exclusively.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifndef _HAPROXY_QUIC_CONN_H
#define _HAPROXY_QUIC_CONN_H
#ifdef USE_QUIC
#ifndef USE_OPENSSL
#error "Must define USE_OPENSSL"
#endif

#include <inttypes.h>

#include <import/eb64tree.h>
#include <import/ebmbtree.h>

#include <haproxy/counters.h>
#include <haproxy/chunk.h>
#include <haproxy/dynbuf.h>
#include <haproxy/ncbmbuf.h>
#include <haproxy/net_helper.h>
#include <haproxy/openssl-compat.h>
#include <haproxy/ticks.h>

#include <haproxy/listener.h>
#include <haproxy/proto_quic.h>
#include <haproxy/quic_cc.h>
#include <haproxy/quic_cid.h>
#include <haproxy/quic_conn-t.h>
#include <haproxy/quic_enc.h>
#include <haproxy/quic_frame.h>
#include <haproxy/quic_loss.h>
#include <haproxy/quic_pacing.h>
#include <haproxy/quic_rx.h>
#include <haproxy/quic_tune.h>
#include <haproxy/mux_quic.h>

#include <openssl/rand.h>

extern struct pool_head *pool_head_quic_connection_id;

int qc_conn_finalize(struct quic_conn *qc, int server);
int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
struct quic_cstream *quic_cstream_new(struct quic_conn *qc);
void quic_cstream_free(struct quic_cstream *cs);
void quic_free_arngs(struct quic_conn *qc, struct quic_arngs *arngs);
struct quic_cstream *quic_cstream_new(struct quic_conn *qc);
struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int state);

void quic_conn_closed_err_count_inc(struct quic_conn *qc, struct quic_frame *frm);
int qc_h3_request_reject(struct quic_conn *qc, uint64_t id);
struct quic_conn *qc_new_conn(void *target,
                              const struct quic_rx_packet *initial_pkt,
                              const struct quic_cid *token_odcid,
                              struct connection *connection,
                              struct quic_connection_id *conn_id,
                              struct sockaddr_storage *local_addr,
                              struct sockaddr_storage *peer_addr);
int quic_build_post_handshake_frames(struct quic_conn *qc,
                                     struct list *to_frms_list);
const struct quic_version *qc_supported_version(uint32_t version);
int quic_peer_validated_addr(struct quic_conn *qc);
void qc_set_timer(struct quic_conn *qc);
void qc_detach_th_ctx_list(struct quic_conn *qc, int closing);
void qc_idle_timer_do_rearm(struct quic_conn *qc, int arm_ack);
void qc_idle_timer_rearm(struct quic_conn *qc, int read, int arm_ack);
void qc_check_close_on_released_mux(struct quic_conn *qc);
int quic_stateless_reset_token_cpy(unsigned char *pos, size_t len,
                                   const unsigned char *salt, size_t saltlen);
int quic_reuse_srv_params(struct quic_conn *qc,
                          const char *alpn,
                          const struct quic_early_transport_params *etps);

/* Returns true if <qc> is used on the backed side (as a client). */
static inline int qc_is_back(const struct quic_conn *qc)
{
	return qc->flags & QUIC_FL_CONN_IS_BACK;
}

static inline enum quic_cid_side qc_cid_side(const struct quic_conn *qc)
{
	return !(qc->flags & QUIC_FL_CONN_IS_BACK) ?
	        QUIC_CID_SIDE_FE : QUIC_CID_SIDE_BE;
}

/* Free the CIDs attached to <conn> QUIC connection. */
static inline void free_quic_conn_cids(struct quic_conn *conn)
{
	struct eb64_node *node;

	if (!conn->cids)
		return;

	node = eb64_first(conn->cids);
	while (node) {
		struct quic_connection_id *conn_id;

		conn_id = eb64_entry(node, struct quic_connection_id, seq_num);

		/* remove the CID from the receiver tree */
		quic_cid_delete(conn_id);

		/* remove the CID from the quic_conn tree */
		node = eb64_next(node);
		eb64_delete(&conn_id->seq_num);
		pool_free(pool_head_quic_connection_id, conn_id);
	}
}

/* Move all the connection IDs from <conn> QUIC connection to <cc_conn> */
static inline void quic_conn_mv_cids_to_cc_conn(struct quic_conn_closed *cc_conn,
                                                struct quic_conn *conn)
{
	struct eb64_node *node;

	node = eb64_first(conn->cids);
	while (node) {
		struct quic_connection_id *conn_id;

		conn_id = eb64_entry(node, struct quic_connection_id, seq_num);
		conn_id->qc = (struct quic_conn *)cc_conn;
		node = eb64_next(node);
	}

}

/* Allocate the underlying required memory for <ncbuf> non-contiguous buffer.
 * Does nothing if buffer is already allocated.
 *
 * Returns the buffer instance or NULL on allocation failure.
 */
static inline struct ncbmbuf *quic_get_ncbuf(struct ncbmbuf *ncbuf)
{
	struct buffer buf = BUF_NULL;

	if (!ncbmb_is_null(ncbuf))
		return ncbuf;

	if (!b_alloc(&buf, DB_MUX_RX))
		return NULL;

	*ncbuf = ncbmb_make(buf.area, buf.size, 0);
	ncbmb_init(ncbuf, 0);

	return ncbuf;
}

/* Release the underlying memory use by <ncbuf> non-contiguous buffer */
static inline void quic_free_ncbuf(struct ncbmbuf *ncbuf)
{
	struct buffer buf;

	if (ncbmb_is_null(ncbuf))
		return;

	buf = b_make(ncbuf->area, ncbuf->size, 0, 0);
	b_free(&buf);
	offer_buffers(NULL, 1);

	*ncbuf = NCBMBUF_NULL;
}

/* Return the address of the QUIC counters attached to the proxy of
 * the owner of the connection whose object type address is <o> for
 * listener and servers, or NULL for others object type.
 */
static inline void *qc_counters(enum obj_type *o, const struct stats_module *m)
{
	struct proxy *p;
	struct listener *l;
	struct server *s;

	if (!o)
		return NULL;

	l = objt_listener(o);
	s = objt_server(o);
	p = l ? l->bind_conf->frontend :
		s ? s->proxy : NULL;

	if (l && p)
		return EXTRA_COUNTERS_GET(p->extra_counters_fe, m);
	else if (s && p)
		return EXTRA_COUNTERS_GET(p->extra_counters_be, m);
	return NULL;
}

void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
void quic_set_tls_alert(struct quic_conn *qc, int alert);
int qc_register_alpn(struct quic_conn *qc, const char *alpn, int alpn_len);
int qc_check_dcid(struct quic_conn *qc, unsigned char *dcid, size_t dcid_len);

void qc_notify_err(struct quic_conn *qc);
int qc_notify_send(struct quic_conn *qc);

void qc_check_close_on_released_mux(struct quic_conn *qc);

int quic_conn_release(struct quic_conn *qc);

void qc_kill_conn(struct quic_conn *qc);

int qc_parse_hd_form(struct quic_rx_packet *pkt,
                     unsigned char **buf, const unsigned char *end);

int qc_bind_tid_prep(struct quic_conn *qc, uint new_tid);
void qc_bind_tid_commit(struct quic_conn *qc, struct listener *new_li);
void qc_bind_tid_reset(struct quic_conn *qc);
void qc_finalize_tid_rebind(struct quic_conn *qc);

int qc_handle_conn_migration(struct quic_conn *qc,
                             const struct sockaddr_storage *peer_addr,
                             const struct sockaddr_storage *local_addr);

/* Function pointer that can be used to compute a hash from first generated CID (derived from ODCID) */
extern uint64_t (*quic_hash64_from_cid)(const unsigned char *cid, int size, const unsigned char *secret, size_t secretlen);
/* Function pointer that can be used to derive a new CID from the previously computed hash */
extern void (*quic_newcid_from_hash64)(unsigned char *cid, int size, uint64_t hash, const unsigned char *secret, size_t secretlen);

/* QUIC xprt layer functions */
int qc_wait_for_conn(const struct quic_conn *qc);
int qc_is_conn_ready(const struct quic_conn *qc);

#endif /* USE_QUIC */
#endif /* _HAPROXY_QUIC_CONN_H */
