diff --git a/pjmedia/include/pjmedia/rtp.h b/pjmedia/include/pjmedia/rtp.h index 5b7b8e646..f41cda053 100644 --- a/pjmedia/include/pjmedia/rtp.h +++ b/pjmedia/include/pjmedia/rtp.h @@ -77,7 +77,8 @@ PJ_BEGIN_DECL /** - * RTP packet header. + * RTP packet header. Note that all RTP functions here will work with this + * header in network byte order. */ #pragma pack(1) struct pjmedia_rtp_hdr @@ -229,7 +230,7 @@ typedef struct pjmedia_rtp_status pjmedia_rtp_status; * * @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. */ @@ -262,11 +263,16 @@ PJ_DECL(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses, * The decode function is guaranteed to point the payload to the correct * 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. * @param payloadlen Upon return will indicate the size of the payload. @@ -285,7 +291,8 @@ PJ_DECL(pj_status_t) pjmedia_rtp_decode_rtp( pjmedia_rtp_session *ses, * calculations. * * @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. */ diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c index 71d19c240..56a45e8a7 100644 --- a/pjmedia/src/pjmedia/transport_ice.c +++ b/pjmedia/src/pjmedia/transport_ice.c @@ -24,7 +24,7 @@ struct transport_ice { pjmedia_transport base; - pj_ice_strans *ice_st; + pj_ice_strans *ice_st; pj_time_val start_ice; @@ -408,6 +408,9 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, unsigned i, cand_cnt; 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; @@ -444,13 +447,17 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, } pass = attr->value; - /* Get all candidates */ + /* Get all candidates in the media */ cand_cnt = 0; for (i=0; iattr_count; ++i) { pjmedia_sdp_attr *attr; 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; status = parse_cand(pool, &attr->value, &cand[cand_cnt]); @@ -463,6 +470,16 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, /* 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 */ return pj_ice_strans_start_ice(tp_ice->ice_st, &uname, &pass, cand_cnt, cand); } @@ -552,8 +569,8 @@ static pj_status_t tp_send_rtp(pjmedia_transport *tp, { 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)); } @@ -564,8 +581,8 @@ static pj_status_t tp_send_rtcp(pjmedia_transport *tp, struct transport_ice *tp_ice = (struct transport_ice*)tp; 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; } diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h index fd7da7511..929a4e828 100644 --- a/pjnath/include/pjnath/ice_session.h +++ b/pjnath/include/pjnath/ice_session.h @@ -444,6 +444,7 @@ struct pj_ice_sess void *user_data; /**< App. data. */ 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. */ pj_ice_sess_cb cb; /**< Callback. */ @@ -549,6 +550,21 @@ PJ_DECL(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, PJ_DECL(pj_status_t) pj_ice_sess_destroy(pj_ice_sess *ice); +/** + * 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 diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h index b26b6a497..59243a00a 100644 --- a/pjnath/include/pjnath/ice_strans.h +++ b/pjnath/include/pjnath/ice_strans.h @@ -27,6 +27,7 @@ #include #include #include +#include PJ_BEGIN_DECL @@ -314,6 +315,8 @@ struct pj_ice_strans pj_bool_t has_rjob; /**< Has pending resolve? */ 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. */ }; diff --git a/pjnath/include/pjnath/stun_msg.h b/pjnath/include/pjnath/stun_msg.h index 6c43ee9b7..6c716a0c9 100644 --- a/pjnath/include/pjnath/stun_msg.h +++ b/pjnath/include/pjnath/stun_msg.h @@ -310,6 +310,8 @@ typedef enum pj_stun_attr_type PJ_STUN_ATTR_ALTERNATE_SERVER = 0x8023,/**< ALTERNATE-SERVER. */ 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 @@ -344,6 +346,7 @@ typedef enum pj_stun_status PJ_STUN_SC_CONNECTION_TIMEOUT = 447, /**< Connection Timeout */ 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 (TURN) */ @@ -418,8 +421,6 @@ typedef struct pj_stun_msg_hdr +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type | Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Value .... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \endverbatim */ @@ -533,13 +534,32 @@ typedef struct pj_stun_uint_attr pj_stun_attr_hdr hdr; /** - * 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; + + /** * This structure represents generic STUN attributes to hold a raw binary * data. @@ -621,19 +641,9 @@ typedef struct pj_stun_errcode_attr pj_stun_attr_hdr hdr; /** - * The value must be zero. + * STUN error code. */ - 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; + int err_code; /** * The reason phrase. @@ -1348,6 +1358,37 @@ PJ_DECL(pj_status_t) pj_stun_msg_add_uint_attr(pj_pool_t *pool, pj_uint32_t value); +/** + * 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. * diff --git a/pjnath/include/pjnath/types.h b/pjnath/include/pjnath/types.h index 2eaf63139..72c326afd 100644 --- a/pjnath/include/pjnath/types.h +++ b/pjnath/include/pjnath/types.h @@ -51,18 +51,11 @@ PJ_END_DECL /* Doxygen documentation below: */ /** - * @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: - * - STUN (Session Traversal Utilities), - * - TURN (Obtaining Relay Addresses from STUN) - * - ICE (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 @@ -70,15 +63,10 @@ PJ_END_DECL * * 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 - * RFC 3489, but since - * then it has been revised into the following: - * - - * draft-ietf-behave-rfc3489bis-06 for the main STUN - * specification, - * - - * draft-ietf-behave-turn-03 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 * STUN-bis specification and the TURN usage of STUN, @@ -133,6 +121,19 @@ PJ_END_DECL * to the STUN components for processing. and it must supply the STUN * components with callback to send outgoing messages. * + * + * \subsection PJNATH_STUN_REF STUN Reference + * + * References for STUN: + * + * - + * draft-ietf-behave-rfc3489bis-06: Session Traversal + * Utilities for (NAT) (STUN), + * - + * draft-ietf-behave-turn-03: Obtaining Relay Addresses + * from Simple Traversal Underneath NAT (STUN) + * - Obsoleted: RFC 3489. + * * \n * * \section PJNATH_ICE ICE Implementation @@ -170,6 +171,14 @@ PJ_END_DECL * - the highest abstraction is ICE media transport, which maintains * ICE stream transport and provides SDP translations to be used * for SIP offer/answer exchanges. + * + * \subsection PJNATH_ICE_REF Reference + * + * References for ICE: + * - + * draft-ietf-mmusic-ice-15.txt: Interactive Connectivity + * Establishment (ICE): A Methodology for Network Address Translator + * (NAT) Traversal for Offer/Answer Protocols */ /** diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index ec87a5c99..096179656 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -56,6 +56,12 @@ static const char *clist_state_name[] = "Completed" }; +static const char *role_names[] = +{ + "Controlled", + "Controlling" +}; + #define CHECK_NAME_LEN 128 #define LOG4(expr) PJ_LOG(4,expr) #define LOG5(expr) PJ_LOG(4,expr) @@ -196,6 +202,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, ice = PJ_POOL_ZALLOC_T(pool, pj_ice_sess); 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), name, ice); @@ -237,9 +245,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, 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; } @@ -293,6 +299,23 @@ PJ_DEF(pj_status_t) pj_ice_sess_destroy(pj_ice_sess *ice) } +/* + * 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; +} + + /* Find component by ID */ static pj_ice_sess_comp *find_comp(const pj_ice_sess *ice, unsigned comp_id) { diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c index ab6524a85..378d13a43 100644 --- a/pjnath/src/pjnath/ice_strans.c +++ b/pjnath/src/pjnath/ice_strans.c @@ -60,6 +60,10 @@ static void stun_on_request_complete(pj_stun_session *sess, pj_stun_tx_data *tdata, 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 static void ice_st_perror(pj_ice_strans *ice_st, const char *title, @@ -125,6 +129,9 @@ static void destroy_ice_st(pj_ice_strans *ice_st, pj_status_t reason) PJ_LOG(4,(obj_name, "ICE stream transport shutting down")); } + /* Kill keep-alive timer, if any */ + stop_ka_timer(ice_st); + /* Destroy ICE if we have ICE */ if (ice_st->ice) { pj_ice_sess_destroy(ice_st->ice); @@ -197,7 +204,6 @@ PJ_DEF(pj_status_t) pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st, return PJ_SUCCESS; } - /* Add new candidate */ static pj_status_t add_cand( pj_ice_strans *ice_st, pj_ice_strans_comp *comp, @@ -505,6 +511,84 @@ static void destroy_component(pj_ice_strans_comp *comp) } +/* 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; icomp_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; jcand_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; +} + /* * Add STUN mapping to a component. @@ -830,21 +914,15 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, return pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len); } -#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, data, &pkt_size, 0, dst_addr, dst_addr_len); return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; -#endif } /* @@ -943,6 +1021,11 @@ static void stun_on_request_complete(pj_stun_session *sess, 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 */ pj_assert(comp->pending_cnt > 0); comp->pending_cnt--; @@ -980,5 +1063,8 @@ static void stun_on_request_complete(pj_stun_session *sess, /* Set this candidate as the default candidate */ 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); } diff --git a/pjnath/src/pjnath/stun_msg.c b/pjnath/src/pjnath/stun_msg.c index 4e9c209df..072a7a1b6 100644 --- a/pjnath/src/pjnath/stun_msg.c +++ b/pjnath/src/pjnath/stun_msg.c @@ -71,6 +71,7 @@ static struct { PJ_STUN_SC_CONNECTION_FAILURE, "Connection Failure"}, { 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"}, { PJ_STUN_SC_GLOBAL_FAILURE, "Global Failure"} @@ -85,7 +86,6 @@ struct attr_desc void **p_attr); pj_status_t (*encode_attr)(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); - }; static pj_status_t decode_sockaddr_attr(pj_pool_t *pool, @@ -122,6 +122,11 @@ static pj_status_t decode_uint_attr(pj_pool_t *pool, void **p_attr); 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, void **p_attr); @@ -429,6 +434,18 @@ static struct attr_desc extended_attr_desc[] = "FINGERPRINT", &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 } }; @@ -551,14 +568,69 @@ PJ_DEF(pj_str_t) pj_stun_get_err_reason(int err_code) } +////////////////////////////////////////////////////////////////////////////// + #define INIT_ATTR(a,t,l) (a)->hdr.type=(pj_uint16_t)(t), \ (a)->hdr.length=(pj_uint16_t)(l) #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); +} ////////////////////////////////////////////////////////////////////////////// /* @@ -626,11 +698,7 @@ static pj_status_t decode_sockaddr_attr(pj_pool_t *pool, /* 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 */ if (attr->hdr.length != STUN_GENERIC_IP_ADDR_LEN) @@ -645,8 +713,8 @@ static pj_status_t decode_sockaddr_attr(pj_pool_t *pool, /* 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 */ *p_attr = attr; @@ -660,32 +728,13 @@ static pj_status_t decode_xored_sockaddr_attr(pj_pool_t *pool, void **p_attr) { pj_stun_sockaddr_attr *attr; - pj_uint32_t val; + pj_status_t status; - /* 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); + 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); @@ -705,16 +754,13 @@ static pj_status_t encode_sockaddr_attr(const void *a, pj_uint8_t *buf, pj_uint8_t *start_buf = buf; const pj_stun_sockaddr_attr *ca = (const pj_stun_sockaddr_attr *)a; - pj_stun_sockaddr_attr *attr; if (len < ATTR_LEN) return PJ_ETOOSMALL; /* 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; /* Ignored */ @@ -819,13 +865,7 @@ static pj_status_t decode_string_attr(pj_pool_t *pool, /* 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 */ value.ptr = ((char*)buf + ATTR_HDR_LEN); @@ -847,7 +887,6 @@ static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, { 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) */ *printed = (ca->value.slen + ATTR_HDR_LEN + 3) & (~3); @@ -856,16 +895,9 @@ static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, return PJ_ETOOSMALL; } - /* 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 */ pj_memcpy(buf+ATTR_HDR_LEN, ca->value.ptr, ca->value.slen); @@ -929,11 +961,7 @@ static pj_status_t decode_empty_attr(pj_pool_t *pool, /* 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 */ if (attr->hdr.length != 0) @@ -949,16 +977,13 @@ static pj_status_t decode_empty_attr(pj_pool_t *pool, static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, 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 */ *printed = ATTR_HDR_LEN; @@ -971,7 +996,6 @@ static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, /* * STUN generic 32bit integer attribute. */ -#define STUN_UINT_LEN 4 /* * Create a STUN generic 32bit value attribute. @@ -987,7 +1011,7 @@ pj_stun_uint_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); 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; *p_attr = attr; @@ -1016,26 +1040,16 @@ static pj_status_t decode_uint_attr(pj_pool_t *pool, const pj_uint8_t *buf, 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); + GETATTRHDR(buf, &attr->hdr); - /* 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); + 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; /* Done */ @@ -1048,36 +1062,111 @@ static pj_status_t decode_uint_attr(pj_pool_t *pool, static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed) { - enum - { - ATTR_LEN = STUN_UINT_LEN + ATTR_HDR_LEN - }; - pj_stun_uint_attr *attr; + const pj_stun_uint_attr *ca = (const pj_stun_uint_attr*)a; - if (len < ATTR_LEN) + 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; } +////////////////////////////////////////////////////////////////////////////// + +/* + * 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 - /* * Create a STUN MESSAGE-INTEGRITY attribute. */ @@ -1090,7 +1179,7 @@ pj_stun_msgint_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); 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; @@ -1115,25 +1204,19 @@ static pj_status_t decode_msgint_attr(pj_pool_t *pool, const pj_uint8_t *buf, 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 */ *p_attr = attr; return PJ_SUCCESS; @@ -1143,24 +1226,19 @@ static pj_status_t decode_msgint_attr(pj_pool_t *pool, static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed) { - enum - { - ATTR_LEN = STUN_MSG_INTEGRITY_LEN + ATTR_HDR_LEN - }; - pj_stun_msgint_attr *attr; + const pj_stun_msgint_attr *ca = (const pj_stun_msgint_attr*)a; - if (len < ATTR_LEN) + 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; } @@ -1197,8 +1275,7 @@ pj_stun_errcode_attr_create(pj_pool_t *pool, 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); *p_attr = attr; @@ -1232,13 +1309,9 @@ static pj_status_t decode_errcode_attr(pj_pool_t *pool, /* Create the attribute */ attr = PJ_POOL_ZALLOC_T(pool, pj_stun_errcode_attr); + GETATTRHDR(buf, &attr->hdr); - /* 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); + attr->err_code = buf[6] * 100 + buf[7]; /* Get pointer to the string in the message */ value.ptr = ((char*)buf + ATTR_HDR_LEN + 4); @@ -1259,21 +1332,16 @@ static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, { 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) return PJ_ETOOSMALL; /* 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 */ pj_memcpy(buf + ATTR_HDR_LEN + 4, ca->reason.ptr, ca->reason.slen); @@ -1355,11 +1423,8 @@ static pj_status_t decode_unknown_attr(pj_pool_t *pool, unsigned i; 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) return PJ_ETOOMANY; @@ -1380,7 +1445,6 @@ static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed) { 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; @@ -1388,21 +1452,13 @@ static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, if (len < ATTR_HDR_LEN + (ca->attr_count << 1)) 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]); } /* Done */ @@ -1432,7 +1488,7 @@ pj_stun_binary_attr_create(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && attr_type && p_attr, PJ_EINVAL); 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) { attr->length = length; @@ -1474,13 +1530,7 @@ static pj_status_t decode_binary_attr(pj_pool_t *pool, /* 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 */ attr->length = attr->hdr.length; @@ -1499,23 +1549,14 @@ static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed) { 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) */ *printed = (ca->length + ATTR_HDR_LEN + 3) & (~3); if (len < *printed) 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 */ pj_memcpy(buf+ATTR_HDR_LEN, ca->data, ca->length); @@ -1582,20 +1623,6 @@ PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, } -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. */ @@ -1614,7 +1641,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, unsigned pdu_len, return PJNATH_EINSTUNMSGTYPE; /* 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)) { @@ -1624,12 +1651,12 @@ PJ_DEF(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, unsigned pdu_len, /* If magic is set, then there is great possibility that this is * 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; if (attr_len != 4) diff --git a/pjnath/src/pjnath/stun_msg_dump.c b/pjnath/src/pjnath/stun_msg_dump.c index 5e83958ee..e888e91ef 100644 --- a/pjnath/src/pjnath/stun_msg_dump.c +++ b/pjnath/src/pjnath/stun_msg_dump.c @@ -140,7 +140,7 @@ static int print_attr(char *buffer, unsigned length, attr = (const pj_stun_errcode_attr*) ahdr; 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); } diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c index 66ff5b0ff..698299196 100644 --- a/pjnath/src/pjnath/stun_transaction.c +++ b/pjnath/src/pjnath/stun_transaction.c @@ -284,14 +284,14 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, err_attr = (pj_stun_errcode_attr*) 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 * of request retransmissions, but otherwise is discarded. */ 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)); return PJ_SUCCESS; @@ -300,8 +300,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, if (err_attr == NULL) { 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); } /* Call callback */