/*
 * simco_nb.c
 *
 * Copyright (C) 2005, Ranch Networks, Inc.
 *
 * 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.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * Author : Srivatsa Chivukula <srivatsa@ranchnetworks.com>
 *
 */

#include "simco_core.h"
#include "simco_client.h"
#include "simco_nb.h"
#include "simco_defs.h"

#ifdef EXTENDED_SIMCO
#include "simco_nb_ext.h"
#endif

int c_debug=0;

static int (*notificationCallback)(simcoPacket_t *);
static int (*sessionEstablishCallback)(int, int);
static int (*sessionAuthenticateCallback)(int, int);
static int (*sessionTerminateCallback)(int, int);
static int (*policyReserveRuleCallback)(int, simcoPacket_t *);
static int (*policyEnableRuleCallback)(int, simcoPacket_t *);
static int (*perAfterReservationCallback)(int, simcoPacket_t *);
static int (*pbrAfterReservationCallback)(int, simcoPacket_t *);
static int (*policyDisableRuleCallback)(int, simcoPacket_t *);
static int (*policyRuleLifetimeChangeCallback)(int, simcoPacket_t *);
static int (*policyRuleStatusCallback)(int, simcoPacket_t *);
static int (*policyRuleListCallback)(int, int*, int);
static int (*pingCallback)(int, int);

static void parseNotification(int con, char *buffer, int size);
static void parseSessionEstablish(int con, char *buffer, int size);
static void parseSessionAuthenticate(int con, char *buffer, int size);
static void parseSessionTerminate(int con, char *buffer, int size);
static void parsePolicyReserveRule(int con, char *buffer, int size);
static void parsePolicyEnableRule(int con, char *buffer, int size);
static void parsePERAfterReservation(int con, char *buffer, int size);
static void parsePBRAfterReservation(int con, char *buffer, int size);
static void parsePolicyDisableRule(int con, char *buffer, int size);
static void parsePolicyRuleLifetimeChange(int con, char *buffer, int size);
static void parsePolicyRuleStatus(int con, char *buffer, int size);
static void parsePolicyRuleList(int con, char *buffer, int size);
static void parsePing(int con, char *buffer, int size);

static int simcoSessionEstablishDefaultCallback(int con, int status);
static int simcoSessionAuthenticateDefaultCallback(int con, int status);
static int simcoSessionTerminateDefaultCallback(int con, int status);
static int simcoPolicyReserveRuleDefaultCallback(int con, simcoPacket_t *sp);
static int simcoPolicyEnableRuleDefaultCallback(int con, simcoPacket_t *sp);
static int simcoPERAfterReservationDefaultCallback(int con, simcoPacket_t *sp);
static int simcoPBRAfterReservationDefaultCallback(int con, simcoPacket_t *sp);
static int simcoPolicyDisableRuleDefaultCallback(int con, simcoPacket_t *sp);
static int simcoPolicyRuleLifetimeChangeDefaultCallback(int con, simcoPacket_t *sp);
static int simcoPolicyRuleStatusDefaultCallback(int con, simcoPacket_t *sp);
static int simcoPolicyRuleListDefaultCallback(int con, int *rlist, int size);
static int simcoPingDefaultCallback(int con, int status);

static void *reader_thread(void *data);

#define MAX_CALLBACK_LIST 1024

callbackList callback_list[MAX_CALLBACK_LIST];

int highestFd;
static int thread_die;

static pthread_mutex_t callbackListMutex;
static pthread_t thread;

int nb_init(void)
{
  pthread_mutex_init(&callbackListMutex, NULL);
  if (pthread_create(&thread, NULL, reader_thread, 0) != 0)
    return 0;

  highestFd = 0;
  thread_die = 0;
  if (sessionEstablishCallback == NULL)
    sessionEstablishCallback = simcoSessionEstablishDefaultCallback;
  if (sessionAuthenticateCallback == NULL)
    sessionAuthenticateCallback = simcoSessionAuthenticateDefaultCallback;
  if (sessionTerminateCallback == NULL)
    sessionTerminateCallback = simcoSessionTerminateDefaultCallback;
  if (policyReserveRuleCallback == NULL)
    policyReserveRuleCallback = simcoPolicyReserveRuleDefaultCallback;
  if (policyEnableRuleCallback == NULL)
    policyEnableRuleCallback = simcoPolicyEnableRuleDefaultCallback;
  if (perAfterReservationCallback == NULL)
    perAfterReservationCallback = simcoPERAfterReservationDefaultCallback;
  if (pbrAfterReservationCallback == NULL)
    pbrAfterReservationCallback = simcoPBRAfterReservationDefaultCallback;
  if (policyDisableRuleCallback == NULL)
    policyDisableRuleCallback = simcoPolicyDisableRuleDefaultCallback;
  if (policyRuleLifetimeChangeCallback == NULL)
    policyRuleLifetimeChangeCallback = simcoPolicyRuleLifetimeChangeDefaultCallback;
  if (policyRuleStatusCallback == NULL)
    policyRuleStatusCallback = simcoPolicyRuleStatusDefaultCallback;
  if (policyRuleListCallback == NULL)
    policyRuleListCallback = simcoPolicyRuleListDefaultCallback;
  if (pingCallback == NULL)
    pingCallback = simcoPingDefaultCallback;
#ifdef EXTENDED_SIMCO
  nb_init_ext();
#endif
  return 1;
}

void nb_destroy(void)
{
  void *thread_result;
  thread_die = 1;
  pthread_join(thread, &thread_result);
  pthread_mutex_destroy(&callbackListMutex);
  return;
}

/**
 * Register callback function for notification response
 * @param i connection structure index
 * @param fn pointer to callback function
 */
void reg_notif_callback(int i, int (*fn)(simcoPacket_t *sp))
{
  notificationCallback = fn;
  register_callback(&conn[i], -1*i-1, 0, -999);
}

/**
 * Register callback function for ping response
 * @param fn pointer to callback function
 */
void reg_ping_callback(int (*fn)(int con, int status))
{
  pingCallback = fn;
}

/**
 * Register callback function for session establish response
 * @param fn pointer to callback function
 */
void reg_se_callback(int (*fn)(int con, int status))
{
  sessionEstablishCallback = fn;
}

/**
 * Register callback function for session authenticate response
 * @param fn pointer to callback function
 */
void reg_sa_callback(int (*fn)(int con, int status))
{
  sessionAuthenticateCallback = fn;
}

/**
 * Register callback function for session terminate response
 * @param fn pointer to callback function
 */
void reg_st_callback(int (*fn)(int con, int status))
{
  sessionTerminateCallback = fn;
}

/**
 * Register callback function for policy reserve rule response
 * @param fn pointer to callback function
 */
void reg_prr_callback(int (*fn)(int con, simcoPacket_t *sp))
{
  policyReserveRuleCallback = fn;
}

/**
 * Register callback function for policy enable rule response
 * @param fn pointer to callback function
 */
void reg_per_callback(int (*fn)(int con, simcoPacket_t *sp))
{
  policyEnableRuleCallback = fn;
}

/**
 * Register callback function for PER after reservation rule response
 * @param fn pointer to callback function
 */
void reg_pea_callback(int (*fn)(int con, simcoPacket_t *sp))
{
  perAfterReservationCallback = fn;
}

/**
 * Register callback function for PBR after reservation rule response
 * @param fn pointer to callback function
 */
void reg_pba_callback(int (*fn)(int con, simcoPacket_t *sp))
{
  pbrAfterReservationCallback = fn;
}

/**
 * Register callback function for policy disable rule response
 * @param fn pointer to callback function
 */
void reg_pdr_callback(int (*fn)(int con, simcoPacket_t *sp))
{
  policyDisableRuleCallback = fn;
}

/**
 * Register callback function for policy change lifetime response
 * @param fn pointer to callback function
 */
void reg_plc_callback(int (*fn)(int con, simcoPacket_t *sp))
{
  policyRuleLifetimeChangeCallback = fn;
}

/**
 * Register callback function for policy rule status response
 * @param fn pointer to callback function
 */
void reg_prs_callback(int (*fn)(int con, simcoPacket_t *sp))
{
  policyRuleStatusCallback = fn;
}

/**
 * Register callback function for policy rule list response
 * @param fn pointer to callback function
 */
void reg_prl_callback(int (*fn)(int con, int *rlist, int size))
{
  policyRuleListCallback = fn;
}

/**
 * For every successful request sent assign a callback to wait for response
 * for 5 seconds (after which it will expire)
 * @param con ssl connection
 * @param tid transaction Id
 * @param subtype sub-type of the request
 * @param timeout time (in seconds) in which the callback expires
 */
void register_callback(MCAgentCon *con, unsigned int tid, char subtype, int timeout)
{
  int index;
#ifdef WIN32
#else
  pthread_mutex_lock(&callbackListMutex);
#endif
  for (index = 0; index < MAX_CALLBACK_LIST; index++)
    {
      if (callback_list[index].tid == 0)
	break;
    }
  if (index == MAX_CALLBACK_LIST)
    return;
  callback_list[index].sock = con->sock;
  callback_list[index].tid = tid;
  callback_list[index].request_subtype = subtype;
  callback_list[index].ttl = timeout;
#ifdef WIN32
#else
  pthread_mutex_unlock(&callbackListMutex);
#endif
}

/**
 * Delete the \a index'th callback function.
 * @param index index of the callback function.
 */
void deregister_callback(int index)
{
#ifdef WIN32
#else
  pthread_mutex_lock(&callbackListMutex);
#endif
  memset((void*)&callback_list[index], 0, sizeof(callbackList));
#ifdef WIN32
#else
  pthread_mutex_unlock(&callbackListMutex);
#endif
}

/**
 * Continously waits if there is any response on the open connections.
 * When there is a response, it is parsed and an appropriate
 * callback is fired.
 */
static void *reader_thread(void *data)
{
  int i, index, tid = 0;
  fd_set readfds;
  struct timeval tv;
  char *resp_buffer;
  int status;

  for (;;)
    {
      if (thread_die)
        {
          for (index = 0; index < MAX_CALLBACK_LIST; index++)
            if (callback_list[index].tid > 0)
              {
                /* printf("waiting for callback tid:%d\n", callback_list[index].tid); */
                break;
              }
          if (index == MAX_CALLBACK_LIST)
            {
              /* printf("reader_thread exiting\n"); */
              break;
            }
        }

      tv.tv_sec = 1;
      tv.tv_usec = 0;

      FD_ZERO(&readfds);
      for (i = 0; i < MAX_CON; i++)
	{
	  if ((conn[i].sock != -1) && (conn[i].inpool != 0))
	    {
	      FD_SET(conn[i].sock, &readfds);
	    }
	}
      i = select(highestFd+1, (void *)&readfds, NULL, NULL, &tv);
      if (i < 0)
	{
#ifndef NO_SSL
	  BIO_printf(bio_err, "bad select %d\n", get_last_socket_error());
#else
	  perror("bad select");
#endif
	  continue;
	}
      if (i == 0)
	{
	  for (index = 0; index < MAX_CALLBACK_LIST; index++)
	    {
	      if (callback_list[index].ttl == -999)
		continue;
	      if (callback_list[index].tid == 0) 
		continue;
	      if (callback_list[index].ttl == 0)
		{
		  printf("callback for TID: %d expired\n", callback_list[index].tid);
		  deregister_callback(index);
		}
	      else
		{
		  callback_list[index].ttl--;
		}
	    }
	  continue;
	}
      for (i = 0; i < MAX_CON; i++)
	{
	  if ((conn[i].sock != -1) && (FD_ISSET(conn[i].sock, &readfds)))
	    {
	      if (conn[i].sbuf == NULL)
		conn[i].sbuf = (char*)realloc(conn[i].sbuf, MAX_READ_SIZE+conn[i].sbuf_offset);
	      status = rnMCAgentCommRead(&conn[i], conn[i].sbuf, &conn[i].sbuf_offset);
	      if (status > 0)
		{
		  tid = ntohl(*((int*)conn[i].sbuf+1));
		  for (index = 0; index < MAX_CALLBACK_LIST; index++)
		    {
		      if (callback_list[index].sock == conn[i].sock)
			{
			  resp_buffer = (char *)malloc(conn[i].sbuf_offset);
			  memcpy((void*)resp_buffer, (void*)conn[i].sbuf, conn[i].sbuf_offset);
			  if (conn[i].sbuf[0] == BSC_NTF_MSG)
			    {
			      parseNotification(i, resp_buffer, conn[i].sbuf_offset);
			    }
			  else if (callback_list[index].tid == tid)
			    {
			      if ((conn[i].sbuf[0] == BSC_POS_RPL_MSG) || (conn[i].sbuf[0] == BSC_NEG_RPL_MSG))
				{
				  if (callback_list[index].request_subtype == SUB_SE_MSG)
				    {
				      parseSessionEstablish(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_SA_MSG)
				    {
				      parseSessionAuthenticate(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_ST_MSG)
				    {
				      parseSessionTerminate(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PNG_MSG)
				    {
				      parsePing(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PRR_MSG)
				    {
				      parsePolicyReserveRule(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PER_MSG)
				    {
				      parsePolicyEnableRule(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PEA_MSG)
				    {
				      parsePERAfterReservation(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PBA_MSG)
				    {
				      parsePBRAfterReservation(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PDR_MSG)
				    {
				      parsePolicyDisableRule(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PLC_MSG)
				    {
				      parsePolicyRuleLifetimeChange(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PRD_MSG)
				    {
				      parsePolicyRuleLifetimeChange(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PS_MSG)
				    {
				      parsePolicyRuleStatus(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
				  else if (callback_list[index].request_subtype == SUB_PL_MSG)
				    {
				      parsePolicyRuleList(i, resp_buffer, conn[i].sbuf_offset);
				      deregister_callback(index);
				    }
#ifdef EXTENDED_SIMCO
				  reader_thread_parsing_contd(index, i, resp_buffer);
#endif
				}
			    }
			  free(resp_buffer);
			}
		    }
		  conn[i].sbuf_offset = 0;
		  free(conn[i].sbuf);
		  conn[i].sbuf = NULL;
		  /* memset((void*)conn[i].sbuf, 0, MAX_READ_SIZE); */
		  /*
		    if (c_debug) {
		    BIO_printf(bio_c_out,"---\nSSL handshake has read %ld bytes and written %ld bytes\n",
		    BIO_number_read(SSL_get_rbio(conn[i].sslcon)),
		    BIO_number_written(SSL_get_wbio(conn[i].sslcon)));
		    }
		  */
		}
	      else if (status < 0)
		{
		  midcomAgentDisconnect(&conn[i]);
		}
	    }
	}
    }
  return NULL;
}

/**
 * Builds a packet for simco session establish request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPSessionEstablish(MCAgentCon *con)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makeSessionEstablishRequest(request);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_SE_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco session authenticate request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @param username agent's username.
 * @param password agent's password.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPSessionAuthenticate(MCAgentCon *con, char *username, char *password)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;
  char str[512];

  strcpy(str, username);
  strcat(str, ":");
  strcat(str, password);

  req_len = makeSessionAuthenticateRequest(request, str);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_SA_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco session terminate request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPSessionTerminate(MCAgentCon *con)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makeSessionTerminateRequest(request);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_ST_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco ping request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPPing(MCAgentCon *con)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makePingRequest(request);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_PNG_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco policy reserve rule request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @param sp pointer to simco packet.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPPolicyReserveRule(MCAgentCon *con, simcoPacket_t *sp)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makePolicyReserveRuleRequest(request, sp);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_PRR_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco policy establish rule request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @param sp pointer to simco packet.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPPolicyEnableRule(MCAgentCon *con, simcoPacket_t *sp)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makePolicyEnableRuleRequest(request, sp);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_PER_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco PER after reservation request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @param sp pointer to simco packet.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPPERAfterReservation(MCAgentCon *con, simcoPacket_t *sp)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makePERAfterReservationRequest(request, sp);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_PEA_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco PBR after reservation request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @param sp pointer to simco packet.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPPBRAfterReservation(MCAgentCon *con, simcoPacket_t *sp)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makePBRAfterReservationRequest(request, sp);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_PBA_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco policy lifetime change request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @param sp pointer to simco packet.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPPolicyRuleLifetimeChange(MCAgentCon *con, simcoPacket_t *sp)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makePolicyRuleLifetimeChangeRequest(request, sp);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_PLC_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco policy rule status request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @param sp pointer to simco packet.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPPolicyRuleStatus(MCAgentCon *con, simcoPacket_t *sp)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makePolicyRuleStatusRequest(request, sp);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_PS_MSG, 5);
  return 0;
}

/**
 * Builds a packet for simco policy rule list request, sends the
 * packet over TCP/IP and registers a callback for the response.
 * @param con a pointer to MCAgenCon structure.
 * @return -1 if communication failed, 0 on success.
 */
int nb_mcAgentTCPPolicyRuleList(MCAgentCon *con)
{
  char request[SET_REQUEST_SIZE];
  int req_len = 0;
  int tid;

  req_len = makePolicyRuleListRequest(request);
  if ((tid = rnMCAgentCommWrite(con, request, req_len)) <= 0)
    return -1;
  register_callback(con, tid, SUB_PL_MSG, 5);
  return 0;
}

/**
 * Parses the notification response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parseNotification(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "notificationCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  (*notificationCallback)(sp);
  free(sp);
  return;
}

/**
 * Parses the session establish response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parseSessionEstablish(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "sessionEstablishCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*sessionEstablishCallback)(con, sp->status);
  free(sp);
  return;
}

/**
 * Parses the session autheticate response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parseSessionAuthenticate(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "sessionAuthenticateCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*sessionAuthenticateCallback)(con, sp->status);
  free(sp);
  return;
}

/**
 * Parses the session terminate response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parseSessionTerminate(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "sessionTerminateCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*sessionTerminateCallback)(con, sp->status);
  free(sp);
  return;
}

/**
 * Parses the ping response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parsePing(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "pingCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*pingCallback)(con, sp->status);
  free(sp);
  return;
}

/**
 * Parses the policy reserve rule response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parsePolicyReserveRule(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "policyReserveRuleCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*policyReserveRuleCallback)(con, sp);
  free(sp);
  return;
}

/**
 * Parses the policy enable rule response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parsePolicyEnableRule(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "policyEnableRuleCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*policyEnableRuleCallback)(con, sp);
  free(sp);
  return;
}

/**
 * Parses the PER after reservation rule response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parsePERAfterReservation(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "perAfterReservationCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*perAfterReservationCallback)(con, sp);
  free(sp);
  return;
}

/**
 * Parses the PBR after reservation rule response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parsePBRAfterReservation(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "pbrAfterReservationCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*pbrAfterReservationCallback)(con, sp);
  free(sp);
  return;
}

/**
 * Parses the policy disable rule response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parsePolicyDisableRule(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "policyDisableRuleCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*policyDisableRuleCallback)(con, sp);
  free(sp);
  return;
}

/**
 * Parses the policy rule lifetime change response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parsePolicyRuleLifetimeChange(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "policyRuleLifetimeChangeCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*policyRuleLifetimeChangeCallback)(con, sp);
  free(sp);
  return;
}

/**
 * Parses the policy rule status response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parsePolicyRuleStatus(int con, char *buffer, int size)
{
  simcoPacket_t *sp;
  sp = (simcoPacket_t*)malloc(sizeof(simcoPacket_t));
  if (sp == NULL)
    {
      fprintf(stderr, "policyRuleStatusCallback: Out of memory\n");
      return;
    }
  memset((void*)sp, 0, sizeof(simcoPacket_t));
  parseSIMCOPdu(sp, buffer, size);
  conn[con].error = sp->status;
  (*policyRuleStatusCallback)(con, sp);
  free(sp);
  return;
}

/**
 * Parses the policy rule list response.
 * @param con index to the connection structure.
 * @param buffer response buffer.
 * @param size buffer size.
 */
static void parsePolicyRuleList(int con, char *buffer, int size)
{
  int *rules, nrules = 128;
  rules = (int *)malloc(nrules*sizeof(int));
  if (rules == NULL)
    {
      fprintf(stderr, "policyRuleListCallback: Out of memory\n");
      return;
    }
  conn[con].error = parseSIMCORuleList(buffer, size, &rules, &nrules);
  (*policyRuleListCallback)(con, rules, nrules);
  free(rules);
  return;
}

static int simcoSessionEstablishDefaultCallback(int con, int status)
{
  fprintf(stderr, "=== Session Establish Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "Status:%d\n", status);
    }
  return 1;
}

static int simcoSessionAuthenticateDefaultCallback(int con, int status)
{
  fprintf(stderr, "=== Session Authenticate Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "Status:%d\n", status);
    }
  return 1;
}

static int simcoSessionTerminateDefaultCallback(int con, int status)
{
  fprintf(stderr, "=== Session Terminate Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "Status:%d\n", status);
    }
  return 1;
}

static int simcoPingDefaultCallback(int con, int status)
{
  fprintf(stderr, "=== Session Ping Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "Status:%d\n", status);
    }
  return 1;
}

static int simcoPolicyReserveRuleDefaultCallback(int con, simcoPacket_t *sp)
{
  fprintf(stderr, "=== Policy Reserve Rule Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "RuleId:%d GroupId:%d Lifetime:%d Outside_IP: %s Outside_Port:%d Status: %d\n",
	      sp->ruleId, sp->groupId, sp->lifetime, sp->outside_ip, sp->outside_port, sp->status);
    }
  return 1;
}

static int simcoPolicyEnableRuleDefaultCallback(int con, simcoPacket_t *sp)
{
  fprintf(stderr, "=== Policy Enable Rule Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "RuleId:%d GroupId:%d Lifetime:%d Outside_IP: %s Outside_Port:%d Status: %d\n",
	      sp->ruleId, sp->groupId, sp->lifetime, sp->outside_ip, sp->outside_port, sp->status);
    }
  return 1;
}

static int simcoPERAfterReservationDefaultCallback(int con, simcoPacket_t *sp)
{
  fprintf(stderr, "=== PER After Reservation Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "RuleId:%d GroupId:%d Lifetime:%d Outside_IP: %s Outside_Port:%d Status: %d\n",
	      sp->ruleId, sp->groupId, sp->lifetime, sp->outside_ip, sp->outside_port, sp->status);
    }
  return 1;
}

static int simcoPBRAfterReservationDefaultCallback(int con, simcoPacket_t *sp)
{
  fprintf(stderr, "=== PBR After Reservation Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "RuleId:%d GroupId:%d Lifetime:%d Outside_IP: %s Outside_Port:%d Status: %d\n",
	      sp->ruleId, sp->groupId, sp->lifetime, sp->outside_ip, sp->outside_port, sp->status);
    }
  return 1;
}

static int simcoPolicyDisableRuleDefaultCallback(int con, simcoPacket_t *sp)
{
  fprintf(stderr, "=== Policy Enable Rule Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "RuleId:%d GroupId:%d Lifetime:%d Outside_IP: %s Outside_Port:%d Status: %d\n",
	      sp->ruleId, sp->groupId, sp->lifetime, sp->outside_ip, sp->outside_port, sp->status);
    }
  return 1;
}

static int simcoPolicyRuleLifetimeChangeDefaultCallback(int con, simcoPacket_t *sp)
{
  fprintf(stderr, "=== Policy Rule Lifetime Change Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "RuleId:%d Lifetime:%d Status: %d\n", sp->ruleId, sp->lifetime, sp->status);
    }
  return 1;
}

static int simcoPolicyRuleStatusDefaultCallback(int con, simcoPacket_t *sp)
{
  fprintf(stderr, "=== Policy Rule Status Callback ===\n");
  if (c_debug)
    {
      fprintf(stderr, "RuleId:%d GroupId:%d Lifetime:%d Outside_IP: %s Outside_Port:%d Status: %d\n",
	      sp->ruleId, sp->groupId, sp->lifetime, sp->outside_ip, sp->outside_port, sp->status);
    }
  return 1;
}

static int simcoPolicyRuleListDefaultCallback(int con, int *rulelist, int size)
{
  int i;
  fprintf(stderr, "=== Policy Rule List Callback ===\n");
  if (c_debug)
    {
      for (i = 0; i < size; i++)
	fprintf(stderr, "Rule %d\n", rulelist[i]);
    }
  return 1;
}

int print_callback_list(void)
{
  int index;
  for (index = 0; index < 10; index++)
    {
      printf("%d. Callback tid:%d ttl:%d subtype:%d sock:%d\n", index, callback_list[index].tid, callback_list[index].ttl, callback_list[index].request_subtype, callback_list[index].sock);
    }
  return 1;
}
