Index: /pjproject/trunk/pjmedia/include/pjmedia/rtp.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/rtp.h	(revision 1113)
+++ /pjproject/trunk/pjmedia/include/pjmedia/rtp.h	(revision 1114)
@@ -78,5 +78,6 @@
 
 /**
- * RTP packet header.
+ * RTP packet header. Note that all RTP functions here will work with this
+ * header in network byte order.
  */
 #pragma pack(1)
@@ -230,5 +231,5 @@
  * @param ses		The session.
  * @param default_pt	Default payload type.
- * @param sender_ssrc	SSRC used for outgoing packets.
+ * @param sender_ssrc	SSRC used for outgoing packets, in host byte order.
  *
  * @return		PJ_SUCCESS if successfull.
@@ -263,9 +264,14 @@
  * position regardless of any options present in the RTP packet.
  *
+ * Note that this function does not modify the returned RTP header to
+ * host byte order.
+ *
  * @param ses		The session.
  * @param pkt		The received RTP packet.
  * @param pkt_len	The length of the packet.
- * @param hdr		Upon return will point to the location of the RTP header
- *			inside the packet.
+ * @param hdr		Upon return will point to the location of the RTP 
+ *			header inside the packet. Note that the RTP header
+ *			will be given back as is, meaning that the fields
+ *			will be in network byte order.
  * @param payload	Upon return will point to the location of the
  *			payload inside the packet.
@@ -286,5 +292,6 @@
  *
  * @param ses	    The session.
- * @param hdr	    The RTP header of the incoming packet.
+ * @param hdr	    The RTP header of the incoming packet. The header must
+ *		    be given with fields in network byte order.
  * @param seq_st    Optional structure to receive the status of the RTP packet
  *		    processing.
Index: /pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 1113)
+++ /pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 1114)
@@ -25,5 +25,5 @@
 {
     pjmedia_transport	 base;
-    pj_ice_strans		*ice_st;
+    pj_ice_strans	*ice_st;
 
     pj_time_val		 start_ice;
@@ -409,4 +409,7 @@
     pj_ice_sess_cand cand[PJ_ICE_MAX_CAND];
     const pjmedia_sdp_media *sdp_med;
+    pj_bool_t remote_is_lite = PJ_FALSE;
+    const pj_str_t STR_CANDIDATE = {"candidate", 9};
+    const pj_str_t STR_ICE_LITE = {"ice-lite", 8};
     pj_str_t uname, pass;
     pj_status_t status;
@@ -445,5 +448,5 @@
     pass = attr->value;
 
-    /* Get all candidates */
+    /* Get all candidates in the media */
     cand_cnt = 0;
     for (i=0; i<sdp_med->attr_count; ++i) {
@@ -451,5 +454,9 @@
 
 	attr = sdp_med->attr[i];
-	if (pj_strcmp2(&attr->name, "candidate")!=0)
+
+	if (pj_strcmp(&attr->name, &STR_ICE_LITE)==0)
+	    remote_is_lite = PJ_TRUE;
+
+	if (pj_strcmp(&attr->name, &STR_CANDIDATE)!=0)
 	    continue;
 
@@ -463,4 +470,14 @@
     /* Mark start time */
     pj_gettimeofday(&tp_ice->start_ice);
+
+    /* If our role was controlled but it turns out that remote is 
+     * a lite implementation, change our role to controlling.
+     */
+    if (remote_is_lite && 
+	tp_ice->ice_st->ice->role == PJ_ICE_SESS_ROLE_CONTROLLED)
+    {
+	pj_ice_sess_change_role(tp_ice->ice_st->ice, 
+				PJ_ICE_SESS_ROLE_CONTROLLING);
+    }
 
     /* Start ICE */
@@ -553,6 +570,6 @@
     struct transport_ice *tp_ice = (struct transport_ice*)tp;
     return pj_ice_strans_sendto(tp_ice->ice_st, 1, 
-			    pkt, size, &tp_ice->remote_rtp,
-			    sizeof(pj_sockaddr_in));
+			        pkt, size, &tp_ice->remote_rtp,
+				sizeof(pj_sockaddr_in));
 }
 
@@ -565,6 +582,6 @@
     if (tp_ice->ice_st->comp_cnt > 1) {
 	return pj_ice_strans_sendto(tp_ice->ice_st, 2, 
-				pkt, size, &tp_ice->remote_rtp,
-				sizeof(pj_sockaddr_in));
+				    pkt, size, &tp_ice->remote_rtp,
+				    sizeof(pj_sockaddr_in));
     } else {
 	return PJ_SUCCESS;
Index: /pjproject/trunk/pjnath/include/pjnath/ice_session.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/ice_session.h	(revision 1113)
+++ /pjproject/trunk/pjnath/include/pjnath/ice_session.h	(revision 1114)
@@ -445,4 +445,5 @@
     pj_mutex_t		*mutex;			    /**< Mutex.		    */
     pj_ice_sess_role	 role;			    /**< ICE role.	    */
+    pj_timestamp	 tie_breaker;		    /**< Tie breaker value  */
     pj_bool_t		 is_complete;		    /**< Complete?	    */
     pj_status_t		 ice_status;		    /**< Error status.	    */
@@ -551,4 +552,19 @@
 
 /**
+ * Change session role. This happens for example when ICE session was
+ * created with controlled role when receiving an offer, but it turns out
+ * that the offer contains "a=ice-lite" attribute when the SDP gets
+ * inspected.
+ *
+ * @param ice		The ICE session.
+ * @param new_role	The new role to be set.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice,
+					     pj_ice_sess_role new_role);
+
+
+/**
  * Add a candidate to this ICE session. Application must add candidates for
  * each components ID before it can start pairing the candidates and 
Index: /pjproject/trunk/pjnath/include/pjnath/ice_strans.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/ice_strans.h	(revision 1113)
+++ /pjproject/trunk/pjnath/include/pjnath/ice_strans.h	(revision 1114)
@@ -28,4 +28,5 @@
 #include <pjlib-util/resolver.h>
 #include <pj/ioqueue.h>
+#include <pj/timer.h>
 
 
@@ -315,4 +316,6 @@
     pj_sockaddr_in	     stun_srv;	/**< STUN server address.	*/
     pj_sockaddr_in	     turn_srv;	/**< TURN server address.	*/
+
+    pj_timer_entry	     ka_timer;	/**< STUN keep-alive timer.	*/
 };
 
Index: /pjproject/trunk/pjnath/include/pjnath/stun_msg.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 1113)
+++ /pjproject/trunk/pjnath/include/pjnath/stun_msg.h	(revision 1114)
@@ -311,4 +311,6 @@
     PJ_STUN_ATTR_REFRESH_INTERVAL   = 0x8024,/**< REFRESH-INTERVAL.	    */
     PJ_STUN_ATTR_FINGERPRINT	    = 0x8028,/**< FINGERPRINT attribute.    */
+    PJ_STUN_ATTR_ICE_CONTROLLED	    = 0x8029,/**< ICE-CCONTROLLED attribute.*/
+    PJ_STUN_ATTR_ICE_CONTROLLING    = 0x802a,/**< ICE-CCONTROLLING attribute*/
 
     PJ_STUN_ATTR_END_EXTENDED_ATTR
@@ -345,4 +347,5 @@
     PJ_STUN_SC_ALLOCATION_QUOTA_REACHED = 486,  /**< Allocation Quota Reached
 						     (TURN) */
+    PJ_STUN_SC_ROLE_CONFLICT		= 487,  /**< Role Conflict	    */
     PJ_STUN_SC_SERVER_ERROR	        = 500,  /**< Server Error	    */
     PJ_STUN_SC_INSUFFICIENT_CAPACITY    = 507,  /**< Insufficient Capacity 
@@ -419,6 +422,4 @@
        |         Type                  |            Length             |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-       |                             Value                 ....        |
-       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
    \endverbatim
@@ -534,9 +535,28 @@
     
     /**
-     * The 32bit value.
+     * The 32bit value, in host byte order.
      */
     pj_uint32_t		value;
 
 } pj_stun_uint_attr;
+
+
+/**
+ * This structure represents a generic STUN attributes with 64bit (unsigned)
+ * integer value, such as ICE-CONTROLLED and ICE-CONTROLLING attributes.
+ */
+typedef struct pj_stun_uint64_attr
+{
+    /**
+     * Standard STUN attribute header.
+     */
+    pj_stun_attr_hdr	hdr;
+    
+    /**
+     * The 64bit value, in host byte order, represented with pj_timestamp.
+     */
+    pj_timestamp	value;
+
+} pj_stun_uint64_attr;
 
 
@@ -622,17 +642,7 @@
 
     /**
-     * The value must be zero.
-     */
-    pj_uint16_t		zero;
-
-    /**
-     * Error class (1-6).
-     */
-    pj_uint8_t		err_class;
-
-    /**
-     * Error number is the error number modulo 100.
-     */
-    pj_uint8_t		number;
+     * STUN error code.
+     */
+    int			err_code;
 
     /**
@@ -1350,4 +1360,35 @@
 
 /**
+ * Create a STUN generic 64bit value attribute.
+ *
+ * @param pool		Pool to allocate memory from.
+ * @param attr_type	Attribute type, from #pj_stun_attr_type.
+ * @param value		Optional value to be assigned.
+ * @param p_attr	Pointer to receive the attribute.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DEF(pj_status_t)  pj_stun_uint64_attr_create(pj_pool_t *pool,
+					        int attr_type,
+					        const pj_timestamp *value,
+					        pj_stun_uint64_attr **p_attr);
+
+
+/**
+ *  Create and add STUN generic 64bit value attribute to the message. 
+ *
+ * @param pool		The pool to allocate memory from.
+ * @param msg		The STUN message
+ * @param attr_type	Attribute type, from #pj_stun_attr_type.
+ * @param value		The 64bit value to be assigned to the attribute.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)  pj_stun_msg_add_uint64_attr(pj_pool_t *pool,
+					          pj_stun_msg *msg,
+					          int attr_type,
+					          const pj_timestamp *value);
+
+/**
  * Create a STUN MESSAGE-INTEGRITY attribute.
  *
Index: /pjproject/trunk/pjnath/include/pjnath/types.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/types.h	(revision 1113)
+++ /pjproject/trunk/pjnath/include/pjnath/types.h	(revision 1114)
@@ -52,16 +52,9 @@
 
 /**
- * @mainpage PJNATH - Open Source STUN, TURN, and ICE Library
+ * @mainpage PJNATH - Open Source ICE, STUN, and TURN Library
  *
  * \n
  * This is the documentation of PJNATH, an Open Source library providing
- * NAT traversal helper functionalities by using standard based protocols 
- * such as:
- *  - <b>STUN</b> (Session Traversal Utilities),
- *  - <b>TURN</b> (Obtaining Relay Addresses from STUN)
- *  - <b>ICE</b> (Interactive Connectivity Establishment).
- * 
- * The following sections will give a short overview about the protocols
- * supported by this library, and how they are implemented in PJNATH.
+ * NAT traversal helper functionalities by using standard based protocols.
  *
  * \n
@@ -71,13 +64,8 @@
  * Session Traversal Utilities (STUN, or previously known as Simple 
  * Traversal of User Datagram Protocol (UDP) Through Network Address 
- * Translators (NAT)s), was previously released as IETF standard
- * <A HREF="http://www.ietf.org/rfc/rfc3489.txt">RFC 3489</A>, but since
- * then it has been revised into the following:
- *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-06.txt">
- *    <B>draft-ietf-behave-rfc3489bis-06</b></A> for the main STUN 
- *    specification,
- *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-03.txt">
- *    <B>draft-ietf-behave-turn-03</B></A> for TURN usage of STUN,
- *  - and several other drafts explaining other STUN usages.
+ * Translators (NAT)s), is a lightweight protocol that serves as a tool for
+ * application protocols in dealing with NAT traversal. It allows a client
+ * to determine the IP address and port allocated to them by a NAT and to 
+ * keep NAT bindings open.
  * 
  * The PJNATH library provides facilities to support both the core 
@@ -134,4 +122,17 @@
  * components with callback to send outgoing messages.
  * 
+ *
+ * \subsection PJNATH_STUN_REF STUN Reference
+ *
+ * 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 
+ *     Utilities for (NAT) (STUN),
+ *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-03.txt">
+ *    <B>draft-ietf-behave-turn-03</B></A>: Obtaining Relay Addresses 
+ *    from Simple Traversal Underneath NAT (STUN)
+ *  - Obsoleted: <A HREF="http://www.ietf.org/rfc/rfc3489.txt">RFC 3489</A>.
+ *
  * \n
  *
@@ -171,4 +172,12 @@
  *    ICE stream transport and provides SDP translations to be used
  *    for SIP offer/answer exchanges.
+ *
+ * \subsection PJNATH_ICE_REF Reference
+ *
+ * References for ICE:
+ *  - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-mmusic-ice-15.txt">
+ *    <B>draft-ietf-mmusic-ice-15.txt</B></A>: Interactive Connectivity 
+ *    Establishment (ICE): A Methodology for Network Address Translator 
+ *    (NAT) Traversal for Offer/Answer Protocols
  */
 
Index: /pjproject/trunk/pjnath/src/pjnath/ice_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1113)
+++ /pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1114)
@@ -57,4 +57,10 @@
 };
 
+static const char *role_names[] = 
+{
+    "Controlled",
+    "Controlling"
+};
+
 #define CHECK_NAME_LEN		128
 #define LOG4(expr)		PJ_LOG(4,expr)
@@ -197,4 +203,6 @@
     ice->pool = pool;
     ice->role = role;
+    ice->tie_breaker.u32.hi = pj_rand();
+    ice->tie_breaker.u32.lo = pj_rand();
 
     pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name),
@@ -238,7 +246,5 @@
     LOG4((ice->obj_name, 
 	 "ICE session created, comp_cnt=%d, role is %s agent",
-	 comp_cnt,
-	 (ice->role==PJ_ICE_SESS_ROLE_CONTROLLING ? 
-	    "controlling":"controlled")));
+	 comp_cnt, role_names[ice->role]));
 
     return PJ_SUCCESS;
@@ -290,4 +296,21 @@
     PJ_ASSERT_RETURN(ice, PJ_EINVAL);
     destroy_ice(ice, PJ_SUCCESS);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Change session role. 
+ */
+PJ_DEF(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice,
+					    pj_ice_sess_role new_role)
+{
+    PJ_ASSERT_RETURN(ice, PJ_EINVAL);
+
+    if (new_role != ice->role) {
+	ice->role = new_role;
+	LOG4((ice->obj_name, "Role changed to %s", role_names[new_role]));
+    }
+
     return PJ_SUCCESS;
 }
Index: /pjproject/trunk/pjnath/src/pjnath/ice_strans.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 1113)
+++ /pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 1114)
@@ -61,4 +61,8 @@
 				     const pj_stun_msg *response);
 
+/* Keep-alive timer */
+static void start_ka_timer(pj_ice_strans *ice_st);
+static void stop_ka_timer(pj_ice_strans *ice_st);
+
 /* Utility: print error */
 #if PJ_LOG_MAX_LEVEL >= 3
@@ -126,4 +130,7 @@
     }
 
+    /* Kill keep-alive timer, if any */
+    stop_ka_timer(ice_st);
+
     /* Destroy ICE if we have ICE */
     if (ice_st->ice) {
@@ -197,5 +204,4 @@
     return PJ_SUCCESS;
 }
-
 
 /* Add new candidate */
@@ -506,4 +512,82 @@
 
 
+/* STUN keep-alive timer callback */
+static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
+{
+    pj_ice_strans *ice_st = (pj_ice_strans*)te->user_data;
+    unsigned i;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(th);
+
+    ice_st->ka_timer.id = PJ_FALSE;
+
+    for (i=0; i<ice_st->comp_cnt; ++i) {
+	pj_ice_strans_comp *comp = ice_st->comp[i];
+	pj_stun_tx_data *tdata;
+	unsigned j;
+
+	/* Does this component have STUN server reflexive candidate? */
+	for (j=0; j<comp->cand_cnt; ++j) {
+	    if (comp->cand_list[j].type == PJ_ICE_CAND_TYPE_SRFLX)
+		break;
+	}
+	if (j == comp->cand_cnt)
+	    continue;
+
+	/* Create STUN binding request */
+	status = pj_stun_session_create_req(comp->stun_sess,
+					    PJ_STUN_BINDING_REQUEST, &tdata);
+	if (status != PJ_SUCCESS)
+	    continue;
+
+	/* tdata->user_data is NULL for keep-alive */
+	tdata->user_data = NULL;
+
+	/* Send STUN binding request */
+	PJ_LOG(5,(ice_st->obj_name, "Sending STUN keep-alive"));
+	status = pj_stun_session_send_msg(comp->stun_sess, PJ_FALSE, 
+					  &ice_st->stun_srv, 
+					  sizeof(pj_sockaddr_in), tdata);
+    }
+
+    /* Start next timer */
+    start_ka_timer(ice_st);
+}
+
+/* Start STUN keep-alive timer */
+static void start_ka_timer(pj_ice_strans *ice_st)
+{
+    pj_time_val delay;
+
+    /* Skip if timer is already running */
+    if (ice_st->ka_timer.id != PJ_FALSE)
+	return;
+
+    delay.sec = 20;
+    delay.msec = 0;
+
+    ice_st->ka_timer.cb = &ka_timer_cb;
+    ice_st->ka_timer.user_data = ice_st;
+    
+    if (pj_timer_heap_schedule(ice_st->stun_cfg.timer_heap, 
+			       &ice_st->ka_timer, &delay)==PJ_SUCCESS)
+    {
+	ice_st->ka_timer.id = PJ_TRUE;
+    }
+}
+
+
+/* Stop STUN keep-alive timer */
+static void stop_ka_timer(pj_ice_strans *ice_st)
+{
+    /* Skip if timer is already stop */
+    if (ice_st->ka_timer.id == PJ_FALSE)
+	return;
+
+    pj_timer_heap_cancel(ice_st->stun_cfg.timer_heap, &ice_st->ka_timer);
+    ice_st->ka_timer.id = PJ_FALSE;
+}
+
 
 /*
@@ -831,12 +915,7 @@
     }
 
-#if 1
-    PJ_UNUSED_ARG(pkt_size);
-    PJ_UNUSED_ARG(status);
-
-    /* Otherwise return error */
-    return PJNATH_EICEINPROGRESS;
-#else
-    /* Otherwise send direcly with the socket */
+    /* Otherwise send direcly with the socket. This is for compatibility
+     * with remote that doesn't support ICE.
+     */
     pkt_size = data_len;
     status = pj_ioqueue_sendto(comp->key, &comp->write_op, 
@@ -845,5 +924,4 @@
     
     return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
-#endif
 }
 
@@ -943,4 +1021,9 @@
     comp = (pj_ice_strans_comp*) pj_stun_session_get_user_data(sess);
     cand = (pj_ice_strans_cand*) tdata->user_data;
+
+    if (cand == NULL) {
+	/* This is keep-alive */
+	return;
+    }
 
     /* Decrement pending count for this component */
@@ -981,4 +1064,7 @@
     comp->default_cand = (cand - comp->cand_list);
     comp->last_status = PJ_SUCCESS;
-}
-
+
+    /* We have STUN, so we must start the keep-alive timer */
+    start_ka_timer(comp->ice_st);
+}
+
Index: /pjproject/trunk/pjnath/src/pjnath/stun_msg.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1113)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_msg.c	(revision 1114)
@@ -72,4 +72,5 @@
     { PJ_STUN_SC_CONNECTION_TIMEOUT,	    "Connection Timeout"},
     { PJ_STUN_SC_ALLOCATION_QUOTA_REACHED,  "Allocation Quota Reached"},
+    { PJ_STUN_SC_ROLE_CONFLICT,		    "Role Conflict"},
     { PJ_STUN_SC_SERVER_ERROR,		    "Server Error"},
     { PJ_STUN_SC_INSUFFICIENT_CAPACITY,	    "Insufficient Capacity"},
@@ -86,5 +87,4 @@
     pj_status_t (*encode_attr)(const void *a, pj_uint8_t *buf, 
 			       unsigned len, unsigned *printed);
-
 };
 
@@ -123,4 +123,9 @@
 static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, 
 				    unsigned len, unsigned *printed);
+static pj_status_t decode_uint64_attr(pj_pool_t *pool, 
+				      const pj_uint8_t *buf, 
+				      void **p_attr);
+static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf, 
+				      unsigned len, unsigned *printed);
 static pj_status_t decode_binary_attr(pj_pool_t *pool, 
 				      const pj_uint8_t *buf,
@@ -430,4 +435,16 @@
 	&decode_uint_attr,
 	&encode_uint_attr
+    },
+    {
+	/* PJ_STUN_ATTR_ICE_CONTROLLED, */
+	"ICE-CCONTROLLED",
+	&decode_uint64_attr,
+	&encode_uint64_attr
+    },
+    {
+	/* PJ_STUN_ATTR_ICE_CONTROLLING, */
+	"ICE-CCONTROLLING",
+	&decode_uint64_attr,
+	&encode_uint64_attr
     }
 };
@@ -552,4 +569,6 @@
 
 
+//////////////////////////////////////////////////////////////////////////////
+
 
 #define INIT_ATTR(a,t,l)    (a)->hdr.type=(pj_uint16_t)(t), \
@@ -557,7 +576,60 @@
 #define ATTR_HDR_LEN	    4
 
-#define getval16(p, pos)    (pj_uint16_t)(((p)[(pos)] << 8) | \
-					  ((p)[(pos) + 1] << 0))
-
+static pj_uint16_t GETVAL16H(const pj_uint8_t *buf, unsigned pos)
+{
+    return (pj_uint16_t) ((buf[pos + 0] << 8) | \
+			  (buf[pos + 1] << 0));
+}
+
+static pj_uint16_t GETVAL16N(const pj_uint8_t *buf, unsigned pos)
+{
+    return pj_htons(GETVAL16H(buf,pos));
+}
+
+static void PUTVAL16H(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);
+}
+
+static pj_uint32_t GETVAL32H(const pj_uint8_t *buf, unsigned pos)
+{
+    return (pj_uint32_t) ((buf[pos + 0] << 24UL) | \
+	                  (buf[pos + 1] << 16UL) | \
+	                  (buf[pos + 2] <<  8UL) | \
+			  (buf[pos + 3] <<  0UL));
+}
+
+static pj_uint32_t GETVAL32N(const pj_uint8_t *buf, unsigned pos)
+{
+    return pj_htonl(GETVAL32H(buf,pos));
+}
+
+static void PUTVAL32H(pj_uint8_t *buf, unsigned pos, pj_uint32_t hval)
+{
+    buf[pos+0] = (pj_uint8_t) ((hval & 0xFF000000UL) >> 24);
+    buf[pos+1] = (pj_uint8_t) ((hval & 0x00FF0000UL) >> 16);
+    buf[pos+2] = (pj_uint8_t) ((hval & 0x0000FF00UL) >>  8);
+    buf[pos+3] = (pj_uint8_t) ((hval & 0x000000FFUL) >>  0);
+}
+
+static void GETVAL64H(const pj_uint8_t *buf, unsigned pos, pj_timestamp *ts)
+{
+    ts->u32.hi = GETVAL32H(buf, pos);
+    ts->u32.lo = GETVAL32H(buf, pos+4);
+}
+
+static void PUTVAL64H(pj_uint8_t *buf, unsigned pos, const pj_timestamp *ts)
+{
+    PUTVAL32H(buf, pos, ts->u32.hi);
+    PUTVAL32H(buf, pos+4, ts->u32.lo);
+}
+
+
+static void GETATTRHDR(const pj_uint8_t *buf, pj_stun_attr_hdr *hdr)
+{
+    hdr->type = GETVAL16H(buf, 0);
+    hdr->length = GETVAL16H(buf, 2);
+}
 
 //////////////////////////////////////////////////////////////////////////////
@@ -627,9 +699,5 @@
     /* Create the attribute */
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_sockaddr_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);
+    GETATTRHDR(buf, &attr->hdr);
 
     /* Check that the attribute length is valid */
@@ -646,6 +714,6 @@
     /* Get port and address */
     pj_sockaddr_in_init(&attr->sockaddr.ipv4, NULL, 0);
-    pj_memcpy(&attr->sockaddr.ipv4.sin_port, buf+ATTR_HDR_LEN+2, 2);
-    pj_memcpy(&attr->sockaddr.ipv4.sin_addr, buf+ATTR_HDR_LEN+4, 4);
+    attr->sockaddr.ipv4.sin_port = GETVAL16N(buf, ATTR_HDR_LEN+2);
+    attr->sockaddr.ipv4.sin_addr.s_addr = GETVAL32N(buf, ATTR_HDR_LEN+4);
 
     /* Done */
@@ -661,30 +729,11 @@
 {
     pj_stun_sockaddr_attr *attr;
-    pj_uint32_t val;
-
-    /* Create the attribute */
-    attr = PJ_POOL_ZALLOC_T(pool, pj_stun_sockaddr_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 != STUN_GENERIC_IP_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;
-
-    /* Get port and address */
-    pj_sockaddr_in_init(&attr->sockaddr.ipv4, NULL, 0);
-    pj_memcpy(&attr->sockaddr.ipv4.sin_port, buf+ATTR_HDR_LEN+2, 2);
-    pj_memcpy(&attr->sockaddr.ipv4.sin_addr, buf+ATTR_HDR_LEN+4, 4);
-
+    pj_status_t status;
+
+    status = decode_sockaddr_attr(pool, buf, &attr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    attr->xor_ed = PJ_TRUE;
     attr->sockaddr.ipv4.sin_port ^= pj_htons(0x2112);
     attr->sockaddr.ipv4.sin_addr.s_addr ^= pj_htonl(0x2112A442);
@@ -706,5 +755,4 @@
     const pj_stun_sockaddr_attr *ca = 
 	(const pj_stun_sockaddr_attr *)a;
-    pj_stun_sockaddr_attr *attr;
 
     if (len < ATTR_LEN) 
@@ -712,8 +760,6 @@
 
     /* Copy and convert headers to network byte order */
-    pj_memcpy(buf, a, ATTR_HDR_LEN);
-    attr = (pj_stun_sockaddr_attr*) buf;
-    attr->hdr.type = pj_htons(attr->hdr.type);
-    attr->hdr.length = pj_htons((pj_uint16_t)STUN_GENERIC_IP_ADDR_LEN);
+    PUTVAL16H(buf, 0, ca->hdr.type);
+    PUTVAL16H(buf, 2, STUN_GENERIC_IP_ADDR_LEN);
     buf += ATTR_HDR_LEN;
     
@@ -820,11 +866,5 @@
     /* Create the attribute */
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_string_attr);
-
-    /* Copy the header */
-    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);
+    GETATTRHDR(buf, &attr->hdr);
 
     /* Get pointer to the string in the message */
@@ -848,5 +888,4 @@
     const pj_stun_string_attr *ca = 
 	(const pj_stun_string_attr*)a;
-    pj_stun_attr_hdr *attr;
 
     /* Calculated total attr_len (add padding if necessary) */
@@ -857,14 +896,7 @@
     }
 
-    /* Copy header */
-    pj_memcpy(buf, a, ATTR_HDR_LEN);
-    attr = (pj_stun_attr_hdr*)buf;
-
-    /* Set the correct length */
-    attr->length = (pj_uint16_t) ca->value.slen;
-
-    /* Convert to network byte order */
-    attr->type = pj_htons(attr->type);
-    attr->length = pj_htons(attr->length);
+    PUTVAL16H(buf, 0, ca->hdr.type);
+    PUTVAL16H(buf, 2, (pj_uint16_t)ca->value.slen);
+    
 
     /* Copy the string */
@@ -930,9 +962,5 @@
     /* Create the attribute */
     attr = PJ_POOL_ZALLOC_T(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);
+    GETATTRHDR(buf, &attr->hdr);
 
     /* Check that the attribute length is valid */
@@ -950,14 +978,11 @@
 				     unsigned len, unsigned *printed)
 {
-    pj_stun_empty_attr *attr;
+    const pj_stun_empty_attr *ca = (pj_stun_empty_attr*)a;
 
     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 = 0;
+    PUTVAL16H(buf, 0, ca->hdr.type);
+    PUTVAL16H(buf, 2, 0);
 
     /* Done */
@@ -972,5 +997,4 @@
  * STUN generic 32bit integer attribute.
  */
-#define STUN_UINT_LEN	4
 
 /*
@@ -988,5 +1012,5 @@
 
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint_attr);
-    INIT_ATTR(attr, attr_type, STUN_UINT_LEN);
+    INIT_ATTR(attr, attr_type, 4);
     attr->value = value;
 
@@ -1017,24 +1041,14 @@
 				    void **p_attr)
 {
-    enum
-    {
-	ATTR_LEN = STUN_UINT_LEN + ATTR_HDR_LEN
-    };
     pj_stun_uint_attr *attr;
-
-    /* Check that the struct address is valid */
-    pj_assert(sizeof(pj_stun_uint_attr) == ATTR_LEN);
 
     /* Create the attribute */
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint_attr);
-    pj_memcpy(attr, buf, ATTR_LEN);
-
-    /* Convert to host byte order */
-    attr->hdr.type = pj_ntohs(attr->hdr.type);
-    attr->hdr.length = pj_ntohs(attr->hdr.length);
-    attr->value = pj_ntohl(attr->value);
+    GETATTRHDR(buf, &attr->hdr);
+
+    attr->value = GETVAL32H(buf, 4);
 
     /* Check that the attribute length is valid */
-    if (attr->hdr.length != STUN_UINT_LEN)
+    if (attr->hdr.length != 4)
 	return PJNATH_ESTUNINATTRLEN;
 
@@ -1049,23 +1063,15 @@
 				    unsigned len, unsigned *printed)
 {
-    enum
-    {
-	ATTR_LEN = STUN_UINT_LEN + ATTR_HDR_LEN
-    };
-    pj_stun_uint_attr *attr;
-
-    if (len < ATTR_LEN) 
+    const pj_stun_uint_attr *ca = (const pj_stun_uint_attr*)a;
+
+    if (len < 8) 
 	return PJ_ETOOSMALL;
 
-    /* Copy and convert attribute to network byte order */
-    pj_memcpy(buf, a, ATTR_LEN);
-    attr = (pj_stun_uint_attr*) buf;
-    attr->hdr.type = pj_htons(attr->hdr.type);
-    pj_assert(attr->hdr.length == STUN_UINT_LEN);
-    attr->hdr.length = pj_htons(STUN_UINT_LEN);
-    attr->value = pj_htonl(attr->value);
-
+    PUTVAL16H(buf, 0, ca->hdr.type);
+    PUTVAL16H(buf, 2, (pj_uint16_t)4);
+    PUTVAL32H(buf, 4, ca->value);
+    
     /* Done */
-    *printed = ATTR_LEN;
+    *printed = 8;
 
     return PJ_SUCCESS;
@@ -1073,9 +1079,92 @@
 
 //////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Create a STUN generic 64bit value attribute.
+ */
+PJ_DEF(pj_status_t) 
+pj_stun_uint64_attr_create(pj_pool_t *pool,
+			   int attr_type,
+			   const pj_timestamp *value,
+			   pj_stun_uint64_attr **p_attr)
+{
+    pj_stun_uint64_attr *attr;
+
+    PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL);
+
+    attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint64_attr);
+    INIT_ATTR(attr, attr_type, 4);
+
+    if (value) {
+	attr->value.u32.hi = value->u32.hi;
+	attr->value.u32.lo = value->u32.lo;
+    }
+
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+/* Create and add STUN generic 64bit value attribute to the message. */
+PJ_DEF(pj_status_t)  pj_stun_msg_add_uint64_attr(pj_pool_t *pool,
+					         pj_stun_msg *msg,
+					         int attr_type,
+					         const pj_timestamp *value)
+{
+    pj_stun_uint64_attr *attr = NULL;
+    pj_status_t status;
+
+    status = pj_stun_uint64_attr_create(pool, attr_type, value, &attr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    return pj_stun_msg_add_attr(msg, &attr->hdr);
+}
+
+static pj_status_t decode_uint64_attr(pj_pool_t *pool, 
+				      const pj_uint8_t *buf, 
+				      void **p_attr)
+{
+    pj_stun_uint64_attr *attr;
+
+    /* Create the attribute */
+    attr = PJ_POOL_ZALLOC_T(pool, pj_stun_uint64_attr);
+    GETATTRHDR(buf, &attr->hdr);
+
+    if (attr->hdr.length != 8)
+	return PJNATH_ESTUNINATTRLEN;
+
+    GETVAL64H(buf, 4, &attr->value);	
+
+    /* Done */
+    *p_attr = attr;
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf, 
+				      unsigned len, unsigned *printed)
+{
+    const pj_stun_uint64_attr *ca = (const pj_stun_uint64_attr*)a;
+
+    if (len < 12) 
+	return PJ_ETOOSMALL;
+
+    PUTVAL16H(buf, 0, ca->hdr.type);
+    PUTVAL16H(buf, 2, ca->hdr.length);
+    PUTVAL64H(buf, 4, &ca->value);
+
+    /* Done */
+    *printed = 12;
+
+    return PJ_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
 /*
  * STUN MESSAGE-INTEGRITY attribute.
  */
-
-#define STUN_MSG_INTEGRITY_LEN	20
 
 /*
@@ -1091,5 +1180,5 @@
 
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_msgint_attr);
-    INIT_ATTR(attr, PJ_STUN_ATTR_MESSAGE_INTEGRITY, STUN_MSG_INTEGRITY_LEN);
+    INIT_ATTR(attr, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 20);
 
     *p_attr = attr;
@@ -1116,22 +1205,16 @@
 				      void **p_attr)
 {
-    enum
-    {
-	ATTR_LEN = STUN_MSG_INTEGRITY_LEN + ATTR_HDR_LEN
-    };
     pj_stun_msgint_attr *attr;
-
-    /* Check that struct size is valid */
-    pj_assert(sizeof(pj_stun_msgint_attr)==ATTR_LEN);
 
     /* Create attribute */
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_msgint_attr);
-    pj_memcpy(attr, buf, sizeof(pj_stun_msgint_attr));
-    attr->hdr.type = pj_ntohs(attr->hdr.type);
-    attr->hdr.length = pj_ntohs(attr->hdr.length);
+    GETATTRHDR(buf, &attr->hdr);
 
     /* Check that the attribute length is valid */
-    if (attr->hdr.length != STUN_MSG_INTEGRITY_LEN)
+    if (attr->hdr.length != 20)
 	return PJNATH_ESTUNINATTRLEN;
+
+    /* Copy hmac */
+    pj_memcpy(attr->hmac, buf+4, 20);
 
     /* Done */
@@ -1144,22 +1227,17 @@
 				      unsigned len, unsigned *printed)
 {
-    enum
-    {
-	ATTR_LEN = STUN_MSG_INTEGRITY_LEN + ATTR_HDR_LEN
-    };
-    pj_stun_msgint_attr *attr;
-
-    if (len < ATTR_LEN) 
+    const pj_stun_msgint_attr *ca = (const pj_stun_msgint_attr*)a;
+
+    if (len < 24) 
 	return PJ_ETOOSMALL;
 
     /* Copy and convert attribute to network byte order */
-    pj_memcpy(buf, a, ATTR_LEN);
-    attr = (pj_stun_msgint_attr*) buf;
-    attr->hdr.type = pj_htons(attr->hdr.type);
-    pj_assert(attr->hdr.length == STUN_MSG_INTEGRITY_LEN);
-    attr->hdr.length = pj_htons(STUN_MSG_INTEGRITY_LEN);
+    PUTVAL16H(buf, 0, ca->hdr.type);
+    PUTVAL16H(buf, 2, ca->hdr.length);
+
+    pj_memcpy(buf+4, ca->hmac, 20);
 
     /* Done */
-    *printed = ATTR_LEN;
+    *printed = 24;
 
     return PJ_SUCCESS;
@@ -1198,6 +1276,5 @@
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_errcode_attr);
     INIT_ATTR(attr, PJ_STUN_ATTR_ERROR_CODE, 4+err_reason->slen);
-    attr->err_class = (pj_uint8_t)(err_code / 100);
-    attr->number = (pj_uint8_t) (err_code % 100);
+    attr->err_code = err_code;
     pj_strdup(pool, &attr->reason, err_reason);
 
@@ -1233,11 +1310,7 @@
     /* Create the attribute */
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_errcode_attr);
-
-    /* Copy the header */
-    pj_memcpy(attr, buf, ATTR_HDR_LEN + 4);
-
-    /* Convert to host byte order */
-    attr->hdr.type = pj_ntohs(attr->hdr.type);
-    attr->hdr.length = pj_ntohs(attr->hdr.length);
+    GETATTRHDR(buf, &attr->hdr);
+
+    attr->err_code = buf[6] * 100 + buf[7];
 
     /* Get pointer to the string in the message */
@@ -1260,5 +1333,4 @@
     const pj_stun_errcode_attr *ca = 
 	(const pj_stun_errcode_attr*)a;
-    pj_stun_errcode_attr *attr;
 
     if (len < ATTR_HDR_LEN + 4 + (unsigned)ca->reason.slen) 
@@ -1266,13 +1338,9 @@
 
     /* Copy and convert attribute to network byte order */
-    pj_memcpy(buf, ca, ATTR_HDR_LEN + 4);
-
-    /* Update length */
-    attr = (pj_stun_errcode_attr*) buf;
-    attr->hdr.length = (pj_uint16_t)(4 + ca->reason.slen);
-
-    /* Convert fiends to network byte order */
-    attr->hdr.type = pj_htons(attr->hdr.type);
-    attr->hdr.length = pj_htons(attr->hdr.length);
+    PUTVAL16H(buf, 0, ca->hdr.type);
+    PUTVAL16H(buf, 2, (pj_uint16_t)(4 + ca->reason.slen));
+    PUTVAL16H(buf, 4, 0);
+    buf[6] = (pj_uint8_t)(ca->err_code / 100);
+    buf[7] = (pj_uint8_t)(ca->err_code % 100);
 
     /* Copy error string */
@@ -1356,9 +1424,6 @@
 
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_unknown_attr);
-    pj_memcpy(attr, buf, ATTR_HDR_LEN);
-
-    attr->hdr.type = pj_ntohs(attr->hdr.type);
-    attr->hdr.length = pj_ntohs(attr->hdr.length);
-
+    GETATTRHDR(buf, &attr->hdr);
+ 
     attr->attr_count = (attr->hdr.length >> 1);
     if (attr->attr_count > PJ_STUN_MAX_ATTR)
@@ -1381,5 +1446,4 @@
 {
     const pj_stun_unknown_attr *ca = (const pj_stun_unknown_attr*) a;
-    pj_stun_unknown_attr *attr;
     pj_uint16_t *dst_unk_attr;
     unsigned i;
@@ -1389,19 +1453,11 @@
 	return PJ_ETOOSMALL;
 
-    /* Copy to message */
-    pj_memcpy(buf, ca, ATTR_HDR_LEN);
-
-    /* Set the correct length */
-    attr = (pj_stun_unknown_attr *) buf;
-    attr->hdr.length = (pj_uint16_t)(ca->attr_count << 1);
-
-    /* Convert to network byte order */
-    attr->hdr.type = pj_htons(attr->hdr.type);
-    attr->hdr.length = pj_htons(attr->hdr.length);
+    PUTVAL16H(buf, 0, ca->hdr.type);
+    PUTVAL16H(buf, 2, (pj_uint16_t)(ca->attr_count << 1));
 
     /* Copy individual attribute */
     dst_unk_attr = (pj_uint16_t*)(buf + ATTR_HDR_LEN);
     for (i=0; i < ca->attr_count; ++i, ++dst_unk_attr) {
-	*dst_unk_attr = pj_htons(attr->attrs[i]);
+	*dst_unk_attr = pj_htons(ca->attrs[i]);
     }
 
@@ -1433,5 +1489,5 @@
 
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_binary_attr);
-    INIT_ATTR(attr, attr_type, sizeof(pj_stun_binary_attr));
+    INIT_ATTR(attr, attr_type, length);
 
     if (data && length) {
@@ -1475,11 +1531,5 @@
     /* Create the attribute */
     attr = PJ_POOL_ZALLOC_T(pool, pj_stun_binary_attr);
-
-    /* Copy the header */
-    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);
+    GETATTRHDR(buf, &attr->hdr);
 
     /* Copy the data to the attribute */
@@ -1500,5 +1550,4 @@
 {
     const pj_stun_binary_attr *ca = (const pj_stun_binary_attr*)a;
-    pj_stun_attr_hdr *attr;
 
     /* Calculated total attr_len (add padding if necessary) */
@@ -1507,14 +1556,6 @@
 	return PJ_ETOOSMALL;
 
-    /* Copy header */
-    pj_memcpy(buf, a, ATTR_HDR_LEN);
-
-    /* Set the correct length */
-    attr = (pj_stun_attr_hdr*)buf;
-    attr->length = (pj_uint16_t) ca->length;
-
-    /* Convert to network byte order */
-    attr->type = pj_htons(attr->type);
-    attr->length = pj_htons(attr->length);
+    PUTVAL16H(buf, 0, ca->hdr.type);
+    PUTVAL16H(buf, 2, (pj_uint16_t) ca->length);
 
     /* Copy the data */
@@ -1583,18 +1624,4 @@
 
 
-PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos)
-{
-    return (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]);
-}
-
-PJ_INLINE(pj_uint32_t) GET_VAL32(const pj_uint8_t *pdu, unsigned pos)
-{
-    return (pdu[pos+0] << 24) +
-	   (pdu[pos+1] << 16) +
-	   (pdu[pos+2] <<  8) +
-	   (pdu[pos+3]);
-}
-
-
 /*
  * Check that the PDU is potentially a valid STUN message.
@@ -1615,5 +1642,5 @@
 
     /* Check the PDU length */
-    msg_len = GET_VAL16(pdu, 2);
+    msg_len = GETVAL16H(pdu, 2);
     if ((msg_len + 20 > pdu_len) || 
 	((options & PJ_STUN_IS_DATAGRAM) && msg_len + 20 != pdu_len))
@@ -1625,10 +1652,10 @@
      * a STUN message.
      */
-    if (GET_VAL32(pdu, 4) == PJ_STUN_MAGIC) {
+    if (GETVAL32H(pdu, 4) == PJ_STUN_MAGIC) {
 
 	/* Check if FINGERPRINT attribute is present */
-	if (GET_VAL16(pdu, msg_len + 20) == PJ_STUN_ATTR_FINGERPRINT) {
-	    pj_uint16_t attr_len = GET_VAL16(pdu, msg_len + 22);
-	    pj_uint32_t fingerprint = GET_VAL32(pdu, msg_len + 24);
+	if (GETVAL16H(pdu, msg_len + 20) == PJ_STUN_ATTR_FINGERPRINT) {
+	    pj_uint16_t attr_len = GETVAL16H(pdu, msg_len + 22);
+	    pj_uint32_t fingerprint = GETVAL32H(pdu, msg_len + 24);
 	    pj_uint32_t crc;
 
Index: /pjproject/trunk/pjnath/src/pjnath/stun_msg_dump.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_msg_dump.c	(revision 1113)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_msg_dump.c	(revision 1114)
@@ -141,5 +141,5 @@
 	    len = pj_ansi_snprintf(p, end-p,
 				   ", err_code=%d, reason=\"%.*s\"\n",
-				   attr->err_class*100 + attr->number,
+				   attr->err_code,
 				   (int)attr->reason.slen,
 				   attr->reason.ptr);
Index: /pjproject/trunk/pjnath/src/pjnath/stun_transaction.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 1113)
+++ /pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 1114)
@@ -285,5 +285,5 @@
 		pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0);
 
-    if (err_attr && err_attr->err_class <= 2) {
+    if (err_attr && err_attr->err_code <= 200) {
 	/* draft-ietf-behave-rfc3489bis-05.txt Section 8.3.2:
 	 * Any response between 100 and 299 MUST result in the cessation
@@ -292,5 +292,5 @@
 	PJ_LOG(4,(tsx->obj_name, 
 		  "STUN rx_msg() error: received provisional %d code (%.*s)",
-		  err_attr->err_class * 100 + err_attr->number,
+		  err_attr->err_code,
 		  (int)err_attr->reason.slen,
 		  err_attr->reason.ptr));
@@ -301,6 +301,5 @@
 	status = PJ_SUCCESS;
     } else {
-	status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_class * 100 + 
-					  err_attr->number);
+	status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code);
     }
 
