Compare commits

...

4 Commits

Author SHA1 Message Date
Benny Prijono 20d443d87b SIPit ICE test: fixed crashed with ICE negotiation, fixed empty (0.0.0.0) srflx addr, etc.
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/ice-turn07@1932 74dad513-b988-da41-8d7b-12977e46ad98
2008-04-17 17:25:37 +00:00
Benny Prijono eb7693143c Use the smart Contact header for TCP/TLS
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/ice-turn07@1930 74dad513-b988-da41-8d7b-12977e46ad98
2008-04-15 10:35:32 +00:00
Benny Prijono b3535a4eb2 More ticket #485: huge changeset to integrate TURN with ICE and PJSUA-LIB/pjsua. Still experimental
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/ice-turn07@1926 74dad513-b988-da41-8d7b-12977e46ad98
2008-04-14 01:48:39 +00:00
Benny Prijono 540f7b7842 TURN-07 integration to ICE etc
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/ice-turn07@1925 74dad513-b988-da41-8d7b-12977e46ad98
2008-04-14 00:25:40 +00:00
21 changed files with 707 additions and 254 deletions

View File

@ -776,9 +776,22 @@ PJ_DECL(unsigned) pj_sockaddr_get_len(const pj_sockaddr_t *addr);
*
* @param dst Destination socket address.
* @param src Source socket address.
*
* @see @pj_sockaddr_cp()
*/
PJ_DECL(void) pj_sockaddr_copy_addr(pj_sockaddr *dst,
const pj_sockaddr *src);
/**
* Copy socket address. This will copy the whole structure depending
* on the address family of the source socket address.
*
* @param dst Destination socket address.
* @param src Source socket address.
*
* @see @pj_sockaddr_copy_addr()
*/
PJ_DECL(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src);
/**
* Get the IP address of an IPv4 socket address.
* The address is returned as 32bit value in host byte order.

View File

@ -389,6 +389,14 @@ PJ_DEF(void) pj_sockaddr_copy_addr( pj_sockaddr *dst,
pj_sockaddr_get_addr_len(src));
}
/*
* Copy socket address.
*/
PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src)
{
pj_memcpy(dst, src, pj_sockaddr_get_len(src));
}
/*
* Set port number of pj_sockaddr_in
*/

View File

@ -62,7 +62,7 @@ typedef struct pjmedia_ice_cb
* @param name Optional name to identify this ICE media transport
* for logging purposes.
* @param comp_cnt Number of components to be created.
* @param stun_cfg Pointer to STUN configuration settings.
* @param cfg Pointer to configuration settings.
* @param cb Optional callbacks.
* @param p_tp Pointer to receive the media transport instance.
*
@ -71,7 +71,7 @@ typedef struct pjmedia_ice_cb
PJ_DECL(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
const char *name,
unsigned comp_cnt,
pj_stun_config *stun_cfg,
const pj_ice_strans_cfg *cfg,
const pjmedia_ice_cb *cb,
pjmedia_transport **p_tp);
@ -104,19 +104,13 @@ PJ_DECL(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
* socket to this particular interface only, and
* no other host candidates will be added for this
* socket.
* @param stun_srv Address of the STUN server, or NULL if STUN server
* reflexive mapping is not to be used.
* @param turn_srv Address of the TURN server, or NULL if TURN relay
* is not to be used.
*
* @return PJ_SUCCESS when the initialization process has started
* successfully, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pjmedia_ice_start_init(pjmedia_transport *tp,
unsigned options,
const pj_sockaddr_in *start_addr,
const pj_sockaddr_in *stun_srv,
const pj_sockaddr_in *turn_srv);
const pj_sockaddr_in *start_addr);
/**
* Poll the initialization status of this media transport.

View File

@ -128,7 +128,7 @@ static const pj_str_t STR_ICE_MISMATCH = {"ice-mismatch", 12};
PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
const char *name,
unsigned comp_cnt,
pj_stun_config *stun_cfg,
const pj_ice_strans_cfg *cfg,
const pjmedia_ice_cb *cb,
pjmedia_transport **p_tp)
{
@ -145,8 +145,8 @@ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
ice_st_cb.on_rx_data = &ice_on_rx_data;
/* Create ICE */
status = pj_ice_strans_create(stun_cfg, name, comp_cnt, NULL,
&ice_st_cb, &ice_st);
status = pj_ice_strans_create(cfg, name, comp_cnt, NULL,
&ice_st_cb, &ice_st);
if (status != PJ_SUCCESS)
return status;
@ -176,17 +176,11 @@ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
*/
PJ_DEF(pj_status_t) pjmedia_ice_start_init( pjmedia_transport *tp,
unsigned options,
const pj_sockaddr_in *start_addr,
const pj_sockaddr_in *stun_srv,
const pj_sockaddr_in *turn_srv)
const pj_sockaddr_in *start_addr)
{
struct transport_ice *tp_ice = (struct transport_ice*)tp;
pj_status_t status;
status = pj_ice_strans_set_stun_srv(tp_ice->ice_st, stun_srv, turn_srv);
if (status != PJ_SUCCESS)
return status;
status = pj_ice_strans_create_comp(tp_ice->ice_st, 1, options, start_addr);
if (status != PJ_SUCCESS)
return status;
@ -349,7 +343,7 @@ static pj_status_t transport_media_create(pjmedia_transport *tp,
case PJ_ICE_CAND_TYPE_RELAYED:
PJ_TODO(RELATED_ADDR_FOR_RELAYED_ADDR);
len = pj_ansi_snprintf(buffer+len, MAXLEN-len,
"srflx raddr %s rport %d",
"relay raddr %s rport %d",
pj_inet_ntoa(cand->base_addr.ipv4.sin_addr),
(int)pj_ntohs(cand->base_addr.ipv4.sin_port));
break;

View File

@ -430,12 +430,14 @@ typedef struct pj_ice_sess_cb
*
* @param ice The ICE session.
* @param comp_id ICE component ID.
* @param cand_id ICE candidate ID.
* @param pkt The STUN packet.
* @param size The size of the packet.
* @param dst_addr Packet destination address.
* @param dst_addr_len Length of destination address.
*/
pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id,
unsigned cand_id,
const void *pkt, pj_size_t size,
const pj_sockaddr_t *dst_addr,
unsigned dst_addr_len);
@ -797,6 +799,14 @@ PJ_DECL(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
*
* @param ice The ICE session.
* @param comp_id Component ID.
* @param cand_id The candidate ID where this packet was received
* from. This parameter will be returned back to
* application in \a on_tx_pkt() callback, and
* application may use it to determine whether to
* send outgoing packet using local socket or with
* the TURN relay. The ICE session will not use
* this information to determine the local candidate
* for this packet.
* @param pkt Incoming packet.
* @param pkt_size Size of incoming packet.
* @param src_addr Source address of the packet.
@ -806,6 +816,7 @@ PJ_DECL(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
*/
PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
unsigned comp_id,
unsigned cand_id,
void *pkt,
pj_size_t pkt_size,
const pj_sockaddr_t *src_addr,

View File

@ -25,6 +25,7 @@
* @brief ICE Stream Transport
*/
#include <pjnath/ice_session.h>
#include <pjnath/turn_sock.h>
#include <pjlib-util/resolver.h>
#include <pj/ioqueue.h>
#include <pj/timer.h>
@ -91,11 +92,6 @@ PJ_BEGIN_DECL
* \a on_ice_complete() to notify application that ICE negotiation
* has completed, either successfully or with failure.
*
* After the ICE stream transport is created, application may set up the
* STUN servers to be used to obtain STUN server reflexive and relayed
* candidate, by calling #pj_ice_strans_set_stun_domain() or
* #pj_ice_strans_set_stun_srv().
*
* Application then creates each component by calling
* #pj_ice_strans_create_comp(); this would create an actual socket
* which listens to the specified local address, and it would also
@ -335,6 +331,8 @@ typedef struct pj_ice_strans_comp
pj_stun_session *stun_sess; /**< STUN session. */
pj_uint8_t ka_tsx_id[12]; /**< ID for keep STUN alives */
pj_turn_sock *turn_relay; /**< TURN relay object. */
pj_sockaddr local_addr; /**< Local/base address. */
unsigned pending_cnt; /**< Pending resolution cnt. */
@ -354,6 +352,54 @@ typedef struct pj_ice_strans_comp
} pj_ice_strans_comp;
/**
* This structure describes ICE stream transport configuration.
*/
typedef struct pj_ice_strans_cfg
{
/**
* STUN config. This setting is mandatory.
*/
pj_stun_config stun_cfg;
/**
* STUN server address, if STUN is enabled.
*
* Default is to have no TURN server.
*/
pj_sockaddr stun_srv;
/**
* TURN server address, if TURN is enabled.
*
* Default is to have no TURN server.
*/
pj_sockaddr turn_srv;
/**
* Type of connection to the TURN server.
*
* Default is PJ_TURN_TP_UDP.
*/
pj_turn_tp_type turn_conn_type;
/**
* Credential to be used for the TURN session.
*
* Default is to have no credential.
*/
pj_stun_auth_cred turn_cred;
/**
* Optional TURN Allocate parameter.
*
* Default is all empty.
*/
pj_turn_alloc_param turn_alloc_param;
} pj_ice_strans_cfg;
/**
* This structure represents the ICE stream transport.
*/
@ -363,7 +409,7 @@ struct pj_ice_strans
pj_pool_t *pool; /**< Pool used by this object. */
void *user_data; /**< Application data. */
pj_stun_config stun_cfg; /**< STUN settings. */
pj_ice_strans_cfg cfg; /**< Configuration. */
pj_ice_strans_cb cb; /**< Application callback. */
pj_ice_sess *ice; /**< ICE session. */
@ -371,11 +417,6 @@ struct pj_ice_strans
unsigned comp_cnt; /**< Number of components. */
pj_ice_strans_comp **comp; /**< Components array. */
pj_dns_resolver *resolver; /**< The resolver instance. */
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. */
};
@ -387,7 +428,7 @@ struct pj_ice_strans
* initialize each components by calling #pj_ice_strans_create_comp()
* function.
*
* @param stun_cfg The STUN settings.
* @param cfg Configuration.
* @param name Optional name for logging identification.
* @param comp_cnt Number of components.
* @param user_data Arbitrary user data to be associated with this
@ -399,7 +440,7 @@ struct pj_ice_strans
* @return PJ_SUCCESS if ICE stream transport is created
* successfully.
*/
PJ_DECL(pj_status_t) pj_ice_strans_create(pj_stun_config *stun_cfg,
PJ_DECL(pj_status_t) pj_ice_strans_create(const pj_ice_strans_cfg *cfg,
const char *name,
unsigned comp_cnt,
void *user_data,
@ -417,50 +458,6 @@ PJ_DECL(pj_status_t) pj_ice_strans_create(pj_stun_config *stun_cfg,
*/
PJ_DECL(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st);
/**
* Set the domain to be used when resolving the STUN servers. If application
* wants to utillize STUN, then STUN server must be specified, either by
* calling this function or by calling #pj_ice_strans_set_stun_srv().
*
* If application calls this function, then the STUN/TURN servers will
* be resolved by querying DNS SRV records for the specified domain.
*
* @param ice_st The ICE stream transport.
* @param resolver The resolver instance that will be used to
* resolve the STUN/TURN servers.
* @param domain The target domain.
*
* @return PJ_SUCCESS if DNS SRV resolution job can be
* started. The resolution process itself will
* complete asynchronously.
*/
PJ_DECL(pj_status_t) pj_ice_strans_set_stun_domain(pj_ice_strans *ice_st,
pj_dns_resolver *resolver,
const pj_str_t *domain);
/**
* Set the STUN and TURN server addresses. If application
* wants to utillize STUN, then STUN server must be specified, either by
* calling this function or by calling #pj_ice_strans_set_stun_domain().
*
* With this function, the STUN and TURN server addresses will be
* assigned immediately, that is no DNS resolution will need to be
* performed.
*
* @param ice_st The ICE stream transport.
* @param stun_srv The STUN server address, or NULL if STUN
* reflexive candidate is not to be used.
* @param turn_srv The TURN server address, or NULL if STUN
* relay candidate is not to be used.
*
* @return PJ_SUCCESS, or the appropriate error code.
*/
PJ_DECL(pj_status_t)
pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st,
const pj_sockaddr_in *stun_srv,
const pj_sockaddr_in *turn_srv);
/**
* Create and initialize the specified component. This function will
* instantiate the socket descriptor for this component, optionally

View File

@ -25,6 +25,8 @@
*/
#include <pjnath/stun_msg.h>
#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/string.h>
@ -102,6 +104,17 @@ PJ_INLINE(void) pj_stun_config_init(pj_stun_config *cfg,
}
/**
* Check that STUN config is valid.
*/
PJ_INLINE(pj_status_t) pj_stun_config_check_valid(const pj_stun_config *cfg)
{
PJ_ASSERT_RETURN(cfg->ioqueue && cfg->pf && cfg->timer_heap &&
cfg->rto_msec && cfg->res_cache_msec, PJ_EINVAL);
return PJ_SUCCESS;
}
/**
* @}
*/

View File

@ -176,7 +176,7 @@ typedef struct pj_turn_session_cb
* This callback is optional.
*/
void (*on_rx_data)(pj_turn_session *sess,
const pj_uint8_t *pkt,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *peer_addr,
unsigned addr_len);
@ -257,7 +257,7 @@ PJ_DECL(const char*) pj_turn_state_name(pj_turn_state_t state);
/**
* Create TURN client session.
*/
PJ_DECL(pj_status_t) pj_turn_session_create(pj_stun_config *cfg,
PJ_DECL(pj_status_t) pj_turn_session_create(const pj_stun_config *cfg,
const char *name,
int af,
pj_turn_tp_type conn_type,
@ -340,7 +340,7 @@ PJ_DECL(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess,
* The packet maybe a STUN packet or ChannelData packet.
*/
PJ_DECL(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
const pj_uint8_t *pkt,
void *pkt,
unsigned pkt_len,
pj_bool_t is_datagram);

View File

@ -16,8 +16,8 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __PJNATH_turn_sock_H__
#define __PJNATH_turn_sock_H__
#ifndef __PJNATH_TURN_SOCK_H__
#define __PJNATH_TURN_SOCK_H__
/**
* @file turn_sock.h
@ -53,7 +53,7 @@ typedef struct pj_turn_sock_cb
* This callback is mandatory.
*/
void (*on_rx_data)(pj_turn_sock *turn_sock,
const pj_uint8_t *pkt,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *peer_addr,
unsigned addr_len);
@ -103,6 +103,19 @@ PJ_DECL(void*) pj_turn_sock_get_user_data(pj_turn_sock *turn_sock);
PJ_DECL(pj_status_t) pj_turn_sock_get_info(pj_turn_sock *turn_sock,
pj_turn_session_info *info);
/**
* Lock the TURN socket. Application may need to call this function to
* synchronize access to other objects to avoid deadlock.
*/
PJ_DECL(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock);
/**
* Unlock the TURN socket.
*/
PJ_DECL(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock);
/**
* Initialize.
*/
@ -138,5 +151,5 @@ PJ_DECL(pj_status_t) pj_turn_sock_bind_channel(pj_turn_sock *turn_sock,
PJ_END_DECL
#endif /* __PJNATH_turn_sock_H__ */
#endif /* __PJNATH_TURN_SOCK_H__ */

View File

@ -101,6 +101,24 @@ typedef struct timer_data
} timer_data;
/* This is the data that will be attached as token to outgoing
* STUN messages.
*/
struct msg_data
{
pj_bool_t is_request;
unsigned cand_id;
union data {
struct request_data {
pj_ice_sess *ice;
pj_ice_sess_checklist *clist;
unsigned ckid;
} req;
} data;
};
/* Forward declarations */
static void destroy_ice(pj_ice_sess *ice,
pj_status_t reason);
@ -1345,26 +1363,13 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
return PJ_SUCCESS;
}
/* This is the data that will be attached as user data to outgoing
* STUN requests, and it will be given back when we receive completion
* status of the request.
*/
struct req_data
{
pj_ice_sess *ice;
pj_ice_sess_checklist *clist;
unsigned ckid;
};
/* Perform check on the specified candidate pair */
static pj_status_t perform_check(pj_ice_sess *ice,
pj_ice_sess_checklist *clist,
unsigned check_id)
{
pj_ice_sess_comp *comp;
struct req_data *rd;
struct msg_data *msg_data;
pj_ice_sess_check *check;
const pj_ice_sess_cand *lcand;
const pj_ice_sess_cand *rcand;
@ -1392,10 +1397,12 @@ static pj_status_t perform_check(pj_ice_sess *ice,
/* Attach data to be retrieved later when STUN request transaction
* completes and on_stun_request_complete() callback is called.
*/
rd = PJ_POOL_ZALLOC_T(check->tdata->pool, struct req_data);
rd->ice = ice;
rd->clist = clist;
rd->ckid = check_id;
msg_data = PJ_POOL_ZALLOC_T(ice->pool, struct msg_data);
msg_data->is_request = PJ_TRUE;
msg_data->cand_id = lcand - ice->lcand;
msg_data->data.req.ice = ice;
msg_data->data.req.clist = clist;
msg_data->data.req.ckid = check_id;
/* Add PRIORITY */
prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535,
@ -1427,7 +1434,7 @@ static pj_status_t perform_check(pj_ice_sess *ice,
*/
/* Initiate STUN transaction to send the request */
status = pj_stun_session_send_msg(comp->stun_sess, (void*)rd, PJ_FALSE,
status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE,
PJ_TRUE, &rcand->addr,
sizeof(pj_sockaddr_in), check->tdata);
if (status != PJ_SUCCESS) {
@ -1655,12 +1662,13 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess,
{
stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess);
pj_ice_sess *ice = sd->ice;
struct msg_data *msg_data = (struct msg_data*) token;
unsigned cand_id;
cand_id = msg_data->cand_id;
PJ_UNUSED_ARG(token);
return (*ice->cb.on_tx_pkt)(ice, sd->comp_id,
pkt, pkt_size,
dst_addr, addr_len);
return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, cand_id,
pkt, pkt_size, dst_addr, addr_len);
}
@ -1673,7 +1681,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len)
{
struct req_data *rd = (struct req_data*) token;
struct msg_data *msg_data = (struct msg_data*) token;
pj_ice_sess *ice;
pj_ice_sess_check *check, *new_check;
pj_ice_sess_cand *lcand;
@ -1684,9 +1692,12 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
PJ_UNUSED_ARG(stun_sess);
PJ_UNUSED_ARG(src_addr_len);
ice = rd->ice;
check = &rd->clist->checks[rd->ckid];
clist = rd->clist;
pj_assert(msg_data->is_request);
ice = msg_data->data.req.ice;
clist = msg_data->data.req.clist;
check = &clist->checks[msg_data->data.req.ckid];
/* Mark STUN transaction as complete */
pj_assert(tdata == check->tdata);
@ -1739,7 +1750,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
/* Resend request */
LOG4((ice->obj_name, "Resending check because of role conflict"));
check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
perform_check(ice, clist, rd->ckid);
perform_check(ice, clist, msg_data->data.req.ckid);
pj_mutex_unlock(ice->mutex);
return;
}
@ -1918,7 +1929,9 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
unsigned src_addr_len)
{
stun_data *sd;
//unsigned *param_cand_id;
const pj_stun_msg *msg = rdata->msg;
struct msg_data *msg_data;
pj_ice_sess *ice;
pj_stun_priority_attr *prio_attr;
pj_stun_use_candidate_attr *uc_attr;
@ -1929,7 +1942,16 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
PJ_UNUSED_ARG(pkt);
PJ_UNUSED_ARG(pkt_len);
PJ_UNUSED_ARG(token);
/*
* Note about candidate ID parameter:
* This parameter is given by us by user, and it cannot be used to
* distinguish local and server reflexive candidate. Just about the
* only thing that we can do with it is to return it back to user
* in the on_tx_pkt(). The user needs this information to determine
* whether to send packet using local socket or the relay.
*/
//param_cand_id = (unsigned*)token;
/* Reject any requests except Binding request */
if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) {
@ -2034,11 +2056,18 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess,
return status;
}
/* Add XOR-MAPPED-ADDRESS attribute */
status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
PJ_STUN_ATTR_XOR_MAPPED_ADDR,
PJ_TRUE, src_addr, src_addr_len);
status = pj_stun_session_send_msg(sess, NULL, PJ_TRUE, PJ_TRUE,
/* Create a msg_data to be associated with this response */
msg_data = PJ_POOL_ZALLOC_T(ice->pool, struct msg_data);
msg_data->is_request = PJ_FALSE;
msg_data->cand_id = ((struct msg_data*)token)->cand_id;
/* Send the response */
status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE,
src_addr, src_addr_len, tdata);
@ -2090,7 +2119,6 @@ static void handle_incoming_check(pj_ice_sess *ice,
pj_ice_sess_cand *lcand = NULL;
pj_ice_sess_cand *rcand;
unsigned i;
pj_bool_t is_relayed;
comp = find_comp(ice, rcheck->comp_id);
@ -2170,11 +2198,6 @@ static void handle_incoming_check(pj_ice_sess *ice,
/*
* Create candidate pair for this request.
*/
/* First check if the source address is the source address of the
* STUN relay, to determine if local candidate is relayed candidate.
*/
PJ_TODO(DETERMINE_IF_REQUEST_COMES_FROM_RELAYED_CANDIDATE);
is_relayed = PJ_FALSE;
/*
* 7.2.1.4. Triggered Checks
@ -2309,6 +2332,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
{
pj_status_t status = PJ_SUCCESS;
pj_ice_sess_comp *comp;
unsigned cand_id;
PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL);
@ -2332,7 +2356,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
goto on_return;
}
status = (*ice->cb.on_tx_pkt)(ice, comp_id, data, data_len,
cand_id = comp->valid_check->lcand - ice->lcand;
status = (*ice->cb.on_tx_pkt)(ice, comp_id, cand_id, data, data_len,
&comp->valid_check->rcand->addr,
sizeof(pj_sockaddr_in));
@ -2344,6 +2370,7 @@ on_return:
PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
unsigned comp_id,
unsigned cand_id,
void *pkt,
pj_size_t pkt_size,
const pj_sockaddr_t *src_addr,
@ -2351,6 +2378,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
{
pj_status_t status = PJ_SUCCESS;
pj_ice_sess_comp *comp;
struct msg_data *msg_data;
pj_status_t stun_status;
PJ_ASSERT_RETURN(ice, PJ_EINVAL);
@ -2363,11 +2391,15 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
goto on_return;
}
msg_data = PJ_POOL_ZALLOC_T(ice->pool, struct msg_data);
msg_data->is_request = PJ_FALSE;
msg_data->cand_id = cand_id;
stun_status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size,
PJ_STUN_IS_DATAGRAM);
if (stun_status == PJ_SUCCESS) {
status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size,
PJ_STUN_IS_DATAGRAM, NULL,
PJ_STUN_IS_DATAGRAM, msg_data,
NULL, src_addr, src_addr_len);
if (status != PJ_SUCCESS) {
pj_strerror(status, ice->tmp.errmsg, sizeof(ice->tmp.errmsg));

View File

@ -40,6 +40,7 @@
static void on_ice_complete(pj_ice_sess *ice, pj_status_t status);
static pj_status_t ice_tx_pkt(pj_ice_sess *ice,
unsigned comp_id,
unsigned cand_id,
const void *pkt, pj_size_t size,
const pj_sockaddr_t *dst_addr,
unsigned dst_addr_len);
@ -73,6 +74,16 @@ static void stun_on_request_complete(pj_stun_session *sess,
const pj_sockaddr_t *src_addr,
unsigned src_addr_len);
/* TURN callbacks */
static void turn_on_rx_data(pj_turn_sock *turn_sock,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *peer_addr,
unsigned addr_len);
static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
pj_turn_state_t new_state);
/* Keep-alive timer */
static void start_ka_timer(pj_ice_strans *ice_st);
static void stop_ka_timer(pj_ice_strans *ice_st);
@ -80,10 +91,28 @@ static void stop_ka_timer(pj_ice_strans *ice_st);
/* Utility: print error */
#define ice_st_perror(ice_st,msg,rc) pjnath_perror(ice_st->obj_name,msg,rc)
/* Validate configuration */
static pj_status_t pj_ice_strans_cfg_check_valid(const pj_ice_strans_cfg *cfg)
{
pj_status_t status;
status = pj_stun_config_check_valid(&cfg->stun_cfg);
if (!status)
return status;
/* If TURN is specified then TURN credential must be specified */
PJ_ASSERT_RETURN(!pj_sockaddr_has_addr(&cfg->turn_srv) ||
cfg->turn_cred.type != PJ_STUN_AUTH_NONE,
PJ_EINVAL);
return PJ_SUCCESS;
}
/*
* Create ICE stream transport
*/
PJ_DEF(pj_status_t) pj_ice_strans_create( pj_stun_config *stun_cfg,
PJ_DEF(pj_status_t) pj_ice_strans_create( const pj_ice_strans_cfg *cfg,
const char *name,
unsigned comp_cnt,
void *user_data,
@ -92,17 +121,23 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( pj_stun_config *stun_cfg,
{
pj_pool_t *pool;
pj_ice_strans *ice_st;
pj_status_t status;
PJ_ASSERT_RETURN(stun_cfg && comp_cnt && cb && p_ice_st, PJ_EINVAL);
PJ_ASSERT_RETURN(stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL);
status = pj_ice_strans_cfg_check_valid(cfg);
if (status != PJ_SUCCESS)
return status;
PJ_ASSERT_RETURN(comp_cnt && cb && p_ice_st, PJ_EINVAL);
if (name == NULL)
name = "icstr%p";
pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_STRANS,
pool = pj_pool_create(cfg->stun_cfg.pf, name, PJNATH_POOL_LEN_ICE_STRANS,
PJNATH_POOL_INC_ICE_STRANS, NULL);
ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_strans);
ice_st->pool = pool;
pj_memcpy(&ice_st->cfg, cfg, sizeof(*cfg));
pj_stun_auth_cred_dup(pool, &ice_st->cfg.turn_cred, &cfg->turn_cred);
pj_memcpy(ice_st->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME);
ice_st->user_data = user_data;
@ -111,7 +146,6 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( pj_stun_config *stun_cfg,
sizeof(void*));
pj_memcpy(&ice_st->cb, cb, sizeof(*cb));
pj_memcpy(&ice_st->stun_cfg, stun_cfg, sizeof(*stun_cfg));
PJ_LOG(4,(ice_st->obj_name, "ICE stream transport created"));
@ -180,32 +214,6 @@ PJ_DEF(pj_status_t) pj_ice_strans_set_stun_domain(pj_ice_strans *ice_st,
return -1;
}
/*
* Set STUN server address.
*/
PJ_DEF(pj_status_t) pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st,
const pj_sockaddr_in *stun_srv,
const pj_sockaddr_in *turn_srv)
{
PJ_ASSERT_RETURN(ice_st, PJ_EINVAL);
/* Must not have pending resolver job */
PJ_ASSERT_RETURN(ice_st->has_rjob==PJ_FALSE, PJ_EINVALIDOP);
if (stun_srv) {
pj_memcpy(&ice_st->stun_srv, stun_srv, sizeof(pj_sockaddr_in));
} else {
pj_bzero(&ice_st->stun_srv, sizeof(pj_sockaddr_in));
}
if (turn_srv) {
pj_memcpy(&ice_st->turn_srv, turn_srv, sizeof(pj_sockaddr_in));
} else {
pj_bzero(&ice_st->turn_srv, sizeof(pj_sockaddr_in));
}
return PJ_SUCCESS;
}
/* Add new candidate */
static pj_status_t add_cand( pj_ice_strans *ice_st,
pj_ice_strans_comp *comp,
@ -327,7 +335,8 @@ static pj_status_t create_component(pj_ice_strans *ice_st,
/* Register to ioqueue */
pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
ioqueue_cb.on_read_complete = &on_read_complete;
status = pj_ioqueue_register_sock(ice_st->pool, ice_st->stun_cfg.ioqueue,
status = pj_ioqueue_register_sock(ice_st->pool,
ice_st->cfg.stun_cfg.ioqueue,
comp->sock, comp, &ioqueue_cb,
&comp->key);
if (status != PJ_SUCCESS)
@ -387,7 +396,9 @@ static pj_status_t create_component(pj_ice_strans *ice_st,
/* If the IP address is equal to local address, assign it
* as default candidate.
*/
if (ifs[i].ipv4.sin_addr.s_addr == comp->local_addr.ipv4.sin_addr.s_addr) {
if (ifs[i].ipv4.sin_addr.s_addr ==
comp->local_addr.ipv4.sin_addr.s_addr)
{
set_default = PJ_TRUE;
local_pref = 65535;
} else {
@ -463,24 +474,33 @@ static void on_read_complete(pj_ioqueue_key_t *key,
* address of this component, or b) subsequent request to keep
* the binding alive.
*
* 2) this could be a packet (STUN or not STUN) sent from the STUN
* relay server. In this case, still there are few options to do
* for this packet: a) process this locally if this packet is
* related to TURN session management (e.g. Allocate response),
* b) forward this packet to ICE if this is related to ICE
* 2) this could be a STUN request or response sent as part of ICE
* discovery process.
*
* 3) this could be a STUN request or response sent as part of ICE
* discovery process.
*
* 4) this could be application's packet, e.g. when ICE processing
* 3) this could be application's packet, e.g. when ICE processing
* is done and agents start sending RTP/RTCP packets to each
* other, or when ICE processing is not done and this ICE stream
* transport decides to allow sending data.
*
* So far we don't have good solution for this.
* The process below is just a workaround.
*/
unsigned cand_id;
/* Find candidate ID for this packet */
for (cand_id=0; cand_id<comp->cand_cnt; ++cand_id) {
if (comp->cand_list[cand_id].type != PJ_ICE_CAND_TYPE_RELAYED)
break;
}
if (cand_id == comp->cand_cnt) {
//pj_assert(!"We should have at least one host/srflx candidate");
//cand_id = 0;
PJ_LOG(2,(ice_st->obj_name,
"Received pkt on comp %d which doesn't have host/srflx "
"candidate",
comp->comp_id));
goto next_packet;
}
/* Is this a STUN message? */
status = pj_stun_msg_check(comp->pkt, bytes_read,
PJ_STUN_IS_DATAGRAM);
@ -495,8 +515,6 @@ static void on_read_complete(pj_ioqueue_key_t *key,
NULL, &comp->src_addr,
comp->src_addr_len);
} else if (ice_st->ice) {
PJ_TODO(DISTINGUISH_BETWEEN_LOCAL_AND_RELAY);
TRACE_PKT((comp->ice_st->obj_name,
"Component %d RX packet from %s:%d",
comp->comp_id,
@ -504,7 +522,7 @@ static void on_read_complete(pj_ioqueue_key_t *key,
(int)pj_ntohs(comp->src_addr.ipv4.sin_port)));
status = pj_ice_sess_on_rx_pkt(ice_st->ice, comp->comp_id,
comp->pkt, bytes_read,
cand_id, comp->pkt, bytes_read,
&comp->src_addr,
comp->src_addr_len);
} else {
@ -524,6 +542,7 @@ static void on_read_complete(pj_ioqueue_key_t *key,
}
/* Read next packet */
next_packet:
for (retry=0; retry<RETRY;) {
pkt_size = sizeof(comp->pkt);
comp->src_addr_len = sizeof(comp->src_addr);
@ -551,6 +570,11 @@ static void on_read_complete(pj_ioqueue_key_t *key,
*/
static void destroy_component(pj_ice_strans_comp *comp)
{
if (comp->turn_relay) {
pj_turn_sock_destroy(comp->turn_relay);
comp->turn_relay = NULL;
}
if (comp->stun_sess) {
pj_stun_session_destroy(comp->stun_sess);
comp->stun_sess = NULL;
@ -610,7 +634,8 @@ static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
pj_inet_ntoa(comp->local_addr.ipv4.sin_addr),
pj_ntohs(comp->local_addr.ipv4.sin_port)));
status = pj_stun_session_send_msg(comp->stun_sess, &comp->cand_list[j],
PJ_FALSE, PJ_TRUE, &ice_st->stun_srv,
PJ_FALSE, PJ_TRUE,
&ice_st->cfg.stun_srv,
sizeof(pj_sockaddr_in), tdata);
if (status != PJ_SUCCESS) {
--comp->pending_cnt;
@ -637,7 +662,7 @@ static void start_ka_timer(pj_ice_strans *ice_st)
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,
if (pj_timer_heap_schedule(ice_st->cfg.stun_cfg.timer_heap,
&ice_st->ka_timer, &delay)==PJ_SUCCESS)
{
ice_st->ka_timer.id = PJ_TRUE;
@ -652,7 +677,7 @@ static void stop_ka_timer(pj_ice_strans *ice_st)
if (ice_st->ka_timer.id == PJ_FALSE)
return;
pj_timer_heap_cancel(ice_st->stun_cfg.timer_heap, &ice_st->ka_timer);
pj_timer_heap_cancel(ice_st->cfg.stun_cfg.timer_heap, &ice_st->ka_timer);
ice_st->ka_timer.id = PJ_FALSE;
}
@ -670,12 +695,8 @@ static pj_status_t get_stun_mapped_addr(pj_ice_strans *ice_st,
PJ_ASSERT_RETURN(ice_st && comp, PJ_EINVAL);
/* Bail out if STUN server is still being resolved */
if (ice_st->has_rjob)
return PJ_EBUSY;
/* Just return (successfully) if STUN server is not configured */
if (ice_st->stun_srv.sin_family == 0)
if (ice_st->cfg.stun_srv.addr.sa_family == 0)
return PJ_SUCCESS;
@ -683,7 +704,7 @@ static pj_status_t get_stun_mapped_addr(pj_ice_strans *ice_st,
pj_bzero(&sess_cb, sizeof(sess_cb));
sess_cb.on_request_complete = &stun_on_request_complete;
sess_cb.on_send_msg = &stun_on_send_msg;
status = pj_stun_session_create(&ice_st->stun_cfg, ice_st->obj_name,
status = pj_stun_session_create(&ice_st->cfg.stun_cfg, ice_st->obj_name,
&sess_cb, PJ_FALSE, &comp->stun_sess);
if (status != PJ_SUCCESS)
return status;
@ -710,7 +731,7 @@ static pj_status_t get_stun_mapped_addr(pj_ice_strans *ice_st,
/* Add new alias to this component */
cand->type = PJ_ICE_CAND_TYPE_SRFLX;
cand->status = PJ_EPENDING;
cand->status = PJ_SUCCESS;
cand->ice_cand_id = -1;
cand->local_pref = 65535;
pj_ice_calc_foundation(ice_st->pool, &cand->foundation,
@ -720,7 +741,7 @@ static pj_status_t get_stun_mapped_addr(pj_ice_strans *ice_st,
/* Send STUN binding request */
status = pj_stun_session_send_msg(comp->stun_sess, (void*)cand, PJ_FALSE,
PJ_TRUE, &ice_st->stun_srv,
PJ_TRUE, &ice_st->cfg.stun_srv,
sizeof(pj_sockaddr_in), tdata);
if (status != PJ_SUCCESS) {
--comp->pending_cnt;
@ -752,16 +773,16 @@ PJ_DEF(pj_status_t) pj_ice_strans_create_comp(pj_ice_strans *ice_st,
/* Can't add new component while ICE is running */
PJ_ASSERT_RETURN(ice_st->ice == NULL, PJ_EBUSY);
/* Can't add new component while resolver is running */
PJ_ASSERT_RETURN(ice_st->has_rjob == PJ_FALSE, PJ_EBUSY);
/* Create component */
status = create_component(ice_st, comp_id, options, addr, &comp);
if (status != PJ_SUCCESS)
return status;
if ((options & PJ_ICE_ST_OPT_DISABLE_STUN) == 0) {
/* Start STUN mapped address resolution */
if ((options & PJ_ICE_ST_OPT_DISABLE_STUN) == 0 &&
pj_sockaddr_has_addr(&ice_st->cfg.stun_srv))
{
status = get_stun_mapped_addr(ice_st, comp);
if (status != PJ_SUCCESS) {
destroy_component(comp);
@ -769,6 +790,52 @@ PJ_DEF(pj_status_t) pj_ice_strans_create_comp(pj_ice_strans *ice_st,
}
}
/* Create TURN relay if wanted. */
if ((options & PJ_ICE_ST_OPT_DISABLE_RELAY) == 0 &&
pj_sockaddr_has_addr(&ice_st->cfg.turn_srv))
{
pj_turn_sock_cb turn_sock_cb;
char ipaddr[PJ_INET6_ADDRSTRLEN+8];
pj_str_t s;
pj_assert(comp->cand_cnt < PJ_ICE_ST_MAX_CAND);
/* Init TURN socket */
pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
turn_sock_cb.on_rx_data = &turn_on_rx_data;
turn_sock_cb.on_state = &turn_on_state;
status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, pj_AF_INET(),
ice_st->cfg.turn_conn_type,
&turn_sock_cb, 0, comp,
&comp->turn_relay);
if (status != PJ_SUCCESS) {
destroy_component(comp);
return status;
}
pj_sockaddr_print(&ice_st->cfg.turn_srv, ipaddr, sizeof(ipaddr), 0);
++comp->pending_cnt;
/* Start allocation */
status = pj_turn_sock_init(comp->turn_relay, pj_cstr(&s, ipaddr),
pj_sockaddr_get_port(&ice_st->cfg.turn_srv),
NULL, &ice_st->cfg.turn_cred,
&ice_st->cfg.turn_alloc_param);
if (status != PJ_SUCCESS) {
if (comp->turn_relay) {
pj_turn_sock_destroy(comp->turn_relay);
}
comp->turn_relay = NULL;
--comp->pending_cnt;
destroy_component(comp);
return status;
}
}
/* Store this component */
ice_st->comp[comp_id-1] = comp;
@ -848,7 +915,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
ice_cb.on_tx_pkt = &ice_tx_pkt;
/* Create! */
status = pj_ice_sess_create(&ice_st->stun_cfg, ice_st->obj_name, role,
status = pj_ice_sess_create(&ice_st->cfg.stun_cfg, ice_st->obj_name, role,
ice_st->comp_cnt, &ice_cb,
local_ufrag, local_passwd, &ice_st->ice);
if (status != PJ_SUCCESS)
@ -876,6 +943,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
for (j=0; j<comp->cand_cnt; ++j) {
pj_ice_strans_cand *cand = &comp->cand_list[j];
pj_sockaddr_t *local_addr, *relay_addr;
/* Skip if candidate is not ready */
if (cand->status != PJ_SUCCESS) {
@ -885,10 +953,25 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
continue;
}
/* Skip if candidate has no address */
if (!pj_sockaddr_has_addr(&cand->addr)) {
PJ_LOG(5,(ice_st->obj_name,
"Candidate %d in component %d is not added",
j, i));
continue;
}
if (cand->type == PJ_ICE_CAND_TYPE_RELAYED) {
local_addr = &cand->addr;
relay_addr = &cand->addr;
} else {
local_addr = &comp->local_addr;
relay_addr = NULL;
}
status = pj_ice_sess_add_cand(ice_st->ice, comp->comp_id,
cand->type, cand->local_pref,
&cand->foundation, &cand->addr,
&comp->local_addr, NULL,
local_addr, relay_addr,
sizeof(pj_sockaddr_in),
(unsigned*)&cand->ice_cand_id);
if (status != PJ_SUCCESS)
@ -977,7 +1060,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st)
}
/*
* Send packet using non-ICE means (e.g. when ICE was not negotiated).
* Application wants to send outgoing packet.
*/
PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st,
unsigned comp_id,
@ -997,7 +1080,14 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st,
/* If ICE is available, send data with ICE */
if (ice_st->ice) {
return pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len);
if (comp->turn_relay) {
pj_turn_sock_lock(comp->turn_relay);
}
status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len);
if (comp->turn_relay) {
pj_turn_sock_unlock(comp->turn_relay);
}
return status;
}
/* Otherwise send direcly with the socket. This is for compatibility
@ -1028,30 +1118,40 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
*/
static pj_status_t ice_tx_pkt(pj_ice_sess *ice,
unsigned comp_id,
unsigned cand_id,
const void *pkt, pj_size_t size,
const pj_sockaddr_t *dst_addr,
unsigned dst_addr_len)
{
pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data;
pj_ice_strans_comp *comp = NULL;
pj_ssize_t pkt_size;
pj_ice_strans_comp *comp;
pj_ice_strans_cand *cand;
pj_status_t status;
PJ_TODO(TX_TO_RELAY);
PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL);
comp = ice_st->comp[comp_id-1];
cand = &comp->cand_list[cand_id];
TRACE_PKT((comp->ice_st->obj_name,
"Component %d TX packet to %s:%d",
comp_id,
"Component %d candidate %d TX packet to %s:%d",
comp_id, cand_id,
pj_inet_ntoa(((pj_sockaddr_in*)dst_addr)->sin_addr),
(int)pj_ntohs(((pj_sockaddr_in*)dst_addr)->sin_port)));
pkt_size = size;
status = pj_ioqueue_sendto(comp->key, &comp->write_op,
pkt, &pkt_size, 0,
dst_addr, dst_addr_len);
if (cand->type == PJ_ICE_CAND_TYPE_RELAYED) {
if (comp->turn_relay) {
status = pj_turn_sock_sendto(comp->turn_relay, pkt, size,
dst_addr, dst_addr_len);
} else {
status = PJ_EINVALIDOP;
}
} else {
pj_ssize_t pkt_size = size;
status = pj_ioqueue_sendto(comp->key, &comp->write_op,
pkt, &pkt_size, 0,
dst_addr, dst_addr_len);
}
return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
}
@ -1206,3 +1306,108 @@ static void stun_on_request_complete(pj_stun_session *sess,
(cand - comp->cand_list));
}
/* Callback when TURN client has received a packet */
static void turn_on_rx_data(pj_turn_sock *turn_sock,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *peer_addr,
unsigned addr_len)
{
pj_ice_strans_comp *comp;
unsigned cand_id;
pj_status_t status;
comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock);
if (comp == NULL) {
return;
}
if (comp->ice_st->ice == NULL) {
/* The session is gone */
return;
}
/* Find candidate ID for this packet */
for (cand_id=0; cand_id<comp->cand_cnt; ++cand_id) {
if (comp->cand_list[cand_id].type == PJ_ICE_CAND_TYPE_RELAYED)
break;
}
if (cand_id == comp->cand_cnt) {
pj_assert(!"Missing relay candidate");
return;
}
/* Hand over the packet to ICE */
status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id,
cand_id, pkt, pkt_len,
peer_addr, addr_len);
if (status != PJ_SUCCESS) {
ice_st_perror(comp->ice_st, "Error processing packet from TURN relay",
status);
}
}
/* Callback when TURN client state has changed */
static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
pj_turn_state_t new_state)
{
pj_ice_strans_comp *comp;
comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock);
if (comp == NULL) {
/* Not interested in further state notification once the relay is
* disconnecting.
*/
return;
}
PJ_LOG(5,(comp->ice_st->obj_name, "TURN client state changed %s --> %s",
pj_turn_state_name(old_state), pj_turn_state_name(new_state)));
if (old_state < PJ_TURN_STATE_READY &&
new_state >= PJ_TURN_STATE_READY)
{
pj_assert(comp->pending_cnt > 0);
comp->pending_cnt--;
}
if (new_state == PJ_TURN_STATE_READY) {
pj_turn_session_info rel_info;
char ipaddr[PJ_INET6_ADDRSTRLEN+8];
pj_ice_strans_cand *cand;
/* Get allocation info */
pj_turn_sock_get_info(turn_sock, &rel_info);
/* Add a relay candidate to this component */
pj_assert(comp->cand_cnt < PJ_ICE_ST_MAX_CAND);
cand = &comp->cand_list[comp->cand_cnt++];
/* Add new candidate to this component */
cand->type = PJ_ICE_CAND_TYPE_RELAYED;
cand->status = PJ_SUCCESS;
cand->ice_cand_id = -1;
cand->local_pref = 65535;
pj_sockaddr_cp(&cand->addr, &rel_info.relay_addr);
pj_ice_calc_foundation(comp->ice_st->pool, &cand->foundation,
PJ_ICE_CAND_TYPE_RELAYED,
&rel_info.relay_addr);
PJ_LOG(4,(comp->ice_st->obj_name,
"Component %d cand %d: relay address: %s",
comp->comp_id, cand - comp->cand_list,
pj_sockaddr_print(&rel_info.relay_addr, ipaddr,
sizeof(ipaddr), 3)));
} else if (new_state >= PJ_TURN_STATE_DEALLOCATING) {
/* Unregister ourself from the TURN relay */
pj_turn_sock_set_user_data(turn_sock, NULL);
comp->turn_relay = NULL;
PJ_LOG(4,(comp->ice_st->obj_name, "Relay destroyed"));
}
}

View File

@ -66,6 +66,7 @@ struct pj_turn_session
const char *obj_name;
pj_turn_session_cb cb;
void *user_data;
pj_stun_config stun_cfg;
pj_lock_t *lock;
int busy;
@ -176,7 +177,7 @@ PJ_DEF(const char*) pj_turn_state_name(pj_turn_state_t state)
/*
* Create TURN client session.
*/
PJ_DEF(pj_status_t) pj_turn_session_create( pj_stun_config *cfg,
PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
const char *name,
int af,
pj_turn_tp_type tp_type,
@ -211,6 +212,9 @@ PJ_DEF(pj_status_t) pj_turn_session_create( pj_stun_config *cfg,
sess->user_data = user_data;
sess->next_ch = PJ_TURN_CHANNEL_MIN;
/* Copy STUN session */
pj_memcpy(&sess->stun_cfg, cfg, sizeof(pj_stun_config));
/* Copy callback */
pj_memcpy(&sess->cb, cb, sizeof(*cb));
@ -233,8 +237,8 @@ PJ_DEF(pj_status_t) pj_turn_session_create( pj_stun_config *cfg,
stun_cb.on_send_msg = &stun_on_send_msg;
stun_cb.on_request_complete = &stun_on_request_complete;
stun_cb.on_rx_indication = &stun_on_rx_indication;
status = pj_stun_session_create(cfg, sess->obj_name, &stun_cb, PJ_FALSE,
&sess->stun);
status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb,
PJ_FALSE, &sess->stun);
if (status != PJ_SUCCESS) {
do_destroy(sess);
return status;
@ -849,7 +853,7 @@ on_return:
* The packet maybe a STUN packet or ChannelData packet.
*/
PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
const pj_uint8_t *pkt,
void *pkt,
unsigned pkt_len,
pj_bool_t is_datagram)
{
@ -864,13 +868,13 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
pj_lock_acquire(sess->lock);
/* Quickly check if this is STUN message */
is_stun = ((pkt[0] & 0xC0) == 0);
is_stun = ((((pj_uint8_t*)pkt)[0] & 0xC0) == 0);
if (is_stun) {
/* This looks like STUN, give it to the STUN session */
unsigned options;
options = PJ_STUN_CHECK_PACKET;
options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
if (is_datagram)
options |= PJ_STUN_IS_DATAGRAM;
status=pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len,
@ -905,8 +909,8 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
}
/* Notify application */
(*sess->cb.on_rx_data)(sess, pkt+sizeof(cd), cd.length,
&peer->addr,
(*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd),
cd.length, &peer->addr,
pj_sockaddr_get_len(&peer->addr));
status = PJ_SUCCESS;

View File

@ -71,7 +71,7 @@ static void turn_on_channel_bound(pj_turn_session *sess,
unsigned addr_len,
unsigned ch_num);
static void turn_on_rx_data(pj_turn_session *sess,
const pj_uint8_t *pkt,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *peer_addr,
unsigned addr_len);
@ -309,6 +309,24 @@ PJ_DEF(pj_status_t) pj_turn_sock_get_info(pj_turn_sock *turn_sock,
}
}
/**
* Lock the TURN socket. Application may need to call this function to
* synchronize access to other objects to avoid deadlock.
*/
PJ_DEF(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock)
{
return pj_lock_acquire(turn_sock->lock);
}
/**
* Unlock the TURN socket.
*/
PJ_DEF(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock)
{
return pj_lock_release(turn_sock->lock);
}
/*
* Initialize.
*/
@ -524,7 +542,7 @@ static void turn_on_channel_bound(pj_turn_session *sess,
* Callback from TURN session upon incoming data.
*/
static void turn_on_rx_data(pj_turn_session *sess,
const pj_uint8_t *pkt,
void *pkt,
unsigned pkt_len,
const pj_sockaddr_t *peer_addr,
unsigned addr_len)

View File

@ -35,8 +35,7 @@ static struct cred_t
{
{ "100", "100" },
{ "700", "700" },
{ "701", "701" },
{ "702", "702" }
{ "701", "701" }
};
#define THE_NONCE "pjnath"

View File

@ -22,6 +22,25 @@
#define THIS_FILE "pjsua_app.c"
#define NO_LIMIT (int)0x7FFFFFFF
#if 0
#define TURN_SERVER "turn.pjsip.org"
#define TURN_PORT 34780
#define TURN_REALM "pjsip.org"
#define TURN_USER "700"
#define TURN_PASSWD "700"
#endif
#if 1
/* Eyeball test */
#define TURN_SERVER "216.187.87.78"
#define TURN_PORT 3478
#define TURN_REALM "test.eyeball.com"
#define TURN_USER "sipit6"
#define TURN_PASSWD "password"
#endif
//#define STEREO_DEMO
/* Call specific data */
@ -55,6 +74,8 @@ static struct app_config
pj_pool_t *pool;
/* Compatibility with older pjsua */
pj_bool_t use_turn;
unsigned codec_cnt;
pj_str_t codec_arg[32];
unsigned codec_dis_cnt;
@ -171,6 +192,10 @@ static void usage(void)
puts ("");
puts ("Media Options:");
puts (" --use-ice Enable ICE (default:no)");
puts (" --use-turn Enable experimantal TURN (default:no)");
puts (" --ice-no-host Disable ICE host candidates");
puts (" --ice-no-srflx Disable ICE srflx candidates");
puts (" --ice-no-rtcp Disable RTCP in ICE");
puts (" --add-codec=name Manually add codec (default is to enable all)");
puts (" --dis-codec=name Disable codec (can be specified multiple times)");
puts (" --clock-rate=N Override conference bridge clock rate");
@ -223,11 +248,11 @@ static void usage(void)
/* Set default config. */
static void default_config(struct app_config *cfg)
{
char tmp[80];
char tmp[120];
unsigned i;
pjsua_config_default(&cfg->cfg);
pj_ansi_sprintf(tmp, "PJSUA v%s/%s", pj_get_version(), PJ_OS_NAME);
pj_ansi_sprintf(tmp, "PJSUA v%s/%s (http://pjsip.org)", pj_get_version(), PJ_OS_NAME);
pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
pjsua_logging_config_default(&cfg->log_cfg);
@ -391,7 +416,8 @@ static pj_status_t parse_args(int argc, char *argv[],
OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE,
OPT_USE_ICE, OPT_USE_TURN, OPT_ICE_NO_HOST, OPT_ICE_NO_SRFLX,
OPT_ICE_NO_RTCP, OPT_USE_SRTP, OPT_SRTP_SECURE,
OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
@ -452,6 +478,10 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "rec-file", 1, 0, OPT_REC_FILE},
{ "rtp-port", 1, 0, OPT_RTP_PORT},
{ "use-ice", 0, 0, OPT_USE_ICE},
{ "use-turn", 0, 0, OPT_USE_TURN},
{ "ice-no-host",0, 0, OPT_ICE_NO_HOST},
{ "ice-no-srflx",0,0, OPT_ICE_NO_SRFLX},
{ "ice-no-rtcp",0, 0, OPT_ICE_NO_RTCP},
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
{ "use-srtp", 1, 0, OPT_USE_SRTP},
{ "srtp-secure",1, 0, OPT_SRTP_SECURE},
@ -825,6 +855,22 @@ static pj_status_t parse_args(int argc, char *argv[],
cfg->media_cfg.enable_ice = PJ_TRUE;
break;
case OPT_USE_TURN:
cfg->use_turn = PJ_TRUE;
break;
case OPT_ICE_NO_HOST:
cfg->media_cfg.ice_options |= PJ_ICE_ST_OPT_DONT_ADD_CAND;
break;
case OPT_ICE_NO_SRFLX:
cfg->media_cfg.ice_options |= PJ_ICE_ST_OPT_DISABLE_STUN;
break;
case OPT_ICE_NO_RTCP:
cfg->media_cfg.ice_no_rtcp = PJ_TRUE;
break;
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
case OPT_USE_SRTP:
app_config.cfg.use_srtp = my_atoi(pj_optarg);
@ -3466,6 +3512,20 @@ pj_status_t app_init(int argc, char *argv[])
app_config.cfg.cb.on_call_replaced = &on_call_replaced;
app_config.cfg.cb.on_nat_detect = &on_nat_detect;
/* Init TURN settings */
#ifdef TURN_SERVER
if (app_config.use_turn) {
app_config.cfg.turn_host = pj_str(TURN_SERVER);
app_config.cfg.turn_port = TURN_PORT;
app_config.cfg.turn_tcp = 0;
app_config.cfg.turn_cred.type = PJ_STUN_AUTH_CRED_STATIC;
app_config.cfg.turn_cred.data.static_cred.realm = pj_str(TURN_REALM);
app_config.cfg.turn_cred.data.static_cred.username = pj_str(TURN_USER);
app_config.cfg.turn_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
app_config.cfg.turn_cred.data.static_cred.data = pj_str(TURN_PASSWD);
}
#endif
/* Initialize pjsua */
status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
&app_config.media_cfg);

View File

@ -1043,9 +1043,24 @@ typedef struct pjsua_config
pj_str_t stun_host;
/**
* Specify STUN relay server to be used.
* Specify TURN server to be used.
*/
pj_str_t stun_relay_host;
pj_str_t turn_host;
/**
* Specify TURN server port number.
*/
pj_uint16_t turn_port;
/**
* Specify if TCP connection to TURN server should be used.
*/
pj_bool_t turn_tcp;
/**
* Specify STUN credential for the TURN connection.
*/
pj_stun_auth_cred turn_cred;
/**
* Support for adding and parsing NAT type in the SDP to assist
@ -3886,9 +3901,14 @@ struct pjsua_media_config
pj_bool_t enable_ice;
/**
* Enable ICE media relay.
* ICE options.
*/
pj_bool_t enable_relay;
unsigned ice_options;
/**
* Disable RTCP in ICE.
*/
pj_bool_t ice_no_rtcp;
};

View File

@ -565,6 +565,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
if (acc->regc != NULL) {
pjsip_regc_destroy(acc->regc);
acc->regc = NULL;
acc->contact.slen = 0;
}
/* Update account's Contact header */
@ -853,6 +854,7 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
param->status);
pjsip_regc_destroy(acc->regc);
acc->regc = NULL;
acc->contact.slen = 0;
/* Stop keep-alive timer if any. */
update_keep_alive(acc, PJ_FALSE, NULL);
@ -863,6 +865,7 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
(int)param->reason.slen, param->reason.ptr));
pjsip_regc_destroy(acc->regc);
acc->regc = NULL;
acc->contact.slen = 0;
/* Stop keep-alive timer if any. */
update_keep_alive(acc, PJ_FALSE, NULL);
@ -872,6 +875,7 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
if (param->expiration < 1) {
pjsip_regc_destroy(acc->regc);
acc->regc = NULL;
acc->contact.slen = 0;
/* Stop keep-alive timer if any. */
update_keep_alive(acc, PJ_FALSE, NULL);
@ -940,6 +944,7 @@ static pj_status_t pjsua_regc_init(int acc_id)
if (acc->regc) {
pjsip_regc_destroy(acc->regc);
acc->regc = NULL;
acc->contact.slen = 0;
}
/* initialize SIP registration if registrar is configured */
@ -986,6 +991,7 @@ static pj_status_t pjsua_regc_init(int acc_id)
pjsip_regc_destroy(acc->regc);
pj_pool_release(pool);
acc->regc = NULL;
acc->contact.slen = 0;
return status;
}

View File

@ -405,13 +405,20 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
/* Reset first response time */
call->res_time.sec = 0;
/* Create suitable Contact header */
status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
acc_id, dest_uri);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
PJSUA_UNLOCK();
return status;
/* Create suitable Contact header unless a Contact header has been
* set in the account.
*/
if (acc->contact.slen) {
contact = acc->contact;
} else {
status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
acc_id, dest_uri);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header",
status);
PJSUA_UNLOCK();
return status;
}
}
/* Create outgoing dialog: */
@ -787,15 +794,20 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
/* Get suitable Contact header */
status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
acc_id, rdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
pjsua_media_channel_deinit(call->index);
PJSUA_UNLOCK();
return PJ_TRUE;
if (pjsua_var.acc[acc_id].contact.slen) {
contact = pjsua_var.acc[acc_id].contact;
} else {
status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
acc_id, rdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header",
status);
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
pjsua_media_channel_deinit(call->index);
PJSUA_UNLOCK();
return PJ_TRUE;
}
}
/* Create dialog: */

View File

@ -113,7 +113,8 @@ PJ_DEF(void) pjsua_config_dup(pj_pool_t *pool,
pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
pj_strdup_with_null(pool, &dst->stun_relay_host, &src->stun_relay_host);
pj_strdup_with_null(pool, &dst->turn_host, &src->turn_host);
pj_stun_auth_cred_dup(pool, &dst->turn_cred, &src->turn_cred);
}
PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)

View File

@ -624,6 +624,7 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
{
unsigned i;
pj_sockaddr_in addr;
pj_ice_strans_cfg ice_cfg;
pj_status_t status;
/* Make sure STUN server resolution has completed */
@ -635,25 +636,65 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
pj_sockaddr_in_init(&addr, 0, (pj_uint16_t)cfg->port);
/* Init ICE config */
pj_bzero(&ice_cfg, sizeof(ice_cfg));
/* Duplicate STUN config */
pj_memcpy(&ice_cfg.stun_cfg, &pjsua_var.stun_cfg, sizeof(pj_stun_config));
/* Set STUN server, if any */
if (pj_sockaddr_has_addr(&pjsua_var.stun_srv))
pj_sockaddr_cp(&ice_cfg.stun_srv, &pjsua_var.stun_srv);
if (pjsua_var.ua_cfg.turn_host.slen) {
/* Set TURN server.
* TODO: DNS SRV
*/
status = pj_sockaddr_in_init(&ice_cfg.turn_srv.ipv4,
&pjsua_var.ua_cfg.turn_host,
pjsua_var.ua_cfg.turn_port);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error resolving TURN server", status);
return status;
}
/* Copy TURN credential */
pj_memcpy(&ice_cfg.turn_cred, &pjsua_var.ua_cfg.turn_cred,
sizeof(pjsua_var.ua_cfg.turn_cred));
/* TURN connection type. */
if (pjsua_var.ua_cfg.turn_tcp)
ice_cfg.turn_conn_type = PJ_TURN_TP_TCP;
else
ice_cfg.turn_conn_type = PJ_TURN_TP_UDP;
}
/* Create each media transport */
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
pj_ice_strans_comp comp;
pjmedia_ice_cb ice_cb;
int next_port;
char name[32];
#if PJMEDIA_ADVERTISE_RTCP
enum { COMP_CNT=2 };
unsigned options, comp_cnt;
#if PJMEDIA_ADVERTISE_RTCP==0
comp_cnt = 1;
#else
enum { COMP_CNT=1 };
if (pjsua_var.media_cfg.ice_no_rtcp)
comp_cnt = 1;
else
comp_cnt = 2;
#endif
options = pjsua_var.media_cfg.ice_options;
pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
ice_cb.on_ice_complete = &on_ice_complete;
pj_ansi_snprintf(name, sizeof(name), "icetp%02d", i);
status = pjmedia_ice_create(pjsua_var.med_endpt, name, COMP_CNT,
&pjsua_var.stun_cfg, &ice_cb,
status = pjmedia_ice_create(pjsua_var.med_endpt, name, comp_cnt,
&ice_cfg, &ice_cb,
&pjsua_var.calls[i].med_tp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
@ -669,8 +710,8 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
PJMEDIA_DIR_DECODING,
pjsua_var.media_cfg.rx_drop_pct);
status = pjmedia_ice_start_init(pjsua_var.calls[i].med_tp, 0, &addr,
&pjsua_var.stun_srv.ipv4, NULL);
status = pjmedia_ice_start_init(pjsua_var.calls[i].med_tp, options,
&addr);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error starting ICE transport",
status);

View File

@ -552,12 +552,17 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
acc_id));
/* Create suitable Contact header */
status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
acc_id, rdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
PJSUA_UNLOCK();
return PJ_TRUE;
if (acc->contact.slen) {
contact = acc->contact;
} else {
status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
acc_id, rdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header",
status);
PJSUA_UNLOCK();
return PJ_TRUE;
}
}
/* Create UAS dialog: */
@ -1123,12 +1128,19 @@ static void subscribe_buddy_presence(unsigned index)
PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
acc_id, index));
/* Generate suitable Contact header */
status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
acc_id, &buddy->uri);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
return;
/* Generate suitable Contact header unless one is already set in
* the account
*/
if (acc->contact.slen) {
contact = acc->contact;
} else {
status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact,
acc_id, &buddy->uri);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header",
status);
return;
}
}
/* Create UAC dialog */