Index: pjproject/trunk/pjnath/include/pjnath/errno.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/errno.h	(revision 2579)
+++ pjproject/trunk/pjnath/include/pjnath/errno.h	(revision 2580)
@@ -115,4 +115,9 @@
  */
 #define PJNATH_ESTUNIPV6NOTSUPP	    (PJNATH_ERRNO_START+41) /* 370041 */
+/**
+ * @hideinitializer
+ * Invalid address family value in STUN message.
+ */
+#define PJNATH_EINVAF		    (PJNATH_ERRNO_START+42) /* 370042 */
 
 /**
@@ -120,5 +125,5 @@
  * Invalid STUN server or server not configured.
  */
-#define PJNATH_ESTUNINSERVER	    (PJNATH_ERRNO_START+42) /* 370042 */
+#define PJNATH_ESTUNINSERVER	    (PJNATH_ERRNO_START+50) /* 370050 */
 
 
Index: pjproject/trunk/pjnath/include/pjnath/types.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/types.h	(revision 2579)
+++ pjproject/trunk/pjnath/include/pjnath/types.h	(revision 2580)
@@ -98,8 +98,7 @@
 keep NAT bindings open.
 
-This version of PJNATH implements the following STUN-bis draft:
-- <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-18.txt">
-  <B>draft-ietf-behave-rfc3489bis-18</b></A>: Session Traversal 
-    Utilities for (NAT) (STUN),
+This version of PJNATH implements the following STUN RFC:
+- <A HREF="http://www.ietf.org/rfc/rfc5389.txt"><B>RFC 5389</b></A>: 
+    Session Traversal Utilities for (NAT) (STUN),
 
 
Index: pjproject/trunk/pjnath/src/pjnath-test/stun.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath-test/stun.c	(revision 2579)
+++ pjproject/trunk/pjnath/src/pjnath-test/stun.c	(revision 2580)
@@ -431,8 +431,13 @@
 }
 
+/*
+ * Test vectors, from:
+ * http://tools.ietf.org/html/draft-denis-behave-rfc3489bis-test-vectors-02
+ */
 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);
+static pj_stun_msg* create_msgint3(pj_pool_t *pool, test_vector *v);
 
 enum
@@ -442,5 +447,5 @@
 };
 
-struct test_vector
+static struct test_vector
 {
     unsigned	   msg_type;
@@ -451,4 +456,6 @@
     char	  *username;
     char	  *password;
+    char	  *realm;
+    char	  *nonce;
     pj_stun_msg* (*create)(pj_pool_t*, test_vector*);
 } test_vectors[] = 
@@ -470,4 +477,6 @@
 	"evtj:h6vY",
 	"VOkJxbRl1RmTxUk/WvJxBt",
+	"",
+	"",
 	&create_msgint1
     },
@@ -475,17 +484,59 @@
 	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",
+	"\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\xe1\x12\xa6\x43"
+	"\x00\x08\x00\x14"
+	"\x2b\x91\xf5\x99\xfd\x9e\x90\xc3\x8c\x74\x89\xf9"
+	"\x2a\xf9\xba\x53\xf0\x6b\xe7\xd7"
+	"\x80\x28\x00\x04"
+	"\xc0\x7d\x4c\x96",
 	80,
 	USE_MESSAGE_INTEGRITY | USE_FINGERPRINT,
 	"evtj:h6vY",
 	"VOkJxbRl1RmTxUk/WvJxBt",
+	"",
+	"",
 	&create_msgint2
+    },
+    {
+	PJ_STUN_BINDING_RESPONSE,
+	"\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae",
+	"\x01\x01\x00\x48" //     Response type and message length
+        "\x21\x12\xa4\x42" //     Message cookie
+        "\xb7\xe7\xa7\x01" //  }
+        "\xbc\x34\xd6\x86" //  }  Transaction ID
+        "\xfa\x87\xdf\xae" //  }
+
+        "\x80\x22\x00\x0b" // SOFTWARE, length=11
+        "\x74\x65\x73\x74"
+        "\x20\x76\x65\x63"
+        "\x74\x6f\x72\x20"
+        "\x00\x20\x00\x14" // XOR-MAPPED-ADDRESS
+        "\x00\x02\xa1\x47"
+        "\x01\x13\xa9\xfa"
+        "\xa5\xd3\xf1\x79"
+        "\xbc\x25\xf4\xb5"
+        "\xbe\xd2\xb9\xd9"
+        "\x00\x08\x00\x14" // MESSAGE-INTEGRITY attribute header
+        "\xa3\x82\x95\x4e" // }
+        "\x4b\xe6\x7b\xf1" // }
+        "\x17\x84\xc9\x7c" // }  HMAC-SHA1 fingerprint
+        "\x82\x92\xc2\x75" // }
+        "\xbf\xe3\xed\x41" // }
+        "\x80\x28\x00\x04" //    FINGERPRINT attribute header
+        "\xc8\xfb\x0b\x4c" //    CRC32 fingerprint
+,
+	92,
+	USE_MESSAGE_INTEGRITY | USE_FINGERPRINT,
+	"evtj:h6vY",
+	"VOkJxbRl1RmTxUk/WvJxBt",
+	"",
+	"",
+	&create_msgint3
     }
 };
@@ -538,5 +589,5 @@
     int rc = 0;
 
-    PJ_LOG(3,(THIS_FILE, "  STUN message test vectors"));
+    PJ_LOG(3,(THIS_FILE, "  draft-denis-behave-rfc3489bis-test-vectors-02"));
 
     pool = pj_pool_create(mem, "fingerprint", 1024, 1024, NULL);
@@ -586,8 +637,10 @@
 	/* 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_STUN_PASSWD_PLAIN, pj_cstr(&s2, v->password));
+	    pj_str_t s1, s2, r;
+
+	    pj_stun_create_key(pool, &key, pj_cstr(&r, v->realm), 
+			       pj_cstr(&s1, v->username), 
+			       PJ_STUN_PASSWD_PLAIN, 
+			       pj_cstr(&s2, v->password));
 	    pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len);
 
@@ -626,6 +679,8 @@
 		pj_bzero(&cred, sizeof(cred));
 		cred.type = PJ_STUN_AUTH_CRED_STATIC;
+		cred.data.static_cred.realm = pj_str(v->realm);
 		cred.data.static_cred.username = pj_str(v->username);
 		cred.data.static_cred.data = pj_str(v->password);
+		cred.data.static_cred.nonce = pj_str(v->nonce);
 
 		status = pj_stun_authenticate_request(buf, len, msg, 
@@ -700,8 +755,35 @@
 				pj_cstr(&s1, "test vector"));
 
-    pj_sockaddr_in_init(&mapped_addr, pj_cstr(&s1, "127.0.0.1"), 32853);
+    pj_sockaddr_in_init(&mapped_addr, pj_cstr(&s1, "192.0.2.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;
+}
+
+
+static pj_stun_msg* create_msgint3(pj_pool_t *pool, test_vector *v)
+{
+    pj_stun_msg *msg;
+    pj_sockaddr 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_SOFTWARE, 
+				pj_cstr(&s1, "test vector"));
+
+    pj_sockaddr_init(pj_AF_INET6(), &mapped_addr,
+		     pj_cstr(&s1, "2001:db8:1234:5678:11:2233:4455:6677"),
+		     32853);
+
+    pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR,
+				  PJ_TRUE, &mapped_addr, 
+				  sizeof(pj_sockaddr));
 
     pj_stun_msg_add_msgint_attr(pool, msg);
Index: pjproject/trunk/pjnath/src/pjnath/errno.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/errno.c	(revision 2579)
+++ pjproject/trunk/pjnath/src/pjnath/errno.c	(revision 2580)
@@ -52,4 +52,5 @@
     PJ_BUILD_ERR( PJNATH_ESTUNNOMAPPEDADDR, "STUN (XOR-)MAPPED-ADDRESS attribute not found"),
     PJ_BUILD_ERR( PJNATH_ESTUNIPV6NOTSUPP,  "STUN IPv6 attribute not supported"),
+    PJ_BUILD_ERR( PJNATH_EINVAF,	    "Invalid STUN address family value"),
     PJ_BUILD_ERR( PJNATH_ESTUNINSERVER,	    "Invalid STUN server or server not configured"),
 
Index: pjproject/trunk/pjnath/src/pjnath/stun_msg.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 2579)
+++ pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 2580)
@@ -89,67 +89,95 @@
     const char   *name;
     pj_status_t	(*decode_attr)(pj_pool_t *pool, const pj_uint8_t *buf, 
-			       void **p_attr);
+			       const pj_stun_msg_hdr *msghdr, void **p_attr);
     pj_status_t (*encode_attr)(const void *a, pj_uint8_t *buf, 
-			       unsigned len, unsigned *printed);
+			       unsigned len, const pj_stun_msg_hdr *msghdr,
+			       unsigned *printed);
     void*       (*clone_attr)(pj_pool_t *pool, const void *src);
 };
 
 static pj_status_t decode_sockaddr_attr(pj_pool_t *pool, 
-				       const pj_uint8_t *buf, 
-				       void **p_attr);
+				        const pj_uint8_t *buf, 
+				        const pj_stun_msg_hdr *msghdr,
+				        void **p_attr);
 static pj_status_t decode_xored_sockaddr_attr(pj_pool_t *pool, 
 					      const pj_uint8_t *buf, 
+					      const pj_stun_msg_hdr *msghdr, 
 					      void **p_attr);
 static pj_status_t encode_sockaddr_attr(const void *a, pj_uint8_t *buf, 
-				       unsigned len, 
-				       unsigned *printed);
+				        unsigned len, 
+					const pj_stun_msg_hdr *msghdr,
+				        unsigned *printed);
 static void*       clone_sockaddr_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_string_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf, 
+				      const pj_stun_msg_hdr *msghdr, 
 				      void **p_attr);
 static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, 
-				      unsigned len, unsigned *printed);
+				      unsigned len, 
+				      const pj_stun_msg_hdr *msghdr,
+				      unsigned *printed);
 static void*       clone_string_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_msgint_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf,
+				      const pj_stun_msg_hdr *msghdr, 
 				      void **p_attr);
 static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, 
-				      unsigned len, unsigned *printed);
+				      unsigned len, 
+				      const pj_stun_msg_hdr *msghdr,
+				      unsigned *printed);
 static void*       clone_msgint_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_errcode_attr(pj_pool_t *pool, 
 				       const pj_uint8_t *buf,
+				       const pj_stun_msg_hdr *msghdr, 
 				       void **p_attr);
 static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, 
-				       unsigned len, unsigned *printed);
+				       unsigned len, 
+				       const pj_stun_msg_hdr *msghdr,
+				       unsigned *printed);
 static void*       clone_errcode_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_unknown_attr(pj_pool_t *pool, 
 				       const pj_uint8_t *buf, 
+				       const pj_stun_msg_hdr *msghdr, 
 				       void **p_attr);
 static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, 
-				       unsigned len, unsigned *printed);
+				       unsigned len, 
+				       const pj_stun_msg_hdr *msghdr,
+				       unsigned *printed);
 static void*       clone_unknown_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_uint_attr(pj_pool_t *pool, 
 				    const pj_uint8_t *buf, 
+				    const pj_stun_msg_hdr *msghdr, 
 				    void **p_attr);
 static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, 
-				    unsigned len, unsigned *printed);
+				    unsigned len, 
+				    const pj_stun_msg_hdr *msghdr,
+				    unsigned *printed);
 static void*       clone_uint_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_uint64_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf, 
+				      const pj_stun_msg_hdr *msghdr, 
 				      void **p_attr);
 static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf, 
-				      unsigned len, unsigned *printed);
+				      unsigned len, 
+				      const pj_stun_msg_hdr *msghdr,
+				      unsigned *printed);
 static void*       clone_uint64_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_binary_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf,
+				      const pj_stun_msg_hdr *msghdr, 
 				      void **p_attr);
 static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf, 
-				      unsigned len, unsigned *printed);
+				      unsigned len, 
+				      const pj_stun_msg_hdr *msghdr,
+				      unsigned *printed);
 static void*       clone_binary_attr(pj_pool_t *pool, const void *src);
 static pj_status_t decode_empty_attr(pj_pool_t *pool, 
 				     const pj_uint8_t *buf, 
+				     const pj_stun_msg_hdr *msghdr, 
 				     void **p_attr);
 static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, 
-				     unsigned len, unsigned *printed);
+				     unsigned len, 
+				     const pj_stun_msg_hdr *msghdr,
+				     unsigned *printed);
 static void*       clone_empty_attr(pj_pool_t *pool, const void *src);
 
@@ -727,5 +755,5 @@
 }
 
-static pj_uint16_t GETVAL16N(const pj_uint8_t *buf, unsigned pos)
+PJ_INLINE(pj_uint16_t) GETVAL16N(const pj_uint8_t *buf, unsigned pos)
 {
     return pj_htons(GETVAL16H(buf,pos));
@@ -738,5 +766,5 @@
 }
 
-static pj_uint32_t GETVAL32H(const pj_uint8_t *buf, unsigned pos)
+PJ_INLINE(pj_uint32_t) GETVAL32H(const pj_uint8_t *buf, unsigned pos)
 {
     return (pj_uint32_t) ((buf[pos + 0] << 24UL) | \
@@ -746,5 +774,5 @@
 }
 
-static pj_uint32_t GETVAL32N(const pj_uint8_t *buf, unsigned pos)
+PJ_INLINE(pj_uint32_t) GETVAL32N(const pj_uint8_t *buf, unsigned pos)
 {
     return pj_htonl(GETVAL32H(buf,pos));
@@ -782,5 +810,6 @@
  * STUN generic IP address container
  */
-#define STUN_GENERIC_IP_ADDR_LEN	8
+#define STUN_GENERIC_IPV4_ADDR_LEN	8
+#define STUN_GENERIC_IPV6_ADDR_LEN	20
 
 /*
@@ -793,9 +822,12 @@
 						unsigned addr_len)
 {
+    unsigned attr_len;
+
     PJ_ASSERT_RETURN(attr && addr_len && addr, PJ_EINVAL);
     PJ_ASSERT_RETURN(addr_len == sizeof(pj_sockaddr_in) ||
 		     addr_len == sizeof(pj_sockaddr_in6), PJ_EINVAL);
 
-    INIT_ATTR(attr, attr_type, STUN_GENERIC_IP_ADDR_LEN);
+    attr_len = pj_sockaddr_get_addr_len(addr) + 4;
+    INIT_ATTR(attr, attr_type, attr_len);
 
     pj_memcpy(&attr->sockaddr, addr, addr_len);
@@ -849,11 +881,16 @@
 static pj_status_t decode_sockaddr_attr(pj_pool_t *pool, 
 				        const pj_uint8_t *buf, 
+					const pj_stun_msg_hdr *msghdr, 
 				        void **p_attr)
 {
     pj_stun_sockaddr_attr *attr;
+    int af;
+    unsigned addr_len;
     pj_uint32_t val;
 
     PJ_CHECK_STACK();
     
+    PJ_UNUSED_ARG(msghdr);
+
     /* Create the attribute */
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_sockaddr_attr);
@@ -861,18 +898,36 @@
 
     /* Check that the attribute length is valid */
-    if (attr->hdr.length != STUN_GENERIC_IP_ADDR_LEN)
+    if (attr->hdr.length != STUN_GENERIC_IPV4_ADDR_LEN &&
+	attr->hdr.length != STUN_GENERIC_IPV6_ADDR_LEN)
+    {
 	return PJNATH_ESTUNINATTRLEN;
+    }
 
     /* Check address family */
     val = *(pj_uint8_t*)(buf + ATTR_HDR_LEN + 1);
 
-    /* Check address family is valid (only supports ipv4 for now) */
-    if (val != 1)
-	return PJNATH_ESTUNIPV6NOTSUPP;
+    /* Check address family is valid */
+    if (val == 1) {
+	if (attr->hdr.length != STUN_GENERIC_IPV4_ADDR_LEN)
+	    return PJNATH_ESTUNINATTRLEN;
+	af = pj_AF_INET();
+	addr_len = 4;
+    } else if (val == 2) {
+	if (attr->hdr.length != STUN_GENERIC_IPV6_ADDR_LEN)
+	    return PJNATH_ESTUNINATTRLEN;
+	af = pj_AF_INET6();
+	addr_len = 16;
+    } else {
+	/* Invalid address family */
+	return PJNATH_EINVAF;
+    }
 
     /* Get port and address */
-    pj_sockaddr_in_init(&attr->sockaddr.ipv4, NULL, 0);
-    attr->sockaddr.ipv4.sin_port = GETVAL16N(buf, ATTR_HDR_LEN+2);
-    attr->sockaddr.ipv4.sin_addr.s_addr = GETVAL32N(buf, ATTR_HDR_LEN+4);
+    pj_sockaddr_init(af, &attr->sockaddr, NULL, 0);
+    pj_sockaddr_set_port(&attr->sockaddr, 
+			 GETVAL16H(buf, ATTR_HDR_LEN+2));
+    pj_memcpy(pj_sockaddr_get_addr(&attr->sockaddr),
+	      buf+ATTR_HDR_LEN+4,
+	      addr_len);
 
     /* Done */
@@ -885,4 +940,5 @@
 static pj_status_t decode_xored_sockaddr_attr(pj_pool_t *pool, 
 					      const pj_uint8_t *buf, 
+					      const pj_stun_msg_hdr *msghdr, 
 					      void **p_attr)
 {
@@ -890,5 +946,5 @@
     pj_status_t status;
 
-    status = decode_sockaddr_attr(pool, buf, p_attr);
+    status = decode_sockaddr_attr(pool, buf, msghdr, p_attr);
     if (status != PJ_SUCCESS)
 	return status;
@@ -897,6 +953,32 @@
 
     attr->xor_ed = PJ_TRUE;
-    attr->sockaddr.ipv4.sin_port ^= pj_htons(0x2112);
-    attr->sockaddr.ipv4.sin_addr.s_addr ^= pj_htonl(0x2112A442);
+
+    if (attr->sockaddr.addr.sa_family == pj_AF_INET()) {
+	attr->sockaddr.ipv4.sin_port ^= pj_htons(PJ_STUN_MAGIC >> 16);
+	attr->sockaddr.ipv4.sin_addr.s_addr ^= pj_htonl(PJ_STUN_MAGIC);
+    } else if (attr->sockaddr.addr.sa_family == pj_AF_INET6()) {
+	unsigned i;
+	pj_uint8_t *dst = (pj_uint8_t*) &attr->sockaddr.ipv6.sin6_addr;
+	pj_uint32_t magic = pj_htonl(PJ_STUN_MAGIC);
+
+	attr->sockaddr.ipv6.sin6_port ^= pj_htons(PJ_STUN_MAGIC >> 16);
+
+	/* If the IP address family is IPv6, X-Address is computed by
+	 * taking the mapped IP address in host byte order, XOR'ing it
+	 * with the concatenation of the magic cookie and the 96-bit 
+	 * transaction ID, and converting the result to network byte 
+	 * order.
+	 */
+	for (i=0; i<4; ++i) {
+	    dst[i] ^= ((const pj_uint8_t*)&magic)[i];
+	}
+	pj_assert(sizeof(msghdr->tsx_id[0]) == 1);
+	for (i=0; i<12; ++i) {
+	    dst[i+4] ^= msghdr->tsx_id[i];
+	}
+
+    } else {
+	return PJNATH_EINVAF;
+    }
 
     /* Done */
@@ -908,59 +990,126 @@
 
 static pj_status_t encode_sockaddr_attr(const void *a, pj_uint8_t *buf, 
-				        unsigned len, unsigned *printed)
-{
-    enum {
-	ATTR_LEN = ATTR_HDR_LEN + STUN_GENERIC_IP_ADDR_LEN
-    };
+				        unsigned len, 
+					const pj_stun_msg_hdr *msghdr,
+					unsigned *printed)
+{
     pj_uint8_t *start_buf = buf;
     const pj_stun_sockaddr_attr *ca = 
 	(const pj_stun_sockaddr_attr *)a;
 
-    if (len < ATTR_LEN) 
-	return PJ_ETOOSMALL;
-
     PJ_CHECK_STACK();
     
-    /* Copy and convert headers to network byte order */
+    /* Common: attribute type */
     PUTVAL16H(buf, 0, ca->hdr.type);
-    PUTVAL16H(buf, 2, STUN_GENERIC_IP_ADDR_LEN);
-    buf += ATTR_HDR_LEN;
+
+    if (ca->sockaddr.addr.sa_family == pj_AF_INET()) {
+	enum {
+	    ATTR_LEN = ATTR_HDR_LEN + STUN_GENERIC_IPV4_ADDR_LEN
+	};
+
+	if (len < ATTR_LEN) 
+	    return PJ_ETOOSMALL;
+
+	/* attribute len */
+	PUTVAL16H(buf, 2, STUN_GENERIC_IPV4_ADDR_LEN);
+	buf += ATTR_HDR_LEN;
     
-    /* Ignored */
-    *buf++ = '\0';
-
-    /* Family (IPv4 only for now) */
-    PJ_ASSERT_RETURN(ca->sockaddr.addr.sa_family == pj_AF_INET(), PJ_EINVAL);
-    *buf++ = 1;
-
-    if (ca->xor_ed) {
-	pj_uint32_t addr;
-	pj_uint16_t port;
-
-	addr = ca->sockaddr.ipv4.sin_addr.s_addr;
-	port = ca->sockaddr.ipv4.sin_port;
-
-	port ^= pj_htons(0x2112);
-	addr ^= pj_htonl(0x2112A442);
-
-	/* Port */
-	pj_memcpy(buf, &port, 2);
-	buf += 2;
-
-	/* Address */
-	pj_memcpy(buf, &addr, 4);
-	buf += 4;
+	/* Ignored */
+	*buf++ = '\0';
+
+	/* Address family, 1 for IPv4 */
+	*buf++ = 1;
+
+	/* IPv4 address */
+	if (ca->xor_ed) {
+	    pj_uint32_t addr;
+	    pj_uint16_t port;
+
+	    addr = ca->sockaddr.ipv4.sin_addr.s_addr;
+	    port = ca->sockaddr.ipv4.sin_port;
+
+	    port ^= pj_htons(PJ_STUN_MAGIC >> 16);
+	    addr ^= pj_htonl(PJ_STUN_MAGIC);
+
+	    /* Port */
+	    pj_memcpy(buf, &port, 2);
+	    buf += 2;
+
+	    /* Address */
+	    pj_memcpy(buf, &addr, 4);
+	    buf += 4;
+
+	} else {
+	    /* Port */
+	    pj_memcpy(buf, &ca->sockaddr.ipv4.sin_port, 2);
+	    buf += 2;
+
+	    /* Address */
+	    pj_memcpy(buf, &ca->sockaddr.ipv4.sin_addr, 4);
+	    buf += 4;
+	}
+
+	pj_assert(buf - start_buf == ATTR_LEN);
+
+    } else if (ca->sockaddr.addr.sa_family == pj_AF_INET6()) {
+	/* IPv6 address */
+	enum {
+	    ATTR_LEN = ATTR_HDR_LEN + STUN_GENERIC_IPV6_ADDR_LEN
+	};
+
+	if (len < ATTR_LEN) 
+	    return PJ_ETOOSMALL;
+
+	/* attribute len */
+	PUTVAL16H(buf, 2, STUN_GENERIC_IPV6_ADDR_LEN);
+	buf += ATTR_HDR_LEN;
+    
+	/* Ignored */
+	*buf++ = '\0';
+
+	/* Address family, 2 for IPv6 */
+	*buf++ = 2;
+
+	/* IPv6 address */
+	if (ca->xor_ed) {
+	    unsigned i;
+	    pj_uint8_t *dst;
+	    const pj_uint8_t *src;
+	    pj_uint32_t magic = pj_htonl(PJ_STUN_MAGIC);
+	    pj_uint16_t port = ca->sockaddr.ipv6.sin6_port;
+
+	    /* Port */
+	    port ^= pj_htons(PJ_STUN_MAGIC >> 16);
+	    pj_memcpy(buf, &port, 2);
+	    buf += 2;
+
+	    /* Address */
+	    dst = buf;
+	    src = (const pj_uint8_t*) &ca->sockaddr.ipv6.sin6_addr;
+	    for (i=0; i<4; ++i) {
+		dst[i] = (pj_uint8_t)(src[i] ^ ((const pj_uint8_t*)&magic)[i]);
+	    }
+	    pj_assert(sizeof(msghdr->tsx_id[0]) == 1);
+	    for (i=0; i<12; ++i) {
+		dst[i+4] = (pj_uint8_t)(src[i+4] ^ msghdr->tsx_id[i]);
+	    }
+
+	    buf += 16;
+
+	} else {
+	    /* Port */
+	    pj_memcpy(buf, &ca->sockaddr.ipv6.sin6_port, 2);
+	    buf += 2;
+
+	    /* Address */
+	    pj_memcpy(buf, &ca->sockaddr.ipv6.sin6_addr, 16);
+	    buf += 16;
+	}
+
+	pj_assert(buf - start_buf == ATTR_LEN);
 
     } else {
-	/* Port */
-	pj_memcpy(buf, &ca->sockaddr.ipv4.sin_port, 2);
-	buf += 2;
-
-	/* Address */
-	pj_memcpy(buf, &ca->sockaddr.ipv4.sin_addr, 4);
-	buf += 4;
-    }
-
-    pj_assert(buf - start_buf == ATTR_LEN);
+	return PJNATH_EINVAF;
+    }
 
     /* Done */
@@ -1041,8 +1190,11 @@
 static pj_status_t decode_string_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf, 
+				      const pj_stun_msg_hdr *msghdr, 
 				      void **p_attr)
 {
     pj_stun_string_attr *attr;
     pj_str_t value;
+
+    PJ_UNUSED_ARG(msghdr);
 
     /* Create the attribute */
@@ -1066,5 +1218,7 @@
 
 static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, 
-				      unsigned len, unsigned *printed)
+				      unsigned len, 
+				      const pj_stun_msg_hdr *msghdr,
+				      unsigned *printed)
 {
     const pj_stun_string_attr *ca = 
@@ -1073,4 +1227,6 @@
     PJ_CHECK_STACK();
     
+    PJ_UNUSED_ARG(msghdr);
+
     /* Calculated total attr_len (add padding if necessary) */
     *printed = (ca->value.slen + ATTR_HDR_LEN + 3) & (~3);
@@ -1155,7 +1311,10 @@
 static pj_status_t decode_empty_attr(pj_pool_t *pool, 
 				     const pj_uint8_t *buf, 
+				     const pj_stun_msg_hdr *msghdr,
 				     void **p_attr)
 {
     pj_stun_empty_attr *attr;
+
+    PJ_UNUSED_ARG(msghdr);
 
     /* Check that the struct address is valid */
@@ -1178,7 +1337,11 @@
 
 static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, 
-				     unsigned len, unsigned *printed)
+				     unsigned len, 
+				     const pj_stun_msg_hdr *msghdr,
+				     unsigned *printed)
 {
     const pj_stun_empty_attr *ca = (pj_stun_empty_attr*)a;
+
+    PJ_UNUSED_ARG(msghdr);
 
     if (len < ATTR_HDR_LEN) 
@@ -1248,7 +1411,10 @@
 static pj_status_t decode_uint_attr(pj_pool_t *pool, 
 				    const pj_uint8_t *buf, 
+				    const pj_stun_msg_hdr *msghdr, 
 				    void **p_attr)
 {
     pj_stun_uint_attr *attr;
+
+    PJ_UNUSED_ARG(msghdr);
 
     /* Create the attribute */
@@ -1270,9 +1436,13 @@
 
 static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, 
-				    unsigned len, unsigned *printed)
+				    unsigned len, 
+				    const pj_stun_msg_hdr *msghdr,
+				    unsigned *printed)
 {
     const pj_stun_uint_attr *ca = (const pj_stun_uint_attr*)a;
 
     PJ_CHECK_STACK();
+
+    PJ_UNUSED_ARG(msghdr);
     
     if (len < 8) 
@@ -1344,7 +1514,10 @@
 static pj_status_t decode_uint64_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf, 
+				      const pj_stun_msg_hdr *msghdr, 
 				      void **p_attr)
 {
     pj_stun_uint64_attr *attr;
+
+    PJ_UNUSED_ARG(msghdr);
 
     /* Create the attribute */
@@ -1365,9 +1538,13 @@
 
 static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf, 
-				      unsigned len, unsigned *printed)
+				      unsigned len, 
+				      const pj_stun_msg_hdr *msghdr,
+				      unsigned *printed)
 {
     const pj_stun_uint64_attr *ca = (const pj_stun_uint64_attr*)a;
 
     PJ_CHECK_STACK();
+
+    PJ_UNUSED_ARG(msghdr);
     
     if (len < 12) 
@@ -1434,7 +1611,10 @@
 static pj_status_t decode_msgint_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf,
+				      const pj_stun_msg_hdr *msghdr, 
 				      void **p_attr)
 {
     pj_stun_msgint_attr *attr;
+
+    PJ_UNUSED_ARG(msghdr);
 
     /* Create attribute */
@@ -1456,5 +1636,7 @@
 
 static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, 
-				      unsigned len, unsigned *printed)
+				      unsigned len, 
+				      const pj_stun_msg_hdr *msghdr,
+				      unsigned *printed)
 {
     const pj_stun_msgint_attr *ca = (const pj_stun_msgint_attr*)a;
@@ -1462,4 +1644,6 @@
     PJ_CHECK_STACK();
     
+    PJ_UNUSED_ARG(msghdr);
+
     if (len < 24) 
 	return PJ_ETOOSMALL;
@@ -1545,8 +1729,11 @@
 static pj_status_t decode_errcode_attr(pj_pool_t *pool, 
 				       const pj_uint8_t *buf,
+				       const pj_stun_msg_hdr *msghdr, 
 				       void **p_attr)
 {
     pj_stun_errcode_attr *attr;
     pj_str_t value;
+
+    PJ_UNUSED_ARG(msghdr);
 
     /* Create the attribute */
@@ -1571,5 +1758,7 @@
 
 static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, 
-				       unsigned len, unsigned *printed)
+				       unsigned len, 
+				       const pj_stun_msg_hdr *msghdr,
+				       unsigned *printed)
 {
     const pj_stun_errcode_attr *ca = 
@@ -1578,4 +1767,6 @@
     PJ_CHECK_STACK();
     
+    PJ_UNUSED_ARG(msghdr);
+
     if (len < ATTR_HDR_LEN + 4 + (unsigned)ca->reason.slen) 
 	return PJ_ETOOSMALL;
@@ -1673,4 +1864,5 @@
 static pj_status_t decode_unknown_attr(pj_pool_t *pool, 
 				       const pj_uint8_t *buf, 
+				       const pj_stun_msg_hdr *msghdr, 
 				       void **p_attr)
 {
@@ -1678,4 +1870,6 @@
     const pj_uint16_t *punk_attr;
     unsigned i;
+
+    PJ_UNUSED_ARG(msghdr);
 
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_unknown_attr);
@@ -1699,5 +1893,7 @@
 
 static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, 
-				       unsigned len, unsigned *printed)
+				       unsigned len, 
+				       const pj_stun_msg_hdr *msghdr,
+				       unsigned *printed)
 {
     const pj_stun_unknown_attr *ca = (const pj_stun_unknown_attr*) a;
@@ -1707,4 +1903,6 @@
     PJ_CHECK_STACK();
     
+    PJ_UNUSED_ARG(msghdr);
+
     /* Check that buffer is enough */
     if (len < ATTR_HDR_LEN + (ca->attr_count << 1))
@@ -1808,7 +2006,10 @@
 static pj_status_t decode_binary_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf,
+				      const pj_stun_msg_hdr *msghdr,
 				      void **p_attr)
 {
     pj_stun_binary_attr *attr;
+
+    PJ_UNUSED_ARG(msghdr);
 
     /* Create the attribute */
@@ -1830,5 +2031,7 @@
 
 static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf, 
-				      unsigned len, unsigned *printed)
+				      unsigned len, 
+				      const pj_stun_msg_hdr *msghdr,
+				      unsigned *printed)
 {
     const pj_stun_binary_attr *ca = (const pj_stun_binary_attr*)a;
@@ -1836,4 +2039,6 @@
     PJ_CHECK_STACK();
     
+    PJ_UNUSED_ARG(msghdr);
+
     /* Calculated total attr_len (add padding if necessary) */
     *printed = (ca->length + ATTR_HDR_LEN + 3) & (~3);
@@ -2221,5 +2426,5 @@
 
 	    /* Parse the attribute */
-	    status = (adesc->decode_attr)(pool, pdu, &attr);
+	    status = (adesc->decode_attr)(pool, pdu, &msg->hdr, &attr);
 
 	    if (status != PJ_SUCCESS) {
@@ -2409,5 +2614,6 @@
 	adesc = find_attr_desc(attr_hdr->type);
 	if (adesc) {
-	    status = adesc->encode_attr(attr_hdr, buf, buf_size, &printed);
+	    status = adesc->encode_attr(attr_hdr, buf, buf_size, &msg->hdr, 
+					&printed);
 	} else {
 	    /* This may be a generic attribute */
@@ -2415,5 +2621,6 @@
 						   attr_hdr;
 	    PJ_ASSERT_RETURN(bin_attr->magic == PJ_STUN_MAGIC, PJ_EBUG);
-	    status = encode_binary_attr(bin_attr, buf, buf_size, &printed);
+	    status = encode_binary_attr(bin_attr, buf, buf_size, &msg->hdr,
+					&printed);
 	}
 
@@ -2520,5 +2727,5 @@
 	/* Put this attribute in the message */
 	status = encode_msgint_attr(amsgint, buf, buf_size, 
-			            &printed);
+			            &msg->hdr, &printed);
 	if (status != PJ_SUCCESS)
 	    return status;
@@ -2542,5 +2749,5 @@
 	/* Put this attribute in the message */
 	status = encode_uint_attr(afingerprint, buf, buf_size, 
-				          &printed);
+				  &msg->hdr, &printed);
 	if (status != PJ_SUCCESS)
 	    return status;
