/* $Id$ */
/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#ifndef __PJ_TURN_SRV_TURN_H__
#define __PJ_TURN_SRV_TURN_H__

#include <pjlib.h>
#include <pjnath.h>

typedef struct pj_turn_relay_res    pj_turn_relay_res;
typedef struct pj_turn_listener	    pj_turn_listener;
typedef struct pj_turn_transport    pj_turn_transport;
typedef struct pj_turn_permission   pj_turn_permission;
typedef struct pj_turn_allocation   pj_turn_allocation;
typedef struct pj_turn_srv	    pj_turn_srv;
typedef struct pj_turn_pkt	    pj_turn_pkt;


#define PJ_TURN_INVALID_LIS_ID	    ((unsigned)-1)

/** 
 * Get transport type name string.
 */
PJ_DECL(const char*) pj_turn_tp_type_name(int tp_type);

/**
 * This structure describes TURN relay resource. An allocation allocates
 * one relay resource, and optionally it may reserve another resource.
 */
struct pj_turn_relay_res
{
    /** Hash table key */
    struct {
	/** Transport type. */
	int		    tp_type;

	/** Transport/relay address */
	pj_sockaddr	    addr;
    } hkey;

    /** Allocation who requested or reserved this resource. */
    pj_turn_allocation *allocation;

    /** Username used in credential */
    pj_str_t	    user;

    /** Realm used in credential. */
    pj_str_t	    realm;

    /** Lifetime, in seconds. */
    unsigned	    lifetime;

    /** Relay/allocation expiration time */
    pj_time_val	    expiry;

    /** Timeout timer entry */
    pj_timer_entry  timer;

    /** Transport. */
    struct {
	/** Transport/relay socket */
	pj_sock_t	    sock;

	/** Transport/relay ioqueue */
	pj_ioqueue_key_t    *key;

	/** Read operation key. */
	pj_ioqueue_op_key_t read_key;

	/** The incoming packet buffer */
	char		    rx_pkt[PJ_TURN_MAX_PKT_LEN];

	/** Source address of the packet. */
	pj_sockaddr	    src_addr;

	/** Source address length */
	int		    src_addr_len;

	/** The outgoing packet buffer. This must be 3wbit aligned. */
	char		    tx_pkt[PJ_TURN_MAX_PKT_LEN+4];
    } tp;
};


/****************************************************************************/
/*
 * TURN Allocation API
 */

/**
 * This structure describes key to lookup TURN allocations in the
 * allocation hash table.
 */
typedef struct pj_turn_allocation_key
{
    int		    tp_type;	/**< Transport type.	    */
    pj_sockaddr	    clt_addr;	/**< Client's address.	    */
} pj_turn_allocation_key;


/**
 * This structure describes TURN pj_turn_allocation session.
 */
struct pj_turn_allocation
{
    /** Hash table key to identify client. */
    pj_turn_allocation_key hkey;

    /** Pool for this allocation. */
    pj_pool_t		*pool;

    /** Object name for logging identification */
    char		*obj_name;

    /** Client info (IP address and port) */
    char		info[80];

    /** Mutex */
    pj_lock_t		*lock;

    /** Server instance. */
    pj_turn_srv		*server;

    /** Transport to send/receive packets to/from client. */
    pj_turn_transport	*transport;

    /** The relay resource for this allocation. */
    pj_turn_relay_res	relay;

    /** Relay resource reserved by this allocation, if any */
    pj_turn_relay_res	*resv;

    /** Requested bandwidth */
    unsigned		bandwidth;

    /** STUN session for this client */
    pj_stun_session	*sess;

    /** Credential for this STUN session. */
    pj_stun_auth_cred	 cred;

    /** Peer hash table (keyed by peer address) */
    pj_hash_table_t	*peer_table;

    /** Channel hash table (keyed by channel number) */
    pj_hash_table_t	*ch_table;
};


/**
 * This structure describes the hash table key to lookup TURN
 * permission.
 */
typedef struct pj_turn_permission_key
{
    /** Peer address. */
    pj_sockaddr		peer_addr;

} pj_turn_permission_key;


/**
 * This structure describes TURN pj_turn_permission or channel.
 */
struct pj_turn_permission
{
    /** Hash table key */
    pj_turn_permission_key hkey;

    /** TURN allocation that owns this permission/channel */
    pj_turn_allocation	*allocation;

    /** Optional channel number, or PJ_TURN_INVALID_CHANNEL if channel number
     *  is not requested for this permission. 
     */
    pj_uint16_t		channel;

    /** Permission expiration time. */
    pj_time_val		expiry;
};

/**
 * Create new allocation.
 */
PJ_DECL(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport,
					       const pj_sockaddr_t *src_addr,
					       unsigned src_addr_len,
					       const pj_stun_rx_data *rdata,
					       pj_stun_session *srv_sess,
					       pj_turn_allocation **p_alloc);
/**
 * Destroy allocation.
 */
PJ_DECL(void) pj_turn_allocation_destroy(pj_turn_allocation *alloc);


/**
 * Handle incoming packet from client.
 */
PJ_DECL(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc,
						  pj_turn_pkt *pkt);

/**
 * Handle transport closure.
 */
PJ_DECL(void) pj_turn_allocation_on_transport_closed(pj_turn_allocation *alloc,
						     pj_turn_transport *tp);

/****************************************************************************/
/*
 * TURN Listener API
 */

/**
 * This structure describes TURN listener socket. A TURN listener socket
 * listens for incoming connections from clients.
 */
struct pj_turn_listener
{
    /** Object name/identification */
    char		*obj_name;

    /** Slightly longer info about this listener */
    char		info[80];

    /** TURN server instance. */
    pj_turn_srv		*server;

    /** Listener index in the server */
    unsigned		id;

    /** Pool for this listener. */
    pj_pool_t	       *pool;

    /** Transport type. */
    int			tp_type;

    /** Bound address of this listener. */
    pj_sockaddr		addr;

    /** Socket. */
    pj_sock_t		sock;

    /** Flags. */
    unsigned		flags;

    /** Destroy handler */
    pj_status_t		(*destroy)(pj_turn_listener*);
};


/**
 * This structure describes TURN transport socket which is used to send and
 * receive packets towards client.
 */
struct pj_turn_transport
{
    /** Object name/identification */
    char		*obj_name;

    /** Slightly longer info about this listener */
    char		*info;

    /** Listener instance */
    pj_turn_listener	*listener;

    /** Sendto handler */
    pj_status_t		(*sendto)(pj_turn_transport *tp,
				  const void *packet,
				  pj_size_t size,
				  unsigned flag,
				  const pj_sockaddr_t *addr,
				  int addr_len);

    /** Addref handler */
    void		(*add_ref)(pj_turn_transport *tp,
				   pj_turn_allocation *alloc);

    /** Decref handler */
    void		(*dec_ref)(pj_turn_transport *tp,
				   pj_turn_allocation *alloc);

};


/**
 * An incoming packet.
 */
struct pj_turn_pkt
{
    /** Pool for this packet */
    pj_pool_t		    *pool;

    /** Transport where the packet was received. */
    pj_turn_transport	    *transport;

    /** Packet buffer (must be 32bit aligned). */
    pj_uint8_t		    pkt[PJ_TURN_MAX_PKT_LEN];

    /** Size of the packet */
    pj_size_t		    len;

    /** Arrival time. */
    pj_time_val		    rx_time;

    /** Source transport type and source address. */
    pj_turn_allocation_key   src;

    /** Source address length. */
    int			    src_addr_len;
};


/**
 * Create a UDP listener on the specified port.
 */
PJ_DECL(pj_status_t) pj_turn_listener_create_udp(pj_turn_srv *srv,
						 int af,
					         const pj_str_t *bound_addr,
					         unsigned port,
						 unsigned concurrency_cnt,
						 unsigned flags,
						 pj_turn_listener **p_lis);

/**
 * Create a TCP listener on the specified port.
 */
PJ_DECL(pj_status_t) pj_turn_listener_create_tcp(pj_turn_srv *srv,
						 int af,
					         const pj_str_t *bound_addr,
					         unsigned port,
						 unsigned concurrency_cnt,
						 unsigned flags,
						 pj_turn_listener **p_lis);

/**
 * Destroy listener.
 */
PJ_DECL(pj_status_t) pj_turn_listener_destroy(pj_turn_listener *listener);


/**
 * Add a reference to a transport.
 */
PJ_DECL(void) pj_turn_transport_add_ref(pj_turn_transport *transport,
					pj_turn_allocation *alloc);


/**
 * Decrement transport reference counter.
 */
PJ_DECL(void) pj_turn_transport_dec_ref(pj_turn_transport *transport,
					pj_turn_allocation *alloc);



/****************************************************************************/
/*
 * TURN Server API
 */
/**
 * This structure describes TURN pj_turn_srv instance.
 */
struct pj_turn_srv
{
    /** Object name */
    char	*obj_name;

    /** Core settings */
    struct {
	/** Pool factory */
	pj_pool_factory *pf;

	/** Pool for this server instance. */
	pj_pool_t       *pool;

	/** Global Ioqueue */
	pj_ioqueue_t    *ioqueue;

	/** Mutex */
	pj_lock_t	*lock;

	/** Global timer heap instance. */
	pj_timer_heap_t *timer_heap;

	/** Number of listeners */
	unsigned         lis_cnt;

	/** Array of listeners. */
	pj_turn_listener **listener;

	/** STUN session to handle initial Allocate request. */
	pj_stun_session	*stun_sess;

	/** Number of worker threads. */
	unsigned        thread_cnt;

	/** Array of worker threads. */
	pj_thread_t     **thread;

	/** Thread quit signal */
	pj_bool_t	quit;

	/** STUN config. */
	pj_stun_config	 stun_cfg;

	/** STUN auth credential. */
	pj_stun_auth_cred cred;

	/** Thread local ID for storing credential */
	long		 tls_key, tls_data;

    } core;

    
    /** Hash tables */
    struct {
	/** Allocations hash table, indexed by transport type and
	 *  client address. 
	 */
	pj_hash_table_t *alloc;

	/** Relay resource hash table, indexed by transport type and
	 *  relay address. 
	 */
	pj_hash_table_t *res;

    } tables;

    /** Ports settings */
    struct {
	/** Minimum UDP port number. */
	pj_uint16_t	    min_udp;

	/** Maximum UDP port number. */
	pj_uint16_t	    max_udp;

	/** Next UDP port number. */
	pj_uint16_t	    next_udp;


	/** Minimum TCP port number. */
	pj_uint16_t	    min_tcp;

	/** Maximum TCP port number. */
	pj_uint16_t	    max_tcp;

	/** Next TCP port number. */
	pj_uint16_t	    next_tcp;

    } ports;
};


/** 
 * Create server.
 */
PJ_DECL(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf,
				        pj_turn_srv **p_srv);

/** 
 * Destroy server.
 */
PJ_DECL(pj_status_t) pj_turn_srv_destroy(pj_turn_srv *srv);

/** 
 * Add listener.
 */
PJ_DECL(pj_status_t) pj_turn_srv_add_listener(pj_turn_srv *srv,
					      pj_turn_listener *lis);

/**
 * Register an allocation.
 */
PJ_DECL(pj_status_t) pj_turn_srv_register_allocation(pj_turn_srv *srv,
						     pj_turn_allocation *alloc);

/**
 * Unregister an allocation.
 */
PJ_DECL(pj_status_t) pj_turn_srv_unregister_allocation(pj_turn_srv *srv,
						       pj_turn_allocation *alloc);

/**
 * This callback is called by UDP listener on incoming packet.
 */
PJ_DECL(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv, 
				    pj_turn_pkt *pkt);


#endif	/* __PJ_TURN_SRV_TURN_H__ */

