Index: /pjproject/trunk/pjnath/include/pjnath/config.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/config.h	(revision 1274)
+++ /pjproject/trunk/pjnath/include/pjnath/config.h	(revision 1275)
@@ -89,10 +89,20 @@
 
 /**
- * Maximum number of STUN retransmission count.
+ * Maximum number of STUN transmission count.
  *
  * Default: 7 (as per RFC 3489-bis)
  */
-#ifndef PJ_STUN_MAX_RETRANSMIT_COUNT
-#   define PJ_STUN_MAX_RETRANSMIT_COUNT		    7
+#ifndef PJ_STUN_MAX_TRANSMIT_COUNT
+#   define PJ_STUN_MAX_TRANSMIT_COUNT		    7
+#endif
+
+
+/**
+ * Duration to keep response in the cache, in msec.
+ *
+ * Default: 10000 (as per RFC 3489-bis)
+ */
+#ifndef PJ_STUN_RES_CACHE_DURATION
+#   define PJ_STUN_RES_CACHE_DURATION		    10000
 #endif
 
Index: /pjproject/trunk/pjnath/include/pjnath/stun_auth.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/stun_auth.h	(revision 1274)
+++ /pjproject/trunk/pjnath/include/pjnath/stun_auth.h	(revision 1275)
@@ -263,5 +263,5 @@
 
 /**
- * Verify credential in the STUN message. Note that before calling this
+ * Verify credential in the STUN request. Note that before calling this
  * function, application must have checked that the message contains
  * PJ_STUN_ATTR_MESSAGE_INTEGRITY attribute by calling pj_stun_msg_find_attr()
@@ -287,12 +287,34 @@
  *			\a p_response.
  */
-PJ_DECL(pj_status_t) pj_stun_verify_credential(const pj_uint8_t *pkt,
-					       unsigned pkt_len,
-					       const pj_stun_msg *msg,
-					       pj_stun_auth_cred *cred,
-					       pj_pool_t *pool,
-					       pj_stun_msg **p_response);
-
-
+PJ_DECL(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
+					          unsigned pkt_len,
+					          const pj_stun_msg *msg,
+					          pj_stun_auth_cred *cred,
+					          pj_pool_t *pool,
+					          pj_stun_msg **p_response);
+
+
+/**
+ * Verify credential in the STUN response. Note that before calling this
+ * function, application must have checked that the message contains
+ * PJ_STUN_ATTR_MESSAGE_INTEGRITY attribute by calling pj_stun_msg_find_attr()
+ * function, because otherwise this function will report authentication
+ * failure.
+ *
+ * @param pkt		The original packet which has been parsed into
+ *			the message. This packet MUST NOT have been modified
+ *			after the parsing.
+ * @param pkt_len	The length of the packet.
+ * @param msg		The parsed message to be verified.
+ * @param key		Authentication key to calculate MESSAGE-INTEGRITY
+ *			value. Application can create this key by using
+ *			#pj_stun_create_key() function.
+ *
+ * @return		PJ_SUCCESS if credential is verified successfully.
+ */
+PJ_DECL(pj_status_t) pj_stun_authenticate_response(const pj_uint8_t *pkt,
+					           unsigned pkt_len,
+					           const pj_stun_msg *msg,
+					           const pj_str_t *key);
 
 
@@ -302,20 +324,4 @@
 
 
-/**
- * Calculate HMAC-SHA1 key for long term credential, by getting
- * MD5 digest of username, realm, and password. 
- *
- * @param digest    The buffer for the digest.
- * @param realm	    The realm of the credential, if long term credential
- *		    is to be used.
- * @param username  The username.
- * @param passwd    The clear text password.
- */
-void pj_stun_calc_md5_key(pj_uint8_t digest[16],
-			  const pj_str_t *realm,
-			  const pj_str_t *username,
-			  const pj_str_t *passwd);
-
-
 PJ_END_DECL
 
Index: /pjproject/trunk/pjnath/include/pjnath/stun_config.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/stun_config.h	(revision 1274)
+++ /pjproject/trunk/pjnath/include/pjnath/stun_config.h	(revision 1275)
@@ -99,5 +99,5 @@
     cfg->timer_heap = timer_heap;
     cfg->rto_msec = PJ_STUN_RTO_VALUE;
-    cfg->res_cache_msec = 10000;
+    cfg->res_cache_msec = PJ_STUN_RES_CACHE_DURATION;
 }
 
Index: /pjproject/trunk/pjnath/include/pjnath/stun_msg.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 1274)
+++ /pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 1275)
@@ -133,27 +133,28 @@
 #define PJ_STUN_IS_REQUEST(msg_type)	(((msg_type) & 0x0110) == 0x0000)
 
+/**
+ * Determine if the message type is a successful response.
+ */
+#define PJ_STUN_IS_SUCCESS_RESPONSE(msg_type) (((msg_type) & 0x0110) == 0x0100)
+
+/**
+ * The response bit in the message type.
+ */
+#define PJ_STUN_RESPONSE_BIT		(0x0100)
+
+/**
+ * Determine if the message type is an error response.
+ */
+#define PJ_STUN_IS_ERROR_RESPONSE(msg_type) (((msg_type) & 0x0110) == 0x0110)
+
+/**
+ * The error response bit in the message type.
+ */
+#define PJ_STUN_ERROR_RESPONSE_BIT	(0x0110)
 
 /**
  * Determine if the message type is a response.
  */
-#define PJ_STUN_IS_SUCCESS_RESPONSE(msg_type) (((msg_type) & 0x0110) == 0x0100)
-
-
-/**
- * The response bit in the message type.
- */
-#define PJ_STUN_RESPONSE_BIT		(0x0100)
-
-/**
- * Determine if the message type is an error response.
- */
-#define PJ_STUN_IS_ERROR_RESPONSE(msg_type) (((msg_type) & 0x0110) == 0x0110)
-
-
-/**
- * The error response bit in the message type.
- */
-#define PJ_STUN_ERROR_RESPONSE_BIT	(0x0110)
-
+#define PJ_STUN_IS_RESPONSE(msg_type) (((msg_type) & 0x0100) == 0x0100)
 
 /**
@@ -1149,15 +1150,18 @@
  * sent to remote destination. This function will take care about 
  * calculating the MESSAGE-INTEGRITY digest as well as FINGERPRINT
- * value.
- *
- * If MESSAGE-INTEGRITY attribute is present, the function assumes
- * that application wants to include credential (short or long term)
- * in the message, and this function will calculate the HMAC digest
- * from the message using the supplied password in the parameter. 
- * If REALM attribute is present, the HMAC digest is calculated as
- * long term credential, otherwise as short term credential.
+ * value, if these attributes are present in the message.
+ *
+ * If application wants to apply credential to the message, it MUST
+ * include a blank MESSAGE-INTEGRITY attribute in the message, as the
+ * last attribute or the attribute before FINGERPRINT. This function will
+ * calculate the HMAC digest from the message using  the supplied key in
+ * the parameter. The key should be set to the password if short term 
+ * credential is used, or calculated from the MD5 hash of the realm, 
+ * username, and password using #pj_stun_create_key() if long term 
+ * credential is used.
  *
  * If FINGERPRINT attribute is present, this function will calculate
- * the FINGERPRINT CRC attribute for the message.
+ * the FINGERPRINT CRC attribute for the message. The FINGERPRINT MUST
+ * be added as the last attribute of the message.
  *
  * @param msg		The STUN message to be printed. Upon return,
@@ -1167,7 +1171,7 @@
  * @param buf_size	Size of the buffer.
  * @param options	Options, which currently must be zero.
- * @param password	Password to be used when credential is to be
- *			included. This parameter MUST be specified when
- *			the message contains MESSAGE-INTEGRITY attribute.
+ * @param key		Authentication key to calculate MESSAGE-INTEGRITY
+ *			value. Application can create this key by using
+ *			#pj_stun_create_key() function.
  * @param p_msg_len	Upon return, it will be filed with the size of 
  *			the packet in bytes, or negative value on error.
@@ -1179,6 +1183,30 @@
 				        unsigned buf_size,
 				        unsigned options,
-					const pj_str_t *password,
+					const pj_str_t *key,
 				        unsigned *p_msg_len);
+
+
+/**
+ * Create authentication key to be used for encoding the message with
+ * MESSAGE-INTEGRITY. If short term credential is used (i.e. the realm
+ * argument is NULL or empty), the key will be copied from the password.
+ * If long term credential is used, the key will be calculated from the
+ * MD5 hash of the realm, username, and password.
+ *
+ * @param pool		Pool to allocate memory for the key.
+ * @param key		String to receive the key.
+ * @param realm		The realm of the credential, if long term credential
+ *			is to be used. If short term credential is wanted,
+ *			application can put NULL or empty string here.
+ * @param username	The username.
+ * @param passwd	The clear text password.
+ */
+PJ_DECL(void) pj_stun_create_key(pj_pool_t *pool,
+				 pj_str_t *key,
+				 const pj_str_t *realm,
+				 const pj_str_t *username,
+				 const pj_str_t *passwd);
+
+
 
 /**
Index: /pjproject/trunk/pjnath/include/pjnath/stun_session.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/stun_session.h	(revision 1274)
+++ /pjproject/trunk/pjnath/include/pjnath/stun_session.h	(revision 1275)
@@ -162,4 +162,6 @@
     pj_uint8_t		 msg_key[12];	/**< Message/transaction key.	    */
 
+    pj_str_t		 auth_key;	/**< Auth key.			    */
+
     void		*pkt;		/**< The STUN packet.		    */
     unsigned		 max_len;	/**< Length of packet buffer.	    */
@@ -306,9 +308,9 @@
  * @return	    PJ_SUCCESS on success, or the appropriate error code.
  */
-PJ_DECL(pj_status_t) pj_stun_session_create_response(pj_stun_session *sess,
-						     const pj_stun_msg *req,
-						     unsigned err_code,
-						     const pj_str_t *err_msg,
-						     pj_stun_tx_data **p_tdata);
+PJ_DECL(pj_status_t) pj_stun_session_create_res(pj_stun_session *sess,
+						const pj_stun_msg *req,
+						unsigned err_code,
+						const pj_str_t *err_msg,
+						pj_stun_tx_data **p_tdata);
 
 
Index: /pjproject/trunk/pjnath/src/pjnath/ice_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1275)
@@ -1870,7 +1870,7 @@
     /* Reject any requests except Binding request */
     if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
-	status = pj_stun_session_create_response(sess, msg, 
-						 PJ_STUN_SC_BAD_REQUEST,
-						 NULL, &tdata);
+	status = pj_stun_session_create_res(sess, msg, 
+					    PJ_STUN_SC_BAD_REQUEST,
+					    NULL, &tdata);
 	if (status != PJ_SUCCESS)
 	    return status;
@@ -1928,7 +1928,7 @@
 	} else {
 	    /* Generate 487 response */
-	    status = pj_stun_session_create_response(sess, msg, 
-					             PJ_STUN_SC_ROLE_CONFLICT,
-						     NULL, &tdata);
+	    status = pj_stun_session_create_res(sess, msg, 
+					        PJ_STUN_SC_ROLE_CONFLICT,
+						NULL, &tdata);
 	    if (status == PJ_SUCCESS) {
 		pj_stun_session_send_msg(sess, PJ_TRUE, 
@@ -1944,7 +1944,7 @@
 	if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) {
 	    /* Generate 487 response */
-	    status = pj_stun_session_create_response(sess, msg, 
-					             PJ_STUN_SC_ROLE_CONFLICT,
-						     NULL, &tdata);
+	    status = pj_stun_session_create_res(sess, msg, 
+					        PJ_STUN_SC_ROLE_CONFLICT,
+						NULL, &tdata);
 	    if (status == PJ_SUCCESS) {
 		pj_stun_session_send_msg(sess, PJ_TRUE, 
@@ -1977,5 +1977,5 @@
      * First send response to this request 
      */
-    status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata);
+    status = pj_stun_session_create_res(sess, msg, 0, NULL, &tdata);
     if (status != PJ_SUCCESS) {
 	pj_mutex_unlock(ice->mutex);
Index: /pjproject/trunk/pjnath/src/pjnath/stun_auth.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_auth.c	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_auth.c	(revision 1275)
@@ -105,11 +105,11 @@
 
 
-/* Verify credential */
-PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt,
-					       unsigned pkt_len,
-					       const pj_stun_msg *msg,
-					       pj_stun_auth_cred *cred,
-					       pj_pool_t *pool,
-					       pj_stun_msg **p_response)
+/* Verify credential in the request */
+PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
+					         unsigned pkt_len,
+					         const pj_stun_msg *msg,
+					         pj_stun_auth_cred *cred,
+					         pj_pool_t *pool,
+					         pj_stun_msg **p_response)
 {
     pj_str_t realm, nonce, password;
@@ -122,5 +122,4 @@
     pj_hmac_sha1_context ctx;
     pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE];
-    pj_uint8_t md5_digest[16];
     pj_str_t key;
     pj_status_t status;
@@ -225,5 +224,5 @@
 
 	/* NONCE must be present. */
-	if (anonce == NULL) {
+	if (anonce == NULL && nonce.slen) {
 	    if (p_response) {
 		create_challenge(pool, msg, PJ_STUN_SC_MISSING_NONCE, 
@@ -320,12 +319,6 @@
     }
 
-    /* Determine which key to use */
-    if (realm.slen) {
-	pj_stun_calc_md5_key(md5_digest, &realm, &auser->value, &password);
-	key.ptr = (char*)md5_digest;
-	key.slen = 16;
-    } else {
-	key = password;
-    }
+    /* Calculate key */
+    pj_stun_create_key(pool, &key, &realm, &auser->value, &password);
 
     /* Now calculate HMAC of the message, adding zero padding if necessary
@@ -334,8 +327,8 @@
     pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen);
     pj_hmac_sha1_update(&ctx, pkt, amsgi_pos);
-    if (amsgi_pos & 0x3F) {
+    if (amsgi_pos & 63) {
 	pj_uint8_t zeroes[64];
 	pj_bzero(zeroes, sizeof(zeroes));
-	pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 0x3F));
+	pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 63));
     }
     pj_hmac_sha1_final(&ctx, digest);
@@ -356,2 +349,76 @@
 
 
+/* Authenticate MESSAGE-INTEGRITY in the response */
+PJ_DEF(pj_status_t) pj_stun_authenticate_response(const pj_uint8_t *pkt,
+					          unsigned pkt_len,
+					          const pj_stun_msg *msg,
+					          const pj_str_t *key)
+{
+    const pj_stun_msgint_attr *amsgi;
+    unsigned amsgi_pos;
+    pj_hmac_sha1_context ctx;
+    pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE];
+
+    PJ_ASSERT_RETURN(pkt && pkt_len && msg && key, PJ_EINVAL);
+
+    /* First check that MESSAGE-INTEGRITY is present */
+    amsgi = (const pj_stun_msgint_attr*)
+	    pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0);
+    if (amsgi == NULL) {
+	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
+    }
+
+
+    /* Check that message length is valid */
+    if (msg->hdr.length < 24) {
+	return PJNATH_EINSTUNMSGLEN;
+    }
+
+    /* Get the position of MESSAGE-INTEGRITY in the packet */
+    amsgi_pos = 20+msg->hdr.length-24;
+    if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
+	/* Found MESSAGE-INTEGRITY as the last attribute */
+    } else {
+	amsgi_pos = 0;
+    }
+    
+    if (amsgi_pos==0) {
+	/* Check that message length is valid */
+	if (msg->hdr.length < 32) {
+	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
+	}
+
+	amsgi_pos = 20+msg->hdr.length-8-24;
+	if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
+	    /* Found MESSAGE-INTEGRITY before FINGERPRINT */
+	} else {
+	    amsgi_pos = 0;
+	}
+    }
+
+    if (amsgi_pos==0) {
+	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
+    }
+
+    /* Now calculate HMAC of the message, adding zero padding if necessary
+     * to make the input 64 bytes aligned.
+     */
+    pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key->ptr, key->slen);
+    pj_hmac_sha1_update(&ctx, pkt, amsgi_pos);
+    if (amsgi_pos & 0x3F) {
+	pj_uint8_t zeroes[64];
+	pj_bzero(zeroes, sizeof(zeroes));
+	pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 0x3F));
+    }
+    pj_hmac_sha1_final(&ctx, digest);
+
+    /* Compare HMACs */
+    if (pj_memcmp(amsgi->hmac, digest, 20)) {
+	/* HMAC value mismatch */
+	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
+    }
+
+    /* Everything looks okay! */
+    return PJ_SUCCESS;
+}
+
Index: /pjproject/trunk/pjnath/src/pjnath/stun_msg.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1275)
@@ -18,5 +18,4 @@
  */
 #include <pjnath/stun_msg.h>
-#include <pjnath/stun_auth.h>
 #include <pjnath/errno.h>
 #include <pjlib-util/crc32.h>
@@ -1949,8 +1948,8 @@
  * MD5 digest of username, realm, and password. 
  */
-void pj_stun_calc_md5_key(pj_uint8_t digest[16],
-			  const pj_str_t *realm,
-			  const pj_str_t *username,
-			  const pj_str_t *passwd)
+static void calc_md5_key(pj_uint8_t digest[16],
+			 const pj_str_t *realm,
+			 const pj_str_t *username,
+			 const pj_str_t *passwd)
 {
     /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking
@@ -1967,7 +1966,7 @@
 
 #define REMOVE_QUOTE(s)	if (s.slen && *s.ptr=='"') \
-		    s.ptr++, s.slen--; \
-		if (s.slen && s.ptr[s.slen-1]=='"') \
-		    s.slen--;
+			    s.ptr++, s.slen--; \
+			if (s.slen && s.ptr[s.slen-1]=='"') \
+			    s.slen--;
 
     /* Add username */
@@ -1998,4 +1997,26 @@
 
 /*
+ * Create authentication key to be used for encoding the message with
+ * MESSAGE-INTEGRITY. 
+ */
+PJ_DEF(void) pj_stun_create_key(pj_pool_t *pool,
+				pj_str_t *key,
+				const pj_str_t *realm,
+				const pj_str_t *username,
+				const pj_str_t *passwd)
+{
+    PJ_ASSERT_ON_FAIL(pool && key && username && passwd, return);
+
+    if (realm && realm->slen) {
+	key->ptr = (char*) pj_pool_alloc(pool, 16);
+	calc_md5_key((pj_uint8_t*)key->ptr, realm, username, passwd);
+	key->slen = 16;
+    } else {
+	pj_strdup(pool, key, passwd);
+    }
+}
+
+
+/*
 static char *print_binary(const pj_uint8_t *data, unsigned data_len)
 {
@@ -2029,14 +2050,11 @@
 				       pj_uint8_t *buf, unsigned buf_size,
 				       unsigned options,
-				       const pj_str_t *password,
+				       const pj_str_t *key,
 				       unsigned *p_msg_len)
 {
-    pj_stun_msg_hdr *hdr;
     pj_uint8_t *start = buf;
-    pj_stun_realm_attr *arealm = NULL;
-    pj_stun_username_attr *auname = NULL;
     pj_stun_msgint_attr *amsgint = NULL;
     pj_stun_fingerprint_attr *afingerprint = NULL;
-    unsigned printed = 0;
+    unsigned printed = 0, body_len;
     pj_status_t status;
     unsigned i;
@@ -2053,14 +2071,14 @@
     if (buf_size < sizeof(pj_stun_msg_hdr))
 	return PJ_ETOOSMALL;
-    pj_memcpy(buf, &msg->hdr, sizeof(pj_stun_msg_hdr));
-    hdr = (pj_stun_msg_hdr*) buf;
-    hdr->magic = pj_htonl(hdr->magic);
-    hdr->type = pj_htons(hdr->type);
-    /* We'll fill in the length later */
+    
+    PUTVAL16H(buf, 0, msg->hdr.type);
+    PUTVAL16H(buf, 2, 0);   /* length will be calculated later */
+    PUTVAL32H(buf, 4, msg->hdr.magic);
+    pj_memcpy(buf+8, msg->hdr.tsx_id, sizeof(msg->hdr.tsx_id));
 
     buf += sizeof(pj_stun_msg_hdr);
     buf_size -= sizeof(pj_stun_msg_hdr);
 
-    /* Print each attribute */
+    /* Encode each attribute to the message */
     for (i=0; i<msg->attr_count; ++i) {
 	const struct attr_desc *adesc;
@@ -2073,12 +2091,4 @@
 	    /* Stop when encountering MESSAGE-INTEGRITY */
 	    break;
-
-	} else if (attr_hdr->type == PJ_STUN_ATTR_USERNAME) {
-	    pj_assert(auname == NULL);
-	    auname = (pj_stun_username_attr*) attr_hdr;
-
-	} else if (attr_hdr->type == PJ_STUN_ATTR_REALM) {
-	    pj_assert(arealm == NULL);
-	    arealm = (pj_stun_realm_attr*) attr_hdr;
 
 	} else if (attr_hdr->type == PJ_STUN_ATTR_FINGERPRINT) {
@@ -2124,23 +2134,22 @@
      */
     if (amsgint && afingerprint) {
-	msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24 + 8);
+	body_len = (pj_uint16_t)((buf - start) - 20 + 24 + 8);
     } else if (amsgint) {
-	msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24);
+	body_len = (pj_uint16_t)((buf - start) - 20 + 24);
     } else if (afingerprint) {
-	msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 8);
+	body_len = (pj_uint16_t)((buf - start) - 20 + 8);
     } else {
-	msg->hdr.length = (pj_uint16_t)((buf - start) - 20);
+	body_len = (pj_uint16_t)((buf - start) - 20);
     }
 
     /* hdr->length = pj_htons(length); */
-    start[2] = (pj_uint8_t)((msg->hdr.length >> 8) & 0x00FF);
-    start[3] = (pj_uint8_t)(msg->hdr.length & 0x00FF);
+    PUTVAL16H(start, 2, (pj_uint16_t)body_len);
 
     /* Calculate message integrity, if present */
     if (amsgint != NULL) {
-
-	pj_uint8_t md5_key_buf[16];
 	pj_hmac_sha1_context ctx;
-	pj_str_t key;
+
+	/* Key MUST be specified */
+	PJ_ASSERT_RETURN(key, PJ_EINVALIDOP);
 
 	/* MESSAGE-INTEGRITY must be the last attribute in the message, or
@@ -2162,30 +2171,8 @@
 	}
 
-	/* Must have USERNAME attribute */
-	if (auname == NULL) {
-	    /* Should not happen for message generated by us */
-	    pj_assert(PJ_FALSE);
-	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_USERNAME);
-	}
-
-	/* Password must be specified */
-	PJ_ASSERT_RETURN(password, PJ_EINVAL);
-
-	/* Get the key to sign the message */
-	if (arealm == NULL ) {
-	    /* For short term credential, the key is the password */
-	    key = *password;
-
-	} else {
-	    pj_stun_calc_md5_key(md5_key_buf, &arealm->value, 
-				 &auname->value, password);
-	    key.ptr = (char*) md5_key_buf;
-	    key.slen = 16;
-	}
-
 	/* Calculate HMAC-SHA1 digest, add zero padding to input
 	 * if necessary to make the input 64 bytes aligned.
 	 */
-	pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen);
+	pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key->ptr, key->slen);
 	pj_hmac_sha1_update(&ctx, (pj_uint8_t*)start, buf-start);
 	if ((buf-start) & 0x3F) {
@@ -2221,5 +2208,8 @@
     }
 
-    /* Done */
+    /* Update message length. */
+    msg->hdr.length = (pj_uint16_t) ((buf - start) - 20);
+
+    /* Return the length */
     if (p_msg_len)
 	*p_msg_len = (buf - start);
Index: /pjproject/trunk/pjnath/src/pjnath/stun_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_session.c	(revision 1275)
@@ -212,11 +212,16 @@
 }
 
-static pj_str_t *get_passwd(pj_stun_session *sess, pj_pool_t *pool,
-			    const pj_stun_msg *msg)
+static pj_status_t get_key(pj_stun_session *sess, pj_pool_t *pool,
+			   const pj_stun_msg *msg, pj_str_t *auth_key)
 {
     if (sess->cred == NULL) {
-	return NULL;
+	auth_key->slen = 0;
+	return PJ_SUCCESS;
     } else if (sess->cred->type == PJ_STUN_AUTH_CRED_STATIC) {
-	return &sess->cred->data.static_cred.data;
+	pj_stun_create_key(pool, auth_key, 
+			   &sess->cred->data.static_cred.realm,
+			   &sess->cred->data.static_cred.username,
+			   &sess->cred->data.static_cred.data);
+	return PJ_SUCCESS;
     } else if (sess->cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) {
 	pj_str_t realm, username, nonce;
@@ -232,8 +237,15 @@
 						       &nonce, &data_type,
 						       password);
-	return password;
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	pj_stun_create_key(pool, auth_key, 
+			   &realm, &username, password);
+
+	return PJ_SUCCESS;
 
     } else {
-	return NULL;
+	pj_assert(!"Unknown credential type");
+	return PJ_EBUG;
     }
 }
@@ -244,4 +256,5 @@
 {
     pj_status_t status = 0;
+    pj_bool_t need_auth;
     pj_str_t realm, username, nonce, password;
     int data_type = 0;
@@ -250,18 +263,14 @@
 
     /* The server SHOULD include a SERVER attribute in all responses */
-    if (sess->srv_name.slen && (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) ||
-			        PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) 
-    {
+    if (sess->srv_name.slen && PJ_STUN_IS_RESPONSE(msg->hdr.type)) {
 	pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SERVER,
 				    &sess->srv_name);
     }
 
-    /* From draft-ietf-behave-rfc3489bis-05.txt
-     * Section 8.3.1.  Formulating the Request Message
-     *
-     * Note: only put MESSAGE-INTEGRITY in non error response.
-     */
+    need_auth = PJ_STUN_IS_REQUEST(msg->hdr.type) ||
+	        PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type);
+
     if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_STATIC &&
-	!PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) 
+	need_auth)
     {
 	realm = sess->cred->data.static_cred.realm;
@@ -272,5 +281,5 @@
 
     } else if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_DYNAMIC &&
-	       !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) 
+	       need_auth) 
     {
 	void *user_data = sess->cred->data.dyn_cred.user_data;
@@ -285,6 +294,6 @@
 
 
-    /* Create and add USERNAME attribute */
-    if (username.slen) {
+    /* Create and add USERNAME attribute for */
+    if (username.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) {
 	status = pj_stun_msg_add_string_attr(pool, msg,
 					     PJ_STUN_ATTR_USERNAME,
@@ -294,5 +303,5 @@
 
     /* Add REALM only when long term credential is used */
-    if (realm.slen) {
+    if (realm.slen &&  PJ_STUN_IS_REQUEST(msg->hdr.type)) {
 	status = pj_stun_msg_add_string_attr(pool, msg,
 					    PJ_STUN_ATTR_REALM,
@@ -302,5 +311,8 @@
 
     /* Add NONCE when desired */
-    if (nonce.slen) {
+    if (nonce.slen && 
+	(PJ_STUN_IS_REQUEST(msg->hdr.type) ||
+	 PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) 
+    {
 	status = pj_stun_msg_add_string_attr(pool, msg,
 					    PJ_STUN_ATTR_NONCE,
@@ -309,5 +321,5 @@
 
     /* Add MESSAGE-INTEGRITY attribute */
-    if (username.slen) {
+    if (username.slen && need_auth) {
 	status = pj_stun_msg_add_msgint_attr(pool, msg);
 	PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
@@ -509,9 +521,9 @@
  * Create a STUN response message.
  */
-PJ_DEF(pj_status_t) pj_stun_session_create_response( pj_stun_session *sess,
-						     const pj_stun_msg *req,
-						     unsigned err_code,
-						     const pj_str_t *err_msg,
-						     pj_stun_tx_data **p_tdata)
+PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
+						const pj_stun_msg *req,
+						unsigned err_code,
+						const pj_str_t *err_msg,
+						pj_stun_tx_data **p_tdata)
 {
     pj_status_t status;
@@ -598,8 +610,16 @@
     }
 
+    status = get_key(sess, tdata->pool, tdata->msg, &tdata->auth_key);
+    if (status != PJ_SUCCESS) {
+	pj_stun_msg_destroy_tdata(sess, tdata);
+	pj_mutex_unlock(sess->mutex);
+	LOG_ERR_(sess, "Error getting creadential's key", status);
+	return status;
+    }
+
     /* Encode message */
     status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt, 
     				tdata->max_len, 0, 
-    				get_passwd(sess, tdata->pool, tdata->msg),
+    				&tdata->auth_key,
 				&tdata->pkt_size);
     if (status != PJ_SUCCESS) {
@@ -738,4 +758,5 @@
 static pj_status_t send_response(pj_stun_session *sess, 
 				 pj_pool_t *pool, pj_stun_msg *response,
+				 const pj_str_t *auth_key,
 				 pj_bool_t retransmission,
 				 const pj_sockaddr_t *addr, unsigned addr_len)
@@ -758,6 +779,5 @@
     /* Encode */
     status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, 
-				get_passwd(sess, pool, response),
-				&out_len);
+				auth_key, &out_len);
     if (status != PJ_SUCCESS) {
 	LOG_ERR_(sess, "Error encoding message", status);
@@ -775,5 +795,5 @@
 
 /* Authenticate incoming message */
-static pj_status_t authenticate_msg(pj_stun_session *sess,
+static pj_status_t authenticate_req(pj_stun_session *sess,
 				    const pj_uint8_t *pkt,
 				    unsigned pkt_len,
@@ -789,9 +809,9 @@
 	return PJ_SUCCESS;
 
-    status = pj_stun_verify_credential(pkt, pkt_len, msg, sess->cred,
-				       tmp_pool, &response);
+    status = pj_stun_authenticate_request(pkt, pkt_len, msg, sess->cred,
+				          tmp_pool, &response);
     if (status != PJ_SUCCESS && response != NULL) {
 	PJ_LOG(5,(SNAME(sess), "Message authentication failed"));
-	send_response(sess, tmp_pool, response, PJ_FALSE, 
+	send_response(sess, tmp_pool, response, NULL, PJ_FALSE, 
 		      src_addr, src_addr_len);
     }
@@ -803,4 +823,7 @@
 /* Handle incoming response */
 static pj_status_t on_incoming_response(pj_stun_session *sess,
+					unsigned options,
+					const pj_uint8_t *pkt,
+					unsigned pkt_len,
 					pj_stun_msg *msg,
 					const pj_sockaddr_t *src_addr,
@@ -816,4 +839,16 @@
 		  "Transaction not found, response silently discarded"));
 	return PJ_SUCCESS;
+    }
+
+    /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
+     * is specified in the option.
+     */
+    if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) {
+	status = pj_stun_authenticate_response(pkt, pkt_len, msg, &tdata->auth_key);
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(5,(SNAME(sess), 
+		      "Response authentication failed"));
+	    return status;
+	}
     }
 
@@ -867,5 +902,5 @@
 		 "Request retransmission, sending cached response"));
 
-	send_response(sess, tmp_pool, t->msg, PJ_TRUE, 
+	send_response(sess, tmp_pool, t->msg, &t->auth_key, PJ_TRUE, 
 		      src_addr, src_addr_len);
 	return PJ_SUCCESS;
@@ -877,4 +912,5 @@
 /* Handle incoming request */
 static pj_status_t on_incoming_request(pj_stun_session *sess,
+				       unsigned options,
 				       pj_pool_t *tmp_pool,
 				       const pj_uint8_t *in_pkt,
@@ -886,4 +922,15 @@
     pj_status_t status;
 
+    /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
+     * is specified in the option.
+     */
+    if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) {
+	status = authenticate_req(sess, (const pj_uint8_t*) in_pkt, in_pkt_len,
+				  msg, tmp_pool, src_addr, src_addr_len);
+	if (status != PJ_SUCCESS) {
+	    return status;
+	}
+    }
+
     /* Distribute to handler, or respond with Bad Request */
     if (sess->cb.on_rx_request) {
@@ -898,5 +945,5 @@
 	if (status == PJ_SUCCESS && response) {
 	    status = send_response(sess, tmp_pool, response, 
-				   PJ_FALSE, src_addr, src_addr_len);
+				   NULL, PJ_FALSE, src_addr, src_addr_len);
 	}
     }
@@ -953,5 +1000,5 @@
 	LOG_ERR_(sess, "STUN msg_decode() error", status);
 	if (response) {
-	    send_response(sess, tmp_pool, response, 
+	    send_response(sess, tmp_pool, response, NULL,
 			  PJ_FALSE, src_addr, src_addr_len);
 	}
@@ -978,24 +1025,15 @@
     }
 
-    /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
-     * is specified in the option.
-     */
-    if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) {
-	status = authenticate_msg(sess, (const pj_uint8_t*) packet, pkt_size, 
-				  msg, tmp_pool, src_addr, src_addr_len);
-	if (status != PJ_SUCCESS) {
-	    goto on_return;
-	}
-    }
-
     /* Handle message */
     if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) ||
 	PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
     {
-	status = on_incoming_response(sess, msg, src_addr, src_addr_len);
+	status = on_incoming_response(sess, options, 
+				      (const pj_uint8_t*) packet, pkt_size, 
+				      msg, src_addr, src_addr_len);
 
     } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
 
-	status = on_incoming_request(sess, tmp_pool, 
+	status = on_incoming_request(sess, options, tmp_pool, 
 				     (const pj_uint8_t*) packet, pkt_size, 
 				     msg, src_addr, src_addr_len);
Index: /pjproject/trunk/pjnath/src/pjnath/stun_transaction.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 1275)
@@ -189,9 +189,9 @@
 	    tsx->retransmit_time.msec = tsx->cfg->rto_msec;
 
-	} else if (tsx->transmit_count < PJ_STUN_MAX_RETRANSMIT_COUNT-1) {
+	} else if (tsx->transmit_count < PJ_STUN_MAX_TRANSMIT_COUNT-1) {
 	    unsigned msec;
 
 	    msec = PJ_TIME_VAL_MSEC(tsx->retransmit_time);
-	    msec = (msec << 1) + 100;
+	    msec <<= 1;
 	    tsx->retransmit_time.sec = msec / 1000;
 	    tsx->retransmit_time.msec = msec % 1000;
@@ -217,4 +217,9 @@
 
 
+    tsx->transmit_count++;
+
+    PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)",
+	      tsx->transmit_count));
+
     /* Send message */
     status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size);
@@ -229,8 +234,4 @@
     }
 
-    tsx->transmit_count++;
-
-    PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)",
-	      tsx->transmit_count));
     return status;
 }
@@ -269,5 +270,5 @@
     PJ_UNUSED_ARG(timer_heap);
 
-    if (tsx->transmit_count >= PJ_STUN_MAX_RETRANSMIT_COUNT) {
+    if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) {
 	/* Retransmission count exceeded. Transaction has failed */
 	tsx->retransmit_timer.id = 0;
Index: /pjproject/trunk/pjnath/src/pjstun-client/client_main.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjstun-client/client_main.c	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjstun-client/client_main.c	(revision 1275)
@@ -108,4 +108,13 @@
 	    {
 		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*)
@@ -120,4 +129,8 @@
 		} else {
 		    pj_memset(&g.relay_addr, 0, sizeof(g.relay_addr));
+		}
+
+		if (al->value == 0) {
+		    PJ_LOG(3,(THIS_FILE, "Relay deallocated"));
 		}
 	    }
@@ -633,5 +646,5 @@
     g.data = g.data_buf;
 
-    while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) {
+    while((c=pj_getopt_long(argc,argv, "r:u:p:N:hF", long_options, &opt_id))!=-1) {
 	switch (c) {
 	case 'r':
Index: /pjproject/trunk/pjnath/src/pjstun-srv-test/bind_usage.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjstun-srv-test/bind_usage.c	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjstun-srv-test/bind_usage.c	(revision 1275)
@@ -158,5 +158,5 @@
 
     /* Create response */
-    status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata);
+    status = pj_stun_session_create_res(sess, msg, 0, NULL, &tdata);
     if (status != PJ_SUCCESS)
 	return status;
Index: /pjproject/trunk/pjnath/src/pjstun-srv-test/main.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjstun-srv-test/main.c	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjstun-srv-test/main.c	(revision 1275)
@@ -79,7 +79,8 @@
     pj_caching_pool cp;
     pj_stun_server *srv;
+    pj_stun_usage *turn;
     pj_status_t status;
 
-    while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) {
+    while((c=pj_getopt_long(argc,argv, "r:u:p:N:hF", long_options, &opt_id))!=-1) {
 	switch (c) {
 	case 'r':
@@ -132,8 +133,20 @@
 
     status = pj_stun_turn_usage_create(srv, PJ_SOCK_DGRAM, NULL,
-				       3478, NULL);
+				       3478, &turn);
     if (status != PJ_SUCCESS) {
 	pj_stun_perror(THIS_FILE, "Unable to create bind usage", status);
 	return 1;
+    }
+
+    if (o.user_name && o.password) {
+	pj_stun_auth_cred cred;
+	pj_bzero(&cred, sizeof(cred));
+	cred.type = PJ_STUN_AUTH_CRED_STATIC;
+	cred.data.static_cred.realm = pj_str(o.realm);
+	cred.data.static_cred.username = pj_str(o.user_name);
+	cred.data.static_cred.data_type = 0;
+	cred.data.static_cred.data = pj_str(o.password);
+	cred.data.static_cred.nonce = pj_str(o.nonce);
+	pj_stun_turn_usage_set_credential(turn, &cred);
     }
 
Index: /pjproject/trunk/pjnath/src/pjstun-srv-test/server.h
===================================================================
--- /pjproject/trunk/pjnath/src/pjstun-srv-test/server.h	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjstun-srv-test/server.h	(revision 1275)
@@ -125,4 +125,6 @@
 					       pj_stun_usage **p_bu);
 
+PJ_DECL(pj_status_t) pj_stun_turn_usage_set_credential(pj_stun_usage *turn,
+						       const pj_stun_auth_cred *cred);
 
 pj_status_t pj_stun_server_register_usage(pj_stun_server *srv,
Index: /pjproject/trunk/pjnath/src/pjstun-srv-test/turn_usage.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjstun-srv-test/turn_usage.c	(revision 1274)
+++ /pjproject/trunk/pjnath/src/pjstun-srv-test/turn_usage.c	(revision 1275)
@@ -79,4 +79,5 @@
     pj_stun_session	*default_session;
     pj_hash_table_t	*client_htable;
+    pj_stun_auth_cred	*cred;
 
     unsigned		 max_bw_kbps;
@@ -222,4 +223,17 @@
     }
 
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_stun_turn_usage_set_credential(pj_stun_usage *turn,
+						      const pj_stun_auth_cred *c)
+{
+    struct turn_usage *tu;
+    tu = (struct turn_usage*) pj_stun_usage_get_user_data(turn);
+
+    tu->cred = PJ_POOL_ZALLOC_T(tu->pool, pj_stun_auth_cred);
+    pj_stun_auth_cred_dup(tu->pool, tu->cred, c);
+    pj_stun_session_set_credential(tu->default_session, tu->cred);
     return PJ_SUCCESS;
 }
@@ -442,7 +456,7 @@
     } else if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) {
 	if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
-	    status = pj_stun_session_create_response(sess, msg, 
-						     PJ_STUN_SC_NO_BINDING,
-						     NULL, &tdata);
+	    status = pj_stun_session_create_res(sess, msg, 
+						PJ_STUN_SC_NO_BINDING,
+						NULL, &tdata);
 	    if (status==PJ_SUCCESS) {
 		status = pj_stun_session_send_msg(sess, PJ_FALSE, 
@@ -631,4 +645,7 @@
     }
 
+    if (tu->cred)
+	pj_stun_session_set_credential(client->session, tu->cred);
+
     sd = PJ_POOL_ZALLOC_T(pool, struct session_data);
     sd->tu = tu;
@@ -864,7 +881,7 @@
 	pj_cstr(&err_msg, custom_msg), p_err_msg = &err_msg;
     
-    status = pj_stun_session_create_response(client->session, msg, 
-					     err_code, p_err_msg, 
-					     &response);
+    status = pj_stun_session_create_res(client->session, msg, 
+					err_code, p_err_msg, 
+					&response);
     if (status == PJ_SUCCESS)
 	status = pj_stun_session_send_msg(client->session, PJ_TRUE,
@@ -1015,6 +1032,6 @@
 
     /* Done successfully, create and send success response */
-    status = pj_stun_session_create_response(client->session, msg, 
-					     0, NULL, &response);
+    status = pj_stun_session_create_res(client->session, msg, 
+					0, NULL, &response);
     if (status != PJ_SUCCESS) {
 	return status;
@@ -1063,6 +1080,6 @@
 
     /* Create response */
-    status = pj_stun_session_create_response(session, msg, 0, NULL, 
-					     &tdata);
+    status = pj_stun_session_create_res(session, msg, 0, NULL, 
+					&tdata);
     if (status != PJ_SUCCESS)
 	return status;
Index: /pjproject/trunk/third_party/build/Makefile
===================================================================
--- /pjproject/trunk/third_party/build/Makefile	(revision 1274)
+++ /pjproject/trunk/third_party/build/Makefile	(revision 1275)
@@ -1,2 +1,4 @@
+DIRS = resample
+
 include ../../build.mak
 include $(PJDIR)/build/common.mak
Index: /pjproject/trunk/third_party/build/os-auto.mak.in
===================================================================
--- /pjproject/trunk/third_party/build/os-auto.mak.in	(revision 1274)
+++ /pjproject/trunk/third_party/build/os-auto.mak.in	(revision 1275)
@@ -1,3 +1,2 @@
-DIRS = resample
 
 ifneq (@ac_no_gsm_codec@,1)
