Index: /pjproject/trunk/pjnath/include/pjnath/stun_msg.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 2588)
+++ /pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 2589)
@@ -84,4 +84,9 @@
 
     /**
+     * STUN/TURN CreatePermission method as defined by draft-ietf-behave-turn
+     */
+    PJ_STUN_CREATE_PERM_METHOD		    = 8,
+
+    /**
      * STUN/TURN ChannelBind as defined by draft-ietf-behave-turn
      */
@@ -252,4 +257,20 @@
      */
     PJ_STUN_DATA_INDICATION		    = 0x0017,
+
+
+    /**
+     * TURN CreatePermission request
+     */
+    PJ_STUN_CREATE_PERM_REQUEST		    = 0x0008,
+
+    /**
+     * TURN CreatePermission successful response.
+     */
+    PJ_STUN_CREATE_PERM_RESPONSE	    = 0x0108,
+
+    /**
+     * TURN CreatePermission failure response
+     */
+    PJ_STUN_CREATE_PERM_ERROR_RESPONSE	    = 0x0118,
 
 
@@ -293,12 +314,13 @@
     PJ_STUN_ATTR_MAGIC_COOKIE	    = 0x000F,/**< MAGIC-COOKIE attr (deprec)*/
     PJ_STUN_ATTR_BANDWIDTH	    = 0x0010,/**< TURN BANDWIDTH (deprec)   */
-    PJ_STUN_ATTR_PEER_ADDR	    = 0x0012,/**< TURN PEER-ADDRESS attr.   */
+    PJ_STUN_ATTR_XOR_PEER_ADDR	    = 0x0012,/**< TURN XOR-PEER-ADDRESS	    */
     PJ_STUN_ATTR_DATA		    = 0x0013,/**< DATA attribute.	    */
     PJ_STUN_ATTR_REALM		    = 0x0014,/**< REALM attribute.	    */
     PJ_STUN_ATTR_NONCE		    = 0x0015,/**< NONCE attribute.	    */
-    PJ_STUN_ATTR_RELAYED_ADDR	    = 0x0016,/**< RELAYED-ADDRESS attribute.*/
+    PJ_STUN_ATTR_XOR_RELAYED_ADDR   = 0x0016,/**< TURN XOR-RELAYED-ADDRESS  */
     PJ_STUN_ATTR_REQ_ADDR_TYPE	    = 0x0017,/**< REQUESTED-ADDRESS-TYPE    */
-    PJ_STUN_ATTR_REQ_PROPS	    = 0x0018,/**< REQUESTED-PROPS	    */
-    PJ_STUN_ATTR_REQ_TRANSPORT	    = 0x0019,/**< REQUESTED-TRANSPORT	    */
+    PJ_STUN_ATTR_EVEN_PORT	    = 0x0018,/**< TURN EVEN-PORT	    */
+    PJ_STUN_ATTR_REQ_TRANSPORT	    = 0x0019,/**< TURN REQUESTED-TRANSPORT  */
+    PJ_STUN_ATTR_DONT_FRAGMENT	    = 0x001A,/**< TURN DONT-FRAGMENT	    */
     PJ_STUN_ATTR_XOR_MAPPED_ADDR    = 0x0020,/**< XOR-MAPPED-ADDRESS	    */
     PJ_STUN_ATTR_TIMER_VAL	    = 0x0021,/**< TIMER-VAL attribute.	    */
@@ -333,4 +355,5 @@
     PJ_STUN_SC_BAD_REQUEST		= 400,  /**< Bad Request	    */
     PJ_STUN_SC_UNAUTHORIZED	        = 401,  /**< Unauthorized	    */
+    PJ_STUN_SC_FORBIDDEN		= 403,	/**< Forbidden (TURN)	    */
     PJ_STUN_SC_UNKNOWN_ATTRIBUTE        = 420,  /**< Unknown Attribute	    */
 #if 0
@@ -350,6 +373,4 @@
     PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO   = 442,  /**< Unsupported Transport or
 						     Protocol (TURN) */
-    PJ_STUN_SC_INVALID_IP_ADDR		= 443,  /**< Invalid IP Address(TURN)*/
-    PJ_STUN_SC_INVALID_PORT	        = 444,  /**< Invalid Port (TURN)    */
     PJ_STUN_SC_OPER_TCP_ONLY		= 445,  /**< Operation for TCP Only */
     PJ_STUN_SC_CONNECTION_FAILURE       = 446,  /**< Connection Failure	    */
@@ -359,7 +380,5 @@
     PJ_STUN_SC_ROLE_CONFLICT		= 487,  /**< Role Conflict	    */
     PJ_STUN_SC_SERVER_ERROR	        = 500,  /**< Server Error	    */
-    PJ_STUN_SC_INSUFFICIENT_CAPACITY    = 507,  /**< Insufficient Capacity 
-						     (TURN) */
-    PJ_STUN_SC_INSUFFICIENT_PORT_CAPACITY=508,  /**< Insufficient Port Capacity 
+    PJ_STUN_SC_INSUFFICIENT_CAPACITY	= 508,  /**< Insufficient Capacity 
 						     (TURN) */
     PJ_STUN_SC_GLOBAL_FAILURE	        = 600   /**< Global Failure	    */
@@ -913,10 +932,10 @@
 
 /**
- * This describes the STUN PEER-ADDRESS attribute.
- * The PEER-ADDRESS specifies the address and port of the peer as seen
+ * This describes the STUN XOR-PEER-ADDRESS attribute.
+ * The XOR-PEER-ADDRESS specifies the address and port of the peer as seen
  * from the TURN server.  It is encoded in the same way as XOR-MAPPED-
  * ADDRESS. 
  */
-typedef struct pj_stun_sockaddr_attr pj_stun_peer_addr_attr;
+typedef struct pj_stun_sockaddr_attr pj_stun_xor_peer_addr_attr;
 
 
@@ -932,10 +951,10 @@
 
 /**
- * This describes the STUN RELAYED-ADDRESS attribute.
- * The RELAYED-ADDRESS is present in Allocate responses.  It specifies the
+ * This describes the STUN XOR-RELAYED-ADDRESS attribute. The 
+ * XOR-RELAYED-ADDRESS is present in Allocate responses.  It specifies the
  * address and port that the server allocated to the client.  It is
  * encoded in the same way as XOR-MAPPED-ADDRESS.
  */
-typedef struct pj_stun_sockaddr_attr pj_stun_relayed_addr_attr;
+typedef struct pj_stun_sockaddr_attr pj_stun_xor_relayed_addr_attr;
 
 
@@ -956,69 +975,35 @@
  \endverbatim
  */
-typedef struct pj_stun_uint_attr pj_stun_req_addr_type;
-
-/**
- * This describes the TURN REQUESTED-PROPS attribute, encoded as
- * STUN 32bit integer attribute. Few macros are provided to manipulate
- * the values in this attribute: #PJ_STUN_GET_PROP_TYPE(), and
- * #PJ_STUN_SET_PROP_TYPE().
+typedef struct pj_stun_uint_attr pj_stun_req_addr_type_attr;
+
+
+/**
+ * This describes the TURN REQUESTED-TRANSPORT attribute, encoded in
+ * STUN generic integer attribute.
+ *
+ * This attribute allows the client to request that the port in the
+ * relayed-transport-address be even, and (optionally) that the server
+ * reserve the next-higher port number.  The attribute is 8 bits long.
+ * Its format is:
+
+\verbatim
+      0
+      0 1 2 3 4 5 6 7
+     +-+-+-+-+-+-+-+-+
+     |R|    RFFU     |
+     +-+-+-+-+-+-+-+-+
+
+\endverbatim
+
+ * The attribute contains a single 1-bit flag:
  * 
- * This attribute allows the client to request that the allocation have
- * certain properties, and by the server to indicate which properties
- * are supported.  The attribute is 32 bits long.  Its format is:
-
- \verbatim
-
-      0                   1                   2                   3
-      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     |   Prop-type   |                  Reserved = 0                 |
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- \endverbatim
-
- * The field labeled "Prop-type" is an 8-bit field specifying the
- * desired property.  The rest of the attribute is RFFU (Reserved For
- * Future Use) and MUST be set to 0 on transmission and ignored on
- * reception.
- *
- * The "Prop-type" field is formatted as follows:
-
- \verbatim
-
-      0 1 2 3 4 5 6 7 
-     +-+-+-+-+-+-+-+-+
-     |E|R|P|         |
-     +-+-+-+-+-+-+-+-+
-
- \endverbatim
-
-   The bits in "Prop-type" are:
-
-   E: If 1, the port number for the relayed-transport-address must be
-      even.  If 0, the port number can be even or odd.
-
-   R: If 1, the server must reserve the next highest port for a
-      subsequent allocation.  If 0, no such reservation is requested.
-      If the client sets the R bit to 1, it MUST also set the E bit to 1
-      (however, the E bit may be 1 when the R bit is 0).
-
-   P: If 1, the allocation must be a Preserving allocation.  If 0, the
-      allocation can be either Preserving or Non-Preserving.
-
- */
-typedef struct pj_stun_uint_attr pj_stun_req_props_attr;
-
-/**
- * Get the 8bit Prop-type value from a 32bit integral value of TURN 
- * TURN REQUESTED-PROPS attribute.
- */
-#define PJ_STUN_GET_PROP_TYPE(u32)	(u32 >> 24)
-
-/**
- * Convert 8bit Prop-type value to a 32bit integral value of TURN 
- * REQUESTED-PROPS attribute.
- */
-#define PJ_STUN_SET_PROP_TYPE(PropType)	(PropType << 24)
+ * R: If 1, the server is requested to reserve the next higher port
+ *    number (on the same IP address) for a subsequent allocation.  If
+ *    0, no such reservation is requested.
+ * 
+ * The other 7 bits of the attribute must be set to zero on transmission
+ * and ignored on reception.
+ */
+typedef struct pj_stun_uint_attr pj_stun_even_port_attr;
 
 
@@ -1064,4 +1049,14 @@
 #define PJ_STUN_SET_RT_PROTO(proto)   (((pj_uint32_t)(proto)) << 24)
 
+
+/**
+ * This describes the TURN DONT-FRAGMENT attribute.
+ *
+ * This attribute is used by the client to request that the server set
+ * the DF (Don't Fragment) bit in the IP header when relaying the
+ * application data onward to the peer.  This attribute has no value
+ * part and thus the attribute length field is 0.
+ */
+typedef struct pj_stun_empty_attr pj_stun_use_candidate_attr;
 
 
Index: /pjproject/trunk/pjnath/include/pjnath/stun_session.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/stun_session.h	(revision 2588)
+++ /pjproject/trunk/pjnath/include/pjnath/stun_session.h	(revision 2589)
@@ -328,13 +328,15 @@
 
 /**
- * Set server name to be included in all response.
- *
- * @param sess	    The STUN session instance.
- * @param srv_name  Server name string.
- *
- * @return	    The user data associated with this STUN session.
- */
-PJ_DECL(pj_status_t) pj_stun_session_set_server_name(pj_stun_session *sess,
-						     const pj_str_t *srv_name);
+ * Set SOFTWARE name to be included in all requests and responses.
+ *
+ * @param sess	    The STUN session instance.
+ * @param sw	    Software name string. If this argument is NULL or
+ *		    empty, the session will not include SOFTWARE attribute
+ *		    in STUN requests and responses.
+ *
+ * @return	    PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess,
+						       const pj_str_t *sw);
 
 /**
Index: /pjproject/trunk/pjnath/include/pjnath/turn_session.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/turn_session.h	(revision 2588)
+++ /pjproject/trunk/pjnath/include/pjnath/turn_session.h	(revision 2589)
@@ -435,4 +435,19 @@
 PJ_DECL(void) pj_turn_session_set_log(pj_turn_session *sess,
 				      unsigned flags);
+
+
+/**
+ * Configure the SOFTWARE name to be sent in all STUN requests by the
+ * TURN session.
+ *
+ * @param sess	    The TURN client session.
+ * @param sw	    Software name string. If this argument is NULL or
+ *		    empty, the session will not include SOFTWARE attribute
+ *		    in STUN requests and responses.
+ *
+ * @return	    PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_turn_session_set_software_name(pj_turn_session *sess,
+						       const pj_str_t *sw);
 
 
@@ -520,4 +535,28 @@
 
 /**
+ * Create or renew permission in the TURN server for the specified peer IP
+ * addresses. Application must install permission for a particular (peer)
+ * IP address before it sends any data to that IP address, or otherwise
+ * the TURN server will drop the data.
+ *
+ * @param sess		The TURN client session.
+ * @param addr_cnt	Number of IP addresses.
+ * @param addr		Array of peer IP addresses. Only the address family
+ *			and IP address portion of the socket address matter.
+ * @param options	Specify 1 to let the TURN client session automatically
+ *			renew the permission later when they are about to
+ *			expire.
+ *
+ * @return		PJ_SUCCESS if the operation has been successfully
+ *			issued, or the appropriate error code. Note that
+ *			the operation itself will complete asynchronously.
+ */
+PJ_DECL(pj_status_t) pj_turn_session_set_perm(pj_turn_session *sess,
+					      unsigned addr_cnt,
+					      const pj_sockaddr addr[],
+					      unsigned options);
+
+
+/**
  * Send a data to the specified peer address via the TURN relay. This 
  * function will encapsulate the data as STUN Send Indication or TURN
Index: /pjproject/trunk/pjnath/include/pjnath/turn_sock.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/turn_sock.h	(revision 2588)
+++ /pjproject/trunk/pjnath/include/pjnath/turn_sock.h	(revision 2589)
@@ -249,4 +249,27 @@
 
 /**
+ * Create or renew permission in the TURN server for the specified peer IP
+ * addresses. Application must install permission for a particular (peer)
+ * IP address before it sends any data to that IP address, or otherwise
+ * the TURN server will drop the data.
+ *
+ * @param turn_sock	The TURN transport instance.
+ * @param addr_cnt	Number of IP addresses.
+ * @param addr		Array of peer IP addresses. Only the address family
+ *			and IP address portion of the socket address matter.
+ * @param options	Specify 1 to let the TURN client session automatically
+ *			renew the permission later when they are about to
+ *			expire.
+ *
+ * @return		PJ_SUCCESS if the operation has been successfully
+ *			issued, or the appropriate error code. Note that
+ *			the operation itself will complete asynchronously.
+ */
+PJ_DECL(pj_status_t) pj_turn_sock_set_perm(pj_turn_sock *turn_sock,
+					   unsigned addr_cnt,
+					   const pj_sockaddr addr[],
+					   unsigned options);
+
+/**
  * Send a data to the specified peer address via the TURN relay. This 
  * function will encapsulate the data as STUN Send Indication or TURN
Index: /pjproject/trunk/pjnath/include/pjnath/types.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/types.h	(revision 2588)
+++ /pjproject/trunk/pjnath/include/pjnath/types.h	(revision 2589)
@@ -108,9 +108,13 @@
 operation of the relay and to exchange packets with its peers using the relay.
 
-This version of PJNATH implements both TCP and UDP client transport and it
-complies with the following TURN draft:
- - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-09.txt">
-   <B>draft-ietf-behave-turn-09</B></A>: Obtaining Relay Addresses 
+Features:
+ - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-13.txt">
+   <B>draft-ietf-behave-turn-13</B></A>: Obtaining Relay Addresses 
    from Simple Traversal Underneath NAT (STUN)
+ - DNS SRV resolution
+ - Fallback to DNS A resolution if SRV record is not found
+ - UDP and TCP connection to TURN server
+ - automatic management of allocation refresh
+
 
 
Index: /pjproject/trunk/pjnath/src/pjnath-test/server.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath-test/server.c	(revision 2588)
+++ /pjproject/trunk/pjnath/src/pjnath-test/server.c	(revision 2589)
@@ -311,6 +311,6 @@
     pj_stun_msg_add_uint_attr(pool, resp, PJ_STUN_ATTR_LIFETIME, lifetime);
     if (lifetime != 0) {
-	/* Add RELAYED-ADDRESS */
-	pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_RELAYED_ADDR, PJ_TRUE, &alloc->alloc_addr,
+	/* Add XOR-RELAYED-ADDRESS */
+	pj_stun_msg_add_sockaddr_attr(pool, resp, PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE, &alloc->alloc_addr,
 				      pj_sockaddr_get_len(&alloc->alloc_addr));
 	/* Add XOR-MAPPED-ADDRESS */
Index: /pjproject/trunk/pjnath/src/pjnath/stun_msg.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 2588)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 2589)
@@ -44,5 +44,5 @@
     "Send",			/* 6 */
     "Data",			/* 7 */
-    "???",			/* 8 */
+    "CreatePermission",		/* 8 */
     "ChannelBind",		/* 9 */
 };
@@ -57,4 +57,5 @@
     { PJ_STUN_SC_BAD_REQUEST,		    "Bad Request"},
     { PJ_STUN_SC_UNAUTHORIZED,		    "Unauthorized"},
+    { PJ_STUN_SC_FORBIDDEN,		    "Forbidden"},
     { PJ_STUN_SC_UNKNOWN_ATTRIBUTE,	    "Unknown Attribute"},
     //{ PJ_STUN_SC_STALE_CREDENTIALS,	    "Stale Credentials"},
@@ -70,6 +71,4 @@
     { PJ_STUN_SC_WRONG_CREDENTIALS,	    "Wrong Credentials"},
     { PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO,    "Unsupported Transport Protocol"},
-    { PJ_STUN_SC_INVALID_IP_ADDR,	    "Invalid IP Address"},
-    { PJ_STUN_SC_INVALID_PORT,		    "Invalid Port"},
     { PJ_STUN_SC_OPER_TCP_ONLY,		    "Operation for TCP Only"},
     { PJ_STUN_SC_CONNECTION_FAILURE,	    "Connection Failure"},
@@ -79,5 +78,4 @@
     { PJ_STUN_SC_SERVER_ERROR,		    "Server Error"},
     { PJ_STUN_SC_INSUFFICIENT_CAPACITY,	    "Insufficient Capacity"},
-    { PJ_STUN_SC_INSUFFICIENT_PORT_CAPACITY,"Insufficient Port Capacity"},
     { PJ_STUN_SC_GLOBAL_FAILURE,	    "Global Failure"}
 };
@@ -311,6 +309,6 @@
     },
     {
-	/* PJ_STUN_ATTR_PEER_ADDRESS, */
-	"PEER-ADDRESS",
+	/* PJ_STUN_ATTR_XOR_PEER_ADDRESS, */
+	"XOR-PEER-ADDRESS",
 	&decode_xored_sockaddr_attr,
 	&encode_sockaddr_attr,
@@ -339,6 +337,6 @@
     },
     {
-	/* PJ_STUN_ATTR_RELAY_ADDRESS, */
-	"RELAYED-ADDRESS",
+	/* PJ_STUN_ATTR_XOR_RELAYED_ADDR, */
+	"XOR-RELAYED-ADDRESS",
 	&decode_xored_sockaddr_attr,
 	&encode_sockaddr_attr,
@@ -353,6 +351,6 @@
     },
     {
-	/* PJ_STUN_ATTR_REQUESTED_PROPS, */
-	"REQUESTED-PROPS",
+	/* PJ_STUN_ATTR_EVEN_PORT, */
+	"EVEN-PORT",
 	&decode_uint_attr,
 	&encode_uint_attr,
@@ -367,9 +365,9 @@
     },
     {
-	/* ID 0x001A is not assigned */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	/* PJ_STUN_ATTR_DONT_FRAGMENT */
+	"DONT-FRAGMENT",
+	&decode_empty_attr,
+	&encode_empty_attr,
+	&clone_empty_attr
     },
     {
Index: /pjproject/trunk/pjnath/src/pjnath/stun_msg_dump.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_msg_dump.c	(revision 2588)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_msg_dump.c	(revision 2589)
@@ -73,6 +73,6 @@
     case PJ_STUN_ATTR_CHANGED_ADDR:
     case PJ_STUN_ATTR_REFLECTED_FROM:
-    case PJ_STUN_ATTR_PEER_ADDR:
-    case PJ_STUN_ATTR_RELAYED_ADDR:
+    case PJ_STUN_ATTR_XOR_PEER_ADDR:
+    case PJ_STUN_ATTR_XOR_RELAYED_ADDR:
     case PJ_STUN_ATTR_XOR_MAPPED_ADDR:
     case PJ_STUN_ATTR_XOR_REFLECTED_FROM:
@@ -117,5 +117,5 @@
     case PJ_STUN_ATTR_BANDWIDTH:
     case PJ_STUN_ATTR_REQ_ADDR_TYPE:
-    case PJ_STUN_ATTR_REQ_PROPS:
+    case PJ_STUN_ATTR_EVEN_PORT:
     case PJ_STUN_ATTR_REQ_TRANSPORT:
     case PJ_STUN_ATTR_TIMER_VAL:
@@ -224,4 +224,5 @@
 	break;
     case PJ_STUN_ATTR_USE_CANDIDATE:
+    case PJ_STUN_ATTR_DONT_FRAGMENT:
     default:
 	len = pj_ansi_snprintf(p, end-p, "\n");
Index: /pjproject/trunk/pjnath/src/pjnath/stun_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 2588)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 2589)
@@ -218,6 +218,11 @@
     pj_str_t realm, username, nonce, auth_key;
 
-    /* The server SHOULD include a SOFTWARE attribute in all responses */
-    if (sess->srv_name.slen && PJ_STUN_IS_RESPONSE(msg->hdr.type)) {
+    /* If the agent is sending a request, it SHOULD add a SOFTWARE attribute
+     * to the request. The server SHOULD include a SOFTWARE attribute in all 
+     * responses 
+     */
+    if (sess->srv_name.slen && !PJ_STUN_IS_INDICATION(msg->hdr.type) &&
+	pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL) 
+    {
 	pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE,
 				    &sess->srv_name);
@@ -487,5 +492,5 @@
     sess->srv_name.ptr = (char*) pj_pool_alloc(pool, 32);
     sess->srv_name.slen = pj_ansi_snprintf(sess->srv_name.ptr, 32,
-					   "pj_stun-%s", pj_get_version());
+					   "pjnath-%s", pj_get_version());
 
     sess->rx_pool = pj_pool_create(sess->cfg->pf, name, 
@@ -591,10 +596,10 @@
 }
 
-PJ_DEF(pj_status_t) pj_stun_session_set_server_name(pj_stun_session *sess,
-						    const pj_str_t *srv_name)
+PJ_DEF(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess,
+						      const pj_str_t *sw)
 {
     PJ_ASSERT_RETURN(sess, PJ_EINVAL);
-    if (srv_name)
-	pj_strdup(sess->pool, &sess->srv_name, srv_name);
+    if (sw && sw->slen)
+	pj_strdup(sess->pool, &sess->srv_name, sw);
     else
 	sess->srv_name.slen = 0;
Index: /pjproject/trunk/pjnath/src/pjnath/turn_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 2588)
+++ /pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 2589)
@@ -29,9 +29,12 @@
 #include <pj/os.h>
 #include <pj/pool.h>
+#include <pj/rand.h>
 #include <pj/sock.h>
 
 #define PJ_TURN_CHANNEL_MIN	    0x4000
-#define PJ_TURN_CHANNEL_MAX	    0xFFFE  /* inclusive */
-#define PJ_TURN_PEER_HTABLE_SIZE    8
+#define PJ_TURN_CHANNEL_MAX	    0x7FFF  /* inclusive */
+#define PJ_TURN_CHANNEL_HTABLE_SIZE 8
+#define PJ_TURN_PERM_HTABLE_SIZE    8
+#define PJ_TURN_RENEWAL_BEFORE	    10	/* seconds before renewals */
 
 static const char *state_names[] = 
@@ -54,13 +57,54 @@
 };
 
-
-struct peer
-{
-    pj_uint16_t	    ch_id;
+/* This structure describes a channel binding. A channel binding is index by
+ * the channel number or IP address and port number of the peer.
+ */
+struct ch_t
+{
+    /* The channel number */
+    pj_uint16_t	    num;
+
+    /* PJ_TRUE if we've received successful response to ChannelBind request
+     * for this channel.
+     */
     pj_bool_t	    bound;
+
+    /* The peer IP address and port */
     pj_sockaddr	    addr;
+
+    /* The channel binding expiration */
     pj_time_val	    expiry;
 };
 
+
+/* This structure describes a permission. A permission is identified by the
+ * IP address only.
+ */
+struct perm_t
+{
+    /* Cache of hash value to speed-up lookup */
+    pj_uint32_t	    hval;
+
+    /* The permission IP address. The port number MUST be zero */
+    pj_sockaddr	    addr;
+
+    /* Number of peers that uses this permission. */
+    unsigned	    peer_cnt;
+
+    /* Automatically renew this permission once it expires? */
+    pj_bool_t	    renew;
+
+    /* The permission expiration */
+    pj_time_val	    expiry;
+
+    /* Arbitrary/random pointer value (token) to map this perm with the 
+     * request to create it. It is used to invalidate this perm when the 
+     * request fails.
+     */
+    void	   *req_token;
+};
+
+
+/* The TURN client session structure */
 struct pj_turn_session
 {
@@ -103,5 +147,6 @@
     pj_sockaddr		 relay_addr;
 
-    pj_hash_table_t	*peer_table;
+    pj_hash_table_t	*ch_table;
+    pj_hash_table_t	*perm_table;
 
     pj_uint32_t		 send_ind_tsx_id[3];
@@ -143,11 +188,17 @@
 				pj_status_t status,
 				const pj_dns_srv_record *rec);
-static struct peer *lookup_peer_by_addr(pj_turn_session *sess,
-					const pj_sockaddr_t *addr,
-					unsigned addr_len,
-					pj_bool_t update,
-					pj_bool_t bind_channel);
-static struct peer *lookup_peer_by_chnum(pj_turn_session *sess,
-					 pj_uint16_t chnum);
+static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess,
+				      const pj_sockaddr_t *addr,
+				      unsigned addr_len,
+				      pj_bool_t update,
+				      pj_bool_t bind_channel);
+static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess,
+				       pj_uint16_t chnum);
+static struct perm_t *lookup_perm(pj_turn_session *sess,
+				  const pj_sockaddr_t *addr,
+				  unsigned addr_len,
+				  pj_bool_t update);
+static void invalidate_perm(pj_turn_session *sess,
+			    struct perm_t *perm);
 static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e);
 
@@ -225,5 +276,8 @@
 
     /* Peer hash table */
-    sess->peer_table = pj_hash_create(pool, PJ_TURN_PEER_HTABLE_SIZE);
+    sess->ch_table = pj_hash_create(pool, PJ_TURN_CHANNEL_HTABLE_SIZE);
+
+    /* Permission hash table */
+    sess->perm_table = pj_hash_create(pool, PJ_TURN_PERM_HTABLE_SIZE);
 
     /* Session lock */
@@ -483,4 +537,20 @@
 
 
+/*
+ * Set software name
+ */
+PJ_DEF(pj_status_t) pj_turn_session_set_software_name( pj_turn_session *sess,
+						       const pj_str_t *sw)
+{
+    pj_status_t status;
+
+    pj_lock_acquire(sess->lock);
+    status = pj_stun_session_set_software_name(sess->stun, sw);
+    pj_lock_release(sess->lock);
+
+    return status;
+}
+
+
 /**
  * Set the server or domain name of the server.
@@ -705,4 +775,99 @@
 
 /*
+ * Install or renew permissions
+ */
+PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess,
+					      unsigned addr_cnt,
+					      const pj_sockaddr addr[],
+					      unsigned options)
+{
+    pj_stun_tx_data *tdata;
+    pj_hash_iterator_t it_buf, *it;
+    void *req_token;
+    unsigned i, attr_added=0;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(sess && addr_cnt && addr, PJ_EINVAL);
+
+    pj_lock_acquire(sess->lock);
+
+    /* Create a bare CreatePermission request */
+    status = pj_stun_session_create_req(sess->stun, 
+					PJ_STUN_CREATE_PERM_REQUEST,
+					PJ_STUN_MAGIC, NULL, &tdata);
+    if (status != PJ_SUCCESS) {
+	pj_lock_release(sess->lock);
+	return status;
+    }
+
+    /* Create request token to map the request to the perm structures
+     * which the request belongs.
+     */
+    req_token = (void*)(long)pj_rand();
+
+    /* Process the addresses */
+    for (i=0; i<addr_cnt; ++i) {
+	struct perm_t *perm;
+
+	/* Lookup the perm structure and create if it doesn't exist */
+	perm = lookup_perm(sess, &addr[i], pj_sockaddr_get_len(&addr[i]),
+			   PJ_TRUE);
+	perm->renew = (options & 0x01);
+
+	/* Only add to the request if the request doesn't contain this
+	 * address yet.
+	 */
+	if (perm->req_token != req_token) {
+	    perm->req_token = req_token;
+
+	    /* Add XOR-PEER-ADDRESS */
+	    status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
+						   PJ_STUN_ATTR_XOR_PEER_ADDR,
+						   PJ_TRUE,
+						   &addr[i],
+						   sizeof(addr[i]));
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+
+	    ++attr_added;
+	}
+    }
+
+    pj_assert(attr_added != 0);
+
+    /* Send the request */
+    status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE, 
+				      (sess->conn_type==PJ_TURN_TP_UDP),
+				      sess->srv_addr,
+				      pj_sockaddr_get_len(sess->srv_addr), 
+				      tdata);
+    if (status != PJ_SUCCESS) {
+	/* tdata is already destroyed */
+	tdata = NULL;
+	goto on_error;
+    }
+
+    pj_lock_release(sess->lock);
+    return PJ_SUCCESS;
+
+on_error:
+    /* destroy tdata */
+    if (tdata) {
+	pj_stun_msg_destroy_tdata(sess->stun, tdata);
+    }
+    /* invalidate perm structures associated with this request */
+    it = pj_hash_first(sess->perm_table, &it_buf);
+    while (it) {
+	struct perm_t *perm = (struct perm_t*)
+			      pj_hash_this(sess->perm_table, it);
+	it = pj_hash_next(sess->perm_table, it);
+	if (perm->req_token == req_token)
+	    invalidate_perm(sess, perm);
+    }
+    pj_lock_release(sess->lock);
+    return status;
+}
+
+/*
  * Send REFRESH
  */
@@ -758,5 +923,6 @@
 					    unsigned addr_len)
 {
-    struct peer *peer;
+    struct ch_t *ch;
+    struct perm_t *perm;
     pj_status_t status;
 
@@ -772,12 +938,27 @@
     pj_lock_acquire(sess->lock);
 
-    /* Lookup peer to see whether we've assigned a channel number
-     * to this peer.
-     */
-    peer = lookup_peer_by_addr(sess, addr, addr_len, PJ_TRUE, PJ_FALSE);
-    pj_assert(peer != NULL);
-
-    if (peer->ch_id != PJ_TURN_INVALID_CHANNEL && peer->bound) {
-	/* Peer is assigned Channel number, we can use ChannelData */
+    /* Lookup permission first */
+    perm = lookup_perm(sess, addr, pj_sockaddr_get_len(addr), PJ_FALSE);
+    if (perm == NULL) {
+	/* Permission doesn't exist, install it first */
+	char ipstr[PJ_INET6_ADDRSTRLEN+2];
+
+	PJ_LOG(4,(sess->obj_name, 
+		  "sendto(): IP %s has no permission, requesting it first..",
+		  pj_sockaddr_print(addr, ipstr, sizeof(ipstr), 2)));
+
+	status = pj_turn_session_set_perm(sess, 1, (const pj_sockaddr*)addr, 
+					  0);
+	if (status != PJ_SUCCESS) {
+	    pj_lock_release(sess->lock);
+	    return status;
+	}
+    }
+
+    /* See if the peer is bound to a channel number */
+    ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr), 
+			   PJ_FALSE, PJ_FALSE);
+    if (ch && ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound) {
+	/* Peer is assigned a channel number, we can use ChannelData */
 	pj_turn_channel_data *cd = (pj_turn_channel_data*)sess->tx_pkt;
 	
@@ -789,5 +970,5 @@
 	}
 
-	cd->ch_number = pj_htons((pj_uint16_t)peer->ch_id);
+	cd->ch_number = pj_htons((pj_uint16_t)ch->num);
 	cd->length = pj_htons((pj_uint16_t)pkt_len);
 	pj_memcpy(cd+1, pkt, pkt_len);
@@ -800,7 +981,5 @@
 
     } else {
-	/* Peer has not been assigned Channel number, must use Send
-	 * Indication.
-	 */
+	/* Use Send Indication. */
 	pj_stun_sockaddr_attr peer_attr;
 	pj_stun_binary_attr data_attr;
@@ -818,6 +997,6 @@
 	    goto on_return;
 
-	/* Add PEER-ADDRESS */
-	pj_stun_sockaddr_attr_init(&peer_attr, PJ_STUN_ATTR_PEER_ADDR,
+	/* Add XOR-PEER-ADDRESS */
+	pj_stun_sockaddr_attr_init(&peer_attr, PJ_STUN_ATTR_XOR_PEER_ADDR,
 				   PJ_TRUE, addr, addr_len);
 	pj_stun_msg_add_attr(&send_ind, (pj_stun_attr_hdr*)&peer_attr);
@@ -855,5 +1034,5 @@
 						 unsigned addr_len)
 {
-    struct peer *peer;
+    struct ch_t *ch;
     pj_stun_tx_data *tdata;
     pj_uint16_t ch_num;
@@ -872,15 +1051,16 @@
 	goto on_return;
 
-    /* Lookup peer */
-    peer = lookup_peer_by_addr(sess, peer_adr, addr_len, PJ_TRUE, PJ_FALSE);
-    pj_assert(peer);
-
-    if (peer->ch_id != PJ_TURN_INVALID_CHANNEL) {
+    /* Lookup if this peer has already been assigned a number */
+    ch = lookup_ch_by_addr(sess, peer_adr, pj_sockaddr_get_len(peer_adr),
+			   PJ_TRUE, PJ_FALSE);
+    pj_assert(ch);
+
+    if (ch->num != PJ_TURN_INVALID_CHANNEL) {
 	/* Channel is already bound. This is a refresh request. */
-	ch_num = peer->ch_id;
+	ch_num = ch->num;
     } else {
 	PJ_ASSERT_ON_FAIL(sess->next_ch <= PJ_TURN_CHANNEL_MAX, 
 			    {status=PJ_ETOOMANY; goto on_return;});
-	peer->ch_id = ch_num = sess->next_ch++;
+	ch->num = ch_num = sess->next_ch++;
     }
 
@@ -890,7 +1070,7 @@
 			      PJ_STUN_SET_CH_NB(ch_num));
 
-    /* Add PEER-ADDRESS attribute */
+    /* Add XOR-PEER-ADDRESS attribute */
     pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
-				  PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE,
+				  PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE,
 				  peer_adr, addr_len);
 
@@ -898,5 +1078,5 @@
      * for future reference when we receive the ChannelBind response.
      */
-    status = pj_stun_session_send_msg(sess->stun, peer, PJ_FALSE, 
+    status = pj_stun_session_send_msg(sess->stun, ch, PJ_FALSE, 
 				      (sess->conn_type==PJ_TURN_TP_UDP),
 				      sess->srv_addr,
@@ -950,5 +1130,5 @@
 	/* This must be ChannelData. */
 	pj_turn_channel_data cd;
-	struct peer *peer;
+	struct ch_t *ch;
 
 	if (pkt_len < 4) {
@@ -981,7 +1161,7 @@
 	}
 
-	/* Lookup peer */
-	peer = lookup_peer_by_chnum(sess, cd.ch_number);
-	if (!peer || !peer->bound) {
+	/* Lookup channel */
+	ch = lookup_ch_by_chnum(sess, cd.ch_number);
+	if (!ch || !ch->bound) {
 	    status = PJ_ENOTFOUND;
 	    goto on_return;
@@ -991,6 +1171,6 @@
 	if (sess->cb.on_rx_data) {
 	    (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd), 
-				   cd.length, &peer->addr,
-				   pj_sockaddr_get_len(&peer->addr));
+				   cd.length, &ch->addr,
+				   pj_sockaddr_get_len(&ch->addr));
 	}
 
@@ -1090,5 +1270,5 @@
 {
     const pj_stun_lifetime_attr *lf_attr;
-    const pj_stun_relayed_addr_attr *raddr_attr;
+    const pj_stun_xor_relayed_addr_attr *raddr_attr;
     const pj_stun_sockaddr_attr *mapped_attr;
     pj_str_t s;
@@ -1138,6 +1318,6 @@
      * address family.
      */
-    raddr_attr = (const pj_stun_relayed_addr_attr*)
-		 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_RELAYED_ADDR, 0);
+    raddr_attr = (const pj_stun_xor_relayed_addr_attr*)
+		 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_RELAYED_ADDR, 0);
     if (raddr_attr == NULL && method==PJ_STUN_ALLOCATE_METHOD) {
 	on_session_fail(sess, method, PJNATH_EINSTUNMSG,
@@ -1302,13 +1482,13 @@
 	{
 	    /* Successful ChannelBind response */
-	    struct peer *peer = (struct peer*)token;
-
-	    pj_assert(peer->ch_id != PJ_TURN_INVALID_CHANNEL);
-	    peer->bound = PJ_TRUE;
+	    struct ch_t *ch = (struct ch_t*)token;
+
+	    pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL);
+	    ch->bound = PJ_TRUE;
 
 	    /* Update hash table */
-	    lookup_peer_by_addr(sess, &peer->addr,
-				pj_sockaddr_get_len(&peer->addr),
-				PJ_TRUE, PJ_TRUE);
+	    lookup_ch_by_addr(sess, &ch->addr,
+			      pj_sockaddr_get_len(&ch->addr),
+			      PJ_TRUE, PJ_TRUE);
 
 	} else {
@@ -1333,4 +1513,56 @@
 	}
 
+    } else if (method == PJ_STUN_CREATE_PERM_METHOD) {
+	/* Handle CreatePermission response */
+	if (status==PJ_SUCCESS && 
+	    PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type)) 
+	{
+	    /* No special handling when the request is successful. */
+	} else {
+	    /* Iterate the permission table and invalidate all permissions
+	     * that are related to this request.
+	     */
+	    pj_hash_iterator_t it_buf, *it;
+	    char ipstr[PJ_INET6_ADDRSTRLEN+10];
+	    int err_code;
+	    char errbuf[PJ_ERR_MSG_SIZE];
+	    pj_str_t reason;
+
+	    if (status != PJ_SUCCESS) {
+		err_code = status;
+		reason = pj_strerror(status, errbuf, sizeof(errbuf));
+	    } else {
+		const pj_stun_errcode_attr *eattr;
+
+		eattr = (const pj_stun_errcode_attr*)
+			pj_stun_msg_find_attr(response, 
+					      PJ_STUN_ATTR_ERROR_CODE, 0);
+		if (eattr) {
+		    err_code = eattr->err_code;
+		    reason = eattr->reason;
+		} else {
+		    err_code = -1;
+		    reason = pj_str("?");
+		}
+	    }
+
+	    it = pj_hash_first(sess->perm_table, &it_buf);
+	    while (it) {
+		struct perm_t *perm = (struct perm_t*)
+				      pj_hash_this(sess->perm_table, it);
+		it = pj_hash_next(sess->perm_table, it);
+
+		if (perm->req_token == token) {
+		    PJ_LOG(1,(sess->obj_name, 
+			      "CreatePermission failed for IP %s: %d/%.*s",
+			      pj_sockaddr_print(&perm->addr, ipstr, 
+						sizeof(ipstr), 2),
+			      err_code, (int)reason.slen, reason.ptr));
+
+		    invalidate_perm(sess, perm);
+		}
+	    }
+	}
+
     } else {
 	PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s response",
@@ -1353,5 +1585,5 @@
 {
     pj_turn_session *sess;
-    pj_stun_peer_addr_attr *peer_attr;
+    pj_stun_xor_peer_addr_attr *peer_attr;
     pj_stun_icmp_attr *icmp;
     pj_stun_data_attr *data_attr;
@@ -1380,7 +1612,7 @@
     }
 
-    /* Get PEER-ADDRESS attribute */
-    peer_attr = (pj_stun_peer_addr_attr*)
-		pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
+    /* Get XOR-PEER-ADDRESS attribute */
+    peer_attr = (pj_stun_xor_peer_addr_attr*)
+		pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
 
     /* Get DATA attribute */
@@ -1388,5 +1620,5 @@
 		pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0);
 
-    /* Must have both PEER-ADDRESS and DATA attributes */
+    /* Must have both XOR-PEER-ADDRESS and DATA attributes */
     if (!peer_attr || !data_attr) {
 	PJ_LOG(4,(sess->obj_name, 
@@ -1473,59 +1705,106 @@
  * Lookup peer descriptor from its address.
  */
-static struct peer *lookup_peer_by_addr(pj_turn_session *sess,
-					const pj_sockaddr_t *addr,
-					unsigned addr_len,
-					pj_bool_t update,
-					pj_bool_t bind_channel)
-{
-    unsigned hval = 0;
-    struct peer *peer;
-
-    peer = (struct peer*) pj_hash_get(sess->peer_table, addr, addr_len, &hval);
-    if (peer == NULL && update) {
-	peer = PJ_POOL_ZALLOC_T(sess->pool, struct peer);
-	peer->ch_id = PJ_TURN_INVALID_CHANNEL;
-	pj_memcpy(&peer->addr, addr, addr_len);
+static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess,
+				      const pj_sockaddr_t *addr,
+				      unsigned addr_len,
+				      pj_bool_t update,
+				      pj_bool_t bind_channel)
+{
+    pj_uint32_t hval = 0;
+    struct ch_t *ch;
+
+    ch = (struct ch_t*) 
+	 pj_hash_get(sess->ch_table, addr, addr_len, &hval);
+    if (ch == NULL && update) {
+	ch = PJ_POOL_ZALLOC_T(sess->pool, struct ch_t);
+	ch->num = PJ_TURN_INVALID_CHANNEL;
+	pj_memcpy(&ch->addr, addr, addr_len);
 
 	/* Register by peer address */
-	pj_hash_set(sess->pool, sess->peer_table, &peer->addr, addr_len,
-		    hval, peer);
-    }
-
-    if (peer && update) {
-	pj_gettimeofday(&peer->expiry);
-	if (peer->bound) {
-	    peer->expiry.sec += PJ_TURN_CHANNEL_TIMEOUT - 10;
-	} else {
-	    peer->expiry.sec += PJ_TURN_PERM_TIMEOUT - 10;
-	}
+	pj_hash_set(sess->pool, sess->ch_table, &ch->addr, addr_len,
+		    hval, ch);
+    }
+
+    if (ch && update) {
+	pj_gettimeofday(&ch->expiry);
+	ch->expiry.sec += PJ_TURN_PERM_TIMEOUT - PJ_TURN_RENEWAL_BEFORE;
 
 	if (bind_channel) {
 	    pj_uint32_t hval = 0;
 	    /* Register by channel number */
-	    pj_assert(peer->ch_id != PJ_TURN_INVALID_CHANNEL && peer->bound);
-
-	    if (pj_hash_get(sess->peer_table, &peer->ch_id, 
-			    sizeof(peer->ch_id), &hval)==0) {
-		pj_hash_set(sess->pool, sess->peer_table, &peer->ch_id,
-			    sizeof(peer->ch_id), hval, peer);
+	    pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound);
+
+	    if (pj_hash_get(sess->ch_table, &ch->num, 
+			    sizeof(ch->num), &hval)==0) {
+		pj_hash_set(sess->pool, sess->ch_table, &ch->num,
+			    sizeof(ch->num), hval, ch);
 	    }
 	}
     }
 
-    return peer;
-}
-
-
-/*
- * Lookup peer descriptor from its channel number.
- */
-static struct peer *lookup_peer_by_chnum(pj_turn_session *sess,
+    return ch;
+}
+
+
+/*
+ * Lookup channel descriptor from its channel number.
+ */
+static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess,
 					 pj_uint16_t chnum)
 {
-    return (struct peer*) pj_hash_get(sess->peer_table, &chnum, 
+    return (struct ch_t*) pj_hash_get(sess->ch_table, &chnum, 
 				      sizeof(chnum), NULL);
 }
 
+
+/*
+ * Lookup permission and optionally create if it doesn't exist.
+ */
+static struct perm_t *lookup_perm(pj_turn_session *sess,
+				  const pj_sockaddr_t *addr,
+				  unsigned addr_len,
+				  pj_bool_t update)
+{
+    pj_uint32_t hval = 0;
+    pj_sockaddr perm_addr;
+    struct perm_t *perm;
+
+    /* make sure port number if zero */
+    if (pj_sockaddr_get_port(addr) != 0) {
+	pj_memcpy(&perm_addr, addr, addr_len);
+	pj_sockaddr_set_port(&perm_addr, 0);
+	addr = &perm_addr;
+    }
+
+    /* lookup and create if it doesn't exist and wanted */
+    perm = (struct perm_t*) 
+	   pj_hash_get(sess->perm_table, addr, addr_len, &hval);
+    if (perm == NULL && update) {
+	perm = PJ_POOL_ZALLOC_T(sess->pool, struct perm_t);
+	pj_memcpy(&perm->addr, addr, addr_len);
+	perm->hval = hval;
+
+	pj_hash_set(sess->pool, sess->perm_table, &perm->addr, addr_len,
+		    perm->hval, perm);
+    }
+
+    if (perm && update) {
+	pj_gettimeofday(&perm->expiry);
+	perm->expiry.sec += PJ_TURN_PERM_TIMEOUT - PJ_TURN_RENEWAL_BEFORE;
+
+    }
+
+    return perm;
+}
+
+/*
+ * Delete permission
+ */
+static void invalidate_perm(pj_turn_session *sess,
+			    struct perm_t *perm)
+{
+    pj_hash_set(NULL, sess->perm_table, &perm->addr,
+		pj_sockaddr_get_len(&perm->addr), perm->hval, NULL);
+}
 
 /*
@@ -1565,19 +1844,19 @@
 
 	/* Scan hash table to refresh bound channels */
-	it = pj_hash_first(sess->peer_table, &itbuf);
+	it = pj_hash_first(sess->ch_table, &itbuf);
 	while (it) {
-	    struct peer *peer = (struct peer*) 
-				pj_hash_this(sess->peer_table, it);
-	    if (peer->bound && PJ_TIME_VAL_LTE(peer->expiry, now)) {
+	    struct ch_t *ch = (struct ch_t*) 
+			      pj_hash_this(sess->ch_table, it);
+	    if (ch->bound && PJ_TIME_VAL_LTE(ch->expiry, now)) {
 
 		/* Send ChannelBind to refresh channel binding and 
 		 * permission.
 		 */
-		pj_turn_session_bind_channel(sess, &peer->addr,
-					     pj_sockaddr_get_len(&peer->addr));
+		pj_turn_session_bind_channel(sess, &ch->addr,
+					     pj_sockaddr_get_len(&ch->addr));
 		pkt_sent = PJ_TRUE;
 	    }
 
-	    it = pj_hash_next(sess->peer_table, it);
+	    it = pj_hash_next(sess->ch_table, it);
 	}
 
Index: /pjproject/trunk/pjnath/src/pjnath/turn_sock.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/turn_sock.c	(revision 2588)
+++ /pjproject/trunk/pjnath/src/pjnath/turn_sock.c	(revision 2589)
@@ -388,4 +388,18 @@
 
 /*
+ * Install permission
+ */
+PJ_DEF(pj_status_t) pj_turn_sock_set_perm( pj_turn_sock *turn_sock,
+					   unsigned addr_cnt,
+					   const pj_sockaddr addr[],
+					   unsigned options)
+{
+    if (turn_sock->sess == NULL)
+	return PJ_EINVALIDOP;
+
+    return pj_turn_session_set_perm(turn_sock->sess, addr_cnt, addr, options);
+}
+
+/*
  * Send packet.
  */ 
Index: /pjproject/trunk/pjnath/src/pjturn-client/client_main.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjturn-client/client_main.c	(revision 2588)
+++ /pjproject/trunk/pjnath/src/pjturn-client/client_main.c	(revision 2589)
@@ -139,4 +139,5 @@
 	char name[] = "peer0";
 	pj_uint16_t port;
+	pj_stun_sock_cfg ss_cfg;
 	pj_str_t server;
 
@@ -147,7 +148,13 @@
 	g.peer[i].mapped_addr.addr.sa_family = pj_AF_INET();
 
+	pj_stun_sock_cfg_default(&ss_cfg);
+#if 1
+	/* make reading the log easier */
+	ss_cfg.ka_interval = 300;
+#endif
+
 	name[strlen(name)-1] = '0'+i;
 	status = pj_stun_sock_create(&g.stun_config, name, pj_AF_INET(), 
-				     &stun_sock_cb, NULL,
+				     &stun_sock_cb, &ss_cfg,
 				     &g.peer[i], &g.peer[i].stun_sock);
 	if (status != PJ_SUCCESS) {
@@ -416,20 +423,21 @@
 
     puts("\n");
-    puts("+====================================================================+");
-    puts("|             CLIENT                |             PEER-0             |");
-    puts("|                                   |                                |");
-    printf("| State     : %-12s          | Address: %-21s |\n",
+    puts("+=====================================================================+");
+    puts("|             CLIENT                 |             PEER-0             |");
+    puts("|                                    |                                |");
+    printf("| State     : %-12s           | Address: %-21s |\n",
 	   client_state, peer0_addr);
-    printf("| Relay addr: %-21s |                                |\n",
+    printf("| Relay addr: %-21s  |                                |\n",
 	   relay_addr);
-    puts("|                                   | 0  Send data to relay address  |");
-    puts("| a      Allocate relay             +--------------------------------+	");
-    puts("| s,ss   Send data to peer 0/1      |             PEER-1             |");
-    puts("| b,bb   BindChannel to peer 0/1    |                                |");
-    printf("| x      Delete allocation          | Address: %-21s |\n",
+    puts("|                                    | 0  Send data to relay address  |");
+    puts("| a      Allocate relay              |                                |");
+    puts("| p,pp   Set permission for peer 0/1 +--------------------------------+");
+    puts("| s,ss   Send data to peer 0/1       |             PEER-1             |");
+    puts("| b,bb   BindChannel to peer 0/1     |                                |");
+    printf("| x      Delete allocation           | Address: %-21s |\n",
 	  peer1_addr);
-    puts("+-----------------------------------+                                |");
-    puts("| q  Quit                  d  Dump  | 1  Send data to relay adderss  |");
-    puts("+-----------------------------------+--------------------------------+");
+    puts("+------------------------------------+                                |");
+    puts("| q  Quit                  d  Dump   | 1  Send data to relay adderss  |");
+    puts("+------------------------------------+--------------------------------+");
     printf(">>> ");
     fflush(stdout);
@@ -488,4 +496,18 @@
 	    if (status != PJ_SUCCESS)
 		my_perror("turn_udp_bind_channel() failed", status);
+	    break;
+	case 'p':
+	    if (g.relay == NULL) {
+		puts("Error: no relay");
+		continue;
+	    }
+	    if (input[1]!='p')
+		peer = &g.peer[0];
+	    else
+		peer = &g.peer[1];
+
+	    status = pj_turn_sock_set_perm(g.relay, 1, &peer->mapped_addr, 1);
+	    if (status != PJ_SUCCESS)
+		my_perror("pj_turn_sock_set_perm() failed", status);
 	    break;
 	case 'x':
Index: /pjproject/trunk/pjnath/src/pjturn-srv/allocation.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjturn-srv/allocation.c	(revision 2588)
+++ /pjproject/trunk/pjnath/src/pjturn-srv/allocation.c	(revision 2589)
@@ -110,5 +110,4 @@
     pj_stun_req_transport_attr *attr_req_tp;
     pj_stun_res_token_attr *attr_res_token;
-    pj_stun_req_props_attr *attr_rpp;
     pj_stun_lifetime_attr *attr_lifetime;
 
@@ -165,16 +164,4 @@
     }
 
-    /* Get REQUESTED-PROPS attribute, if any */
-    attr_rpp = (pj_stun_req_props_attr*)
-	       pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0);
-    if (attr_rpp) {
-	/* We don't support REQUESTED-PROPS for now */
-	pj_stun_session_respond(sess, rdata, 
-				PJ_STUN_SC_BAD_REQUEST,
-				"REQUESTED-PROPS is not supported", 
-				NULL, PJ_TRUE, src_addr, src_addr_len);
-	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
-    }
-
     /* Get LIFETIME attribute */
     attr_lifetime = (pj_stun_uint_attr*)
@@ -212,7 +199,7 @@
 	return status;
 
-    /* Add RELAYED-ADDRESS attribute */
+    /* Add XOR-RELAYED-ADDRESS attribute */
     pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
-				  PJ_STUN_ATTR_RELAYED_ADDR, PJ_TRUE,
+				  PJ_STUN_ATTR_XOR_RELAYED_ADDR, PJ_TRUE,
 				  &alloc->relay.hkey.addr,
 				  pj_sockaddr_get_len(&alloc->relay.hkey.addr));
@@ -1071,5 +1058,5 @@
 
 	pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, 
-				      PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE,
+				      PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE,
 				      src_addr, pj_sockaddr_get_len(src_addr));
 	pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
@@ -1231,11 +1218,11 @@
 	 */
 	pj_stun_channel_number_attr *ch_attr;
-	pj_stun_peer_addr_attr *peer_attr;
+	pj_stun_xor_peer_addr_attr *peer_attr;
 	pj_turn_permission *p1, *p2;
 
 	ch_attr = (pj_stun_channel_number_attr*)
 		  pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0);
-	peer_attr = (pj_stun_peer_addr_attr*)
-		    pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
+	peer_attr = (pj_stun_xor_peer_addr_attr*)
+		    pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
 
 	if (!ch_attr || !peer_attr) {
@@ -1334,5 +1321,5 @@
 					 unsigned src_addr_len)
 {
-    pj_stun_peer_addr_attr *peer_attr;
+    pj_stun_xor_peer_addr_attr *peer_attr;
     pj_stun_data_attr *data_attr;
     pj_turn_allocation *alloc;
@@ -1354,9 +1341,9 @@
     }
 
-    /* Get PEER-ADDRESS attribute */
-    peer_attr = (pj_stun_peer_addr_attr*)
-		pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0);
-
-    /* MUST have PEER-ADDRESS attribute */
+    /* Get XOR-PEER-ADDRESS attribute */
+    peer_attr = (pj_stun_xor_peer_addr_attr*)
+		pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
+
+    /* MUST have XOR-PEER-ADDRESS attribute */
     if (!peer_attr)
 	return PJ_SUCCESS;
