Index: pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_msg.c
===================================================================
--- pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_msg.c	(revision 929)
+++ pjproject/branches/iceproject/pjlib-util/src/pjlib-util/stun_msg.c	(revision 990)
@@ -80,4 +80,7 @@
 #define ATTR_HDR_LEN	    4
 
+#define getval16(p, pos)    (pj_uint16_t)(((p)[(pos)] << 8) | \
+					  ((p)[(pos) + 1] << 0))
+
 
 //////////////////////////////////////////////////////////////////////////////
@@ -93,17 +96,30 @@
 pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool,
 				    int attr_type,
-				    pj_uint32_t ip_addr,
-				    int port,
+				    pj_bool_t xor_ed,
+				    unsigned addr_len,
+				    const pj_sockaddr_t *addr,
 				    pj_stun_generic_ip_addr_attr **p_attr)
 {
     pj_stun_generic_ip_addr_attr *attr;
 
-    PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pool && addr_len && addr && p_attr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(addr_len == sizeof(pj_sockaddr_in) ||
+		     addr_len == sizeof(pj_sockaddr_in6), PJ_EINVAL);
 
     attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_ip_addr_attr);
     INIT_ATTR(attr, attr_type, STUN_GENERIC_IP_ADDR_LEN);
-    attr->family = 1;
-    attr->port = (pj_uint16_t) port;
-    attr->addr.ipv4 = ip_addr;
+
+    if (!xor_ed) {
+	pj_memcpy(&attr->addr, addr, addr_len);
+    } else if (addr_len == sizeof(pj_sockaddr_in)) {
+	const pj_sockaddr_in *addr4 = (const pj_sockaddr_in*) addr;
+
+	pj_sockaddr_in_init(&attr->addr.ipv4, NULL, 0);
+	attr->addr.ipv4.sin_port = (pj_uint16_t)(addr4->sin_port ^ 0x2112);
+	attr->addr.ipv4.sin_addr.s_addr = (addr4->sin_addr.s_addr ^ 
+					   pj_htonl(0x2112A442));
+    } else {
+	return PJ_ENOTSUP;
+    }
 
     *p_attr = attr;
@@ -117,26 +133,14 @@
 					       void **p_attr)
 {
-    enum
-    {
-	ATTR_LEN = STUN_GENERIC_IP_ADDR_LEN + ATTR_HDR_LEN
-    };
     pj_stun_generic_ip_addr_attr *attr;
-
-    /* Check that the struct address is valid */
-    pj_assert(sizeof(pj_stun_generic_ip_addr_attr) == ATTR_LEN);
+    pj_uint32_t val;
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_generic_ip_addr_attr);
-    pj_memcpy(attr, buf, ATTR_LEN);
-
-    /* Check address family is valid (only supports ipv4 for now) */
-    if (attr->family != 1)
-	return PJLIB_UTIL_ESTUNINATTR;
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_ip_addr_attr);
+    pj_memcpy(attr, buf, ATTR_HDR_LEN);
 
     /* Convert to host byte order */
     attr->hdr.type = pj_ntohs(attr->hdr.type);
     attr->hdr.length = pj_ntohs(attr->hdr.length);
-    attr->port = pj_ntohs(attr->port);
-    attr->addr.ipv4 = pj_ntohl(attr->addr.ipv4);
 
     /* Check that the attribute length is valid */
@@ -144,4 +148,16 @@
 	return PJLIB_UTIL_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 PJLIB_UTIL_ESTUNINATTR;
+
+    /* Get port and address */
+    pj_sockaddr_in_init(&attr->addr.ipv4, NULL, 0);
+    attr->addr.ipv4.sin_port = getval16(buf, ATTR_HDR_LEN + 2);
+    pj_memcpy(&attr->addr.ipv4.sin_addr, buf+ATTR_HDR_LEN+4, 4);
+
     /* Done */
     *p_attr = attr;
@@ -154,8 +170,10 @@
 					       unsigned len, unsigned *printed)
 {
-    enum
-    {
-	ATTR_LEN = STUN_GENERIC_IP_ADDR_LEN + ATTR_HDR_LEN
+    enum {
+	ATTR_LEN = ATTR_HDR_LEN + STUN_GENERIC_IP_ADDR_LEN
     };
+    pj_uint8_t *start_buf = buf;
+    const pj_stun_generic_ip_addr_attr *ca = 
+	(const pj_stun_generic_ip_addr_attr *)a;
     pj_stun_generic_ip_addr_attr *attr;
 
@@ -163,14 +181,30 @@
 	return PJ_ETOOSMALL;
 
-    /* Copy and convert attribute to network byte order */
-    pj_memcpy(buf, a, ATTR_LEN);
+    /* Copy and convert headers to network byte order */
+    pj_memcpy(buf, a, ATTR_HDR_LEN);
     attr = (pj_stun_generic_ip_addr_attr*) buf;
     attr->hdr.type = pj_htons(attr->hdr.type);
-    attr->hdr.length = pj_htons(attr->hdr.length);
-    attr->port = pj_htons(attr->port);
-    attr->addr.ipv4 = pj_htonl(attr->addr.ipv4);
-
-    /* Done */
-    *printed = ATTR_LEN;
+    attr->hdr.length = pj_htons((pj_uint16_t)ATTR_LEN);
+    buf += ATTR_HDR_LEN;
+    
+    /* Ignored */
+    *buf++ = '\0';
+
+    /* Family (IPv4 only for now) */
+    PJ_ASSERT_RETURN(ca->addr.addr.sa_family == PJ_AF_INET, PJ_EINVAL);
+    *buf++ = 1;
+
+    /* Port */
+    pj_memcpy(buf, &ca->addr.ipv4.sin_port, 2);
+    buf += 2;
+
+    /* Address */
+    pj_memcpy(buf, &ca->addr.ipv4.sin_addr, 4);
+    buf += 4;
+
+    pj_assert(buf - start_buf == ATTR_LEN);
+
+    /* Done */
+    *printed = buf - start_buf;
 
     return PJ_SUCCESS;
@@ -214,5 +248,5 @@
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_generic_string_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_string_attr);
 
     /* Copy the header */
@@ -247,12 +281,14 @@
     /* Calculated total attr_len (add padding if necessary) */
     *printed = (ca->value.slen + ATTR_HDR_LEN + 3) & (~3);
-    if (len < *printed)
+    if (len < *printed) {
+	*printed = 0;
 	return PJ_ETOOSMALL;
+    }
 
     /* Copy header */
     pj_memcpy(buf, a, ATTR_HDR_LEN);
+    attr = (pj_stun_attr_hdr*)buf;
 
     /* Set the correct length */
-    attr = (pj_stun_attr_hdr*)buf;
     attr->length = (pj_uint16_t) ca->value.slen;
 
@@ -265,4 +301,79 @@
 
     /* Done */
+    return PJ_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * STUN empty attribute (used by USE-CANDIDATE).
+ */
+
+/*
+ * Create a STUN empty attribute.
+ */
+PJ_DEF(pj_status_t) 
+pj_stun_empty_attr_create(pj_pool_t *pool,
+			  int attr_type,
+			  pj_stun_empty_attr **p_attr)
+{
+    pj_stun_empty_attr *attr;
+
+    PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL);
+
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_empty_attr);
+    INIT_ATTR(attr, attr_type, sizeof(pj_stun_empty_attr));
+
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t decode_empty_attr(pj_pool_t *pool, 
+				     const pj_uint8_t *buf, 
+				     void **p_attr)
+{
+    pj_stun_empty_attr *attr;
+
+    /* Check that the struct address is valid */
+    pj_assert(sizeof(pj_stun_empty_attr) == ATTR_HDR_LEN);
+
+    /* Create the attribute */
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_empty_attr);
+    pj_memcpy(attr, buf, ATTR_HDR_LEN);
+
+    /* Convert to host byte order */
+    attr->hdr.type = pj_ntohs(attr->hdr.type);
+    attr->hdr.length = pj_ntohs(attr->hdr.length);
+
+    /* Check that the attribute length is valid */
+    if (attr->hdr.length != ATTR_HDR_LEN)
+	return PJLIB_UTIL_ESTUNINATTRLEN;
+
+    /* Done */
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, 
+				     unsigned len, unsigned *printed)
+{
+    pj_stun_empty_attr *attr;
+
+    if (len < ATTR_HDR_LEN) 
+	return PJ_ETOOSMALL;
+
+    /* Copy and convert attribute to network byte order */
+    pj_memcpy(buf, a, ATTR_HDR_LEN);
+    attr = (pj_stun_empty_attr*) buf;
+    attr->hdr.type = pj_htons(attr->hdr.type);
+    attr->hdr.length = pj_htons(attr->hdr.length);
+
+    /* Done */
+    *printed = ATTR_HDR_LEN;
+
     return PJ_SUCCESS;
 }
@@ -312,5 +423,5 @@
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_generic_uint_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_uint_attr);
     pj_memcpy(attr, buf, ATTR_LEN);
 
@@ -397,5 +508,5 @@
 
     /* Create attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_msg_integrity_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg_integrity_attr);
     pj_memcpy(attr, buf, sizeof(pj_stun_msg_integrity_attr));
     attr->hdr.type = pj_ntohs(attr->hdr.type);
@@ -486,5 +597,5 @@
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_error_code_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_error_code_attr);
 
     /* Copy the header */
@@ -683,5 +794,5 @@
 
     /* Create the attribute */
-    attr = PJ_POOL_ALLOC_TYPE(pool, pj_stun_binary_attr);
+    attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_binary_attr);
 
     /* Copy the header */
@@ -740,7 +851,4 @@
 struct attr_desc
 {
-    unsigned  type;
-    const char *attr_name;
-
     pj_status_t	(*decode_attr)(pj_pool_t *pool, const pj_uint8_t *buf, 
 			       void **p_attr);
@@ -748,182 +856,261 @@
 			       unsigned len, unsigned *printed);
 
-} attr_desc[] = 
-{
-    {
-	PJ_STUN_ATTR_MAPPED_ADDR,
-	"MAPPED-ADDRESS",
+};
+
+struct attr_desc mandatory_attr_desc[] = 
+{
+    {
+	/* type zero */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_MAPPED_ADDR, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_RESPONSE_ADDR,
-	"RESPONSE-ADDRESS",
+	/* PJ_STUN_ATTR_RESPONSE_ADDR, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_CHANGE_REQUEST,
-	"CHANGE-REQUEST",
+	/* PJ_STUN_ATTR_CHANGE_REQUEST, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_SOURCE_ADDR, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_SOURCE_ADDR,
-	"SOURCE-ADDRESS",
+	/* PJ_STUN_ATTR_CHANGED_ADDR, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_CHANGED_ADDR,
-	"CHANGED-ADDRESS",
+	/* PJ_STUN_ATTR_USERNAME, */
+	&decode_generic_string_attr,
+	&encode_generic_string_attr
+    },
+    {
+	/* PJ_STUN_ATTR_PASSWORD, */
+	&decode_generic_string_attr,
+	&encode_generic_string_attr
+    },
+    {
+	/* PJ_STUN_ATTR_MESSAGE_INTEGRITY, */
+	&decode_msg_integrity_attr,
+	&encode_msg_integrity_attr
+    },
+    {
+	/* PJ_STUN_ATTR_ERROR_CODE, */
+	&decode_error_code_attr,
+	&encode_error_code_attr
+    },
+    {
+	/* PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, */
+	&decode_unknown_attr,
+	&encode_unknown_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REFLECTED_FROM, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_USERNAME,
-	"USERNAME",
+	/* ID 0x000C is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_LIFETIME, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* ID 0x000E is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x000F is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_BANDWIDTH, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* ID 0x0011 is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_REMOTE_ADDRESS, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	/* PJ_STUN_ATTR_DATA, */
+	&decode_binary_attr,
+	&encode_binary_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REALM, */
 	&decode_generic_string_attr,
 	&encode_generic_string_attr
     },
     {
-	PJ_STUN_ATTR_PASSWORD,
-	"PASSWORD",
+	/* PJ_STUN_ATTR_NONCE, */
 	&decode_generic_string_attr,
 	&encode_generic_string_attr
     },
     {
-	PJ_STUN_ATTR_MESSAGE_INTEGRITY,
-	"MESSAGE-INTEGRITY",
-	&decode_msg_integrity_attr,
-	&encode_msg_integrity_attr
-    },
-    {
-	PJ_STUN_ATTR_ERROR_CODE,
-	"ERROR-CODE",
-	&decode_error_code_attr,
-	&encode_error_code_attr
-    },
-    {
-	PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES,
-	"UNKNOWN-ATTRIBUTES",
-	&decode_unknown_attr,
-	&encode_unknown_attr
-    },
-    {
-	PJ_STUN_ATTR_REFLECTED_FROM,
-	"REFLECTED-FROM",
+	/* PJ_STUN_ATTR_RELAY_ADDRESS, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_REALM,
-	"REALM",
+	/* PJ_STUN_ATTR_REQUESTED_ADDR_TYPE, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REQUESTED_PORT_PROPS, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REQUESTED_TRANSPORT, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* ID 0x001A is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001B is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001C is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001D is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001E is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* ID 0x001F is not assigned */
+	NULL,
+	NULL
+    },
+    {
+	/* PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	/* PJ_STUN_ATTR_TIMER_VAL, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_REQUESTED_IP, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	/* PJ_STUN_ATTR_XOR_REFLECTED_FROM, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+    {
+	/* PJ_STUN_ATTR_PRIORITY, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_USE_CANDIDATE, */
+	&decode_empty_attr,
+	&encode_empty_attr
+    },
+    {
+	/* PJ_STUN_ATTR_XOR_INTERNAL_ADDR, */
+	&decode_generic_ip_addr_attr,
+	&encode_generic_ip_addr_attr
+    },
+
+    /* Sentinel */
+    {
+	/* PJ_STUN_ATTR_END_MANDATORY_ATTR */
+	NULL,
+	NULL
+    }
+};
+
+static struct attr_desc extended_attr_desc[] =
+{
+    {
+	/* PJ_STUN_ATTR_FINGERPRINT, */
+	&decode_generic_uint_attr,
+	&encode_generic_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_SERVER, */
 	&decode_generic_string_attr,
 	&encode_generic_string_attr
     },
     {
-	PJ_STUN_ATTR_NONCE,
-	"NONCE",
-	&decode_generic_string_attr,
-	&encode_generic_string_attr
-    },
-    {
-	PJ_STUN_ATTR_XOR_MAPPED_ADDRESS,
-	"XOR-MAPPED-ADDRESS",
+	/* PJ_STUN_ATTR_ALTERNATE_SERVER, */
 	&decode_generic_ip_addr_attr,
 	&encode_generic_ip_addr_attr
     },
     {
-	PJ_STUN_ATTR_FINGERPRINT,
-	"FINGERPRINT",
+	/* PJ_STUN_ATTR_REFRESH_INTERVAL, */
 	&decode_generic_uint_attr,
 	&encode_generic_uint_attr
     },
-    {
-	PJ_STUN_ATTR_SERVER,
-	"SERVER",
-	&decode_generic_string_attr,
-	&encode_generic_string_attr
-    },
-    {
-	PJ_STUN_ATTR_ALTERNATE_SERVER,
-	"ALTERNATE-SERVER",
-	&decode_generic_ip_addr_attr,
-	&encode_generic_ip_addr_attr
-    },
-    {
-	PJ_STUN_ATTR_REFRESH_INTERVAL,
-	"REFRESH-INTERVAL",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_LIFETIME,
-	"LIFETIME",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_BANDWIDTH,
-	"BANDWIDTH",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_REMOTE_ADDRESS,
-	"REMOTE-ADDRESS",
-	&decode_generic_ip_addr_attr,
-	&encode_generic_ip_addr_attr
-    },
-    {
-	PJ_STUN_ATTR_DATA,
-	"DATA",
-	&decode_binary_attr,
-	&encode_binary_attr
-    },
-    {
-	PJ_STUN_ATTR_RELAY_ADDRESS,
-	"RELAY-ADDRESS",
-	&decode_generic_ip_addr_attr,
-	&encode_generic_ip_addr_attr
-    },
-    {
-	PJ_STUN_ATTR_REQUESTED_PORT_PROPS,
-	"REQUESTED-PORT-PROPS",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_REQUESTED_TRANSPORT,
-	"REQUESTED-TRANSPORT",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    },
-    {
-	PJ_STUN_ATTR_REQUESTED_IP,
-	"STUN REQUESTED-IP",
-	&decode_generic_ip_addr_attr,
-	&encode_generic_ip_addr_attr
-    },
-    {
-	PJ_STUN_ATTR_TIMER_VAL,
-	"TIMER-VAL",
-	&decode_generic_uint_attr,
-	&encode_generic_uint_attr
-    }
 };
 
-static struct attr_desc *find_attr_desc(unsigned attr_type)
-{
-    unsigned i;
-
-    for (i=0; i<PJ_ARRAY_SIZE(attr_desc); ++i) {
-	if (attr_desc[i].type == attr_type)
-	    return &attr_desc[i];
-    }
-
-    return NULL;
-}
-
+
+static const struct attr_desc *find_attr_desc(unsigned attr_type)
+{
+    struct attr_desc *desc;
+
+    /* Check that attr_desc array is valid */
+    pj_assert(PJ_ARRAY_SIZE(mandatory_attr_desc)==
+	      PJ_STUN_ATTR_END_MANDATORY_ATTR+1);
+    pj_assert(mandatory_attr_desc[PJ_STUN_ATTR_END_MANDATORY_ATTR].decode_attr
+	      == NULL);
+    pj_assert(mandatory_attr_desc[PJ_STUN_ATTR_USE_CANDIDATE].decode_attr 
+	      == &decode_empty_attr);
+    pj_assert(PJ_ARRAY_SIZE(extended_attr_desc) ==
+	      PJ_STUN_ATTR_END_EXTENDED_ATTR-PJ_STUN_ATTR_START_EXTENDED_ATTR);
+
+    if (attr_type < PJ_STUN_ATTR_START_EXTENDED_ATTR)
+	desc = &mandatory_attr_desc[attr_type];
+    else if (attr_type >= PJ_STUN_ATTR_START_EXTENDED_ATTR &&
+	     attr_type < PJ_STUN_ATTR_END_EXTENDED_ATTR)
+	desc = &extended_attr_desc[attr_type-PJ_STUN_ATTR_START_EXTENDED_ATTR];
+    else
+	return NULL;
+
+    return desc->decode_attr == NULL ? NULL : desc;
+}
 
 
@@ -933,4 +1120,5 @@
 PJ_DEF(pj_status_t) pj_stun_msg_create( pj_pool_t *pool,
 					unsigned msg_type,
+					pj_uint32_t magic,
 					const pj_uint8_t tsx_id[12],
 					pj_stun_msg **p_msg)
@@ -942,5 +1130,5 @@
     msg = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg);
     msg->hdr.type = (pj_uint16_t) msg_type;
-    msg->hdr.magic = (pj_uint32_t) PJ_STUN_MAGIC;
+    msg->hdr.magic = magic;
 
     if (tsx_id) {
@@ -969,4 +1157,18 @@
 
 /*
+ * Add STUN attribute to STUN message.
+ */
+PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg,
+					 pj_stun_attr_hdr *attr)
+{
+    PJ_ASSERT_RETURN(msg && attr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(msg->attr_count < PJ_STUN_MAX_ATTR, PJ_ETOOMANY);
+
+    msg->attr[msg->attr_count++] = attr;
+    return PJ_SUCCESS;
+}
+
+
+/*
  * Check that the PDU is potentially a valid STUN message.
  */
@@ -985,5 +1187,5 @@
     /* First byte of STUN message is always 0x00 or 0x01. */
     if ((*(const char*)pdu) != 0x00 && (*(const char*)pdu) != 0x01)
-	return PJ_FALSE;
+	return PJLIB_UTIL_ESTUNINMSGTYPE;
 
     /* If magic is set, then there is great possibility that this is
@@ -991,12 +1193,12 @@
      */
     if (pj_ntohl(hdr->magic) == PJ_STUN_MAGIC)
-	return PJ_TRUE;
+	return PJ_SUCCESS;
 
     /* Check the PDU length */
     if (pj_ntohs(hdr->length) > pdu_len)
-	return PJ_FALSE;
+	return PJLIB_UTIL_ESTUNINMSGLEN;
 
     /* Could be a STUN message */
-    return PJ_TRUE;
+    return PJ_SUCCESS;
 }
 
@@ -1031,5 +1233,5 @@
 
     /* Create the message, copy the header, and convert to host byte order */
-    msg = PJ_POOL_ALLOC_TYPE(pool, pj_stun_msg);
+    msg = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg);
     pj_memcpy(&msg->hdr, pdu, sizeof(pj_stun_msg_hdr));
     msg->hdr.type = pj_ntohs(msg->hdr.type);
@@ -1040,9 +1242,12 @@
     pdu_len -= sizeof(pj_stun_msg_hdr);
 
+    if (p_err_code)
+	*p_err_code = 0;
+
     /* Parse attributes */
     uattr_cnt = 0;
     while (pdu_len > 0) {
 	unsigned attr_type, attr_val_len;
-	struct attr_desc *adesc;
+	const struct attr_desc *adesc;
 
 	/* Get attribute type and length. If length is not aligned
@@ -1053,10 +1258,10 @@
 	attr_val_len = (attr_val_len + 3) & (~3);
 
-	/* Get the attribute descriptor */
-	adesc = find_attr_desc(attr_type);
-
 	/* Check length */
 	if (pdu_len < attr_val_len)
 	    return PJLIB_UTIL_ESTUNINATTRLEN;
+
+	/* Get the attribute descriptor */
+	adesc = find_attr_desc(attr_type);
 
 	if (adesc == NULL) {
@@ -1076,5 +1281,5 @@
 		 * if we don't understand the attribute.
 		 */
-		if (p_err_code)
+		if (p_err_code && *p_err_code == 0)
 		    *p_err_code = PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE;
 
@@ -1090,6 +1295,6 @@
 	    if (status != PJ_SUCCESS) {
 		PJ_LOG(4,(THIS_FILE, 
-			  "Error parsing STUN %s attribute: status=%d",
-			  adesc->attr_name, status));
+			  "Error parsing STUN attribute type %d: status=%d",
+			  attr_type, status));
 		return status;
 	    }
@@ -1151,5 +1356,5 @@
     /* Print each attribute */
     for (i=0; i<msg->attr_count; ++i) {
-	struct attr_desc *adesc;
+	const struct attr_desc *adesc;
 	const pj_stun_attr_hdr *attr_hdr;
 	unsigned printed;
@@ -1169,5 +1374,7 @@
     }
 
-    /* Update the message length in the header */
+    /* Update the message length in the header. 
+     * Note that length is not including the 20 bytes header.
+     */
     hdr->length = pj_htons((pj_uint16_t)(buf - start));
 
