Index: pjproject/trunk/pjnath/src/pjnath/turn_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 1929)
+++ pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 1988)
@@ -30,6 +30,7 @@
 #include <pj/sock.h>
 
-#define MAX_SRV_CNT	    4
-#define REFRESH_SEC_BEFORE  60
+#define PJ_TURN_CHANNEL_MIN	    0x4000
+#define PJ_TURN_CHANNEL_MAX	    0xFFFE  /* inclusive */
+#define PJ_TURN_PEER_HTABLE_SIZE    8
 
 static const char *state_names[] = 
@@ -67,4 +68,5 @@
     pj_turn_session_cb	 cb;
     void		*user_data;
+    pj_stun_config	 stun_cfg;
 
     pj_lock_t		*lock;
@@ -72,4 +74,5 @@
 
     pj_turn_state_t	 state;
+    pj_status_t		 last_status;
     pj_bool_t		 pending_destroy;
     pj_bool_t		 destroy_notified;
@@ -88,5 +91,5 @@
 
     pj_uint16_t		 af;
-    pj_turn_tp_type	 tp_type;
+    pj_turn_tp_type	 conn_type;
     pj_uint16_t		 srv_addr_cnt;
     pj_sockaddr		*srv_addr_list;
@@ -96,4 +99,5 @@
     pj_turn_alloc_param	 alloc_param;
 
+    pj_sockaddr		 mapped_addr;
     pj_sockaddr		 relay_addr;
 
@@ -177,11 +181,11 @@
  * Create TURN client session.
  */
-PJ_DEF(pj_status_t) pj_turn_session_create( pj_stun_config *cfg,
+PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
 					    const char *name,
 					    int af,
-					    pj_turn_tp_type tp_type,
+					    pj_turn_tp_type conn_type,
 					    const pj_turn_session_cb *cb,
+					    unsigned options,
 					    void *user_data,
-					    unsigned options,
 					    pj_turn_session **p_sess)
 {
@@ -207,8 +211,11 @@
     sess->timer_heap = cfg->timer_heap;
     sess->af = (pj_uint16_t)af;
-    sess->tp_type = tp_type;
+    sess->conn_type = conn_type;
     sess->ka_interval = PJ_TURN_KEEP_ALIVE_SEC;
     sess->user_data = user_data;
     sess->next_ch = PJ_TURN_CHANNEL_MIN;
+
+    /* Copy STUN session */
+    pj_memcpy(&sess->stun_cfg, cfg, sizeof(pj_stun_config));
 
     /* Copy callback */
@@ -234,6 +241,6 @@
     stun_cb.on_request_complete = &stun_on_request_complete;
     stun_cb.on_rx_indication = &stun_on_rx_indication;
-    status = pj_stun_session_create(cfg, sess->obj_name, &stun_cb, PJ_FALSE,
-				    &sess->stun);
+    status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb,
+				    PJ_FALSE, &sess->stun);
     if (status != PJ_SUCCESS) {
 	do_destroy(sess);
@@ -334,7 +341,8 @@
 	break;
     case PJ_TURN_STATE_RESOLVING:
-	pj_assert(sess->dns_async != NULL);
-	pj_dns_resolver_cancel_query(sess->dns_async, PJ_FALSE);
-	sess->dns_async = NULL;
+	if (sess->dns_async != NULL) {
+	    pj_dns_resolver_cancel_query(sess->dns_async, PJ_FALSE);
+	    sess->dns_async = NULL;
+	}
 	break;
     case PJ_TURN_STATE_RESOLVED:
@@ -366,4 +374,6 @@
 	pj_time_val delay = {0, 0};
 
+	set_state(sess, PJ_TURN_STATE_DESTROYING);
+
 	if (sess->timer.id != TIMER_NONE) {
 	    pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
@@ -371,6 +381,4 @@
 	}
 
-	set_state(sess, PJ_TURN_STATE_DESTROYING);
-
 	sess->timer.id = TIMER_DESTROY;
 	pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
@@ -401,4 +409,6 @@
 PJ_DEF(pj_status_t) pj_turn_session_destroy( pj_turn_session *sess)
 {
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
     set_state(sess, PJ_TURN_STATE_DEALLOCATED);
     sess_shutdown(sess, PJ_SUCCESS);
@@ -420,6 +430,7 @@
 
     info->state = sess->state;
-    info->tp_type = sess->tp_type;
+    info->conn_type = sess->conn_type;
     info->lifetime = sess->expiry.sec - now.sec;
+    info->last_status = sess->last_status;
 
     if (sess->srv_addr)
@@ -428,5 +439,8 @@
 	pj_bzero(&info->server, sizeof(info->server));
 
-    pj_memcpy(&info->relay_addr, &sess->relay_addr, sizeof(sess->relay_addr));
+    pj_memcpy(&info->mapped_addr, &sess->mapped_addr, 
+	      sizeof(sess->mapped_addr));
+    pj_memcpy(&info->relay_addr, &sess->relay_addr, 
+	      sizeof(sess->relay_addr));
 
     return PJ_SUCCESS;
@@ -451,4 +465,17 @@
 {
     return sess->user_data;
+}
+
+
+/*
+ * Configure message logging. By default all flags are enabled.
+ *
+ * @param sess		The TURN client session.
+ * @param flags		Bitmask combination of #pj_stun_sess_msg_log_flag
+ */
+PJ_DEF(void) pj_turn_session_set_log( pj_turn_session *sess,
+				      unsigned flags)
+{
+    pj_stun_session_set_log(sess->stun, flags);
 }
 
@@ -462,4 +489,6 @@
 						pj_dns_resolver *resolver)
 {
+    pj_sockaddr tmp_addr;
+    pj_bool_t is_ip_addr;
     pj_status_t status;
 
@@ -469,5 +498,11 @@
     pj_lock_acquire(sess->lock);
 
-    if (resolver) {
+    /* See if "domain" contains just IP address */
+    tmp_addr.addr.sa_family = sess->af;
+    status = pj_inet_pton(sess->af, domain, 
+			  pj_sockaddr_get_addr(&tmp_addr));
+    is_ip_addr = (status == PJ_SUCCESS);
+
+    if (!is_ip_addr && resolver) {
 	/* Resolve with DNS SRV resolution, and fallback to DNS A resolution
 	 * if default_port is specified.
@@ -476,5 +511,5 @@
 	pj_str_t res_name;
 
-	switch (sess->tp_type) {
+	switch (sess->conn_type) {
 	case PJ_TURN_TP_UDP:
 	    res_name = pj_str("_turn._udp.");
@@ -502,4 +537,10 @@
 	set_state(sess, PJ_TURN_STATE_RESOLVING);
 
+	/* User may have destroyed us in the callback */
+	if (sess->state != PJ_TURN_STATE_RESOLVING) {
+	    status = PJ_ECANCELLED;
+	    goto on_return;
+	}
+
 	status = pj_dns_srv_resolve(domain, &res_name, default_port, 
 				    sess->pool, resolver, opt, sess, 
@@ -521,5 +562,5 @@
 	sess->default_port = (pj_uint16_t)default_port;
 
-	cnt = MAX_SRV_CNT;
+	cnt = PJ_TURN_MAX_DNS_SRV_CNT;
 	ai = (pj_addrinfo*)
 	     pj_pool_calloc(sess->pool, cnt, sizeof(pj_addrinfo));
@@ -527,4 +568,11 @@
 	PJ_LOG(5,(sess->obj_name, "Resolving %.*s with DNS A",
 		  (int)domain->slen, domain->ptr));
+	set_state(sess, PJ_TURN_STATE_RESOLVING);
+
+	/* User may have destroyed us in the callback */
+	if (sess->state != PJ_TURN_STATE_RESOLVING) {
+	    status = PJ_ECANCELLED;
+	    goto on_return;
+	}
 
 	status = pj_getaddrinfo(sess->af, domain, &cnt, ai);
@@ -637,5 +685,5 @@
     /* Send request */
     set_state(sess, PJ_TURN_STATE_ALLOCATING);
-    retransmit = (sess->tp_type == PJ_TURN_TP_UDP);
+    retransmit = (sess->conn_type == PJ_TURN_TP_UDP);
     status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, 
 				      retransmit, sess->srv_addr,
@@ -682,5 +730,5 @@
 
     status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE, 
-				      (sess->tp_type==PJ_TURN_TP_UDP),
+				      (sess->conn_type==PJ_TURN_TP_UDP),
 				      sess->srv_addr,
 				      pj_sockaddr_get_len(sess->srv_addr), 
@@ -834,5 +882,5 @@
      */
     status = pj_stun_session_send_msg(sess->stun, peer, PJ_FALSE, 
-				      (sess->tp_type==PJ_TURN_TP_UDP),
+				      (sess->conn_type==PJ_TURN_TP_UDP),
 				      sess->srv_addr,
 				      pj_sockaddr_get_len(sess->srv_addr),
@@ -850,10 +898,10 @@
  */
 PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
-					      const pj_uint8_t *pkt,
-					      unsigned pkt_len,
-					      pj_bool_t is_datagram)
+					      void *pkt,
+					      unsigned pkt_len)
 {
     pj_bool_t is_stun;
     pj_status_t status;
+    pj_bool_t is_datagram;
 
     /* Packet could be ChannelData or STUN message (response or
@@ -864,6 +912,8 @@
     pj_lock_acquire(sess->lock);
 
+    is_datagram = (sess->conn_type==PJ_TURN_TP_UDP);
+
     /* Quickly check if this is STUN message */
-    is_stun = ((pkt[0] & 0xC0) == 0);
+    is_stun = ((((pj_uint8_t*)pkt)[0] & 0xC0) == 0);
 
     if (is_stun) {
@@ -871,5 +921,5 @@
 	unsigned options;
 
-	options = PJ_STUN_CHECK_PACKET;
+	options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
 	if (is_datagram)
 	    options |= PJ_STUN_IS_DATAGRAM;
@@ -906,6 +956,6 @@
 
 	/* Notify application */
-	(*sess->cb.on_rx_data)(sess, pkt+sizeof(cd), cd.length,
-			       &peer->addr,
+	(*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd), 
+			       cd.length, &peer->addr,
 			       pj_sockaddr_get_len(&peer->addr));
 
@@ -954,4 +1004,6 @@
 			     const pj_str_t *reason)
 {
+    sess->last_status = status;
+
     do {
 	pj_str_t reason1;
@@ -1011,4 +1063,5 @@
     const pj_stun_lifetime_attr *lf_attr;
     const pj_stun_relay_addr_attr *raddr_attr;
+    const pj_stun_sockaddr_attr *mapped_attr;
     pj_str_t s;
     pj_time_val timeout;
@@ -1070,4 +1123,10 @@
 				    " address family is not supported "
 				    "for now"));
+	return;
+    }
+    if (raddr_attr && !pj_sockaddr_has_addr(&raddr_attr->sockaddr)) {
+	on_session_fail(sess, method, PJNATH_EINSTUNMSG,
+			pj_cstr(&s, "Error: Invalid IP address in "
+				    "RELAY-ADDRESS attribute"));
 	return;
     }
@@ -1092,4 +1151,12 @@
     }
 
+    /* Get mapped address */
+    mapped_attr = (const pj_stun_sockaddr_attr*)
+		  pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0);
+    if (mapped_attr) {
+	pj_memcpy(&sess->mapped_addr, &mapped_attr->sockaddr,
+		  sizeof(mapped_attr->sockaddr));
+    }
+
     /* Success */
 
@@ -1133,4 +1200,15 @@
 
     if (method == PJ_STUN_ALLOCATE_METHOD) {
+
+	/* Destroy if we have pending destroy request */
+	if (sess->pending_destroy) {
+	    if (status == PJ_SUCCESS)
+		sess->state = PJ_TURN_STATE_READY;
+	    else
+		sess->state = PJ_TURN_STATE_DEALLOCATED;
+	    sess_shutdown(sess, PJ_SUCCESS);
+	    return;
+	}
+
 	/* Handle ALLOCATE response */
 	if (status==PJ_SUCCESS && 
@@ -1299,5 +1377,5 @@
 {
     pj_turn_session *sess = (pj_turn_session*) user_data;
-    unsigned i, cnt;
+    unsigned i, cnt, tot_cnt;
 
     /* Clear async resolver */
@@ -1310,9 +1388,25 @@
     }
 
+    /* Calculate total number of server entries in the response */
+    tot_cnt = 0;
+    for (i=0; i<rec->count; ++i) {
+	tot_cnt += rec->entry[i].server.addr_count;
+    }
+
+    if (tot_cnt > PJ_TURN_MAX_DNS_SRV_CNT)
+	tot_cnt = PJ_TURN_MAX_DNS_SRV_CNT;
+
+    /* Allocate server entries */
+    sess->srv_addr_list = (pj_sockaddr*)
+		           pj_pool_calloc(sess->pool, tot_cnt, 
+					  sizeof(pj_sockaddr));
+
     /* Copy results to server entries */
-    for (i=0, cnt=0; i<rec->count && cnt<MAX_SRV_CNT; ++i) {
+    for (i=0, cnt=0; i<rec->count && cnt<PJ_TURN_MAX_DNS_SRV_CNT; ++i) {
 	unsigned j;
 
-	for (j=0; j<rec->entry[i].server.addr_count && cnt<MAX_SRV_CNT; ++j) {
+	for (j=0; j<rec->entry[i].server.addr_count && 
+		  cnt<PJ_TURN_MAX_DNS_SRV_CNT; ++j) 
+	{
 	    pj_sockaddr_in *addr = &sess->srv_addr_list[cnt].ipv4;
 
