/* $Id: pjnath_client.c,v 1.14 2008/08/10 02:19:06 gg2265 Exp $ */
/* 
 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include "pjnath_client.h"

/**
 * P2PP ISSUES
 * 1. logging (separate file for STUN/TURN/ICE
 * 2. register callbacks to pass on data
 * 3. What is peer address
 * 4. Will stun client receive data ? (It should not)
 * 5. Interface to pass on STUN packets to client.
 * 6. 
 **/

#define THIS_FILE	"pjnath_client.c"
#define BANDWIDTH	64		      /* -1 to disable */
#define LIFETIME	600		      /* -1 to disable */
#define REQ_TRANSPORT	-1		  /* 0: udp, 1: tcp, -1: disable */
#define REQ_PORT_PROPS	-1		/* -1 to disable */
#define REQ_IP		0		        /* IP address string */

//#define OPTIONS		PJ_STUN_NO_AUTHENTICATE
#define OPTIONS		0

//GLOBALS 
//struct client_options gClientOptions;

void (*app_nat_detect_cb)(const pj_stun_nat_type result) = NULL;
void (*app_bind_cb)(const pj_sockaddr_in*) = NULL;
void (*app_allocate_cb)(const pj_sockaddr_in*) = NULL;
void (*app_send_sad_cb)(const int result) = NULL;
void (*app_send_ind_cb)(const int result) = NULL;
void (*app_send_raw_data_to_srv_cb)(const int result) = NULL;
void (*app_send_raw_data_to_relay_cb)(const int result) = NULL;

static pj_status_t parse_addr(const char *input, pj_sockaddr_in *addr);
static     pj_thread_t		*gpThread=NULL;


static void my_perror(const char *title, pj_status_t status) {
  char errmsg[PJ_ERR_MSG_SIZE];
  pj_strerror(status, errmsg, sizeof(errmsg));

  PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg));
}//my_perror

/*
 * Handle incoming Binding request.
 * This function is called by client_handle_stun_msg() below.
 */
static pj_status_t handle_binding_req(pj_stun_session *session,
				      const pj_stun_msg *msg,
				      const pj_sockaddr_t *src_addr,
				      unsigned src_addr_len)
{
  pj_stun_tx_data *tdata;
  pj_status_t status;

  /* Create response */
  status = pj_stun_session_create_res(session, msg, 0, NULL,
				      &tdata);
  if (status != PJ_SUCCESS)
    return status;

  /* Create MAPPED-ADDRESS attribute */
  pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
				PJ_STUN_ATTR_MAPPED_ADDR,
				PJ_FALSE,
				src_addr, src_addr_len);

  /* On the presence of magic, create XOR-MAPPED-ADDRESS attribute */
  if (msg->hdr.magic == PJ_STUN_MAGIC) {
    status =
      pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
				    PJ_STUN_ATTR_XOR_MAPPED_ADDR,
				    PJ_TRUE,
				    src_addr, src_addr_len);
  }

  /* Send */
  status = pj_stun_session_send_msg(session, PJ_TRUE,
				    src_addr, src_addr_len, tdata);

  return status;
}

pj_status_t on_send_msg(pj_stun_session *sess,
			       const void *pkt,
			       pj_size_t pkt_size,
			       const pj_sockaddr_t *srv_addr,
			       unsigned addr_len)
//			       pj_sock_t src_sock)
{
    struct client_options *g;
    pj_ssize_t len;
    pj_status_t status;
    g=(ClientOptions*)pj_stun_session_get_user_data(sess);
    //    g= (struct client_options*)sess->user_data;
    
    if(!g){
      return -1;
    }
	if(g->sock==-1) {
		g->pktlen=pkt_size;
		memcpy(g->pkt, pkt, pkt_size);
		status=PJ_SUCCESS;
	} else {
		len = (pj_ssize_t)pkt_size;
		//    printf("send using sock %d\n", (int) src_sock);
		status = pj_sock_sendto(g->sock, pkt, &len, 0, srv_addr, addr_len);
	}

    if (status != PJ_SUCCESS)
      my_perror("Error sending packet", status);

    return status;
}

void on_request_complete(pj_stun_session *sess,
			        pj_status_t status,
			        pj_stun_tx_data *tdata,
			        const pj_stun_msg *response,
				const pj_sockaddr_t *src_addr,
				unsigned src_addr_len)
{
    ClientOptions *g=(ClientOptions*)pj_stun_session_get_user_data(sess);

    PJ_UNUSED_ARG(src_addr);
    PJ_UNUSED_ARG(src_addr_len);

    PJ_LOG(3,(THIS_FILE, "on request complete"));

    if(!g){
      return;
    }

    if (status == PJ_SUCCESS) {
        g->response_type = response->hdr.type;
	switch (response->hdr.type) {
	case PJ_STUN_ALLOCATE_RESPONSE:
	    {
		pj_stun_relay_addr_attr *ar;
		pj_stun_lifetime_attr *al;

		al = (pj_stun_lifetime_attr*)
		     pj_stun_msg_find_attr(response, 
					   PJ_STUN_ATTR_LIFETIME, 0);
		if (!al) {
		    PJ_LOG(1,(THIS_FILE, "Error: LIFETIME attribute not present"));
		    return;
		}

		ar = (pj_stun_relay_addr_attr*)
		     pj_stun_msg_find_attr(response, 
					   PJ_STUN_ATTR_RELAY_ADDR, 0);
		if (ar) {
		    pj_memcpy(&g->response_addr, &ar->sockaddr.ipv4,
			      sizeof(pj_sockaddr_in));
		    PJ_LOG(3,(THIS_FILE, "Relay address is %s:%d",
			      pj_inet_ntoa(g->response_addr.sin_addr),
			      (int)pj_ntohs(g->response_addr.sin_port)));
		} else {
		    pj_memset(&g->response_addr, 0, sizeof(g->response_addr));
		}
		/*
		if(app_allocate_cb){
		  //  app_allocate_cb(&g->relay_addr);
		}
		*/
		if (al->value == 0) {
		    PJ_LOG(3,(THIS_FILE, "Relay deallocated"));
		}
	    }
	    //break;
	case PJ_STUN_BINDING_RESPONSE:
	  {
	    pj_stun_mapped_addr_attr *ma;

	    PJ_LOG(3,(THIS_FILE, "Binding response1"));
	    
	    ma = (pj_stun_mapped_addr_attr*)
	      pj_stun_msg_find_attr(response, 
				    PJ_STUN_ATTR_MAPPED_ADDR, 0);

	    
	    if (ma) {
	      pj_memcpy(&g->binding_addr, &ma->sockaddr.ipv4,
			sizeof(pj_sockaddr_in));
	      PJ_LOG(3,(THIS_FILE, "Binding response is %s:%d",
			pj_inet_ntoa(g->binding_addr.sin_addr),
			(int)pj_ntohs(g->binding_addr.sin_port)));
	    } else {
	      pj_memset(&g->binding_addr, 0, sizeof(g->binding_addr));
	    }
	    /*
	    if(g->ma){
	      PJ_LOG(3,(THIS_FILE, "Binding response2"));
	      //	      app_bind_cb(&g->ma->sockaddr.ipv4);
	    }
	    */
	  }
	  break;
	case PJ_STUN_BINDING_ERROR_RESPONSE:
	  break;
	case PJ_STUN_ALLOCATE_ERROR_RESPONSE:
	  break;
	case PJ_STUN_SET_ACTIVE_DESTINATION_RESPONSE:
	  break;
	case PJ_STUN_SET_ACTIVE_DESTINATION_ERROR_RESPONSE:
	  break;

	default:
	  break;
	}
    } else {
	my_perror("Client transaction error", status);
    }
}

pj_status_t on_receive_indication(pj_stun_session *sess,
			    const pj_uint8_t *pkt,
			    unsigned pkt_len,
			    const pj_stun_msg *indication,
			    const pj_sockaddr_t *src_addr,
				unsigned src_addr_len) {

  pj_stun_remote_addr_attr *a_raddr;
  pj_stun_data_attr *a_data;
  
  //get clientoptions object from stun session
  ClientOptions *g=(ClientOptions*)pj_stun_session_get_user_data(sess);

  //set response for indication type
  g->response_type =indication->hdr.type;
  
  switch(indication->hdr.type) {

  case PJ_STUN_SEND_INDICATION:
	  //Handle send indication
  case PJ_STUN_DATA_INDICATION:
    
    /* Get the DATA attribute */
    a_data = (pj_stun_data_attr*)
      pj_stun_msg_find_attr(indication, PJ_STUN_ATTR_DATA, 0);    
    if (a_data) {
      //g->data=a_data->data;
      memcpy(g->data, a_data->data, a_data->length);
      g->datalen=a_data->length;
      g->data[a_data->length] = 0;
    }

    /* Get REMOTE-ADDRESS attribute */
    a_raddr = (pj_stun_remote_addr_attr*)
        pj_stun_msg_find_attr(indication, PJ_STUN_ATTR_REMOTE_ADDR, 0);
    if (!a_raddr) {
      /* REMOTE-ADDRESS not present, discard packet */
      return PJ_SUCCESS;

    } else if (a_raddr->sockaddr.addr.sa_family != pj_AF_INET()) {
      /* REMOTE-ADDRESS present but not IPv4, discard packet */
      return PJ_SUCCESS;

    } else {
      //store remote addr for sending (THIS IS A HACK FIXME)
      memcpy( &g->remote_addr, &a_raddr->sockaddr.ipv4, sizeof(a_raddr->sockaddr.ipv4)); 
    }

    //pj_stun_msg_dump(indication, (char*)g->data, sizeof(g->data), NULL);	
    break;
  default:
	  break;
  }//end switch
  return PJ_SUCCESS;

}//on_receive_indication

static pj_status_t on_rx_request(pj_stun_session *sess,
					 const pj_uint8_t *pkt,
					 unsigned pkt_len,
					 const pj_stun_msg *msg,
					 const pj_sockaddr_t *src_addr,
					 unsigned src_addr_len)
{
  if (msg->hdr.type == PJ_STUN_BINDING_REQUEST) {
    return handle_binding_req(sess, msg, src_addr, src_addr_len);
  }

  return PJ_SUCCESS;
}//on_rx_request

static int worker_thread(void *unused) {
  ClientOptions *g= (ClientOptions*)unused;

  while (!g->quit) {
    pj_time_val timeout =  {0, 50};
    
    pj_timer_heap_poll(g->th, NULL);
    pj_ioqueue_poll(g->stun_config.ioqueue, &timeout);
  }

  return 0;
}//worker_thread

/*
static int listner_thread(void *unused)
{
    pj_sock_t listen_sock = *(pj_sock_t*)unused;
    pj_uint8_t buffer[512];
    pj_ssize_t len;
    pj_sockaddr_in addr;
    int addrlen;
    pj_status_t rc;
    
    len = sizeof(buffer);
    addrlen = sizeof(addr);
    
    rc = pj_sock_recvfrom(listen_sock, buffer, &len, 0, &addr, &addrlen);
    if (rc != PJ_SUCCESS || len <= 0)
      return; 
    
    if (pj_stun_msg_check(buffer, len, PJ_STUN_IS_DATAGRAM)==PJ_SUCCESS) {
      rc = pj_stun_session_on_rx_pkt(g.sess, buffer, len, 
				     OPTIONS, 
				     NULL, &addr, addrlen);
      if (rc != PJ_SUCCESS)
	my_perror("Error processing packet", rc);
      
    } else {
      buffer[len] = '\0';
      PJ_LOG(3,(THIS_FILE, "Received data on client sock: %s", (char*)buffer));
    }
    return 0;
}
*/

int init_pjnath() {
  pj_status_t status;
  pj_log_set_level(0);
  status=pj_init();
  status=pjlib_util_init();
  status=pjnath_init();

  return status;
}//init_pjnath


int change_stun_srv(ClientOptions *g, char* srv_ip, char* srv_port) {
  pj_str_t s;
  pj_uint16_t port;
  pj_status_t status;

  if (srv_port) {
	  port = (pj_uint16_t) atoi(srv_port);
  }	else {
    port = PJ_STUN_PORT;
  }

	status = pj_sockaddr_in_init(&g->srv_addr, pj_cstr(&s, srv_ip), port);

	return status;
}

int init_client(ClientOptions *g) {
  pj_stun_session_cb stun_cb;
  pj_status_t status;

  if(g->sock ==PJ_INVALID_SOCKET){
    PJ_LOG(3,(THIS_FILE, "Invalid socket\n"));
    return 0;
  }
  
  pj_caching_pool_init(&g->cp, &pj_pool_factory_default_policy, 0);

  if (g->ch_srv_addr) {
	  pj_str_t s;
	  pj_uint16_t port;

    if (g->ch_srv_port) {
      port = (pj_uint16_t) atoi(g->ch_srv_port);
    } else {
      port = PJ_STUN_PORT;
    }

	  status = pj_sockaddr_in_init(&g->srv_addr, pj_cstr(&s, g->ch_srv_addr), port);
    if (status != PJ_SUCCESS) {
      my_perror("Invalid address", status);
      return status;
    }

	    //printf("Destination address set to %s:%d\n", g->ch_srv_addr, (int)port);
  } else {
    //printf("Error: address must be specified\n");
	  return PJ_EINVAL;
  }

  g->pool = pj_pool_create(&g->cp.factory, NULL, 1000, 1000, NULL);

  status = pj_timer_heap_create(g->pool, 1000, &g->th);
  pj_assert(status == PJ_SUCCESS);

  pj_stun_config_init(&g->stun_config, &g->cp.factory, 0, NULL, g->th);
  pj_assert(status == PJ_SUCCESS);

  /**********
  status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &g->peer_sock);
  pj_assert(status == PJ_SUCCESS);

  status = pj_sock_bind_in(g->peer_sock, 0, 0);
  pj_assert(status == PJ_SUCCESS);

  status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &g->sock);
  pj_assert(status == PJ_SUCCESS);

  status = pj_sockaddr_in_init(&addr, NULL, 0);
  pj_assert(status == PJ_SUCCESS);

  addr.sin_port = pj_htons((pj_uint16_t)LOCAL_PORT);
  status = pj_sock_bind(g->sock, &addr, sizeof(addr));
  pj_assert(status == PJ_SUCCESS);

  len = sizeof(addr);
  status = pj_sock_getsockname(g->sock, &addr, &len);
  pj_assert(status == PJ_SUCCESS);

  PJ_LOG(3,(THIS_FILE, "Listening on port %d", (int)pj_ntohs(addr.sin_port)));

  len = sizeof(g->peer_addr);
  status = pj_sock_getsockname(g->peer_sock, &g->peer_addr, &len);
  if (g->peer_addr.sin_addr.s_addr == 0)
  pj_gethostip(&g->peer_addr.sin_addr);

  PJ_LOG(3,(THIS_FILE, "Peer is on port %d", (int)pj_ntohs(g->peer_addr.sin_port)));
  ************************/

  pj_memset(&stun_cb, 0, sizeof(stun_cb));
  stun_cb.on_send_msg = &on_send_msg;
  stun_cb.on_request_complete = &on_request_complete;
  stun_cb.on_rx_indication = &on_receive_indication;
  stun_cb.on_rx_request = &on_rx_request;

  status = pj_stun_session_create(&g->stun_config, NULL, &stun_cb, 
	      g->use_fingerprint!=0, &g->sess);
  pj_assert(status == PJ_SUCCESS);

  if (g->user_name) {
    pj_stun_auth_cred cred;

    pj_bzero(&cred, sizeof(cred));

    cred.type = PJ_STUN_AUTH_CRED_STATIC;
    cred.data.static_cred.realm = pj_str(g->realm);
    cred.data.static_cred.username = pj_str(g->user_name);
    cred.data.static_cred.data_type = 0;
    cred.data.static_cred.data = pj_str(g->password);
    cred.data.static_cred.nonce = pj_str(g->nonce);

    pj_stun_session_set_credential(g->sess, &cred);
    //TEMP: printf commented
    //puts("Session credential set");
  } else {
    //TEMP: printf commented
	  //puts("Credential not set");
  }
  
  //store user data P2PP
  pj_stun_session_set_user_data(g->sess,(void*)g);

  /*    if (g->peer_addr) {
	if (parse_addr(g->peer_addr, &g->peer_addr)!=PJ_SUCCESS)
	    return -1;
	}*/

  status = pj_ioqueue_create(g->pool, 16, &g->stun_config.ioqueue);

  //initialize ice
  init_ice(g);
  g->rcand_cnt=0;

  if (status != PJ_SUCCESS) {
	  return status;
  }

  if(!gpThread) {
    status = pj_thread_create(g->pool, "stun", &worker_thread, (void*)g, 
          0, 0, &gpThread);
    
    if (status != PJ_SUCCESS) {
      return status;
    }
  }//end if
  
  return PJ_SUCCESS;

}//init_client


int shutdown_client(ClientOptions* g) {
  if (g->sess) {
    pj_stun_session_destroy(g->sess);
  }  
  
  if (g->th) {
    pj_timer_heap_destroy(g->th);
  }

  if (g->pool) {
    pj_pool_release(g->pool);
  }

  pj_pool_factory_dump(&g->cp.factory, PJ_TRUE);
  pj_caching_pool_destroy(&g->cp);

  return PJ_SUCCESS;
}//shutdown_client


void send_bind_request(int sock, ClientOptions* g) {
  pj_stun_tx_data *tdata;
  pj_status_t rc;

  g->sock = (pj_sock_t)sock;
  //    app_bind_cb = cb;

  rc = pj_stun_session_create_req(g->sess, PJ_STUN_BINDING_REQUEST, 
		      PJ_STUN_MAGIC, NULL, &tdata);
  pj_assert(rc == PJ_SUCCESS);

  rc = pj_stun_session_send_msg(g->sess, PJ_FALSE, 
		    &g->srv_addr, sizeof(g->srv_addr),
		    tdata);
  if (rc != PJ_SUCCESS) {
    my_perror("Error sending STUN request", rc);
  }
  /*
  rc = pj_thread_create(g->pool, "stun", &listner_thread, 
	    (void*)&g->sock[g->sock_count], 0, 0, &g->thread);
  if (rc != PJ_SUCCESS)
    my_perror("Error creating thread", rc);
  g->sock_count++;
  g->sock_count=g->sock_count%10;
  */
}//send_bind_request


void send_allocate_request(int sock, pj_bool_t allocate, ClientOptions* g) {
  pj_stun_tx_data *tdata;
  pj_status_t rc;

  g->sock = (pj_sock_t)sock;
  //    app_allocate_cb = cb;

  rc = pj_stun_session_create_req(g->sess, PJ_STUN_ALLOCATE_REQUEST, 
		      PJ_STUN_MAGIC, NULL, &tdata);
  pj_assert(rc == PJ_SUCCESS);


  if (BANDWIDTH != -1) {
    pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, 
      PJ_STUN_ATTR_BANDWIDTH, BANDWIDTH);
  }

  if (!allocate) {
	  pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, 
      PJ_STUN_ATTR_LIFETIME, 0);

  } else {
    if (LIFETIME != -1) {
	    pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, 
        PJ_STUN_ATTR_LIFETIME, LIFETIME);
    }

    if (REQ_TRANSPORT != -1) {
      pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, 
        PJ_STUN_ATTR_REQ_TRANSPORT, REQ_TRANSPORT);
    }

    if (REQ_PORT_PROPS != -1) {
        pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg, 
			          PJ_STUN_ATTR_REQ_PORT_PROPS, REQ_PORT_PROPS);
    }

    if (REQ_IP) {
      pj_sockaddr_in addr;
      pj_str_t tmp;

      pj_sockaddr_in_init(&addr, pj_cstr(&tmp, REQ_IP), 0);
      pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
        PJ_STUN_ATTR_REQ_IP, PJ_FALSE, &addr, sizeof(addr));
    }
  }//end else
    
  //    printf("send using sock1 %d\n", (int)sock);
  rc = pj_stun_session_send_msg(g->sess, PJ_FALSE, 
        &g->srv_addr, sizeof(g->srv_addr),  tdata);
  pj_assert(rc == PJ_SUCCESS);
  /*
  rc = pj_thread_create(g->pool, "stun", &listner_thread, 
    (void*)&g->sock[g->sock_count], 0, 0, &g->thread);
  if (rc != PJ_SUCCESS)
  my_perror("Error creating thread", rc);
  g->sock_count++;
  g->sock_count=g->sock_count%10;
  */
}//send_allocate_request


void send_sad_request(int sock, pj_bool_t set, char *active_dest_addr,
                      ClientOptions* g) {
  pj_stun_tx_data *tdata;
  pj_status_t rc;
  pj_sockaddr_in	 ad_addr;


  g->sock = (pj_sock_t)sock;
  //    app_send_sad_cb = cb;


  parse_addr(active_dest_addr, &ad_addr);

  rc = pj_stun_session_create_req(g->sess, 
		      PJ_STUN_SET_ACTIVE_DESTINATION_REQUEST, 
		      PJ_STUN_MAGIC, NULL, &tdata);
  pj_assert(rc == PJ_SUCCESS);

  if (set) {
	  pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
				     PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
				     &ad_addr, sizeof(ad_addr));
  }

  rc = pj_stun_session_send_msg(g->sess, PJ_FALSE, 
			  &g->srv_addr, sizeof(g->srv_addr),
			  tdata);
  pj_assert(rc == PJ_SUCCESS);
  /*
  rc = pj_thread_create(g->pool, "stun", &listner_thread, 
		  (void*)&g->sock[g->sock_count], 0, 0, &g->thread);
  if (rc != PJ_SUCCESS)
    my_perror("Error creating thread", rc);

  g->sock_count++;
  g->sock_count=g->sock_count%10;
  */

}//send_sad_request

void create_send_ind(ClientOptions* g, char *buf, int *len) {
  pj_stun_tx_data *tdata;
  pj_status_t rc;
  int oldsock= g->sock;
  //this is must
  g->sock = -1;  
  //    app_send_ind_cb = cb;

  if (g->peer_addr.sin_addr.s_addr == 0 ||
    g->peer_addr.sin_port == 0) {
    puts("Error: peer address is not set");
    return;
  }

  rc = pj_stun_session_create_ind(g->sess, PJ_STUN_SEND_INDICATION, &tdata);
  pj_assert(rc == PJ_SUCCESS);

  pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
		    PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
		    &g->remote_addr, sizeof(g->remote_addr));
  pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
		  PJ_STUN_ATTR_DATA, (pj_uint8_t*)buf, *len);

  rc = pj_stun_session_send_msg(g->sess, PJ_FALSE, 
		    &g->srv_addr, sizeof(g->srv_addr),
		    tdata);
  pj_assert(rc == PJ_SUCCESS);

  g->sock=oldsock;
}//create_send_ind


void send_raw_data_to_srv(int sock, ClientOptions* g) {
  pj_ssize_t len;

  if (g->srv_addr.sin_addr.s_addr == 0 ||
	  g->srv_addr.sin_port == 0) {
	  puts("Error: server address is not set");
	  return;
  }

  len = (pj_ssize_t)strlen(g->data);
  if (len==0) {
	  puts("Error: data is not set");
	  return;
  }

  pj_sock_sendto(sock, g->data, &len, 0, &g->srv_addr, sizeof(g->srv_addr));

}//send_raw_data_to_srv

void send_raw_data_to_relay(int sock, char *relay_address,
			    ClientOptions* g) {
  pj_ssize_t len;
  pj_sockaddr_in	 relay_addr;

  //    g->sock = (pj_sock_t)sock;
  //    app_send_raw_data_to_relay_cb = cb;

  parse_addr(relay_address, &relay_addr);

  len = (pj_ssize_t)strlen(g->data);
  pj_sock_sendto(sock, g->data, &len, 0, &relay_addr, sizeof(relay_addr));

}//send_raw_data_to_relay


static pj_status_t parse_addr(const char *input,
			      pj_sockaddr_in *addr) {
  const char *pos;
  pj_str_t ip;
  pj_uint16_t port;
  pj_sockaddr_in tmp_addr;

  pos = pj_ansi_strchr(input, ':');
  if (pos==NULL) {
    puts("Invalid format");
    return -1;
  }

  ip.ptr = (char*)input;
  ip.slen = pos - input;
  port = (pj_uint16_t)atoi(pos+1);

  if (port==0) {
	  puts("Invalid port");
	  return -1;
  }

  if (pj_sockaddr_in_init(&tmp_addr, &ip, port)!=PJ_SUCCESS) {
    puts("Invalid address");
    return -1;
  }

  pj_memcpy(addr, &tmp_addr, sizeof(tmp_addr));

  return PJ_SUCCESS;

}//parse_addr


static void nat_detect_cb(void *user_data,
			  const pj_stun_nat_detect_result *res)
{
    ClientOptions *g= (ClientOptions*)user_data;
    g->detect_result = res->status;
    g->nat_type = res->nat_type;

    if (res->status == PJ_SUCCESS) {
	PJ_LOG(3,(THIS_FILE, "NAT successfully detected as %s", res->nat_type_name));
    } else {
	PJ_LOG(2,(THIS_FILE, "Error detecting NAT type: %s", res->status_text));
    }
    /*
    if(app_nat_detect_cb){
      app_nat_detect_cb(res->nat_type);
    }
    */
	g->quit=1;
}

pj_stun_nat_type perform_detection(ClientOptions *g)
{
    pj_status_t status;

    //    app_nat_detect_cb = cb;
    
    g->detect_result = PJ_EPENDING;
    status = pj_stun_detect_nat_type(&g->srv_addr, &g->stun_config, (void*)g, 
				     &nat_detect_cb);
    if (status != PJ_SUCCESS)
	return status;

    while (g->detect_result == PJ_EPENDING)
	pj_thread_sleep(100);

    status = g->detect_result;
	pj_thread_join(gpThread);

    return status;
}

/*
void set_config(struct client_options *client_opt){
  memcpy(&gClientOptions, client_opt, sizeof(struct client_options));
}
*/

/*
 *stun_msg_check()
 *Checks if the packet is a STUN packet
 *1 if a STUN packet
 *0 if not a STUN packet
 */
int stun_msg_check(unsigned char* buffer, int len) {
  if (pj_stun_msg_check((pj_int8_t*)buffer, len, PJ_STUN_IS_DATAGRAM)==PJ_SUCCESS) {
    return 1;
  }
  return 0;  
}//stun_msg_check



int parse_stun_msg(ClientOptions *g,
		   unsigned char* buffer, int len, 
		   pj_sockaddr_in *addr, int addrlen,  
		   pj_stun_msg_type *msg_type,
		   int *result, pj_sockaddr_in *result_addr) {

  pj_status_t rc;

  if (pj_stun_msg_check((pj_int8_t*)buffer, len, 
                        PJ_STUN_IS_DATAGRAM)!=PJ_SUCCESS) {
    return 0;
  }

  rc = pj_stun_session_on_rx_pkt(g->sess, buffer, len, OPTIONS, 
          NULL, addr, addrlen);

  if (rc != PJ_SUCCESS){
    my_perror("Error processing packet", rc);
    return 0;
  }

  memcpy(msg_type, &g->response_type, sizeof(pj_stun_msg_type));
  *result = 1;
  memcpy(result_addr, &g->response_addr, sizeof(pj_sockaddr_in));

  return 1;
}//parse_stun_msg

