Hi Folks:

I worked out the kinks in my multi-task threaded Libuv oriented application and an overview of its salient features follows. It is now highly reliable and I think this overview closes the relevant Libuv issues and provides insight about using Libuv
in a threaded environment.

My application includes two Libuv oriented tasks. Their primary functions are performed with standard Libuv mechanisms. Each task has its own uv_loop_t poll loop structure which is accessed only by that task and is used to perform related Libuv operations. The tasks can also perform Libuv operations on behalf of other tasks upon receipt of Libuv async. messages from other tasks. An overview of the Libuv task functionality is as follows.

Libuv Task/Threads
------------------
* main(): Handles incoming TCP network connection establishment and Linux/Unix signals.
  (SSL connectivity is also handled by extension.)
* IO_Task(): Handles incoming network data.

The relevant files, which are attached for your convenience, are as follows.

Relevant Files
----------------
* main.c. The main() task. The make_incoming_connection() routine handles
incoming network connections and the connect_proxy() routine handles async.
  messages from other tasks.
* network_io.c: The IO_Task() task. The poll_callback() routine handles
incoming network data and the poll_proxy() routine handles async. messages
  from other tasks.
* async.c: The send_async_msg() routine is used to send messages to the Libuv oriented tasks. * ssl.c: Performs SSL related operations. This file is included to resolve ambiguity in
  the details of the make_incoming_connection() routine.

An overview of the other message oriented application tasks is as follows.

Message Oriented Task/Threads
---------------------------------
* Protocol_Task(): Implements the data transfer state machine used for data transfer
  between the Server and a Client on a per TCP connection basis.
* DB_Task(): Performs database insertion operations.
* Scram_Task(): Performs slow SCRAM authentication proof computation operations. * Transmit_Task(): Transmits data from the Server to the Client on a per TCP connection basis.
* Timer_Task(): Manages protocol and application timers.

Best Regards,

Paul R.


On 05/14/2021 09:37 AM, pa...@rcom-software.com wrote:
Hi:

I think it is safe to perform connection acceptance and epoll() oriented TCP socket transactions in separate thread/tasks if you adhere to the following rules.

My server follows these rules and works seamlessly with the following exception. If it uses Linux close() TCP socket descriptors, after uv_poll_stop() and uv_close() have succeeded for a particular uv_poll_t connection handle, internal Libuv structures are corrupted intermittently after a long period of successfully making and releasing many TCP connections.

This implies that Libuv automatically closes TCP socket descriptors because my Server
never runs out of them no matter how long it runs.

RULES
-----
* There is separate uv_loop_t Loop structure for accepting incoming connections and epoll() I/O events.

  In my case they are declared as follows:

    uv_loop_t Connect_Loop;        // Libuv loop for incoming connections.
static uv_loop_t Poll_Loop; // Libuv loop for reading incoming network data.

* The Loop structures are owned by separate tasks and only accessed by the task that owns the Loop structure.

In my case the main() task/thread owns Connect_Loop and the IO_Task() thread/task owns Poll_Loop.

* Only the task/thread which owns the incoming connection acceptance Loop calls connection management related Libuv API routines, such uv_accept() and uv_close(), and access uv_tcp_t connection
  handles allocated during connection acceptance.

In my case the main() task/thread follows this rule and owns Connect_Loop.

* Only the task/thread which owns the epoll() I/O event Loop calls polling related Libuv API routines, such as uv_poll_start() and uv_poll_stop(), and access uv_poll_t connection handles allocated
  during polling initiation.

In my case the the IO_Task() thread/task follows this rule and owns the Poll_Loop.


Best Regards,

Paul R.

On Tuesday, May 11, 2021 at 6:45:59 AM UTC-7 pa...@rcom-software.com wrote:

    Hi:

    Perhaps it would be useful to know that all Libuv epoll()
    operations occur in the IO_Task() which
    is dedicated to reception of networrk data. This provides nice
    modularity and minimizes the
    possibiliy of deadlocks due to interactions with other kinds of
    functionality.

    Best Regards,

    Paul R.

    On Sunday, May 9, 2021 at 12:14:28 PM UTC-7
    pa...@rcom-software.com wrote:

        Hi:

        I get the FD immediately after accepting an incomig connection
        with uv_fileno().
        Then subsequently it is used as follows:

        * IO_Task(): When the poll callback executes correctly, it is
        used with Linux recv() to
          read incoming network data in non-blocking mode. (All
        incoming data and polling
          is done in this thread/task.)

        * Transmit_Task(): It is used with Linux send() to send data
        across the network in non-blocking
           mode. This thread/ task is driven by message reception
        outside Libuv mechanisms.

        That's it other than the final close() we discussed. I never
        observed any problems unrelated
        to Libuv connection acceptance and release, and starting and
        stoping Libuv polling.

        Best Regards,

        Paul R.


        On 05/09/2021 11:58 AM, Jameson Nash wrote:
        Are you extracting a fd from a libuv object then calling
        close or uv_poll on it? Either would cause problems, and
        shouldn't be done.

        On Sun, May 9, 2021 at 2:53 PM Jameson Nash
        <vtj...@gmail.com> wrote:

            uv_poll_stop and uv_accept must happen on the same
            thread, so how are they racing? Once uv_poll_stop is
            called, it should be safe to notify another thread to
            call close, even if that then happens concurrently, as
            the kernel should be thread-safe (though weird things may
            happen in userspace if you close a fd that is
            concurrently in use on another thread or event queue).

            On Sun, May 9, 2021 at 1:24 PM Paul Romero
            <pa...@rcom-software.com> wrote:

                Hi:

                Using TSAN did turn up one very signifcant problem.
                The root cause of the TCP socket descriptor corrpution
                is that accept4() executes concurrently in the main()
                task, with close() in the Protocol_Task().

                As an interim measure I avoid the problem by simply
                not calling close(). So far this has worked
                seemlesly and Libuv appears to automatically free
                socket descriptors. The Libuv documentation
                about this is somewhat ambiguous. It indicates that
                after calling uv_poll_stop() or uv_close(),
                for a particular uv_poll_t poll handle, the socket
                descriptor is returned to the user per the Libuv
                contract. I am not sure what that means, and in
                particular, if it means the socket descriptor is freed.
                Can you clarify this ?

                A tagential issue is whether Linux accept4() and
                close() are thread safe. I believe they
                are and the crucial data is protected in the kernel.
                Is it possible Libuv is not handling the accept4()
                return codes correctly ? The Linux accept4() man page
                details how errors should be handled and is it
                somewhat fussy. The Linux close() man page also
                details error handling but it is straight forward.

                Also, I haven't been able to make the program
                compiled with TSAN dump core. Do you have
                any suggestions ? Incidentally, I had to use clang
                rather than the usual gcc to get
                TSAN to work on my system.

                Best Regards,

                Paul R.

                On 05/08/2021 08:51 AM, Jameson Nash wrote:
                Are you still accessing libuv (sans explicitly
                thread-safe functions such as uv_async_send) from
                multiple threads, as you mentioned earlier? If so,
                I'd suggest fixing that first. In conjunction, I
                recommend running TSan and making sure it runs
                cleanly before checking for library or logic
                problems. Then, if it is still a rare failure, I
                recommend debugging under `rr` as you'll be able to
                run forward to the problem, then walk backwards
                through the code to see what happened to your state
                and file descriptors.


                On Sat, May 8, 2021 at 11:27 AM
                pa...@rcom-software.com <pa...@rcom-software.com> wrote:

                    Hi:

                    Addition to my last message. When uv__nonblock()
                    fails it is indicative of a Linux FIONBIO
                    ioctl() failure. What would cause
                    setting non-blocking mode to fail ?

                    Best Regards,

                    Paul R.

-- You received this message because you are subscribed
                to the Google Groups "libuv" group.
                To unsubscribe from this group and stop receiving
                emails from it, send an email to
                libuv+un...@googlegroups.com.
                To view this discussion on the web visit
                
https://groups.google.com/d/msgid/libuv/CADnnjUW6KL3OQw5C54aNfKh95z1OpQiq2bgVtXya8z_BeqMS9w%40mail.gmail.com.

--

                Paul Romero
                -----------
                RCOM Communications Software
                EMAIL:pa...@rcom-software.com
                PHONE:(510)482-2769 <tel:%28510%29%20482-2769>




-- You received this message because you are subscribed
                to the Google Groups "libuv" group.
                To unsubscribe from this group and stop receiving
                emails from it, send an email to
                libuv+un...@googlegroups.com.
                To view this discussion on the web visit
                
https://groups.google.com/d/msgid/libuv/60981AE7.3000809%40rcom-software.com.

-- You received this message because you are subscribed to the
        Google Groups "libuv" group.
        To unsubscribe from this group and stop receiving emails from
        it, send an email to libuv+un...@googlegroups.com.
        To view this discussion on the web visit
        
https://groups.google.com/d/msgid/libuv/CADnnjUWfszx9K8FdGPwGD%3D_5C0s1QVrDkaJD2ML2%3DEygefFD_A%40mail.gmail.com.

--

        Paul Romero
        -----------
        RCOM Communications Software
        EMAIL:pa...@rcom-software.com
        PHONE:(510)482-2769 <tel:%28510%29%20482-2769>




--
You received this message because you are subscribed to a topic in the Google Groups "libuv" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/libuv/_4ClQoaVPCg/unsubscribe. To unsubscribe from this group and all its topics, send an email to libuv+unsubscr...@googlegroups.com <mailto:libuv+unsubscr...@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/libuv/843aca48-bc3a-4c4d-8260-241e6c56e0d5n%40googlegroups.com <https://groups.google.com/d/msgid/libuv/843aca48-bc3a-4c4d-8260-241e6c56e0d5n%40googlegroups.com?utm_medium=email&utm_source=footer>.

--


Paul Romero
-----------
RCOM Communications Software
EMAIL: pa...@rcom-software.com
PHONE: (510)482-2769




--
You received this message because you are subscribed to the Google Groups 
"libuv" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to libuv+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/libuv/60B4F7E7.1050901%40rcom-software.com.
#include "os_def.h"
#include "basic_def.h"
#include <uv.h>
#include "scram.h"
#include "framework.h"

#include <sys/types.h>
#include <sys/socket.h>

//
// Use this definition once debugging is complete.
//
// #define PRIVATE static
//
#define PRIVATE static

//
// ***********************************************************************************************
// *************************************** DATA **************************************************
// ***********************************************************************************************
//

//
// ***********************************************************************************************
// ************************************* PROTOTYPES  *********************************************
// ***********************************************************************************************
//

void send_async_msg(CONN_DESC *, const ASYNC_DATA *);
void async_close_callback(uv_handle_t *);

void SendProtoMsg(CONN_DESC *, MSG *);

void insert_msg(MSG_FIFO *, MSG *);

MSG *MSG_ALLOC(int, BOOL);
void VFREE(UCHAR *);

//
// Send a message to the target loop task. 
//
//
// FUNCTION - send_async_task(): Sends an async. message to the specified task.
//
// ARGUMENTS
// -----------
// cdesc: The connection descriptor corresponding to the connection from which message is sent.
// parm: Specifies the task to which the message will be sent, and the relevant task attributes.
//
// RETURN VALUE
// --------------
// None
//
// NOTES
// -------
// Care must be taken to specify the following attributes correctly.
//
// * type: The message type.
// * async_handle: The Libuv async. handle used to convey the message.
// * async_q: The message input queue of the task.
// * async_mutex: The Mutex that protects the queue.
// * object_handle: If this field is not NULL, it contains the Libuv handle necessary
//   for performing the operation conveyed by the message type.
//
// The message is handled by a proxy routine which already must be specified via
// a earlier call to uv_async_init().
//
ROUTINE void send_async_msg(CONN_DESC *cdesc, const ASYNC_DATA *parm)
{
	MSG *msg;
	uv_handle_t **ppdata;

	msg = MSG_ALLOC( sizeof(uv_handle_t *), FALSE );

	msg->class = C_ASYNC;
	msg->type = parm->type;
	msg->conn_desc = (void *) cdesc;
	//
	// Store the Libuv handle if it is specified.
	//
	if(parm->object_handle)
	   {
		ppdata = (uv_handle_t **) &msg->buffer[0];
		*ppdata = (uv_handle_t *) parm->object_handle;
	  }

ENTER_MUTEX(parm->async_mutex);
	insert_msg(parm->async_q, msg);
EXIT_MUTEX(parm->async_mutex);

	uv_async_send(parm->async_handle);

	return;
}

//
// FUNCTION - async_close_callback(): Frees a Libuv handle and notifies the task
// that initiated an async. operation that it is complete.
//
// ARGUMENTS
// -----------
// handle: The Libuv handle.
//
// RETURN VALUE
// --------------
// None
//
// NOTES
// -------
// This is a Libuv specific callback routine whose execution is specified by a call to uv_close().
//
ROUTINE void async_close_callback(uv_handle_t *handle)
{
	MSG *msg;

	CONN_DESC *cdesc = (CONN_DESC *) handle->data;
	int type = handle->type;

	VFREE((UCHAR *) handle);
	//
	// Send a notification message to the Protocol_Task.
	//
	msg = MSG_ALLOC(0, FALSE);
	msg->class = C_NOTIFY;
	msg->type = T_DISMANTLE_RSP;
	msg->info = 0;

	SendProtoMsg(cdesc, msg);
	//
	// Signal that the poll handle is released.
	//
	if(type == UV_POLL)
		syslog(LOG_INFO, "IO_Task: CONN[%d] Poll Release Complete", cdesc->index);

	return;
}

#include "os_def.h"
#include "basic_def.h"
#include <uv.h>
#include "scram.h"
#include "framework.h"

//
// Use this definition once debugging is complete.
//
// #define PRIVATE static
//
#define PRIVATE static


//
// *************************************************************************************
// ************************************ DATA *******************************************
// *************************************************************************************
//

//
// **************** Data Shared by multple Tasks ******************************
//

//
// Protects network connectivity data shared by the main() task and Protocol_Task()
//
uv_mutex_t			Network_Conn_Mutex;
int N_Sockets;			// The current number of connections accepted.
//
// Protects protocol data shared by or accessible to  multiple tasks.
//
uv_mutex_t			Shared_Data_Mutex;
int N_Tasks;			// The number of initialized thread tasks.

//
// Connection Descriptor Table.
//------------------------------------
// Using a statically allocated table like this is only appropriate for
// an implementation with a small maximum number of connections.
//
// TBD: In the case  of large maximum number of connections this table
// should be replaced by one that holds pointers to  dynamically allocated
// connection descriptors.
//
CONN_DESC Conn_Desc_Table[MAX_CONN_DESC];

//
// **************************** main() Process/Task Data *********************
//

//
// Loop for detecting incoming connections and handling Linux signals.
// (Owned by the main() task.)
//
PRIVATE uv_loop_t	Connect_Loop;
//
// TRUE when the connection accept callback is executing.
//
PRIVATE BOOL		Connect_Accept_Busy = FALSE;
//
// TRUE when the connection plumbing proxy is executing.
//
PRIVATE BOOL		Connect_Proxy_Busy = FALSE;

//
// The main() task handles incoming connections and Linux signals.
//
TASK_DATA	Main_Tdata;


//
// **************************** IO_Task() Data *****************************
//

//
// The IO_Task() handles epoll() read I/O events with a poll loop of type uv_poll_t.
// It is dedicated to network data reception.
//
void IO_Task(void *arg);
TASK_DATA		IO_Tdata;

//
// **************************** DB_Task() Data *************************
//

//
// The DB_Task performs slow database insertions.
//
void DB_Task(void *);
TASK_DATA		DB_Tdata;

#ifdef USE_SCRAM

//
// ***************************** Scram_Task() Data *****************************
//

#ifndef DYNAMIC_SCRAM_THREAD

//
// The SCRAM task handles authentication with the remote Mobile.
//
void Scram_Task(void *);
TASK_DATA		Scram_Tdata;

#endif // DYNAMIC_SCRAM_THREAD

#endif // USE_SCRAM

//
// ***************************** Timer_Task() Data **********************
//

//
// The Time_Task() implements protocol timers and  handlles ticks every 1/10 of second in the Timer_Loop.
//
void Timer_Task(void *);
TASK_DATA		Timer_Tdata;

//
//  *************************  Trasnsit_Task() Data ******************************
//

//
// The tranmission task handle.
//
void Transmit_Task(void *);
TASK_DATA		Transmit_Tdata;


//
// ************************* Protocol_Task() Data *****************************
//

//
// The Service Queue is doubly linked  Circular queue containing
// only Connection Descriptors with SDUs on their Task Input Queue.
// It is mutex protected by the Task mutex.
//
CONN_DESC *Service_Q;	// The head of the queue.
//
// The Protocol_Task() handles protocol messages from a remote Mobile.
//
void Protocol_Task(void *arg);
TASK_DATA		Service_Tdata;


//
// ******************************************************************************************
// ********************************** PROTOTYPES *********************************************
// ******************************************************************************************
//

void send_async_msg(CONN_DESC *, const ASYNC_DATA *);
void async_close_callback(uv_handle_t *);

PRIVATE void make_incoming_connection(uv_stream_t *, int );	// This is a callback routine.
PRIVATE void connect_proxy(uv_async_t * );		// This callback routine executes in the main() process.
PRIVATE void forced_close_callback(uv_handle_t *);

char *host_to_ip(const char *, char [], int );

#ifdef USE_SCRAM

#ifdef DYNAMIC_SCRAM_THREAD
void scram_init_task_pool();
#endif // DYNAMIC_SCRAM_THREAD

#endif // USE_SCRAM

#ifdef USE_SSL

SSL *Ssl_Accept(CONN_DESC *, int );
void Ssl_ReleaseConnect(CONN_DESC *, uv_mutex_t * );
void Ssl_Init();

#ifdef USE_CRYPTO_LOCK
ROUTINE void Ssl_InitLock(int );
#endif // USE_CRYPTO_LOCK

#endif // USE_SSL

void sig_hup_callback(uv_signal_t *, int );
void sig_term_callback(uv_signal_t *, int );
void sig_kill_callback(uv_signal_t *, int );

MSG *remove_msg(MSG_FIFO *);

CONN_DESC *ALLOC_CONN_DESC(int );
BOOL DELETE_CONN(CONN_DESC *);

void MSG_FREE(MSG *);

void Msg_Task_Init(TASK_DATA *);
void Async_Task_Init(TASK_DATA *, uv_loop_t *,  uv_async_cb );
void IO_Task_Init(TASK_DATA *);
void Timer_Task_Init(TASK_DATA *);

UCHAR *VALLOC(int n);
void VFREE(UCHAR *);
void VMEM_INIT();

#ifdef NO_DUP_USER
void duser_init();
#endif // NO_DUP_USER

#ifdef LIBUV_DETACH
int uv_thread_detached_create(uv_thread_t *, void (* )(void * ), void * );
#else // LIBUV_DETACH
int uv_thread_detach(uv_thread_t *);
#endif // LIBUV_DETACH

BOOL ip_address(char *, const char *, int );
void fatal_error(int , const char *, ...);

int main()
{
	static char Server[NI_MAXHOST];

	uv_tcp_t listen_handle;
	uv_signal_t sig_term_handle, sig_hup_handle, sig_int_handle;
	struct sockaddr_in addr;
	int r, k;

	//
	// Open the debug log.
	//
	openlog("pexd", LOG_PID, LOG_LOCAL1);
	syslog(LOG_ALERT, "PEXD DAEMON: VERSION %s STARTING", VERSION);
	syslog(LOG_INFO, "DAEMON: Initializing System");
	//
	// Initalize memory managment.
	//
	VMEM_INIT();
#ifdef NO_DUP_USER
	//
	// Prevent multiple concurrent connections by the same database user.
	//
	duser_init();
#endif // NO_DUP_USER
	//
	// Initilize global data.
	//
	N_Tasks = 0;
	N_Sockets = 0;
	bzero((void *) Conn_Desc_Table, sizeof(Conn_Desc_Table));
	for(k = 0; k < MAX_CONN_DESC; k++)
	   {
		Conn_Desc_Table[k].index = k;
		Conn_Desc_Table[k].fd = -1;
		Conn_Desc_Table[k].busy = FALSE;
		Conn_Desc_Table[k].decoder.buf_ptr = Conn_Desc_Table[k].decoder.buffer;
	   }

	//
	// Intialize the network data mutex.
	//
	uv_mutex_init(&Network_Conn_Mutex);
	//
	// Initialize the protocol data Mutex.
	//
	uv_mutex_init(&Shared_Data_Mutex);
	//
	// Intialize main() task data.
	//
	Connect_Accept_Busy = FALSE;
	Connect_Proxy_Busy = FALSE;
	Async_Task_Init(&Main_Tdata, &Connect_Loop, connect_proxy);
	//
	// Initialize IO_Task() data.
	//
	IO_Task_Init(&IO_Tdata);
	//
	// Initialize Protocol_Task() data.
	//
	// Initialize the Service_Q, used in the Protocol_Task() and
	// its Mutex and condition variable.
	//
	Service_Q = NULL;
	Msg_Task_Init(&Service_Tdata);
	//
	// Initialize DB_Task() data.
	//
	Msg_Task_Init(&DB_Tdata);
	//
	// Initialize Scram_Task() data.
	//
	Msg_Task_Init(&Scram_Tdata);
	//
	// Initialize Timer_Task() data.
	//
	Timer_Task_Init(&Timer_Tdata);
	//
	// Initialize Transmit_Task() data.
	//
	Msg_Task_Init(&Transmit_Tdata);

#ifdef USE_SSL
	//
	// Initialize SSL connection infrastructure.
	//
	Ssl_Init();

#ifdef USE_CRYPTO_LOCK
	//
	// Protect internal crypto. operations.
	//
	Ssl_InitLock(MAIN_TASK);
#endif // USE_CRYPTO_LOCK

#endif // USE_SSL
	//
	// Launch the Timer_Task(), IO_Task(), DB_Task(), Protocol_Task(), Transmit_Task() and Scram_Task().
	//
	// TBD: When a newer version of Libuv is available use uv_thread_detach() instead
	// of uv_thread_detached_create(). Note that uv_thread_detached_create() is a custom 
	// routine I added to the src/unix/thread.c code and not part of the official Libuv
	// package.
	//
	syslog(LOG_INFO, "DAEMON: Spawning Tasks");

#ifdef DYNAMIC_SCRAM_THREAD
	scram_init_task_pool();
#endif // DYNAMIC_SCRAM_THREAD

#ifdef LIBUV_DETACH
	uv_thread_detached_create(&IO_Tdata.handle, IO_Task, NULL);
	uv_thread_detached_create(&Service_Tdata.handle, Protocol_Task, NULL);
	uv_thread_detached_create(&DB_Tdata.handle, DB_Task, NULL);

#ifdef USE_SCRAM

#ifndef DYNAMIC_SCRAM_THREAD
	uv_thread_detached_create(&Scram_Tdata.handle, Scram_Task, &Scram_Tdata);
#endif // DYNAMIC_SCRAM_THREAD

#endif // USE_SCRAM

	uv_thread_detached_create(&Transmit_Tdata.handle, Transmit_Task, NULL);
	uv_thread_detached_create(&Timer_Tdata.handle, Timer_Task, NULL);

#else // LIBUV_DETACH

	tid = uv_thread_create(&IO_Tdata.handle, IO_Task, NULL);
	uv_thread_detach(IO_Tdata.handle);
	tid = uv_thread_create(&Service_Tdata.handle, Protocol_Task, NULL);
	uv_thread_detach(Service_Tdata.handle);
	tid = uv_thread_create(&DB_Tdata.handle, DB_Task, NULL);
	uv_thread_detach(DB_Tdata.handle);

#ifdef USE_SCRAM
	tid = uv_thread_create(&Scram_Tdata.handle, Scram_Task, Scram_Tdata);
	uv_thread_detach(&Scram_Tdata.handle);
#endif // USE_SCRAM

	tid = uv_thread_create(&Transmit_Tdata.handle, Transmit_Task, NULL);
	uv_thread_detach(Transmit_Tdata.handle);
	tid = uv_thread_create(&Timer_Tdata.handle, Timer_Task, NULL);
	uv_thread_detach(Timer_Tdata.handle);

#endif // LIBUV_DETACH


	//
	// Initialize Linux signal handling in this task.
	//
	uv_signal_init(&Connect_Loop, &sig_term_handle);
    	uv_signal_start(&sig_term_handle, sig_term_callback, SIGTERM);

	uv_signal_init(&Connect_Loop, &sig_hup_handle);
    	uv_signal_start(&sig_hup_handle, sig_hup_callback, SIGHUP);

	uv_signal_init(&Connect_Loop, &sig_int_handle);
    	uv_signal_start(&sig_int_handle, sig_kill_callback, SIGKILL);
	//
	// TBD: This signal should be disabled in real life.
	//
	uv_signal_init(&Connect_Loop, &sig_int_handle);
    	uv_signal_start(&sig_int_handle, sig_kill_callback, SIGINT);
	//
	// Initialize the incoming connection handle.
	//
	uv_tcp_init(&Connect_Loop, &listen_handle);
	//
	// Bind to a listen address.
	//
	if(ip_address(Server, SERVER_DEVICE, IP_VERSION) != TRUE)
	  {
		fatal_error(LOG_ALERT, "PEXD DAEMON: NO IP ADDRESS for DEV %s IP Version %d\n",
			 SERVER_DEVICE, IP_VERSION);
	  }

	uv_ip4_addr(Server, SERVER_PORT, &addr);

	if(uv_tcp_bind(&listen_handle, (const struct sockaddr *) &addr, 0))
	  {
		fatal_error(LOG_ALERT, "PEXD DAEMON: Can't bind to address %s:%d. Error %d:%s\n",
			 Server, SERVER_PORT, errno, strerror(errno));
	  }

	//
	// Wait for all the thread tasks to be initialized.
	//
	for(;;)
	  {

ENTER_MUTEX(&Shared_Data_Mutex);

#ifdef DYNAMIC_SCRAM_THREAD
		if(N_Tasks == 5)
#else // DYNAMIC_SCRAM_THREAD
		if(N_Tasks == 6)
#endif // DYNAMIC_SCRAM_THREAD
		   {
EXIT_MUTEX(&Shared_Data_Mutex);
			break;
		   }
EXIT_MUTEX(&Shared_Data_Mutex);

	 	pthread_yield();
	  }

	syslog(LOG_INFO, "MAIN_TASK: Started. ID %ld\n", uv_thread_self());
	syslog(LOG_INFO, "DAEMON: All Spawned Tasks Running");

	//
	// Configure the listen socket.
	//
	if( (r = uv_listen((uv_stream_t*) &listen_handle, MAX_CONN_DESC, make_incoming_connection)) )
        	fatal_error(LOG_ERR, "MAIN_TASK: Can't Configure Listening Socket. Error %d: %s", r, uv_err_name(r));

	syslog(LOG_ALERT, "PEXD DAEMON: RUNNING");
	//
	// Wait for a connection.
	//
	for(;;)
	  {

    		r = uv_run(&Connect_Loop, UV_RUN_DEFAULT);
		if(r)
		  {
			// fprintf(stderr, "MAIN: Run Error %d: %s\n", uv_err_name(r));
			r = 0;
		  }
	  }

	return(r);

}

#ifdef LIBUV_DETACH

//
// FUNCTION - uv_thread_detach(): Create a detached thread task and put into execution.
//
// ARGUMENTS
// -----------
// tid: Points to memory for holding the Livuv thread ID.
// entry: The thread task.
// arg: Points to data to be passed to the task when it starts.
//
// RETURN VALUE
// --------------
// Returns 0 if successful and a Libuv error code otherwise.
//
// NOTES
// -------
// This routine is kludge which supplies the same functionality as the uv_thread_detach() API.
// It is necessary because some version of Libuv don't include this functionality.
//
ROUTINE int uv_thread_detached_create(uv_thread_t *tid, void (*entry )(void * ), void *arg )
{
	int rval;

	rval = uv_thread_create(tid, entry, arg);
	if(rval != 0)
		rval = pthread_detach(*tid);

	return(rval);
}

#endif // LIBUV_DETACH

//
// FUNCTION - make_incoming_connection(): A callback routine that executes when an incoming connection event occurs.
//
// ARGUMENTS
// -----------
// listen_handle: The TCP listen socket handle.
// status: The value is 0 when a connection detected without errors.
//
// RETURN VALUE
// --------------
// None
//
// NOTES
// -----
// The execution of this routine must be preconfigured with Libuv uv_listen().
//
ROUTINE PRIVATE void make_incoming_connection(uv_stream_t *listen_handle, int status)
{
	ASYNC_DATA poll_data;
	CONN_DESC *cdesc;
	uv_tcp_t *conn_handle;
	int r;

	BOOL reject = FALSE;


	if (status == -1)
		fatal_error(LOG_ERR, "MAIN_TASK: Invalid Incoming Connection Status");

	//
	// If this routine is called before the work from the last invocation is finished,
	// we have no choice but to reject the connection.
	//
	if(Connect_Accept_Busy)
		reject = TRUE;
	else
		Connect_Accept_Busy = TRUE;

	syslog(LOG_INFO, "MAIN_TASK: CONNECTION ATTEMPT: N Sockets = %d", N_Sockets);
	//
	// Initialize the connection handle.
	//
    	conn_handle = (uv_tcp_t *) VALLOC(sizeof(uv_tcp_t));
	uv_tcp_init(&Connect_Loop, conn_handle);

	if((r = uv_accept(listen_handle, (uv_stream_t *) conn_handle)) == 0)
	   {
		int nsock;
		//
		// A new connection occured.
		//
        	uv_os_fd_t fd;

ENTER_MUTEX(&Network_Conn_Mutex);
		nsock = N_Sockets++;
EXIT_MUTEX(&Network_Conn_Mutex);

		if(reject || nsock >= MAX_CONN_DESC)
		  {
			syslog(LOG_DEBUG, "MAIN_TASK: CONNECTION REJECTION - Transient, N Sockets = %d", nsock);

ENTER_MUTEX(&Network_Conn_Mutex);
			N_Sockets--;
EXIT_MUTEX(&Network_Conn_Mutex);

			conn_handle->data = (void *) NULL;
			uv_close((uv_handle_t *) conn_handle, forced_close_callback);
			return;
		  }
		//
		// Fetch the socket descriptor from the connection handle.
		//
		uv_fileno((const uv_handle_t*) conn_handle, &fd);
		//
		// Allocate the connection descriptor.
		//
		cdesc = ALLOC_CONN_DESC(fd);
		if( !cdesc )
		  {
			syslog(LOG_DEBUG, "MAIN_TASK: CONNECTION REJECTION - No Connection Descriptors, N =  %d", nsock);

ENTER_MUTEX(&Network_Conn_Mutex);
			N_Sockets--;
EXIT_MUTEX(&Network_Conn_Mutex);

			cdesc->conn_handle = (uv_tcp_t *) conn_handle;
			conn_handle->data = (void *) cdesc;
			uv_close((uv_handle_t *) conn_handle, forced_close_callback);

			return;
		  }

#ifdef USE_SSL
		if( !Ssl_Accept(cdesc, fd) )
		  {
ENTER_MUTEX(&Network_Conn_Mutex);

			N_Sockets--;

			cdesc->busy = FALSE;
ENTER_MUTEX(&Service_Tdata.mutex);
			DELETE_CONN(cdesc);
EXIT_MUTEX(&Service_Tdata.mutex);

EXIT_MUTEX(&Network_Conn_Mutex);

			cdesc->conn_handle = (uv_tcp_t *) conn_handle;
			conn_handle->data = (void *) cdesc;
			uv_close((uv_handle_t *) conn_handle, forced_close_callback);

			return;
		  }
#endif // USE_SSL

		//
		// Save the connection handle and start polling.
		//
		cdesc->conn_handle = (uv_tcp_t *) conn_handle;

		syslog(LOG_INFO, "MAIN_TASK: NEW CONNECTION ESTABLISHED - CONN %d FD %d", cdesc->index, cdesc->fd);
		//
		// Set up epoll() plumbing by sending a message to IO_Task();
		//
		bzero((void *) &poll_data, sizeof(ASYNC_DATA));
		poll_data.type = T_POLL_CONFIG_REQ;
		poll_data.async_handle = &IO_Tdata.async_handle;
		poll_data.async_q = &IO_Tdata.q;
		poll_data.async_mutex = &IO_Tdata.mutex;
		poll_data.object_handle = NULL;
		//
		// The IO_Task() sends the T_POLL_CONFIG_RSP message to the Protocol_Task(), which
		// is in S_IDLE state, once polling has been initiated.
		//
		send_async_msg(cdesc, &poll_data);
	   }
	else
		fatal_error(LOG_ERR, "MAIN_TASK: CONNECTION REJECTION - Libuv Accept Failure. Error %d: %s", r, uv_err_name(r));

	Connect_Accept_Busy = FALSE;

	return;
}


//
// FUNCTION - connect_proxy(): Dismantles and releases a Libuv connection handle on behalf of another task.
// The proxy routine executes in the main() process/task bound to the Connect_Loop.
//
// ARGUMENTS
//-----------
// handle: The async. channel handle. (Connect_Task_Async_Handle)
//
// RETURN VALUE
// -----------
// None
//
// NOTES
// --------
// Execution of the routine is triggered by  a call to async_send() in the Protocol_Task().
//
ROUTINE PRIVATE void connect_proxy(uv_async_t *handle)
{
	CONN_DESC *cdesc;
	MSG *msg;
	uv_tcp_t *conn_handle;
	uv_handle_t **ppdata;
	BOOL done;

	syslog(LOG_DEBUG, "MAIN_TASK: CONNECT PROXY\n");
	//
	// Avoid clobbering a previous invocation of this routine.
	//
	if(Connect_Proxy_Busy)
		return;

	Connect_Proxy_Busy = TRUE;

	//
	// Handle all messages from the Protocol_Task()
	//
	done = FALSE;
	while(done != TRUE)
	  {
ENTER_MUTEX(&Main_Tdata.mutex);
		msg = remove_msg(&Main_Tdata.q);
EXIT_MUTEX(&Main_Tdata.mutex);

		if(msg)
		  {
			cdesc = (CONN_DESC *) msg->conn_desc;

			syslog(LOG_DEBUG, "MAIN_TASK(Proxy): CONN[%d] Type %d\n", cdesc->index, msg->type);

			switch(msg->type)
			{
			case T_CONN_DISMANTLE_REQ:
				//
				// Release a uv_tcp_t connection handle.
				// The protocol task is notified by async_close_callback()
				// when the operation is complete.
				//
				ppdata = (uv_handle_t **) &msg->buffer[0];
				conn_handle = (uv_tcp_t *) *ppdata;
				conn_handle->data = (void *) cdesc;
				uv_close((uv_handle_t *) conn_handle, async_close_callback);
				break;
			default:
				fatal_error(LOG_EMERG, "MAIN_TASK: CONNECT_PROXY - Invalid Message = %d", msg->type);
			}

			MSG_FREE(msg);
		  }
		else
			done = TRUE;
	  }

	Connect_Proxy_Busy = FALSE;

	return;
}


//
// FUNCTION - force_close_callback(): Deallocate a Libuv handle.
//
// ARGUMENTS
//-----------
// handle: The handle.
//
// NOTES
// ------
// Releases a uv_tcp_t handle in the case it was allocated before an incoming connection was rejected.
//
ROUTINE PRIVATE void forced_close_callback(uv_handle_t *handle)
{

	CONN_DESC *cdesc = (CONN_DESC *) handle->data;

	VFREE((UCHAR *) handle);

	if(cdesc)
	  {
		close(cdesc->fd);
		cdesc->conn_handle = NULL;
	  }

// printf("ASYNC[X]: REJECT COMPLETE\n");
// fflush(stdout);

	//
	// Setting this variable indicates the handle has been freed.
	//
	Connect_Accept_Busy = FALSE;

	return;
}


ROUTINE void Msg_Task_Init(TASK_DATA *ptask)
{
	bzero((void *) ptask, sizeof(TASK_DATA));

	ptask->q.head = ptask->q.tail = NULL;

	uv_mutex_init(&ptask->mutex);
	uv_cond_init(&ptask->cond);

	return;
}

ROUTINE void Async_Task_Init(TASK_DATA *ptask, uv_loop_t *ploop,  uv_async_cb proxy_routine)
{

	bzero((void *) ptask, sizeof(TASK_DATA));
	ptask->q.head = ptask->q.tail = NULL;

	uv_mutex_init(&ptask->mutex);
	uv_cond_init(&ptask->cond);

	uv_loop_init(ploop);
	uv_async_init(ploop, &ptask->async_handle, proxy_routine);

	return;
}
#include "os_def.h"
#include "basic_def.h"
#include <uv.h>
#include "scram.h"
#include "framework.h"
//
// Use this definition once debugging is complete.
//
// #define PRIVATE static
//
#define PRIVATE static

//
// **********************************************************
// ******************** DATA ***********************************
// **************************************************************
//

//
// This data is used to indicate when the task is initialized.
//
extern uv_mutex_t		Shared_Data_Mutex;
extern int			 N_Tasks;


//
// Task data.
//
extern	TASK_DATA		IO_Tdata;
//
// Loop for handling epoll() read I/O events.
//
PRIVATE uv_loop_t	Poll_Loop;
//
// TRUE when the Poll proxy routine is executing.
//
PRIVATE BOOL		Poll_Proxy_Busy;

//
// *******************************************************
// *********************** PROTOTYPES *******************
// *****************************************************
//
PRIVATE void poll_callback(uv_poll_t *, int , int );
PRIVATE void poll_proxy(uv_async_t *);
void async_close_callback(uv_handle_t *);

#ifdef USE_SSL
int Ssl_RxData(SSL *, UCHAR *, int );
#endif // USE_SSL

DECODE_RESULT rx_sdu(CONN_DESC *, int);

void SendProtoMsg(CONN_DESC *, MSG *);

void IO_Task_Init(TASK_DATA *);
void Async_Task_Init(TASK_DATA *, uv_loop_t *,  uv_async_cb );

void insert_msg(MSG_FIFO *, MSG *);
MSG *remove_msg(MSG_FIFO *);

MSG *MSG_ALLOC(int, BOOL);
void MSG_FREE(MSG *);

UCHAR *VALLOC(int n);
void VFREE(UCHAR *);

void fatal_error(int , const char *, ...);

// void verify_watch(void *, uv_loop_t *);

//
// Monitor connections for incoming data with epoll()
//
TASK void IO_Task(void *arg)
{
	int r;

ENTER_MUTEX(&Shared_Data_Mutex);
	N_Tasks++;
EXIT_MUTEX(&Shared_Data_Mutex);

	syslog(LOG_INFO, "IO_TASK: Started. ID %ld\n", uv_thread_self());

	for(;;)
	  {
		r = uv_run(&Poll_Loop, UV_RUN_DEFAULT);
		if(r)
		  {
			syslog(LOG_ERR,  "IO_TASK: Run Error %d", r);
			r = 0;
		  }
	  }

	return;
}

//
// FUNCTION - poll_callback(): A callback routine that executes when incoming data is available.
// and reads the  data.
//
// ARGUMENTS
// ---------------
// poll_handle: The Libuv poll handle.
// status: The value is 0 if execution occurs due to incoming data.
// events: The value should always be UV_READABLE, which means incoming
// data is available, if status is 0.
//
// RETURNS
//--------
// None
//
// NOTES
// ------
// This routine is invoked by the Libuv epoll() mechanism.
//
ROUTINE PRIVATE void poll_callback(uv_poll_t *poll_handle, int status, int events)
{
	if(status == 0)
	  {
		if(events & UV_READABLE)
		   {
			//
			// Since the right connection descriptor is known, all you
			// neeed to to is read data into the connection descriptor buffer.
			//
			int n;
			MSG *sdu;

			CONN_DESC *cdesc = (CONN_DESC *)  poll_handle->data;

#ifdef USE_SSL
			n = Ssl_RxData(cdesc->ssl_socket, cdesc->rx_buf, RX_BUF_SIZE);
#else // USE_SSL
			n = recv(cdesc->fd, cdesc->rx_buf, RX_BUF_SIZE, MSG_DONTWAIT);
#endif // USE_SSL

			if(n > 0)
			   {
				DECODE_RESULT r;
				//
				// Recognize the SDU in the usual way.
				// If the SDU is complete, send the it to the Protocol_Task().
				//
				r = rx_sdu(cdesc, n);
				switch(r)
				{
				case RX_PARTIAL:
				case RX_COMPLETE:
				case RX_EMPTY:
					break;
				case RX_OVERFLOW:
				case RX_NOMEMORY:
					//
					// There is no choice but to send an abort, reinitialize everything,
					// and release the connection.
					//
					syslog(LOG_ERR, "IO_TASK: Irrecoverable SDU Reception Error %d", r);

					sdu = MSG_ALLOC(0, FALSE);
					sdu->class = C_NOTIFY;
					sdu->type = T_FAULT;
					sdu->info = (int) r;

					SendProtoMsg(cdesc, sdu);
					break;
				default:
					fatal_error(LOG_EMERG, "IO_TASK: Invalid SDU Recognition Code, %d", r);
				}

			   }
			else
			if(n < 0)
			  {
#ifdef USE_SSL
				MSG *msg;

				n = 0;
				syslog(LOG_INFO, "IO_TASK: RX DISCONNECT %d - %s !", errno, strerror(errno) );

				msg = MSG_ALLOC(0, FALSE);
				msg->class = C_NOTIFY;
				msg->type = T_DISCONNECT;
				msg->info = 0;
				SendProtoMsg(cdesc, msg);
#else // USE_SSL
				n = 0;
				if( !(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) )
				  {
					MSG *msg;

					syslog(LOG_INFO, "IO_TASK: RX DISCONNECT %d - %s !", errno, strerror(errno) );

					msg = MSG_ALLOC(0, FALSE);
					msg->class = C_NOTIFY;
					msg->type = T_DISCONNECT;
					msg->info = 0;
					SendProtoMsg(cdesc, msg);
				  }
#endif // USE_SSL
			  }
		   }
	  }
	else
		syslog(LOG_ERR, "IO_TASK: poll_callback - Bad Status %d", status);


	return;
}


//
// FUNCTION - poll_proxy(): The proxy routine executes in the IO_Task() Poll Loop when
// it has unserviced async. messages from other tasks.
//
// ARGUMENTS
// -----------
// handle: A Libuv async. operation handle.
//
// RETURNS
// -------
// Nothing
//
// NOTES
// -----
// The main() and Protocol_Tasks() use the async. handled to send messages to the IO_Task().
// They do so by inserting messages in the IO_Task() IO_Tdata.input_q which is in shared memory.
//
ROUTINE PRIVATE void poll_proxy(uv_async_t *handle)
{
	CONN_DESC *cdesc;
	MSG *msg, *rsp;
	uv_poll_t *poll_handle;
	uv_handle_t **ppdata;
	int r;
	BOOL done;

	syslog(LOG_DEBUG, "IO_TASK: POLL PROXY");
	//
	// Do nothing if execution is already in progress.
	//
	if(Poll_Proxy_Busy)
		return;

	Poll_Proxy_Busy = TRUE;
	//
	// Handle messages from other tasks.
	//
	done = FALSE;
	while(done != TRUE)
	  {

ENTER_MUTEX(&IO_Tdata.mutex);
		msg = remove_msg(&IO_Tdata.q);
EXIT_MUTEX(&IO_Tdata.mutex);

		if(msg)
		  {
			cdesc = (CONN_DESC *) msg->conn_desc;

			syslog(LOG_DEBUG, "IO_TASK(Proxy): RX EVENT - CONN[%d] Type %d\n", cdesc->index, msg->type);

			switch(msg->type)
			{
			case T_POLL_CONFIG_REQ:
				//
				// Main process request to start Libuv/epoll() operation.
				//
				poll_handle = (uv_poll_t *) VALLOC(sizeof(uv_poll_t));
				if( (r = uv_poll_init(&Poll_Loop, poll_handle, cdesc->fd)) != 0)
				  {

					fatal_error(LOG_CRIT, "IO_TASK: POLL_PROXY -  Polling Initialization Error %d, %s",
					 	r, uv_err_name(r));
				  }

				poll_handle->data = (void *) cdesc;
				if((r = uv_poll_start(poll_handle, UV_READABLE, poll_callback)) < 0)
				  {
					fatal_error(LOG_CRIT, "IO_TASK: POLL_PROXY -  Polling Initiation Error %d, %s",
						 r, uv_err_name(r));
				  }
				//
				// Notify the Protocol_Task(), that polling has started.
				//
				rsp = MSG_ALLOC(0, FALSE);

				rsp->class = C_NOTIFY;
				rsp->type = T_POLL_CONFIG_RSP;
				rsp->info = 0;
				rsp->conn_desc = (void *) poll_handle;

				SendProtoMsg(cdesc, rsp);

				break;
			case T_POLL_DISMANTLE_REQ:
				//
				// Protocol_Task() request to cease Libuv/epoll() operation and
				// release the poll handle and its resources.
				//
				ppdata = (uv_handle_t **) &msg->buffer[0];
				poll_handle = (uv_poll_t *) *ppdata;

				if( (r = uv_poll_stop(poll_handle)) )
				  {
					fatal_error(LOG_CRIT, "IO_TASK: POLL_PROXY - Poll Stop Error %d, %s",
						 r, uv_err_name(r) );
				  }
				//
				// The callback routine notifies the Protocol_Task() when everything is done.
				//
				poll_handle->data = (void *) cdesc;
				uv_close((uv_handle_t *) poll_handle, async_close_callback);

				break;
			default:
				fatal_error(LOG_EMERG, "IO_TASK: POLL_PROXY - Invalid Message = %d", msg->type);

			}

			if(msg)
				MSG_FREE(msg);
		  }
		else
		  {
			done = TRUE;
		  }
	  }

	Poll_Proxy_Busy = FALSE;

	return;
}

ROUTINE void IO_Task_Init(TASK_DATA *pdata)
{
	Poll_Proxy_Busy = FALSE;
	Async_Task_Init(pdata, &Poll_Loop, poll_proxy);

	return;
}
#include "os_def.h"
#include "basic_def.h"
#include <uv.h>
#include "scram.h"
#include "framework.h"

#ifdef USE_SSL

//
// Use this definition once debugging is complete.
//
// #define PRIVATE static
//
#define PRIVATE static

// #define VTRACE


//
// **************************************************************************************
// ************************************ DATA *******************************************
// **************************************************************************************
//

//
// Protects the Service_Q and its condition variable.
//
extern uv_mutex_t			Network_Conn_Mutex;

#ifdef USE_CRYPTO_LOCK

//
// An array of Mutexes used by the cryptography software and libraries to
// prevent corruption of shared memory by the main task/thread.
//
PRIVATE uv_mutex_t *Main_Ssl_Mutex_List;

#endif // USE_CRYPTO_LOCK

//
// *****************************************************************************************
// *********************************** PROTOTYPES *******************************************
// *****************************************************************************************
//

SSL *Ssl_Accept(CONN_DESC *, int );
void Ssl_ReleaseConnect(CONN_DESC *, uv_mutex_t * );

int Ssl_RxData(SSL *, UCHAR *, int );
int Ssl_TxData(SSL *, const UCHAR *, int );

void Ssl_Init();

PRIVATE void Ssl_GetCredentials(SSL_CTX * );

#ifdef MOBILE_AUTH_SSL
PRIVATE BOOL Ssl_ConfigTrust(SSL_CTX *, X509_DESC * );
void Ssl_ReleaseAuth(CONN_DESC * );

#ifdef USE_CRYPTO_LOCK

void Ssl_InitLock(int );

PRIVATE void Ssl_TaskID(CRYPTO_THREADID * );
PRIVATE void Ssl_MainCryptoLock(int , int , const char *, int );

#endif // USE_CRYPTO_LOCK

#endif // MOBILE_AUTH_SSL


#ifdef VTRACE
void PRIVATE ShowCerts(SSL *);
#endif // VTRACE

void fatal_error(int , const char *, ...);

UCHAR *VALLOC(int n);
void VFREE(UCHAR *);


//
// FUNCTION - Ssl_Accept(): Establishes an incoming SSL connection, including insuring
// assessing the client authentication handshake result. 
//
// ARGUMENTS
// -----------
// cdesc: A Connection Descriptor.
// sock_fd: The TCP socket descriptor obtained during incoming TCP connection establishment.
//
// RETURN VALUE
// --------------
// Returns an SSL operation socket handle if successful and NULL otherwise.
//
// NOTES
// -------
// If MOBILE_AUTH_SSL is defined, the Server validates the Client.
// The authentication handshake is just configured and initiated in the Server,
// and the handshake protocol exchanges occur within the SSL layer.
//
ROUTINE SSL *Ssl_Accept(CONN_DESC *cdesc, int sock_fd)
{
#ifdef MOBILE_AUTH_SSL
	X509_DESC *x509_data;
#endif // MOBILE_AUTH_SSL

	SSL_CTX *ctx;
	SSL_METHOD *method;
	SSL *socket;
	int flags, r;
	BOOL error;

// printf("TRY\n");

	syslog(LOG_INFO, "MAIN_TASK: ATTEMPT SSL CONNECTION - CONN %d FD %d", cdesc->index, cdesc->fd);

ENTER_MUTEX(&Network_Conn_Mutex);
	cdesc->ssl_ctx = NULL;
	cdesc->ssl_socket = NULL;
EXIT_MUTEX(&Network_Conn_Mutex);

#ifdef MOBILE_AUTH_SSL
	x509_data = &cdesc->x509_data;
	bzero((void *) x509_data, sizeof(X509_DESC));
#endif // MOBILE_AUTH_SSL

	//
	// Allocate an SSL/TLS V1.2  Context.
	//
	method = (SSL_METHOD *) TLSv1_2_server_method();
	//method = (SSL_METHOD *) SSLv3_server_method();
	if( (ctx = SSL_CTX_new(method)) == NULL)
	  {
		syslog(LOG_ERR, "MAIN_TASK: SSL Error - Context Allocation Failure");

		ERR_print_errors_fp(stderr);
		abort();

	  }
	else
	  {
ENTER_MUTEX(&Network_Conn_Mutex);
		cdesc->ssl_ctx = ctx;
EXIT_MUTEX(&Network_Conn_Mutex);

		SSL_CTX_set_options(ctx,
			(SSL_OP_TLS_BLOCK_PADDING_BUG | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1));
			// (SSL_OP_TLS_BLOCK_PADDING_BUG | SSL_OP_NO_SSLv2 | SSL_OP_NO_TLSv1));
	  }

	Ssl_GetCredentials(ctx);
	//
	// Allocate a new SSL socket operation handle.
	//
       if( !(socket = SSL_new(ctx)) )
	 {
		syslog(LOG_INFO, "MAIN_TASK: CONN[%d] SSL Error - Context Allocation Failure", cdesc->index);
		ERR_print_errors_fp(stderr);

ENTER_MUTEX(&Network_Conn_Mutex);
		cdesc->ssl_ctx = NULL;
EXIT_MUTEX(&Network_Conn_Mutex);

		SSL_CTX_free(ctx);
	  }
	else
	  {
ENTER_MUTEX(&Network_Conn_Mutex);
		cdesc->ssl_socket = socket;
EXIT_MUTEX(&Network_Conn_Mutex);
		//
		// Configure the SSL authentication characteristics.
		//
		SSL_set_accept_state(socket);
#ifdef MOBILE_AUTH_SSL
		//
		// The Server will authenticate the Client.
		//
		SSL_set_verify(socket, (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT), NULL);
		SSL_set_verify_depth(socket, 1);
		//
		// Configure the Server as a trusted CA to enable authentication of the Client.
		//
		if(Ssl_ConfigTrust(ctx, x509_data) != TRUE)
		  {
			Ssl_ReleaseConnect(cdesc, &Network_Conn_Mutex);
			socket = NULL;
			ctx = NULL;
		  }
#else // MOBILE_AUTH_SSL
		//
		// No authentication.
		//
		SSL_set_verify(socket, SSL_VERIFY_NONE, NULL);
#endif // MOBILE_AUTH_SSL


		if(socket)
		  {
			// Establish the SSL connection.
			//
			// Set up SSL socket I/O
			//
			if( (r = SSL_set_fd(socket, sock_fd)) != 1) 
			  {
				syslog(LOG_ERR, "MAIN_TASK: SSL Error - Can't Set Socket Descriptor: Return %d Error %d",
					 r, SSL_get_error(socket, r));

				ERR_print_errors_fp(stderr);
				abort();
			  }
			//
			// Accept the SSL connection when the authentication handshake is complete.
			//
			error = FALSE;
			while ((r = SSL_accept(socket)) != 1 )     /* do SSL-protocol accept */
			  {
				r = SSL_get_error(socket, r);
				switch(r)
				{
				case SSL_ERROR_WANT_READ:
				case SSL_ERROR_WANT_WRITE:
					pthread_yield();
					break;
				default:
					error = TRUE;
				}

				if(error)
					break;
			  }

			if(error)
			  {
printf("SNAG %d: %s\n", errno, strerror(errno));
				syslog(LOG_INFO, "MAIN_TASK: CONN[%d]  SSL CONNECTION REJECTED: Return %d Error %d",
				 	cdesc->index, r, SSL_get_error(socket, r));

				ERR_print_errors_fp(stderr);

				Ssl_ReleaseConnect(cdesc, &Network_Conn_Mutex);
				socket = NULL;
				ctx = NULL;
			  }
			else
			  {
				syslog(LOG_INFO, "MAIN_TASK: CONN[%d] SSL CONNECTION ESTABLISHED", cdesc->index);
// printf("OK\n");
#ifdef VTRACE
				//
				// Display certficates from client.
				//
				ShowCerts(socket);
#endif // VTRACE
				//
				// Configure the socket for non-blocking operation.
				// Simple experimentation confirms this does not break Libuv and works.
				//
				flags = fcntl(sock_fd, F_GETFL, 0);
				fcntl(sock_fd, F_SETFL, (flags | O_NONBLOCK));
			  }
		  }
	  }

	return(socket);
}

#ifdef MOBILE_AUTH_SSL
//
// FUNCTION - Ssl_ConfigTrust(): Configures Server authentication of the Client.
//
// ARGUMENTS
// -----------
// ctx: An SSL context.
// x509_data: Points a caller allocated structure for holding the essential X509 authentication items.
//
// RETURN VALUE
// --------------
// Returns TRUE if successful and FALSE otherwise.
//
// NOTES
// -------
// None
//
ROUTINE PRIVATE BOOL Ssl_ConfigTrust(SSL_CTX *ctx, X509_DESC *x509_data)
{
	X509_STORE *pstore = NULL;
	X509_STORE_CTX *storeCtx = NULL;
	BOOL rval = TRUE;
	int r = 0;

	syslog(LOG_DEBUG, "MAIN_TASK: Configure SSL Authentication");
	//
	// We no longer load the client's public certificate into memory
	// because the underlying bugs making it necessary are fixed.
	//
	// Now allocate a store context
	//
	if( !(storeCtx = X509_STORE_CTX_new()) )
	   {
		rval = FALSE;
		syslog(LOG_ERR, "MAIN_TASK: X509 Error - Store Context Allocation Failure");
	   }
	else
	//
	// First, fabricate a temporary trust store and
	// add the CA Certificate to the new trust store.
	//
	if( !(pstore = X509_STORE_new()) )
	  {
		rval = FALSE;
		syslog(LOG_INFO, "MAIN_TASK: X509 Error - Store Allocation Failure");
	  }
	else
	if( (r = X509_STORE_load_locations(pstore, SSL_CA_CERT_FILE, NULL)) != 1)
		syslog(LOG_ERR, "MAIN_TASK: X509 Error - Store Certificate Addition Failure");
	else
	if( (r = X509_STORE_set_default_paths(pstore)) != 1)
		syslog(LOG_ERR, "MAIN_TASK: X509 Error - Store Path Configuration Failure");
	else
	//
	// Now configure the authentication characteristics.
	//
	if( (r = X509_STORE_CTX_init(storeCtx, pstore, NULL, NULL)) != 1)
		syslog(LOG_ERR, "MAIN_TASK: X509 Error - Store Initialization Failure");
	else
	  {
		//
		// Insert the new store settings in the SSL context.
		//
		// Finally set the authentication characteristics.
		// This flag is just enhance debugging on most SSL versions and
		// should be set.
		//
		SSL_CTX_set_cert_store(ctx, pstore);
		X509_STORE_CTX_set_flags(storeCtx, ( X509_V_FLAG_CB_ISSUER_CHECK ) );
	  }

	if(r != 1)
	  {
		syslog(LOG_ERR, "MAIN_TASK: SSL Authentication Configuration Error");

		ERR_print_errors_fp(stderr);
		abort();
	  }

	if(rval)
	  {
		syslog(LOG_DEBUG, "MAIN_TASK: Authentication Configuration Succeeded");

		x509_data->storeCtx = storeCtx;
		x509_data->store = pstore;
	  }
	else
	  {
		syslog(LOG_INFO, "MAIN_TASK: Transient SSL Authentication Configuration Failure");

		if(pstore)
			X509_STORE_free(pstore);
		if(storeCtx)
		  {
			X509_STORE_CTX_cleanup(storeCtx);
			X509_STORE_CTX_free(storeCtx);
		  }
	  }

	return(rval);
}

#endif // MOBILE_AUTH_SSL

//
// FUNCTION - Ssl_TxData(): Writes data to the SSL network socket.
//
// ARGUMENTS
// ---------
// socket: The SSL socket.
// buf: The buffer with the data.
// nbytes: The number of bytes to be written.
//
// RETURNS
// -------
// Returns the number of bytes written if successful and -1 if an irrecoverable error occurs.
//
// NOTES
// -----
// If the return value is not -1, it is possible to write more data on the socket.
// Otherwise, for logical purposes, the socket connection is lost.
//
// A threaded environment is assumed.
//
ROUTINE int Ssl_TxData(SSL *socket, const UCHAR *buf, int nbytes)
{
	int n, err;
	int rval;

	if(nbytes <= 0)
	  {
		if(nbytes < 0)
			rval = -1;

		return(rval);
	  }

	n = SSL_write(socket, buf, nbytes);
	if(n > 0)
		rval = n;
	else
	  {
		err = SSL_get_error(socket, n);
		switch(err)
		{
		case SSL_ERROR_NONE:
		case SSL_ERROR_WANT_WRITE:
		case SSL_ERROR_WANT_READ:
			//
			// Retry
			//
			pthread_yield();
			break;
		case SSL_ERROR_SYSCALL:
		case SSL_ERROR_ZERO_RETURN:
			//
			// The write failed and we have to asuume to connection is lost.
			// If errno is ECONNRESET, the connection is lost due to a remote disconnect.
			//
			ERR_print_errors_fp(stderr);

			rval = -1;
			break;
		case SSL_ERROR_WANT_CONNECT:
		case SSL_ERROR_WANT_ACCEPT:
		case SSL_ERROR_WANT_X509_LOOKUP:
			//
			// We should never get this return value because, the X509 authentication
			// should be complete when this routine is called, the client cannot
			// initiate renegotriation, and the server does not currently initiate
			// renegotiaon.
			//
			syslog(LOG_EMERG, "TRANSMIT_TASK: SSL Error - Unexpected Negotiation");

			ERR_print_errors_fp(stderr);
			abort();
			break;
		case SSL_ERROR_SSL:
			syslog(LOG_EMERG, "TRANSMIT_TASK: SSL Error - Libary Failure");

			ERR_print_errors_fp(stderr);
			abort();
			break;
		default:
			fatal_error(LOG_EMERG, "BUG - Invalid SSL Write Return = %d", err);
		
		}
	  }


	return(rval);
}

//
// FUNCTION - Ssl_RxData(): Reads incoming network data from an SSL socket.
//
// ARGUMENTS
// ---------
// socket: The SSL socket.
// buf: The buffer for the data.
// size: Ths size of the buffer.
//
// RETURNS
// -------
// Returns the postive number of bytes read if successful, 0 if no data is read, and
// -1 if an irrecoverable error occurs.
//
// NOTES
// -----
// If the return value is not -1, it is possible more data may arrive on the socket.
// Otherwise, for logical purposes, the socket connection is lost.
//
// A threaded environment is assumed.
//
ROUTINE int Ssl_RxData(SSL *socket, UCHAR *buf, int size)
{
	int n, err;

// printf("START READ\n");

	n = SSL_read(socket, buf, size);
	if(n <= 0)
	  {
		err = SSL_get_error(socket, n);
// printf("ERR %d\n", err);
		switch(err)
		{
		case SSL_ERROR_NONE:
			//
			// No data.
			//
			break;
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			//
			// Retry later.
			//
			if(n <= 0)
				pthread_yield();
			break;
		case SSL_ERROR_SYSCALL:
			if(n && errno != EINTR)
			  {
printf("Bad Call ! E %d\n", errno);
				//
				// Assume the connection is lost in all cases.
				// If errno is ECONNRESET, it is lost due to a remote disconnect.
				//
				syslog(LOG_ERR, "IO_TASK: SSL Error - Read Failure. Errno %d:%s",
					 errno, strerror(errno));

				ERR_print_errors_fp(stderr);
				if(n <= 0)
					n = -1;
			  }
			else
			  {
				//
				// No data
				//
				// TBD: It isn't clear if there is still a connection at this point.
				//
				if(errno == ECONNRESET)
				  {
					syslog(LOG_ERR, "IO_TASK: SSL Error - Connection Loss on Read");

					ERR_print_errors_fp(stderr);

					if(n <= 0)
						n = -1;
				  }
				break;
			  }
			break;
		case SSL_ERROR_WANT_CONNECT:
		case SSL_ERROR_WANT_ACCEPT:
		case SSL_ERROR_WANT_X509_LOOKUP:
			//
			// We should never get this return value because, the X509 authentication
			// should be complete when this routine is called, the client cannot
			// initiate renegotriation, and the server does not currently initiate
			// renegotiaon.
			//
			syslog(LOG_EMERG, "TRANSMIT_TASK: SSL Error - Unexpected Negotiation");

			ERR_print_errors_fp(stderr);
			abort();
			break;
		case SSL_ERROR_ZERO_RETURN:
			syslog(LOG_ERR, "IO_TASK: SSL Error - Unexpected Connection Loss");

			if(n <= 0)
				n = -1;
			break;
		case SSL_ERROR_SSL:
			syslog(LOG_EMERG, "IO_TASK: SSL Error - Library Failure");

			ERR_print_errors_fp(stderr);
			abort();
			break;
		default:
			fatal_error(LOG_EMERG, "IO_TASK: BUG - Invalid SSL Read Return = %d", err);
		}
	  }

// printf("RX DONE: %d\n", n);

	return(n);
}

#ifdef VTRACE
//
// Display certificates sent by the Client.
//
ROUTINE PRIVATE void ShowCerts(SSL* ssl)
{
    X509 *cert;
    char *line;


    cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
    if ( cert != NULL )
    {
	syslog(LOG_DEBUG, "MAIN_TASK:  *** Client Certificate ***");

        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        syslog(LOG_DEBUG, "Subject: %s", line);
        free(line);

        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        syslog(LOG_DEBUG, "Issuer: %s", line);
        free(line);

        X509_free(cert);
    }
    else
        syslog(LOG_DEBUG, "MAIN_TASK: *** No Client Certificate ***");

    return;
}
#endif // VTRACE


//
// FUNCTION - Ssl_GetCreditails(): Configure the SSL context with the certificate files
// required for SSL connection establishment.
//
// ARGUMENTS
// -----------
// ctx: An SSL context.
//
// RETURN VALUE
// --------------
// None
//
// NOTES
// -------
// None
//
ROUTINE PRIVATE void Ssl_GetCredentials(SSL_CTX* ctx)
{
	int r = 0;

	if( (r = SSL_CTX_use_certificate_file(ctx, SSL_SERVER_CERT_FILE, SSL_FILETYPE_PEM)) != 1)
		syslog(LOG_ERR, "MAIN_TASK: SSL Error - Invalid Server Certificate File = %s", SSL_SERVER_CERT_FILE);
	else
	if( (r = SSL_CTX_use_PrivateKey_file(ctx, SSL_SERVER_KEY_FILE, SSL_FILETYPE_PEM)) != 1)
		syslog(LOG_ERR, "MAIN_TASK: SSL Error - Invalid Client Key File = %s", SSL_SERVER_KEY_FILE);
	else
	if ( (r = SSL_CTX_check_private_key(ctx)) != 1)
		syslog(LOG_ERR, "MAIN_TASK: SSL Error -Private Key Public Certificate Mismatch");

	if(r != 1)
	  {
		ERR_print_errors_fp(stderr);
		abort();
	  }

	return;
}


//
// FUNCTION - ReleaseConnect(): the Releases resources during an SSL encryption session.
//
// ARGUMENTS
// -----------
// cdesc: A connection descriptor.
//
// RETURN VALUE
// --------------
// None
//
// NOTES
// -------
// None
//
ROUTINE void Ssl_ReleaseConnect(CONN_DESC *cdesc, uv_mutex_t *mutex)
{
	SSL_CTX *ctx;
	SSL *sock;


	syslog(LOG_INFO, "PROTO_TASK: CONN[%d] SSL Release Resources", cdesc->index);

	if(mutex)
		ENTER_MUTEX(mutex);

	if(cdesc->ssl_socket)
	  {
		ctx = cdesc->ssl_ctx;
		sock = cdesc->ssl_socket;

		cdesc->ssl_socket = NULL;
		cdesc->ssl_ctx = NULL;

		if(mutex)
			EXIT_MUTEX(mutex);

		SSL_free(sock);

		SSL_CTX_sess_set_remove_cb(ctx, NULL);
		SSL_CTX_free(ctx);

// printf("FREED SSL CTX %p\n", cdesc->ssl_ctx);
// printf("FREED SSL SOCKET %p\n", cdesc->ssl_socket);

	  }
	else
	  {

		if(mutex)
			EXIT_MUTEX(mutex);
	  }

	return;
}

//
// Initialize SSL operation a startup time.
//
ROUTINE void Ssl_Init()
{
	//
	// Initialize the basic SSL data.
	//
	ERR_load_crypto_strings();

	SSL_library_init();
	OpenSSL_add_all_algorithms();
	OPENSSL_config(NULL);
	SSL_load_error_strings();

	CRYPTO_malloc_debug_init(); 
	CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL); 
	CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);

	return;
}

#ifdef USE_CRYPTO_LOCK

//
// A callback function used to set the ID of the executing task.
//
ROUTINE PRIVATE void Ssl_TaskID(CRYPTO_THREADID *id)
{
	ULONG tid = uv_thread_self();
	CRYPTO_THREADID_set_numeric(id, tid);

	return;
}

//
// A callback fuction for aquiring and releasing a Mutex.
//
ROUTINE PRIVATE void Ssl_MainCryptoLock(int mode, int type, const char *file, int line)
{
	if(mode & CRYPTO_LOCK)
		ENTER_MUTEX(&Main_Ssl_Mutex_List[type]);
	else
		EXIT_MUTEX(&Main_Ssl_Mutex_List[type]);
	return;
}


//
// FUNCTION - Ssl_InitLock(): Intialize the cryptograpy software locking mechanism.
//
// ARGUMENTS
// -----------
// task: The static ID of the main task/thread.
//
// RETURN VALUE
// --------------
// None
//
// NOTES
// -------
// This must be called within the main() thread/task at startup time.
// A set of Mutexes is used by the cryptography libraries and software to
// to prevent data corruption in a threaded environment. This mechanism
// is not used in recent versions of the OpenSSL library.
//
ROUTINE void Ssl_InitLock(int task)
{
	int nlocks, k;

//
// Determine if the SSL library is compiled with thread support.
//
// #if defined(OPENSSL_THREADS)
// 	printf("THREADS OK\n");
// #else
// 	printf("THREADS FUCKED\n");
// #endif
//

	//
	// Just make sure the right task is calling this routine.
	//
	switch(task)
	{
	case MAIN_TASK:
		// fprintf(stderr, "MAIN_TASK: SSL/Crypto Lock Init\n");
		break;
	default:
		fatal_error(LOG_EMERG, "BUG: Can't Lock Non SSL Task: %d", task);
	}
	//
	// Allocate and intialize the Mutexes.
	//
	nlocks = CRYPTO_num_locks();
	Main_Ssl_Mutex_List = (uv_mutex_t *) VALLOC( (nlocks * sizeof(uv_mutex_t)) );
	for(k = 0; k < nlocks; k++)
		uv_mutex_init(&Main_Ssl_Mutex_List[k]);

	//
	// Configure the callback functions. Ssl_TaskID sets the thread/task ID of
	// the executing task, and SSl_MainCryptoLock() invokes the appropriate Mutex
	// as required.
	//
	CRYPTO_THREADID_set_callback((void (*) (CRYPTO_THREADID * )) Ssl_TaskID);
	CRYPTO_set_locking_callback((void (*)(int, int, const char*, int)) Ssl_MainCryptoLock);

	syslog(LOG_INFO, "Task %d: SSL/Crypto Lock Configured", task);

	return;
}

#endif // USE_CRYPTO_LOCK

#endif // USE_SSL

Reply via email to