/*
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * https://opensource.org/license/bsd-3-clause
 *
 * Copyright (C) 2011, 2012, 2013 Citrix Systems
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef __TURN_SERVER__
#define __TURN_SERVER__

#include "ns_turn_session.h"
#include "ns_turn_utils.h"

#ifdef __cplusplus
extern "C" {
#endif

//////////// defines //////////////

#define TURN_SESSION_ID_FACTOR (1000000000000000LL)

//////////// ALTERNATE-SERVER /////////////

struct _turn_server_addrs_list {
  ioa_addr *addrs;
  volatile size_t size;
  TURN_MUTEX_DECLARE(m)
};

typedef struct _turn_server_addrs_list turn_server_addrs_list_t;

void init_turn_server_addrs_list(turn_server_addrs_list_t *l);

////////// RFC 5780 ///////////////////////

typedef int (*get_alt_addr_cb)(ioa_addr *addr, ioa_addr *alt_addr);
typedef int (*send_message_cb)(ioa_engine_handle e, ioa_network_buffer_handle nbh, ioa_addr *origin,
                               ioa_addr *destination);

//////////////////////////////////////////

extern int TURN_MAX_ALLOCATE_TIMEOUT;
extern int TURN_MAX_ALLOCATE_TIMEOUT_STUN_ONLY;

typedef uint8_t turnserver_id;

enum _MESSAGE_TO_RELAY_TYPE { RMT_UNKNOWN = 0, RMT_SOCKET, RMT_CB_SOCKET, RMT_MOBILE_SOCKET, RMT_CANCEL_SESSION };
typedef enum _MESSAGE_TO_RELAY_TYPE MESSAGE_TO_RELAY_TYPE;

///////// ALLOCATION DEFAULT ADDRESS FAMILY TYPES /////////////////////
enum _ALLOCATION_DEFAULT_ADDRESS_FAMILY {
  ALLOCATION_DEFAULT_ADDRESS_FAMILY_IPV4 = 0,
  ALLOCATION_DEFAULT_ADDRESS_FAMILY_IPV6,
  ALLOCATION_DEFAULT_ADDRESS_FAMILY_KEEP,
};
typedef enum _ALLOCATION_DEFAULT_ADDRESS_FAMILY ALLOCATION_DEFAULT_ADDRESS_FAMILY;

struct socket_message {
  ioa_socket_handle s;
  ioa_net_data nd;
  int can_resume;
};

typedef enum {
  DONT_FRAGMENT_UNSUPPORTED = 0,
  DONT_FRAGMENT_SUPPORTED,
  DONT_FRAGMENT_SUPPORT_EMULATED
} dont_fragment_option_t;

struct _turn_turnserver;
typedef struct _turn_turnserver turn_turnserver;

typedef void (*get_username_resume_cb)(int success, int oauth, int max_session_time, hmackey_t hmackey, password_t pwd,
                                       turn_turnserver *server, uint64_t ctxkey, ioa_net_data *in_buffer,
                                       uint8_t *realm);
typedef uint8_t *(*get_user_key_cb)(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth,
                                    uint8_t *uname, uint8_t *realm, get_username_resume_cb resume,
                                    ioa_net_data *in_buffer, uint64_t ctxkey, int *postpone_reply);
typedef int (*check_new_allocation_quota_cb)(uint8_t *username, int oauth, uint8_t *realm);
typedef void (*release_allocation_quota_cb)(uint8_t *username, int oauth, uint8_t *realm);
typedef int (*send_socket_to_relay_cb)(turnserver_id id, uint64_t cid, stun_tid *tid, ioa_socket_handle s,
                                       int message_integrity, MESSAGE_TO_RELAY_TYPE rmt, ioa_net_data *nd,
                                       int can_resume);
typedef int (*send_turn_session_info_cb)(struct turn_session_info *tsi);
typedef void (*send_https_socket_cb)(ioa_socket_handle s);

typedef band_limit_t (*allocate_bps_cb)(band_limit_t bps, int positive);
typedef void (*unauthenticated_401_metric_cb)(void);

struct _turn_turnserver {

  turnserver_id id;

  turnsession_id session_id_counter;
  ur_map *sessions_map;

  turn_time_t ctime;

  ioa_engine_handle e;
  int verbose;
  int fingerprint;
  int rfc5780;
  bool *check_origin;
  vintp stale_nonce;
  vintp max_allocate_lifetime;
  vintp channel_lifetime;
  vintp permission_lifetime;
  bool *stun_only;
  bool *no_stun;
  bool software_attribute;
  bool *web_admin_listen_on_workers;
  bool *secure_stun;
  turn_credential_type ct;
  get_alt_addr_cb alt_addr_cb;
  send_message_cb sm_cb;
  dont_fragment_option_t dont_fragment;
  int (*disconnect)(ts_ur_super_session *);
  get_user_key_cb userkeycb;
  check_new_allocation_quota_cb chquotacb;
  release_allocation_quota_cb raqcb;
  int external_ip_set;
  ioa_addr external_ip;
  bool *allow_loopback_peers;
  bool *no_multicast_peers;
  send_turn_session_info_cb send_turn_session_info;
  send_https_socket_cb send_https_socket;
  int sock_buf_size;

  /* RFC 6062 ==>> */
  bool *no_udp_relay;
  bool *no_tcp_relay;
  ur_map *tcp_relay_connections;
  send_socket_to_relay_cb send_socket_to_relay;
  /* <<== RFC 6062 */

  /* Alternate servers ==>> */
  turn_server_addrs_list_t *alternate_servers_list;
  size_t as_counter;
  turn_server_addrs_list_t *tls_alternate_servers_list;
  size_t tls_as_counter;
  turn_server_addrs_list_t *aux_servers_list;
  int self_udp_balance;

  /* White/black listing of address ranges */
  ip_range_list_t *ip_whitelist;
  ip_range_list_t *ip_blacklist;

  /* Mobility */
  bool *mobility;
  ur_map *mobile_connections_map;

  /* Server relay */
  int server_relay;

  /* Bandwidth draft: */
  allocate_bps_cb allocate_bps_func;

  /* oAuth: */
  int oauth;
  const char *oauth_server_name;

  /* ACME redirect URL */
  const char *acme_redirect;

  /* Allocation Default Address Family */
  ALLOCATION_DEFAULT_ADDRESS_FAMILY allocation_default_address_family;

  /* Log Binding Requrest */
  bool *log_binding;

  /* Enable handling old STUN Binding Requests and enable MAPPED-ADDRESS attribute in response */
  bool *stun_backward_compatibility;

  /* Return an HTTP 400 response to HTTP connections made to ports not
     otherwise handling HTTP. */
  bool *respond_http_unsupported;

  /* Include descriptive reason strings in STUN/TURN error responses. */
  bool include_reason_string;

  /* Set to true on SIGUSR1 */
  bool is_draining;

  bool multiplex_peer_mode;

  /* Both of these are pointers into turn_params so the CLI flags affect
   * every relay thread without per-thread copies. They must stay pointers
   * (not a `bool`/value copy): the server dereferences them live at the use
   * sites, so a value copy would snapshot the flag at struct-init time and
   * ignore later updates. */
  bool *ratelimit_unauthorized_requests;
  vintp ratelimit_unauthorized_requests_per_sec;
  unauthenticated_401_metric_cb unauthenticated_401_request_cb;
  unauthenticated_401_metric_cb unauthenticated_401_response_cb;
  unauthenticated_401_metric_cb unauthenticated_401_dropped_response_cb;
};

const char *get_version(turn_turnserver *server);

///////////////////////////////////////////

void init_turn_server(
    turn_turnserver *server, turnserver_id id, int verbose, ioa_engine_handle e, turn_credential_type ct,
    int fingerprint, dont_fragment_option_t dont_fragment, get_user_key_cb userkeycb,
    check_new_allocation_quota_cb chquotacb, release_allocation_quota_cb raqcb, ioa_addr *external_addr,
    bool *check_origin, bool *no_tcp_relay, bool *no_udp_relay, vintp stale_nonce, vintp max_allocate_lifetime,
    vintp channel_lifetime, vintp permission_lifetime, bool *stun_only, bool *no_stun, bool software_attribute,
    bool *web_admin_listen_on_workers, turn_server_addrs_list_t *alternate_servers_list,
    turn_server_addrs_list_t *tls_alternate_servers_list, turn_server_addrs_list_t *aux_servers_list,
    int self_udp_balance, bool *no_multicast_peers, bool *allow_loopback_peers, ip_range_list_t *ip_whitelist,
    ip_range_list_t *ip_blacklist, send_socket_to_relay_cb send_socket_to_relay, bool *secure_stun, bool *mobility,
    int server_relay, send_turn_session_info_cb send_turn_session_info, send_https_socket_cb send_https_socket,
    int sock_buf_size, allocate_bps_cb allocate_bps_func, int oauth, const char *oauth_server_name,
    const char *acme_redirect, ALLOCATION_DEFAULT_ADDRESS_FAMILY allocation_default_address_family, bool *log_binding,
    bool *stun_backward_compatibility, bool *respond_http_unsupported, bool include_reason_string,
    bool *ratelimit_unauthorized_requests, vintp ratelimit_unauthorized_requests_per_sec);

ioa_engine_handle turn_server_get_engine(turn_turnserver *s);

////////// RFC 5780 ///////////////////////

void set_rfc5780(turn_turnserver *server, get_alt_addr_cb cb, send_message_cb smcb);

///////////////////////////////////////////

int open_client_connection_session(turn_turnserver *server, struct socket_message *sm);
int shutdown_client_connection(turn_turnserver *server, ts_ur_super_session *ss, int force, const char *reason);
void set_disconnect_cb(turn_turnserver *server, int (*disconnect)(ts_ur_super_session *));
void set_unauthenticated_401_metric_cbs(turn_turnserver *server, unauthenticated_401_metric_cb request_cb,
                                        unauthenticated_401_metric_cb response_cb,
                                        unauthenticated_401_metric_cb dropped_response_cb);

int turnserver_accept_tcp_client_data_connection(turn_turnserver *server, tcp_connection_id tcid, stun_tid *tid,
                                                 ioa_socket_handle s, int message_integrity, ioa_net_data *nd,
                                                 int can_resume);

int report_turn_session_info(turn_turnserver *server, ts_ur_super_session *ss, int force_invalid);

turn_time_t get_turn_server_time(turn_turnserver *server);

void turn_cancel_session(turn_turnserver *server, turnsession_id sid);

/* Non-static relay input handler — called by multiplex-peer dispatch */
void turn_peer_input_handler(ioa_socket_handle s, int event_type, ioa_net_data *data, void *arg, int can_resume);

///////////////////////////////////////////

#ifdef __cplusplus
}
#endif

#endif //__TURN_SERVER__
