/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2017-2025 Brett Sheffield <bacs@librecast.net> */

#ifndef _LIBRECAST_PVT_H
#define _LIBRECAST_PVT_H 1
#define _LIBRECAST_PRIVATE

#include <config.h>
#include <librecast/types.h>
#include <librecast/crypto.h>
#include <pthread.h>
#include <stddef.h>
#ifdef HAVE_LIBLCRQ
# include <lcrq.h>
#endif
#ifdef HAVE_MLD
# include <mld.h>
# include <librecast/q.h>
#endif

#ifdef HAVE_ENDIAN_H
# include <endian.h>
#elif defined(HAVE_SYS_ENDIAN_H)
# include <sys/endian.h>
#elif defined(HAVE_LIBKERN_OSBYTEORDER_H)
# include <libkern/OSByteOrder.h>
# define htobe64(x) OSSwapHostToBigInt64(x)
# define be64toh(x) OSSwapBigToHostInt64(x)
#endif

/* fall back to CLOCK_REALTIME when Linux-specific CLOCK_TAI is unavailable */
#ifndef CLOCK_TAI
# define CLOCK_TAI CLOCK_REALTIME
#endif

#define NANO 1000000000

enum {
	DELETE_LATER       = 1, /* marked for deletion */
	DELETE_IN_PROGRESS = 2, /* deletion in progress */
};

#define aload(x) __atomic_load_n((x), __ATOMIC_ACQUIRE)
#define astor(x, y) __atomic_store_n((x),(y), __ATOMIC_RELEASE)
#define fetch_add(x, y) __atomic_fetch_add((x),(y), __ATOMIC_ACQ_REL)
#define add_fetch(x, y) __atomic_add_fetch((x),(y), __ATOMIC_ACQ_REL)
#define CAE(a, b, c) __atomic_compare_exchange_n((a), (b), (c), 0, \
		__ATOMIC_RELEASE, __ATOMIC_ACQUIRE)
#define CAE_weak(a, b, c) __atomic_compare_exchange_n((a), (b), (c), 1, \
		__ATOMIC_RELEASE, __ATOMIC_ACQUIRE)
#define CAE_loop(a, b, c) while(!__atomic_compare_exchange_n((a), (b), (c), 1, \
		__ATOMIC_RELEASE, __ATOMIC_ACQUIRE))
#define fetch_and_ACQ(x, y) __atomic_fetch_and((x), (y), __ATOMIC_ACQUIRE)
#define fetch_and(x, y) __atomic_fetch_and((x), (y), __ATOMIC_ACQ_REL)
#define fetch_or_REL(x, y) __atomic_fetch_or((x), (y), __ATOMIC_RELEASE)
#define or_fetch(x, y) __atomic_or_fetch((x), (y), __ATOMIC_ACQ_REL)
#define fetch_or_REL(x, y) __atomic_fetch_or((x), (y), __ATOMIC_RELEASE)

typedef struct lc_ctx_t {
	lc_ctx_t *next;
	lc_socket_t *sock_list;
	lc_channel_t *chan_list;
	lc_router_t *router_list;
	mdex_t *mdex;
	q_t *q; /* context queue */
	pthread_t *tid; /* array of nthreads worker threads */
	pthread_mutex_t mtx; /* mutex for sock + chan lists */
	lc_key_t key; /* symmetric key */
	lc_key_t pek; /* public encryption key */
	lc_key_t sek; /* secret encryption key */
	lc_key_t psk; /* public signing key */
	lc_key_t ssk; /* secret signing key */
	FILE *stream;
	int debug;
	int coding;   /* encryption/encoding to use */
	int nthreads; /* number of ctx worker threads */
	size_t bps_out; /* send rate limit (bps) */
	size_t bps_in;  /* recv rate limit (bps) */
	unsigned int ifx; /* default interface for sockets and channels */
	int sock; /* AF_LOCAL socket for ioctls */
	int readers; /* reference count (readers) */
	int deleted; /* marked for deletion */
#ifdef HAVE_LIBLCRQ
	int rq_oh;       /* RaptorQ automatic overhead pkts. Default: -1 (off) */
#endif
} lc_ctx_t;

#ifndef IPV6_MULTICAST_ALL
typedef struct lc_grplist_s lc_grplist_t;
struct lc_grplist_s {
	lc_grplist_t *next;
	struct in6_addr grp;
};
#endif

typedef struct lc_socket_t {
	lc_socket_t *next;
	lc_socket_t *pair; /* other socket, if type == LC_SOCK_PAIR */
	lc_ctx_t *ctx;
	lc_channel_t *chan_active; /* channel currently in use */
	lc_router_t *router;
	unsigned char *oil;
	lc_socktype_t type;
	pthread_t thread;
	size_t byt_out; /* bytes sent */
	size_t byt_in;  /* bytes recv */
	unsigned int pkt_out; /* pkts sent */
	unsigned int pkt_in;  /* pkts recv */
	struct timespec byt_reset; /* byte counter reset */
	unsigned int ifx; /* interface index, 0 = all (default) */
#ifndef IPV6_MULTICAST_ALL
	lc_grplist_t *grps;
#endif
	int ttl;   /* Time to Live for packets sent from this socket */
	int bound; /* how many channels are bound to this socket */
	int ccoding; /* combined bits of all channel encodings in use on this socket */
	int status; /* router port status */
	int sock;
	int readers; /* reference count (readers) */
	int deleted; /* marked for deletion */
	uint16_t T; /* RaptorQ symbol size (bytes) */
} lc_socket_t;

/* single packet being logged for NACK/replay thread */
typedef struct lc_packet_log_t lc_packet_log_t;
struct lc_packet_log_t {
	lc_packet_log_t * next;
	size_t len;         /* length of original packet on the wire */
	time_t time;        /* time the packet was logged */
	lc_seq_t seq;       /* packet's sequence number */
	char data[];        /* packet data as sent on the wire */
};

/* information used when logging messages for a NACK/replay thread */
typedef struct lc_channel_logging_t {
	pthread_t nack_thread;
	int n_seconds;
	lc_channel_t * chan_nack; /* channel we receive NACKs on */
	lc_socket_t * sock_nack; /* socket we receive NACKs on */
	lc_channel_t * chan_data; /* channel we send replay packets on */
	pthread_mutex_t mutex; /* lock this before accessing anything below */
	lc_packet_log_t * oldest; /* linked list of logged messages */
	lc_packet_log_t * newest; /* other end of list pointed to by oldest */
} lc_channel_logging_t;

/* information used when detecting gaps in sequence numbers and generating NACKs */
typedef struct lc_channel_gap_detect_t {
	lc_seq_t next; /* next sequence number expected, 0 if we don't know */
	lc_channel_t * chan_nack; /* channel we send NACKs on */
	lc_socket_t * sock_nack; /* socket we send NACKs on */
	uint64_t bitmap; /* which of the last 64 packets actually arrived; bit n == next + 64 - n */
} lc_channel_gap_detect_t;

typedef struct lc_oid_s {
	unsigned char hash[HASHSIZE];
	int status;
} lc_oid_t;

typedef struct lc_channel_t {
	lc_channel_t *next;
	lc_ctx_t *ctx;
	struct lc_socket_t *sock;
	struct sockaddr_in6 sa;
	unsigned char hash[HASHSIZE]; /* full channel hash */
	size_t byt_out; /* bytes sent */
	size_t byt_in;  /* bytes recv */
	size_t bps_out; /* send rate limit (bps) */
	size_t bps_in;  /* recv rate limit (bps) */
	struct timespec byt_reset; /* byte counter reset */
	lc_seq_t seq; /* sequence number (Lamport clock) */
	lc_rnd_t rnd; /* random nonce */
	lc_key_t key; /* symmetric key */
	lc_key_t pek; /* public encryption key */
	lc_key_t sek; /* secret encryption key */
	lc_key_t psk; /* public signing key */
	lc_key_t ssk; /* secret signing key */
	lc_filter_t *filter; /* channel filter */
	lc_token_t *token;   /* channel token (sending) */
	int coding;   /* encryption/encoding to use */
#ifdef HAVE_LIBLCRQ
	rq_pid_t pid;
	rq_t *rq;        /* RaptorQ context */
	uint32_t *ESI;   /* RaptorQ Encoding Symbol IDs (ESI): 24-bit unsigned integer */
	uint8_t *rq_blk; /* RaptorQ blocks */
	uint16_t T;      /* RaptorQ symbol size (bytes) */
	uint16_t orig;   /* original symbols received */
	uint16_t syms;   /* symbols received */
	lc_oid_t oid[2]; /* Object ID */
	int oid_cur;     /* idx of most recent oid */
	int rq_oh;       /* RaptorQ automatic overhead pkts. Default: -1 (off) */
#endif
	lc_channel_logging_t *logging;
	lc_channel_gap_detect_t *gap_detect;
	int readers; /* reference count (readers) */
	int deleted; /* marked for deletion */
} lc_channel_t;

typedef struct lc_message_head_t {
	uint64_t timestamp; /* nanosecond timestamp */
	lc_seq_t seq; /* sequence number */
	lc_rnd_t rnd; /* nonce */
	uint8_t op;
	lc_len_t len;
} __attribute__((__packed__)) lc_message_head_t;

struct lc_sync_options_s {
	char *share;
	size_t sharelen;
	size_t trim;
};

#ifdef HAVE_MLD
#include <semaphore.h>
#include <librecast/mdex.h>
struct qentry_s {
	struct in6_addr grp;
	mdex_entry_t entry;
};
struct lc_share_s {
	lc_ctx_t *lctx;
	lc_stat_t *stats;
	mdex_t *mdex;
	mld_t *mld;
	mld_watch_t *watch;
	pthread_t *tsend;
	q_t q;
	int nthread_send;
	unsigned int ifx;
	int flags;
	int readers; /* reference count (readers) */
	int deleted; /* marked for deletion */
};
#endif

extern uint32_t ctx_id;
extern uint32_t sock_id;
extern uint32_t chan_id;

extern lc_ctx_t *ctx_list;
extern lc_socket_t *sock_list;
extern lc_channel_t *chan_list;

#define BUFSIZE 1500
#define DEFAULT_FLAGS 0x1e
#define SIDE_CHANNEL_NACK "NACK"

#endif /* _LIBRECAST_PVT_H */
