Index: pjproject/trunk/pjnath/include/pjnath/config.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/config.h	(revision 1438)
+++ pjproject/trunk/pjnath/include/pjnath/config.h	(revision 1439)
@@ -50,5 +50,5 @@
 
 /* **************************************************************************
- * STUN CLIENT CONFIGURATION
+ * STUN CONFIGURATION
  */
 
@@ -81,8 +81,8 @@
  * after this time, the STUN transaction will be considered to have failed.
  *
- * The default value is 1600 miliseconds (as per RFC 3489-bis).
+ * The default value is 16x RTO (as per RFC 3489-bis).
  */
 #ifndef PJ_STUN_TIMEOUT_VALUE
-#   define PJ_STUN_TIMEOUT_VALUE		    1600
+#   define PJ_STUN_TIMEOUT_VALUE		    (16 * PJ_STUN_RTO_VALUE)
 #endif
 
@@ -121,4 +121,13 @@
 #define PJ_STUN_PORT				    3478
 
+
+/**
+ * Padding character for string attributes.
+ *
+ * Default: ASCII 0
+ */
+#ifndef PJ_STUN_STRING_ATTR_PAD_CHR
+#   define PJ_STUN_STRING_ATTR_PAD_CHR		    0
+#endif
 
 
Index: pjproject/trunk/pjnath/include/pjnath/stun_msg.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 1438)
+++ pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 1439)
@@ -281,5 +281,5 @@
     PJ_STUN_ATTR_CHANGED_ADDR	    = 0x0005,/**< CHANGED-ADDRESS (deprecatd)*/
     PJ_STUN_ATTR_USERNAME	    = 0x0006,/**< USERNAME attribute.	    */
-    PJ_STUN_ATTR_PASSWORD	    = 0x0007,/**< PASSWORD attribute.	    */
+    PJ_STUN_ATTR_PASSWORD	    = 0x0007,/**< was PASSWORD attribute.   */
     PJ_STUN_ATTR_MESSAGE_INTEGRITY  = 0x0008,/**< MESSAGE-INTEGRITY.	    */
     PJ_STUN_ATTR_ERROR_CODE	    = 0x0009,/**< ERROR-CODE.		    */
@@ -330,12 +330,15 @@
     PJ_STUN_SC_UNAUTHORIZED	        = 401,  /**< Unauthorized	    */
     PJ_STUN_SC_UNKNOWN_ATTRIBUTE        = 420,  /**< Unknown Attribute	    */
-    PJ_STUN_SC_STALE_CREDENTIALS        = 430,  /**< Stale Credentials	    */
-    PJ_STUN_SC_INTEGRITY_CHECK_FAILURE  = 431,  /**< Integrity Chk Fail	    */
-    PJ_STUN_SC_MISSING_USERNAME		= 432,  /**< Missing Username	    */
-    PJ_STUN_SC_USE_TLS			= 433,  /**< Use TLS		    */
-    PJ_STUN_SC_MISSING_REALM		= 434,  /**< Missing Realm	    */
-    PJ_STUN_SC_MISSING_NONCE		= 435,  /**< Missing Nonce	    */
-    PJ_STUN_SC_UNKNOWN_USERNAME		= 436,  /**< Unknown Username	    */
-    PJ_STUN_SC_NO_BINDING	        = 437,  /**< No Binding.	    */
+#if 0
+    /* These were obsolete in recent rfc3489bis */
+    //PJ_STUN_SC_STALE_CREDENTIALS      = 430,  /**< Stale Credentials	    */
+    //PJ_STUN_SC_INTEGRITY_CHECK_FAILURE= 431,  /**< Integrity Chk Fail	    */
+    //PJ_STUN_SC_MISSING_USERNAME	= 432,  /**< Missing Username	    */
+    //PJ_STUN_SC_USE_TLS		= 433,  /**< Use TLS		    */
+    //PJ_STUN_SC_MISSING_REALM		= 434,  /**< Missing Realm	    */
+    //PJ_STUN_SC_MISSING_NONCE		= 435,  /**< Missing Nonce	    */
+    //PJ_STUN_SC_UNKNOWN_USERNAME	= 436,  /**< Unknown Username	    */
+    //PJ_STUN_SC_NO_BINDING	        = 437,  /**< No Binding.	    */
+#endif
     PJ_STUN_SC_STALE_NONCE	        = 438,  /**< Stale Nonce	    */
     PJ_STUN_SC_TRANSITIONING		= 439,  /**< Transitioning.	    */
@@ -1092,4 +1095,13 @@
 
 /**
+ * Internal: set the padding character for string attribute.
+ * The default padding character is PJ_STUN_STRING_ATTR_PAD_CHR.
+ *
+ * @return		The previous padding character.
+ */
+PJ_DECL(int) pj_stun_set_padding_char(int chr);
+
+
+/**
  * Create a generic STUN message.
  *
Index: pjproject/trunk/pjnath/include/pjnath/types.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/types.h	(revision 1438)
+++ pjproject/trunk/pjnath/include/pjnath/types.h	(revision 1439)
@@ -143,6 +143,6 @@
  * References for STUN:
  *
- *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-06.txt">
- *    <B>draft-ietf-behave-rfc3489bis-06</b></A>: Session Traversal 
+ *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-10.txt">
+ *    <B>draft-ietf-behave-rfc3489bis-10</b></A>: Session Traversal 
  *     Utilities for (NAT) (STUN),
  *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-03.txt">
Index: pjproject/trunk/pjnath/src/pjnath-test/stun.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/stun.c	(revision 1438)
+++ pjproject/trunk/pjnath/src/pjnath-test/stun.c	(revision 1439)
@@ -17,39 +17,389 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
+#include "test.h"
+
+#define THIS_FILE   "stun.c"
+
+static pj_stun_msg* create1(pj_pool_t*);
+static int verify1(pj_stun_msg*);
+static int verify2(pj_stun_msg*);
+static int verify5(pj_stun_msg*);
+
+static struct test
+{
+    const char    *title;
+    char	      *pdu;
+    unsigned       pdu_len;
+    pj_stun_msg* (*create)(pj_pool_t*);
+    pj_status_t    expected_status;
+    int          (*verify)(pj_stun_msg*);
+} tests[] = 
+{
+    {
+	"Invalid message type",
+	"\x11\x01\x00\x00\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	20,
+	NULL,
+	PJNATH_EINSTUNMSGTYPE,
+	NULL
+    },
+    {
+	"Short message (1) (partial header)",
+	"\x00\x01",
+	2,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Short message (2) (partial header)",
+	"\x00\x01\x00\x00\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00",
+	16,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Short message (3), (missing attribute)",
+	"\x00\x01\x00\x08\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	20,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Short message (4), (partial attribute header)",
+	"\x00\x01\x00\x08\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\x28",
+	22,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Short message (5), (partial attribute header)",
+	"\x00\x01\x00\x08\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\x28\x00",
+	23,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Short message (6), (partial attribute header)",
+	"\x00\x01\x00\x08\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\x28\x00\x04",
+	24,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Short message (7), (partial attribute body)",
+	"\x00\x01\x00\x08\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\x28\x00\x04\x00\x00\x00",
+	27,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Message length in header is too long",
+	"\x00\x01\xff\xff\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\x28\x00\x04\x00\x00\x00",
+	27,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Message length in header is shorter",
+	"\x00\x01\x00\x04\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\x28\x00\x04\x00\x00\x00\x00",
+	28,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Invalid magic",
+	"\x00\x01\x00\x08\x00\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\x28\x00\x04\x00\x00\x00\x00",
+	28,
+	NULL,
+	PJ_SUCCESS,
+	NULL
+    },
+    {
+	"Character beyond message",
+	"\x00\x01\x00\x08\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\x28\x00\x04\x00\x00\x00\x00\x0a",
+	29,
+	NULL,
+	PJNATH_EINSTUNMSGLEN,
+	NULL
+    },
+    {
+	"Respond unknown mandatory attribute with 420 and "
+	"UNKNOWN-ATTRIBUTES attribute",
+	NULL,
+	0,
+	&create1,
+	0,
+	&verify1
+    },
+    {
+	"Unknown but non-mandatory should be okay",
+	"\x00\x01\x00\x08\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\xff\x00\x04\x00\x00\x00\x00",
+	28,
+	NULL,
+	PJ_SUCCESS,
+	&verify2
+    },
+    {
+	"String attr length larger than message",
+	"\x00\x01\x00\x08\x00\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x06\x00\xff\x00\x00\x00\x00",
+	28,
+	NULL,
+	PJNATH_ESTUNINATTRLEN,
+	NULL
+    },
+    {
+	"Attribute other than FINGERPRINT after MESSAGE-INTEGRITY is allowed",
+	"\x00\x01\x00\x20\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x08\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+			"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // M-I
+	"\x80\x24\x00\x04\x00\x00\x00\x00",  // REFRESH-INTERVAL
+	52,
+	NULL,
+	PJ_SUCCESS,
+	NULL
+    },
+    {
+	"Attribute between MESSAGE-INTEGRITY and FINGERPRINT is allowed", 
+	"\x00\x01\x00\x28\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x08\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+			"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // M-I
+	"\x80\x24\x00\x04\x00\x00\x00\x00"  // REFRESH-INTERVAL
+	"\x80\x28\x00\x04\xc7\xde\xdd\x65", // FINGERPRINT
+	60,
+	NULL,
+	PJ_SUCCESS,
+	&verify5
+    },
+    {
+	"Attribute past FINGERPRINT is not allowed", 
+	"\x00\x01\x00\x10\x21\x12\xa4\x42"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x80\x28\x00\x04\x00\x00\x00\x00"
+	"\x80\x24\x00\x04\x00\x00\x00\x00",
+	36,
+	NULL,
+	PJNATH_ESTUNFINGERPOS,
+	NULL
+    }
+};
+
+static const char *err(pj_status_t status)
+{
+    static char errmsg[PJ_ERR_MSG_SIZE];
+    pj_strerror(status, errmsg, sizeof(errmsg));
+    return errmsg;
+}
+
+static const pj_str_t USERNAME = {"user", 4};
+static const pj_str_t PASSWORD = {"password", 8};
 
 static int decode_test(void)
 {
-    /* Invalid message type */
-
-    /* Short message */
-
-    /* Long, random message */
-
-    /* Message length in header is shorter */
-
-    /* Message length in header is longer */
-
-    /* Invalid magic */
-
-    /* Attribute length is not valid */
-
-    /* Unknown mandatory attribute type should generate error */
-
-    /* Unknown but non-mandatory should be okay */
-
-    /* String/binary attribute length is larger than the message */
-
-    /* Valid message with MESSAGE-INTEGRITY */
-
-    /* Valid message with FINGERPRINT */
-
-    /* Valid message with MESSAGE-INTEGRITY and FINGERPRINT */
-
-    /* Another attribute not FINGERPRINT exists after MESSAGE-INTEGRITY */
-
-    /* Another attribute exists after FINGERPRINT */
+    unsigned i;
+    pj_pool_t *pool;
+    int rc = 0;
+    
+    pool = pj_pool_create(mem, "decode_test", 1024, 1024, NULL);
+
+    PJ_LOG(3,(THIS_FILE, "  STUN decode test"));
+
+    for (i=0; i<PJ_ARRAY_SIZE(tests); ++i) {
+	struct test *t = &tests[i];
+	pj_stun_msg *msg, *msg2;
+	pj_uint8_t buf[1500];
+	pj_str_t key;
+	unsigned len;
+	pj_status_t status;
+
+	PJ_LOG(3,(THIS_FILE, "   %s", t->title));
+
+	if (t->pdu) {
+	    status = pj_stun_msg_decode(pool, (pj_uint8_t*)t->pdu, t->pdu_len,
+				        PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, 
+					&msg, NULL, NULL);
+
+	    /* Check expected decode result */
+	    if (t->expected_status != status) {
+		PJ_LOG(1,(THIS_FILE, "    expecting status %d, got %d",
+		          t->expected_status, status));
+		rc = -10;
+		goto on_return;
+	    }
+
+	} else {
+	    msg = t->create(pool);
+	    status = PJ_SUCCESS;
+	}
+
+	if (status != PJ_SUCCESS)
+	    continue;
+
+	/* Try to encode message */
+	pj_stun_create_key(pool, &key, NULL, &USERNAME, &PASSWORD);
+	status = pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len);
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(1,(THIS_FILE, "    encode error: %s", err(status)));
+	    rc = -40;
+	    goto on_return;
+	}
+
+	/* Try to decode it once more */
+	status = pj_stun_msg_decode(pool, buf, len, 
+				    PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, 
+				    &msg2, NULL, NULL);
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(1,(THIS_FILE, "    subsequent decoding failed: %s", err(status)));
+	    rc = -50;
+	    goto on_return;
+	}
+
+	/* Verify */
+	if (t->verify) {
+	    rc = t->verify(msg);
+	    if (rc != 0) {
+		goto on_return;
+	    }
+	}
+    }
+
+on_return:
+    pj_pool_release(pool);
+    if (rc == 0)
+	PJ_LOG(3,(THIS_FILE, "...success!"));
+    return rc;
+}
+
+/* Create 420 response */
+static pj_stun_msg* create1(pj_pool_t *pool)
+{
+    char *pdu = "\x00\x01\x00\x08\x21\x12\xa4\x42"
+		"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+		"\x00\xff\x00\x04\x00\x00\x00\x00";
+    unsigned pdu_len = 28;
+    pj_stun_msg *msg, *res;
+    pj_status_t status;
+
+    status = pj_stun_msg_decode(pool, (pj_uint8_t*)pdu, pdu_len,
+				PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
+				&msg, NULL, &res);
+    pj_assert(status != PJ_SUCCESS);
+    pj_assert(res != NULL);
+
+    return res;
+}
+
+/* Error response MUST have ERROR-CODE attribute */
+/* 420 response MUST contain UNKNOWN-ATTRIBUTES */
+static int verify1(pj_stun_msg *msg)
+{
+    pj_stun_errcode_attr *aerr;
+    pj_stun_unknown_attr *aunk;
+
+    if (!PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) {
+	PJ_LOG(1,(THIS_FILE, "    expecting error message"));
+	return -100;
+    }
+
+    aerr = (pj_stun_errcode_attr*)
+	   pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0);
+    if (aerr == NULL) {
+	PJ_LOG(1,(THIS_FILE, "    missing ERROR-CODE attribute"));
+	return -110;
+    }
+
+    if (aerr->err_code != 420) {
+	PJ_LOG(1,(THIS_FILE, "    expecting 420 error"));
+	return -120;
+    }
+
+    aunk = (pj_stun_unknown_attr*)
+	   pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
+    if (aunk == NULL) {
+	PJ_LOG(1,(THIS_FILE, "    missing UNKNOWN-ATTRIBUTE attribute"));
+	return -130;
+    }
+
+    if (aunk->attr_count != 1) {
+	PJ_LOG(1,(THIS_FILE, "    expecting one unknown attribute"));
+	return -140;
+    }
+
+    if (aunk->attrs[0] != 0xff) {
+	PJ_LOG(1,(THIS_FILE, "    expecting 0xff as unknown attribute"));
+	return -150;
+    }
 
     return 0;
 }
+
+/* Attribute count should be zero since unknown attribute is not parsed */
+static int verify2(pj_stun_msg *msg)
+{
+    if (msg->attr_count != 0) {
+	PJ_LOG(1,(THIS_FILE, "    expecting zero attribute count"));
+	return -200;
+    }
+    return 0;
+}
+
+
+/* Attribute between MESSAGE-INTEGRITY and FINGERPRINT is allowed */
+static int verify5(pj_stun_msg *msg)
+{
+    if (msg->attr_count != 3) {
+	PJ_LOG(1,(THIS_FILE, "    expecting 3 attribute count"));
+	return -500;
+    }
+
+    if (msg->attr[0]->type != PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
+	PJ_LOG(1,(THIS_FILE, "    expecting MESSAGE-INTEGRITY"));
+	return -510;
+    }
+    if (msg->attr[1]->type != PJ_STUN_ATTR_REFRESH_INTERVAL) {
+	PJ_LOG(1,(THIS_FILE, "    expecting REFRESH-INTERVAL"));
+	return -520;
+    }
+    if (msg->attr[2]->type != PJ_STUN_ATTR_FINGERPRINT) {
+	PJ_LOG(1,(THIS_FILE, "    expecting FINGERPRINT"));
+	return -530;
+    }
+
+    return 0;
+}
+
 
 static int decode_verify(void)
@@ -108,11 +458,309 @@
 }
 
+typedef struct test_vector test_vector;
+
+static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v);
+static pj_stun_msg* create_msgint2(pj_pool_t *pool, test_vector *v);
+
+enum
+{
+    USE_MESSAGE_INTEGRITY   = 1,
+    USE_FINGERPRINT	    = 2
+};
+
+struct test_vector
+{
+    unsigned	   msg_type;
+    char	  *tsx_id;
+    char	  *pdu;
+    unsigned 	   pdu_len;
+    unsigned	   options;
+    char	  *username;
+    char	  *password;
+    pj_stun_msg* (*create)(pj_pool_t*, test_vector*);
+} test_vectors[] = 
+{
+    {
+	PJ_STUN_BINDING_REQUEST,
+	"\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae",
+	"\x00\x01\x00\x44\x21\x12\xa4\x42\xb7\xe7"
+	"\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae"
+	"\x00\x24\x00\x04\x6e\x00\x01\xff\x80\x29"
+	"\x00\x08\x93\x2f\xf9\xb1\x51\x26\x3b\x36"
+	"\x00\x06\x00\x09\x65\x76\x74\x6a\x3a\x68"
+	"\x36\x76\x59\x20\x20\x20\x00\x08\x00\x14"
+	"\x62\x4e\xeb\xdc\x3c\xc9\x2d\xd8\x4b\x74"
+	"\xbf\x85\xd1\xc0\xf5\xde\x36\x87\xbd\x33"
+	"\x80\x28\x00\x04\xad\x8a\x85\xff",
+	88,
+	USE_MESSAGE_INTEGRITY | USE_FINGERPRINT,
+	"evtj:h6vY",
+	"VOkJxbRl1RmTxUk/WvJxBt",
+	&create_msgint1
+    },
+    {
+	PJ_STUN_BINDING_RESPONSE,
+	"\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae",
+	"\x01\x01\x00\x3c\x21\x12\xa4\x42\xb7\xe7"
+	"\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae"
+	"\x80\x22\x00\x0b\x74\x65\x73\x74\x20\x76"
+	"\x65\x63\x74\x6f\x72\x20\x00\x20\x00\x08"
+	"\x00\x01\xa1\x47\x5e\x12\xa4\x43\x00\x08"
+	"\x00\x14\xab\x4e\x53\x29\x61\x00\x08\x4c"
+	"\x89\xf2\x7c\x69\x30\x33\x5c\xa3\x58\x14"
+	"\xea\x90\x80\x28\x00\x04\xae\x25\x8d\xf2",
+	80,
+	USE_MESSAGE_INTEGRITY | USE_FINGERPRINT,
+	"evtj:h6vY",
+	"VOkJxbRl1RmTxUk/WvJxBt",
+	&create_msgint2
+    }
+};
+
+
+static char* print_binary(const pj_uint8_t *data, unsigned data_len)
+{
+    static char buf[1500];
+    unsigned length = sizeof(buf);
+    char *p = buf;
+    unsigned i;
+
+    for (i=0; i<data_len;) {
+	unsigned j;
+
+	pj_ansi_snprintf(p, 1500-(p-buf), 
+			 "%04d-%04d   ",
+			 i, (i+20 < data_len) ? i+20 : data_len);
+	p += 12;
+
+	for (j=0; j<20 && i<data_len && p<(buf+length-10); ++j, ++i) {
+	    pj_ansi_sprintf(p, "%02x ", (*data) & 0xFF);
+	    p += 3;
+	    data++;
+	}
+
+	pj_ansi_sprintf(p, "\n");
+	p++;
+    }
+
+    return buf;
+}
+
+static int cmp_buf(const pj_uint8_t *s1, const pj_uint8_t *s2, unsigned len)
+{
+    unsigned i;
+    for (i=0; i<len; ++i) {
+	if (s1[i] != s2[i])
+	    return i;
+    }
+
+    return -1;
+}
+
+static int fingerprint_test_vector()
+{
+    pj_pool_t *pool;
+    pj_status_t status;
+    unsigned i;
+    int rc = 0;
+
+    PJ_LOG(3,(THIS_FILE, "  STUN message test vectors"));
+
+    pool = pj_pool_create(mem, "fingerprint", 1024, 1024, NULL);
+
+    for (i=0; i<PJ_ARRAY_SIZE(test_vectors); ++i) {
+	struct test_vector *v;
+	pj_stun_msg *ref_msg, *msg;
+	unsigned parsed_len;
+	unsigned len, pos;
+	pj_uint8_t buf[1500];
+	char print[1500];
+	pj_str_t key;
+
+	PJ_LOG(3,(THIS_FILE, "    Running test %d/%d", i, 
+	          PJ_ARRAY_SIZE(test_vectors)));
+
+	v = &test_vectors[i];
+
+	/* Print reference message */
+	PJ_LOG(4,(THIS_FILE, "Reference message PDU:\n%s",
+	          print_binary((pj_uint8_t*)v->pdu, v->pdu_len)));
+
+	/* Try to parse the reference message first */
+	status = pj_stun_msg_decode(pool, (pj_uint8_t*)v->pdu, v->pdu_len,
+				    PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, 
+				    &ref_msg, &parsed_len, NULL);
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(1,(THIS_FILE, "    Error decoding reference message"));
+	    rc = -1010;
+	    goto on_return;
+	}
+
+	if (parsed_len != v->pdu_len) {
+	    PJ_LOG(1,(THIS_FILE, "    Parsed len error"));
+	    rc = -1020;
+	    goto on_return;
+	}
+
+	/* Print the reference message */
+	pj_stun_msg_dump(ref_msg, print, sizeof(print), NULL);
+	PJ_LOG(4,(THIS_FILE, "Reference message:\n%s", print));
+
+	/* Create our message */
+	msg = v->create(pool, v);
+
+	/* Encode message */
+	if (v->options & USE_MESSAGE_INTEGRITY) {
+	    pj_str_t s1, s2;
+
+	    pj_stun_create_key(pool, &key, NULL, pj_cstr(&s1, v->username), 
+			       pj_cstr(&s2, v->password));
+	    pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len);
+
+	} else {
+	    pj_stun_msg_encode(msg, buf, sizeof(buf), 0, NULL, &len);
+	}
+
+	/* Print our raw message */
+	PJ_LOG(4,(THIS_FILE, "Message PDU:\n%s",
+	          print_binary((pj_uint8_t*)buf, len)));
+
+	/* Print our message */
+	pj_stun_msg_dump(msg, print, sizeof(print), NULL);
+	PJ_LOG(4,(THIS_FILE, "Message is:\n%s", print));
+
+	/* Compare message length */
+	if (len != v->pdu_len) {
+	    PJ_LOG(1,(THIS_FILE, "    Message length mismatch"));
+	    rc = -1050;
+	    goto on_return;
+	}
+
+	pos = cmp_buf(buf, (const pj_uint8_t*)v->pdu, len);
+	if (pos != -1) {
+	    PJ_LOG(1,(THIS_FILE, "    Message mismatch at byte %d", pos));
+	    rc = -1060;
+	    goto on_return;
+	}
+
+	/* Authenticate the request/response */
+	if (v->options & USE_MESSAGE_INTEGRITY) {
+	    if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
+		pj_stun_auth_cred cred;
+		pj_status_t status;
+
+		pj_bzero(&cred, sizeof(cred));
+		cred.type = PJ_STUN_AUTH_CRED_STATIC;
+		cred.data.static_cred.username = pj_str(v->username);
+		cred.data.static_cred.data = pj_str(v->password);
+
+		status = pj_stun_authenticate_request(buf, len, msg, 
+						      &cred, pool, NULL);
+		if (status != PJ_SUCCESS) {
+		    char errmsg[PJ_ERR_MSG_SIZE];
+		    pj_strerror(status, errmsg, sizeof(errmsg));
+		    PJ_LOG(1,(THIS_FILE, 
+			      "    Request authentication failed: %s",
+			      errmsg));
+		    rc = -1070;
+		    goto on_return;
+		}
+
+	    } else if (PJ_STUN_IS_RESPONSE(msg->hdr.type)) {
+		pj_status_t status;
+		status = pj_stun_authenticate_response(buf, len, msg, &key);
+		if (status != PJ_SUCCESS) {
+		    char errmsg[PJ_ERR_MSG_SIZE];
+		    pj_strerror(status, errmsg, sizeof(errmsg));
+		    PJ_LOG(1,(THIS_FILE, 
+			      "    Response authentication failed: %s",
+			      errmsg));
+		    rc = -1080;
+		    goto on_return;
+		}
+	    }
+	}	
+    }
+
+
+on_return:
+    pj_pool_release(pool);
+    return rc;
+}
+
+static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v)
+{
+    pj_stun_msg *msg;
+    pj_timestamp u64;
+    pj_str_t s1;
+
+    pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC,
+		       (pj_uint8_t*)v->tsx_id, &msg);
+
+    pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_PRIORITY, 0x6e0001ff);
+    u64.u32.hi = 0x932ff9b1;
+    u64.u32.lo = 0x51263b36;
+    pj_stun_msg_add_uint64_attr(pool, msg, PJ_STUN_ATTR_ICE_CONTROLLED,
+				&u64);
+
+    pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME, 
+				pj_cstr(&s1, v->username));
+
+    pj_stun_msg_add_msgint_attr(pool, msg);
+
+    pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0);
+
+    return msg;
+}
+
+static pj_stun_msg* create_msgint2(pj_pool_t *pool, test_vector *v)
+{
+    pj_stun_msg *msg;
+    pj_sockaddr_in mapped_addr;
+    pj_str_t s1;
+
+    pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC,
+		       (pj_uint8_t*)v->tsx_id, &msg);
+
+    pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SERVER, 
+				pj_cstr(&s1, "test vector"));
+
+    pj_sockaddr_in_init(&mapped_addr, pj_cstr(&s1, "127.0.0.1"), 32853);
+    pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR,
+				  PJ_TRUE, &mapped_addr, 
+				  sizeof(pj_sockaddr_in));
+
+    pj_stun_msg_add_msgint_attr(pool, msg);
+    pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0);
+
+    return msg;
+}
+
 
 int stun_test(void)
 {
-    decode_verify();
-    decode_test();
-    auth_test();
-    return 0;
-}
-
+    int pad, rc;
+
+    pad = pj_stun_set_padding_char(32);
+
+    rc = decode_test();
+    if (rc != 0)
+	goto on_return;
+
+    rc = decode_verify();
+    if (rc != 0)
+	goto on_return;
+
+    rc = auth_test();
+    if (rc != 0)
+	goto on_return;
+
+    rc = fingerprint_test_vector();
+    if (rc != 0)
+	goto on_return;
+
+on_return:
+    pj_stun_set_padding_char(pad);
+    return rc;
+}
+
Index: pjproject/trunk/pjnath/src/pjnath-test/test.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/test.c	(revision 1438)
+++ pjproject/trunk/pjnath/src/pjnath-test/test.c	(revision 1439)
@@ -67,4 +67,8 @@
     pjnath_init();
 
+#if INCLUDE_STUN_TEST
+    DO_TEST(stun_test());
+#endif
+
 #if INCLUDE_ICE_TEST
     DO_TEST(ice_test());
Index: pjproject/trunk/pjnath/src/pjnath-test/test.h
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/test.h	(revision 1438)
+++ pjproject/trunk/pjnath/src/pjnath-test/test.h	(revision 1439)
@@ -21,6 +21,8 @@
 #include <pjnath.h>
 
+#define INCLUDE_STUN_TEST	    1
 #define INCLUDE_ICE_TEST	    1
 
+extern int stun_test(void);
 extern int ice_test(void);
 extern int test_main(void);
Index: pjproject/trunk/pjnath/src/pjnath/ice_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1438)
+++ pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1439)
@@ -490,5 +490,5 @@
 	/* Verify username */
 	if (pj_strcmp(username, &ice->tx_uname) != 0)
-	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
+	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
 	*data_type = 0;
 	*data = ice->tx_pass;
@@ -508,5 +508,5 @@
 	pos = (const char*)pj_memchr(username->ptr, ':', username->slen);
 	if (pos == NULL)
-	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
+	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
 
 	ufrag.ptr = (char*)username->ptr;
@@ -514,5 +514,5 @@
 
 	if (pj_strcmp(&ufrag, &ice->rx_ufrag) != 0)
-	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
+	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
 
 	*data_type = 0;
Index: pjproject/trunk/pjnath/src/pjnath/stun_auth.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_auth.c	(revision 1438)
+++ pjproject/trunk/pjnath/src/pjnath/stun_auth.c	(revision 1439)
@@ -60,9 +60,16 @@
 
 
+PJ_INLINE(void) PUT_VAL16(pj_uint8_t *buf, unsigned pos, pj_uint16_t hval)
+{
+    buf[pos+0] = (pj_uint8_t) ((hval & 0xFF00) >> 8);
+    buf[pos+1] = (pj_uint8_t) ((hval & 0x00FF) >> 0);
+}
+
+
 /* Send 401 response */
 static pj_status_t create_challenge(pj_pool_t *pool,
 				    const pj_stun_msg *msg,
 				    int err_code,
-				    const pj_str_t *err_msg,
+				    const char *errstr,
 				    const pj_str_t *realm,
 				    const pj_str_t *nonce,
@@ -71,8 +78,10 @@
     pj_stun_msg *response;
     pj_str_t tmp_nonce;
+    pj_str_t err_msg;
     pj_status_t rc;
 
-    rc = pj_stun_msg_create_response(pool, msg, 
-				     err_code,  err_msg, &response);
+    rc = pj_stun_msg_create_response(pool, msg, err_code, 
+			             (errstr?pj_cstr(&err_msg, errstr):NULL), 
+				     &response);
     if (rc != PJ_SUCCESS)
 	return rc;
@@ -117,5 +126,6 @@
     pj_str_t realm, nonce, password;
     const pj_stun_msgint_attr *amsgi;
-    unsigned amsgi_pos;
+    unsigned i, amsgi_pos;
+    pj_bool_t has_attr_beyond_mi;
     const pj_stun_username_attr *auser;
     pj_bool_t username_ok;
@@ -139,5 +149,5 @@
 	p_response = NULL;
 
-    /* Get realm and nonce */
+    /* Get realm and nonce from credential */
     realm.slen = nonce.slen = 0;
     if (cred->type == PJ_STUN_AUTH_CRED_STATIC) {
@@ -154,13 +164,32 @@
     }
 
-    /* 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);
+    /* Look for MESSAGE-INTEGRITY while counting the position */
+    amsgi_pos = 0;
+    has_attr_beyond_mi = PJ_FALSE;
+    amsgi = NULL;
+    for (i=0; i<msg->attr_count; ++i) {
+	if (msg->attr[i]->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
+	    amsgi = (const pj_stun_msgint_attr*) msg->attr[i];
+	} else if (amsgi) {
+	    has_attr_beyond_mi = PJ_TRUE;
+	    break;
+	} else {
+	    amsgi_pos += ((msg->attr[i]->length+3) & ~0x03) + 4;
+	}
+    }
+
     if (amsgi == NULL) {
+	/* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should return 400
+	   for short term, and 401 for long term.
+	   The rule has been changed from rfc3489bis-06
+	*/
+	int code;
+
+	code = realm.slen ? PJ_STUN_SC_UNAUTHORIZED : PJ_STUN_SC_BAD_REQUEST;
 	if (p_response) {
-	    create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, NULL,
+	    create_challenge(pool, msg, code, NULL,
 			     &realm, &nonce, p_response);
 	}
-	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
+	return PJ_STATUS_FROM_STUN_CODE(code);
     }
 
@@ -169,9 +198,14 @@
 	    pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0);
     if (auser == NULL) {
+	/* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should return 400
+	   for both short and long term, since M-I is present.
+	   The rule has been changed from rfc3489bis-06
+	*/
+	int code = PJ_STUN_SC_BAD_REQUEST;
 	if (p_response) {
-	    create_challenge(pool, msg, PJ_STUN_SC_MISSING_USERNAME, NULL,
+	    create_challenge(pool, msg, code, "Missing USERNAME",
 			     &realm, &nonce, p_response);
 	}
-	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_USERNAME);
+	return PJ_STATUS_FROM_STUN_CODE(code);
     }
 
@@ -201,9 +235,12 @@
     if (!username_ok) {
 	/* Username mismatch */
+	/* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should 
+	 * return 401 
+	 */
 	if (p_response) {
-	    create_challenge(pool, msg, PJ_STUN_SC_UNKNOWN_USERNAME, NULL,
+	    create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, NULL,
 			     &realm, &nonce, p_response);
 	}
-	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
+	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
     }
 
@@ -217,8 +254,9 @@
 	/* Long term credential is required and REALM is not present */
 	if (p_response) {
-	    create_challenge(pool, msg, PJ_STUN_SC_MISSING_REALM, NULL,
+	    create_challenge(pool, msg, PJ_STUN_SC_BAD_REQUEST, 
+			     "Missing REALM",
 			     &realm, &nonce, p_response);
 	}
-	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_REALM);
+	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
 
     } else if (realm.slen != 0 && arealm != NULL) {
@@ -228,8 +266,8 @@
 	if (anonce == NULL && nonce.slen) {
 	    if (p_response) {
-		create_challenge(pool, msg, PJ_STUN_SC_MISSING_NONCE, 
-				 NULL, &realm, &nonce, p_response);
+		create_challenge(pool, msg, PJ_STUN_SC_BAD_REQUEST, 
+				 "Missing NONCE", &realm, &nonce, p_response);
 	    }
-	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_NONCE);
+	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
 	}
 
@@ -238,8 +276,8 @@
 	    /* REALM doesn't match */
 	    if (p_response) {
-		create_challenge(pool, msg, PJ_STUN_SC_MISSING_REALM, 
-				 NULL, &realm, &nonce, p_response);
+		create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, 
+				 "Invalid REALM", &realm, &nonce, p_response);
 	    }
-	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_REALM);
+	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
 	}
 
@@ -261,8 +299,8 @@
 	if (nonce.slen != 0) {
 	    if (p_response) {
-		create_challenge(pool, msg, PJ_STUN_SC_MISSING_NONCE, 
-				 NULL, &realm, &nonce, p_response);
+		create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, 
+				 "NONCE required", &realm, &nonce, p_response);
 	    }
-	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_NONCE);
+	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
 	}
     }
@@ -295,28 +333,6 @@
 				 NULL, &realm, &nonce, p_response);
 	    }
-	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_NONCE);
-	}
-    }
-
-    /* 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) {
-	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) {
-	pj_assert(!"Unable to find MESSAGE-INTEGRITY in the message!");
-	return PJ_EBUG;
+	    return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_STALE_NONCE);
+	}
     }
 
@@ -324,24 +340,40 @@
     pj_stun_create_key(pool, &key, &realm, &auser->value, &password);
 
-    /* Now calculate HMAC of the message, adding zero padding if necessary
-     * to make the input 64 bytes aligned.
+    /* Now calculate HMAC of the message. */
+    pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen);
+
+    /* First calculate HMAC for the header.
+     * The calculation is different depending on whether FINGERPRINT attribute
+     * is present in the message.
      */
-    pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen);
-    pj_hmac_sha1_update(&ctx, pkt, amsgi_pos);
-    if (amsgi_pos & 63) {
-	pj_uint8_t zeroes[64];
-	pj_bzero(zeroes, sizeof(zeroes));
-	pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 63));
-    }
+    if (has_attr_beyond_mi) {
+	pj_uint8_t hdr_copy[20];
+	pj_memcpy(hdr_copy, pkt, 20);
+	PUT_VAL16(hdr_copy, 2, (pj_uint16_t)(amsgi_pos + 24));
+	pj_hmac_sha1_update(&ctx, hdr_copy, 20);
+    } else {
+	pj_hmac_sha1_update(&ctx, pkt, 20);
+    }
+
+    /* Now update with the message body */
+    pj_hmac_sha1_update(&ctx, pkt+20, amsgi_pos);
+    // This is no longer necessary as per rfc3489bis-08
+    //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 */
+	/* According to rfc3489bis-10 Sec 10.1.2 we should return 401 */
 	if (p_response) {
-	    create_challenge(pool, msg, PJ_STUN_SC_INTEGRITY_CHECK_FAILURE,
+	    create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED,
 			     NULL, &realm, &nonce, p_response);
 	}
-	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
+	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
     }
 
@@ -382,9 +414,9 @@
     case PJ_STUN_SC_BAD_REQUEST:	    /* 400 (Bad Request)	    */
     case PJ_STUN_SC_UNAUTHORIZED:	    /* 401 (Unauthorized)	    */
-    case PJ_STUN_SC_STALE_CREDENTIALS:	    /* 430 (Stale Credential)	    */
-    case PJ_STUN_SC_MISSING_USERNAME:	    /* 432 (Missing Username)	    */
-    case PJ_STUN_SC_MISSING_REALM:	    /* 434 (Missing Realm)	    */
-    case PJ_STUN_SC_UNKNOWN_USERNAME:	    /* 436 (Unknown Username)	    */
-    case PJ_STUN_SC_INTEGRITY_CHECK_FAILURE:/* 431 (Integrity Check Fail)   */
+    //case PJ_STUN_SC_STALE_CREDENTIALS:    /* 430 (Stale Credential)	    */
+    //case PJ_STUN_SC_MISSING_USERNAME:	    /* 432 (Missing Username)	    */
+    //case PJ_STUN_SC_MISSING_REALM:	    /* 434 (Missing Realm)	    */
+    //case PJ_STUN_SC_UNKNOWN_USERNAME:	    /* 436 (Unknown Username)	    */
+    //case PJ_STUN_SC_INTEGRITY_CHECK_FAILURE:/* 431 (Integrity Check Fail) */
 	return PJ_FALSE;
     default:
@@ -401,5 +433,6 @@
 {
     const pj_stun_msgint_attr *amsgi;
-    unsigned amsgi_pos;
+    unsigned i, amsgi_pos;
+    pj_bool_t has_attr_beyond_mi;
     pj_hmac_sha1_context ctx;
     pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE];
@@ -411,5 +444,5 @@
 	    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);
+	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
     }
 
@@ -420,40 +453,47 @@
     }
 
-    /* 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 */
+    /* Look for MESSAGE-INTEGRITY while counting the position */
+    amsgi_pos = 0;
+    has_attr_beyond_mi = PJ_FALSE;
+    amsgi = NULL;
+    for (i=0; i<msg->attr_count; ++i) {
+	if (msg->attr[i]->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
+	    amsgi = (const pj_stun_msgint_attr*) msg->attr[i];
+	} else if (amsgi) {
+	    has_attr_beyond_mi = PJ_TRUE;
+	    break;
+	} else {
+	    amsgi_pos += ((msg->attr[i]->length+3) & ~0x03) + 4;
+	}
+    }
+
+    if (amsgi == NULL) {
+	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
+    }
+
+    /* Now calculate HMAC of the message. */
+    pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key->ptr, key->slen);
+
+    /* First calculate HMAC for the header.
+     * The calculation is different depending on whether FINGERPRINT attribute
+     * is present in the message.
+     */
+    if (has_attr_beyond_mi) {
+	pj_uint8_t hdr_copy[20];
+	pj_memcpy(hdr_copy, pkt, 20);
+	PUT_VAL16(hdr_copy, 2, (pj_uint16_t)(amsgi_pos+24));
+	pj_hmac_sha1_update(&ctx, hdr_copy, 20);
     } 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_update(&ctx, pkt, 20);
+    }
+
+    /* Now update with the message body */
+    pj_hmac_sha1_update(&ctx, pkt+20, amsgi_pos);
+    // This is no longer necessary as per rfc3489bis-08
+    //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);
 
@@ -461,5 +501,5 @@
     if (pj_memcmp(amsgi->hmac, digest, 20)) {
 	/* HMAC value mismatch */
-	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
+	return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
     }
 
Index: pjproject/trunk/pjnath/src/pjnath/stun_msg.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1438)
+++ pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1439)
@@ -32,4 +32,6 @@
 #define STUN_XOR_FINGERPRINT	0x5354554eL
 
+static int padding_char;
+
 static const char *stun_method_names[] = 
 {
@@ -55,12 +57,12 @@
     { PJ_STUN_SC_UNAUTHORIZED,		    "Unauthorized"},
     { PJ_STUN_SC_UNKNOWN_ATTRIBUTE,	    "Unknown Attribute"},
-    { PJ_STUN_SC_STALE_CREDENTIALS,	    "Stale Credentials"},
-    { PJ_STUN_SC_INTEGRITY_CHECK_FAILURE,   "Integrity Check Failure"},
-    { PJ_STUN_SC_MISSING_USERNAME,	    "Missing Username"},
-    { PJ_STUN_SC_USE_TLS,		    "Use TLS"},
-    { PJ_STUN_SC_MISSING_REALM,		    "Missing Realm"},
-    { PJ_STUN_SC_MISSING_NONCE,		    "Missing Nonce"},
-    { PJ_STUN_SC_UNKNOWN_USERNAME,	    "Unknown Username"},
-    { PJ_STUN_SC_NO_BINDING,		    "No Binding"},
+    //{ PJ_STUN_SC_STALE_CREDENTIALS,	    "Stale Credentials"},
+    //{ PJ_STUN_SC_INTEGRITY_CHECK_FAILURE, "Integrity Check Failure"},
+    //{ PJ_STUN_SC_MISSING_USERNAME,	    "Missing Username"},
+    //{ PJ_STUN_SC_USE_TLS,		    "Use TLS"},
+    //{ PJ_STUN_SC_MISSING_REALM,	    "Missing Realm"},
+    //{ PJ_STUN_SC_MISSING_NONCE,	    "Missing Nonce"},
+    //{ PJ_STUN_SC_UNKNOWN_USERNAME,	    "Unknown Username"},
+    //{ PJ_STUN_SC_NO_BINDING,		    "No Binding"},
     { PJ_STUN_SC_STALE_NONCE,		    "Stale Nonce"},
     { PJ_STUN_SC_TRANSITIONING,		    "Active Destination Already Set"},
@@ -569,4 +571,15 @@
 
 
+/*
+ * Set padding character.
+ */
+PJ_DEF(int) pj_stun_set_padding_char(int chr)
+{
+    int old_pad = padding_char;
+    padding_char = chr;
+    return old_pad;
+}
+
+
 //////////////////////////////////////////////////////////////////////////////
 
@@ -900,4 +913,12 @@
     /* Copy the string */
     pj_memcpy(buf+ATTR_HDR_LEN, ca->value.ptr, ca->value.slen);
+
+    /* Add padding character, if string is not 4-bytes aligned. */
+    if (ca->value.slen & 0x03) {
+	pj_uint8_t pad[3];
+	pj_memset(pad, padding_char, sizeof(pad));
+	pj_memcpy(buf+ATTR_HDR_LEN+ca->value.slen, pad,
+		  4-(ca->value.slen & 0x03));
+    }
 
     /* Done */
@@ -1379,9 +1400,11 @@
      * attributes MUST be repeated in the list.
      */
+    /* No longer necessary
     if ((attr_cnt & 0x01)) {
 	attr->attrs[attr_cnt] = attr_array[attr_cnt-1];
     }
-
-    *p_attr = NULL;
+    */
+
+    *p_attr = attr;
 
     return PJ_SUCCESS;
@@ -1634,4 +1657,11 @@
 	((options & PJ_STUN_IS_DATAGRAM) && msg_len + 20 != pdu_len))
     {
+	return PJNATH_EINSTUNMSGLEN;
+    }
+
+    /* STUN message is always padded to the nearest 4 bytes, thus
+     * the last two bits of the length field are always zero.
+     */
+    if ((msg_len & 0x03) != 0) {
 	return PJNATH_EINSTUNMSGLEN;
     }
@@ -1879,7 +1909,9 @@
 		has_fingerprint = PJ_TRUE;
 	    } else {
-		if (has_msg_int || has_fingerprint) {
+		if (has_fingerprint) {
 		    /* Another attribute is found which is not FINGERPRINT
-		     * after FINGERPRINT or MESSAGE-INTEGRITY */
+		     * after FINGERPRINT. Note that non-FINGERPRINT is
+		     * allowed to appear after M-I
+		     */
 		    if (p_response) {
 			pj_stun_msg_create_response(pool, msg,
@@ -1887,6 +1919,5 @@
 						    NULL, p_response);
 		    }
-		    return has_fingerprint ? PJNATH_ESTUNFINGERPOS :
-					     PJNATH_ESTUNMSGINTPOS;
+		    return PJNATH_ESTUNFINGERPOS;
 		}
 	    }
@@ -2115,14 +2146,9 @@
     }
 
-    /* We MUST update the message length in the header NOW before
-     * calculating MESSAGE-INTEGRITY and FINGERPRINT. 
-     * Note that length is not including the 20 bytes header.
+    /* If MESSAGE-INTEGRITY is present, include the M-I attribute
+     * in message length before calculating M-I
      */
-    if (amsgint && afingerprint) {
-	body_len = (pj_uint16_t)((buf - start) - 20 + 24 + 8);
-    } else if (amsgint) {
+    if (amsgint) {
 	body_len = (pj_uint16_t)((buf - start) - 20 + 24);
-    } else if (afingerprint) {
-	body_len = (pj_uint16_t)((buf - start) - 20 + 8);
     } else {
 	body_len = (pj_uint16_t)((buf - start) - 20);
@@ -2162,9 +2188,10 @@
 	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) {
-	    pj_uint8_t zeroes[64];
-	    pj_bzero(zeroes, sizeof(zeroes));
-	    pj_hmac_sha1_update(&ctx, zeroes, 64-((buf-start) & 0x3F));
-	}
+	// These are obsoleted in rfc3489bis-08
+	//if ((buf-start) & 0x3F) {
+	//    pj_uint8_t zeroes[64];
+	//    pj_bzero(zeroes, sizeof(zeroes));
+	//    pj_hmac_sha1_update(&ctx, zeroes, 64-((buf-start) & 0x3F));
+	//}
 	pj_hmac_sha1_final(&ctx, amsgint->hmac);
 
@@ -2181,4 +2208,8 @@
     /* Calculate FINGERPRINT if present */
     if (afingerprint != NULL) {
+	/* Update message length */
+	PUTVAL16H(start, 2, 
+		 (pj_uint16_t)(GETVAL16H(start, 2)+8));
+
 	afingerprint->value = pj_crc32_calc(start, buf-start);
 	afingerprint->value ^= STUN_XOR_FINGERPRINT;
Index: pjproject/trunk/pjnath/src/pjnath/stun_msg_dump.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_msg_dump.c	(revision 1438)
+++ pjproject/trunk/pjnath/src/pjnath/stun_msg_dump.c	(revision 1439)
@@ -193,4 +193,20 @@
 	}
 	break;
+    case PJ_STUN_ATTR_ICE_CONTROLLED:
+    case PJ_STUN_ATTR_ICE_CONTROLLING:
+	{
+	    const pj_stun_uint64_attr *attr;
+	    pj_uint8_t data[8];
+	    int i;
+
+	    attr = (const pj_stun_uint64_attr*) ahdr;
+
+	    for (i=0; i<8; ++i)
+		data[i] = ((const pj_uint8_t*)&attr->value)[7-i];
+
+	    len = print_binary(p, end-p, data, 8);
+	    APPLY();
+	}
+	break;
     case PJ_STUN_ATTR_USE_CANDIDATE:
     default:
