Bug 886120 - Make ICE respond before receiving peer credentials r=abr

This commit is contained in:
EKR 2013-07-09 20:17:37 -07:00
parent 64a655c353
commit 7fa772890f
13 changed files with 446 additions and 95 deletions

View File

@ -280,17 +280,17 @@ class IceTestPeer : public sigslot::has_slots<> {
}
void GotCandidate(NrIceMediaStream *stream, const std::string &candidate) {
std::cout << "Got candidate " << candidate << std::endl;
std::cerr << "Got candidate " << candidate << std::endl;
candidates_[stream->name()].push_back(candidate);
}
void StreamReady(NrIceMediaStream *stream) {
std::cout << "Stream ready " << stream->name() << std::endl;
std::cerr << "Stream ready " << stream->name() << std::endl;
++ready_ct_;
}
void IceCompleted(NrIceCtx *ctx) {
std::cout << "ICE completed " << name_ << std::endl;
std::cerr << "ICE completed " << name_ << std::endl;
ice_complete_ = true;
}
@ -413,6 +413,21 @@ class IceConnectTest : public ::testing::Test {
ASSERT_TRUE_WAIT(p1_->ice_complete() && p2_->ice_complete(), 5000);
}
void ConnectP1(TrickleMode mode = TRICKLE_NONE) {
p1_->Connect(p2_, mode);
}
void ConnectP2(TrickleMode mode = TRICKLE_NONE) {
p2_->Connect(p1_, mode);
}
void WaitForComplete(int expected_streams = 1) {
ASSERT_TRUE_WAIT(p1_->ready_ct() == expected_streams &&
p2_->ready_ct() == expected_streams, 5000);
ASSERT_TRUE_WAIT(p1_->ice_complete() && p2_->ice_complete(), 5000);
}
void ConnectTrickle() {
p1_->Connect(p2_, TRICKLE_DEFERRED);
p2_->Connect(p1_, TRICKLE_DEFERRED);
@ -425,6 +440,14 @@ class IceConnectTest : public ::testing::Test {
ASSERT_TRUE_WAIT(p2_->is_ready(stream), 5000);
}
void DoTrickleP1(size_t stream) {
p1_->DoTrickle(stream);
}
void DoTrickleP2(size_t stream) {
p2_->DoTrickle(stream);
}
void VerifyConnected() {
}
@ -539,6 +562,40 @@ TEST_F(IceConnectTest, TestConnect) {
Connect();
}
TEST_F(IceConnectTest, TestConnectP2ThenP1) {
AddStream("first", 1);
ASSERT_TRUE(Gather(true));
ConnectP2();
PR_Sleep(1000);
ConnectP1();
WaitForComplete();
}
TEST_F(IceConnectTest, TestConnectP2ThenP1Trickle) {
AddStream("first", 1);
ASSERT_TRUE(Gather(true));
ConnectP2();
PR_Sleep(1000);
ConnectP1(TRICKLE_DEFERRED);
DoTrickleP1(0);
WaitForComplete();
}
TEST_F(IceConnectTest, TestConnectP2ThenP1TrickleTwoComponents) {
AddStream("first", 1);
AddStream("second", 2);
ASSERT_TRUE(Gather(true));
ConnectP2();
PR_Sleep(1000);
ConnectP1(TRICKLE_DEFERRED);
DoTrickleP1(0);
std::cerr << "Sleeping between trickle streams" << std::endl;
PR_Sleep(1000); // Give this some time to settle but not complete
// all of ICE.
DoTrickleP1(1);
WaitForComplete(2);
}
TEST_F(IceConnectTest, TestConnectAutoPrioritize) {
Init(false);
AddStream("first", 1);

View File

@ -53,9 +53,6 @@ int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,
{
nr_ice_cand_pair *pair=0;
UINT8 o_priority, a_priority;
char *lufrag,*rufrag;
char *lpwd,*rpwd;
char *l2ruser=0,*r2lpass=0;
int r,_status;
UINT4 RTO;
nr_ice_candidate tmpcand;
@ -98,17 +95,6 @@ int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,
rcand->foundation,NULL))
ABORT(r);
/* OK, now the STUN data */
lufrag=lcand->stream->ufrag?lcand->stream->ufrag:pctx->ctx->ufrag;
lpwd=lcand->stream->pwd?lcand->stream->pwd:pctx->ctx->pwd;
assert(lufrag);
assert(lpwd);
rufrag=rcand->stream->ufrag?rcand->stream->ufrag:pctx->peer_ufrag;
rpwd=rcand->stream->pwd?rcand->stream->pwd:pctx->peer_pwd;
if (!rufrag || !rpwd)
ABORT(R_BAD_DATA);
/* Compute the RTO per S 16 */
RTO = MAX(100, (pctx->ctx->Ta * pctx->waiting_pairs));
@ -121,38 +107,27 @@ int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,
t_priority = tmpcand.priority;
/* Our sending context */
if(r=nr_concat_strings(&l2ruser,rufrag,":",lufrag,NULL))
ABORT(r);
if(r=nr_stun_client_ctx_create(pair->as_string,
lcand->osock,
&rcand->addr,RTO,&pair->stun_client))
ABORT(r);
if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(l2ruser)))
if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(rcand->stream->l2r_user)))
ABORT(R_NO_MEMORY);
if(r=r_data_make(&pair->stun_client->params.ice_binding_request.password,(UCHAR *)rpwd,strlen(rpwd)))
if(r=r_data_copy(&pair->stun_client->params.ice_binding_request.password,
&rcand->stream->l2r_pass))
ABORT(r);
pair->stun_client->params.ice_binding_request.priority=t_priority;
/* TODO(ekr@rtfm.com): Do we need to frob this when we change role. Bug 890667 */
pair->stun_client->params.ice_binding_request.control = pctx->controlling?
NR_ICE_CONTROLLING:NR_ICE_CONTROLLED;
pair->stun_client->params.ice_use_candidate.priority=t_priority;
pair->stun_client->params.ice_binding_request.tiebreaker=pctx->tiebreaker;
/* Our receiving username/passwords. Stash these for later
injection into the stun server ctx*/
if(r=nr_concat_strings(&pair->r2l_user,lufrag,":",rufrag,NULL))
ABORT(r);
if(!(r2lpass=r_strdup(lpwd)))
ABORT(R_NO_MEMORY);
INIT_DATA(pair->r2l_pwd,(UCHAR *)r2lpass,strlen(r2lpass));
// Give up ownership of r2lpass
r2lpass=0;
*pairp=pair;
_status=0;
abort:
RFREE(l2ruser);
RFREE(r2lpass);
if(_status){
nr_ice_candidate_pair_destroy(&pair);
}
@ -178,9 +153,6 @@ int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp)
nr_stun_client_ctx_destroy(&pair->stun_client);
}
RFREE(pair->r2l_user);
RFREE(pair->r2l_pwd.data);
NR_async_timer_cancel(pair->stun_cb_timer);
NR_async_timer_cancel(pair->restart_controlled_cb_timer);
NR_async_timer_cancel(pair->restart_nominated_cb_timer);
@ -559,7 +531,6 @@ void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s):%d: Restarting pair %s as nominated",pair->pctx->label,pair->local->stream->label,pair->remote->component->component_id,pair->as_string);
nr_stun_client_reset(pair->stun_client);
pair->stun_client->params.ice_binding_request.control=NR_ICE_CONTROLLING;
if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair))
ABORT(r);

View File

@ -61,9 +61,6 @@ struct nr_ice_cand_pair_ {
nr_ice_candidate *remote; /* The remote candidate */
char *foundation; /* The combined foundations */
char *r2l_user; /* Stashed username */
Data r2l_pwd; /* Stashed password */
nr_stun_client_ctx *stun_client; /* STUN context when acting as a client */
void *stun_client_handle;

View File

@ -46,6 +46,59 @@ static char *RCSSTRING __UNUSED__="$Id: ice_component.c,v 1.2 2008/04/28 17:59:0
#include "nr_socket_turn.h"
#include "ice_reg.h"
static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error);
static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp);
/* This function takes ownership of the contents of req (but not req itself) */
static int nr_ice_pre_answer_request_create(nr_socket *sock, nr_stun_server_request *req, nr_ice_pre_answer_request **parp)
{
int r, _status;
nr_ice_pre_answer_request *par = 0;
nr_stun_message_attribute *attr;
if (!(par = RCALLOC(sizeof(nr_ice_pre_answer_request))))
ABORT(R_NO_MEMORY);
par->req = *req; /* Struct assignment */
memset(req, 0, sizeof(*req)); /* Zero contents to avoid confusion */
if (r=nr_socket_getaddr(sock, &par->local_addr))
ABORT(r);
if (!nr_stun_message_has_attribute(par->req.request, NR_STUN_ATTR_USERNAME, &attr))
ABORT(R_INTERNAL);
if (!(par->username = r_strdup(attr->u.username)))
ABORT(R_NO_MEMORY);
*parp=par;
_status=0;
abort:
if (_status) {
/* Erase the request so we don't free it */
memset(&par->req, 0, sizeof(nr_stun_server_request));
nr_ice_pre_answer_request_destroy(&par);
}
return(_status);
}
static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp)
{
nr_ice_pre_answer_request *par;
if (!parp || !*parp)
return(0);
par = *parp;
*parp = 0;
nr_stun_message_destroy(&par->req.request);
nr_stun_message_destroy(&par->req.response);
RFREE(par->username);
return(0);
}
int nr_ice_component_create(nr_ice_media_stream *stream, int component_id, nr_ice_component **componentp)
{
int _status;
@ -54,13 +107,15 @@ int nr_ice_component_create(nr_ice_media_stream *stream, int component_id, nr_ic
if(!(comp=RCALLOC(sizeof(nr_ice_component))))
ABORT(R_NO_MEMORY);
comp->state=NR_ICE_COMPONENT_RUNNING;
comp->state=NR_ICE_COMPONENT_UNPAIRED;
comp->component_id=component_id;
comp->stream=stream;
comp->ctx=stream->ctx;
STAILQ_INIT(&comp->sockets);
TAILQ_INIT(&comp->candidates);
STAILQ_INIT(&comp->pre_answer_reqs);
STAILQ_INSERT_TAIL(&stream->components,comp,entry);
_status=0;
@ -73,6 +128,7 @@ int nr_ice_component_destroy(nr_ice_component **componentp)
nr_ice_component *component;
nr_ice_socket *s1,*s2;
nr_ice_candidate *c1,*c2;
nr_ice_pre_answer_request *r1,*r2;
if(!componentp || !*componentp)
return(0);
@ -89,7 +145,6 @@ int nr_ice_component_destroy(nr_ice_component **componentp)
}
}
/* candidates MUST be destroyed before the sockets so that
they can deregister */
TAILQ_FOREACH_SAFE(c1, &component->candidates, entry_comp, c2){
@ -102,6 +157,11 @@ int nr_ice_component_destroy(nr_ice_component **componentp)
nr_ice_socket_destroy(&s1);
}
STAILQ_FOREACH_SAFE(r1, &component->pre_answer_reqs, entry, r2){
STAILQ_REMOVE(&component->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry);
nr_ice_pre_answer_request_destroy(&r1);
}
if(component->keepalive_timer)
NR_async_timer_cancel(component->keepalive_timer);
nr_stun_client_ctx_destroy(&component->keepalive_ctx);
@ -119,6 +179,9 @@ int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *compon
nr_socket *sock;
nr_ice_socket *isock=0;
nr_ice_candidate *cand=0;
char *lufrag;
char *lpwd;
Data pwd;
int addr_ct;
int i;
int j;
@ -221,6 +284,22 @@ int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *compon
ABORT(r);
if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle))
ABORT(r);
/* Add the default STUN credentials so that we can respond before
we hear about the peer. Note: we need to recompute these because
we have not yet computed the values in the peer media stream.*/
lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag;
assert(lufrag);
if (!lufrag)
ABORT(R_INTERNAL);
lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd;
assert(lpwd);
if (!lpwd)
ABORT(R_INTERNAL);
INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd));
if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, &pwd, nr_ice_component_stun_server_default_cb, component))
ABORT(r);
STAILQ_INSERT_TAIL(&component->sockets,isock,entry);
}
@ -329,12 +408,10 @@ int nr_ice_component_prune_candidates(nr_ice_ctx *ctx, nr_ice_component *comp)
}
/* Section 7.2.1 */
static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *error)
static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_transport_addr *local_addr, nr_stun_server_request *req, int *error)
{
nr_ice_component *comp=cb_arg;
nr_ice_cand_pair *pair;
nr_ice_candidate *pcand=0;
nr_transport_addr local_addr;
nr_stun_message *sreq=req->request;
nr_stun_message_attribute *attr;
int component_id_matched;
@ -343,8 +420,6 @@ static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun
nr_ice_cand_pair *found_invalid=0;
int r=0,_status;
assert(sock!=0);
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)(%d): received request from %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,req->src_addr.as_string);
/* Check for role conficts (7.2.1.1) */
@ -389,13 +464,7 @@ static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun
}
}
/* Find the candidate pair that this maps to */
if(r=nr_socket_getaddr(sock,&local_addr)) {
*error=500;
ABORT(r);
}
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): This STUN request appears to map to local addr %s",comp->stream->pctx->label,local_addr.as_string);
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): This STUN request appears to map to local addr %s",comp->stream->pctx->label,local_addr->as_string);
pair=TAILQ_FIRST(&comp->stream->check_list);
while(pair){
@ -407,7 +476,7 @@ static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun
goto next_pair;
component_id_matched = 1;
if(nr_transport_addr_cmp(&pair->local->base,&local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
if(nr_transport_addr_cmp(&pair->local->base,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
goto next_pair;
local_addr_matched=1;
@ -438,7 +507,7 @@ static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no matching pair",comp->stream->pctx->label);
cand=TAILQ_FIRST(&comp->local_component->candidates);
while(cand){
if(!nr_transport_addr_cmp(&cand->addr,&local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
if(!nr_transport_addr_cmp(&cand->addr,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
break;
cand=TAILQ_NEXT(cand,entry_comp);
@ -447,7 +516,7 @@ static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun
/* Well, this really shouldn't happen, but it's an error from the
other side, so we just throw an error and keep going */
if(!cand){
r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): stun request to unknown local address %s, discarding",comp->stream->pctx->label,local_addr.as_string);
r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): stun request to unknown local address %s, discarding",comp->stream->pctx->label,local_addr->as_string);
*error=400;
ABORT(R_NOT_FOUND);
@ -536,6 +605,57 @@ static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun
return(_status);
}
static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error)
{
nr_ice_component *comp=cb_arg;
nr_transport_addr local_addr;
int r,_status;
/* Find the candidate pair that this maps to */
if(r=nr_socket_getaddr(sock,&local_addr)) {
*error=500;
ABORT(r);
}
if (r=nr_ice_component_process_incoming_check(comp, &local_addr, req, error))
ABORT(r);
_status=0;
abort:
return(_status);
}
int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced)
{
nr_ice_pre_answer_request *r1,*r2;
nr_ice_component *comp = pcomp->local_component;
int r,_status;
if (serviced)
*serviced = 0;
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): looking for pre-answer requests",pctx->label,comp->stream->label,comp->component_id);
STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) {
if (!strcmp(r1->username, username)) {
int error = 0;
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): found pre-answer request",pctx->label,comp->stream->label,comp->component_id);
r = nr_ice_component_process_incoming_check(pcomp, &r1->local_addr, &r1->req, &error);
if (r) {
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/comp(%d): error processing pre-answer request. Would have returned %d",pctx->label,comp->stream->label,comp->component_id, error);
}
(*serviced)++;
STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry);
nr_ice_pre_answer_request_destroy(&r1);
}
}
_status=0;
abort:
return(_status);
}
int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp)
{
nr_ice_candidate *lcand,*pcand;
@ -603,21 +723,6 @@ int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lc
if(!pair)
goto next_cand;
if (!was_paired) {
/* Add the stun username/password pair from the last pair (any
would do) to the stun contexts, but only if we haven't already
done so (was_paired) */
isock=STAILQ_FIRST(&lcomp->sockets);
while(isock){
if(r=nr_stun_server_add_client(isock->stun_server,pctx->label,
pair->r2l_user,&pair->r2l_pwd,nr_ice_component_stun_server_cb,pcomp)) {
ABORT(r);
}
isock=STAILQ_NEXT(isock,entry);
}
}
next_cand:
lcand=TAILQ_NEXT(lcand,entry_comp);
}
@ -628,13 +733,55 @@ int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lc
pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED;
pcand=TAILQ_NEXT(pcand,entry_comp);
}
/* Now register the STUN server callback for this component.
Note that this is a per-component CB so we only need to
do this once.
*/
if (pcomp->state != NR_ICE_COMPONENT_RUNNING) {
isock=STAILQ_FIRST(&lcomp->sockets);
while(isock){
if(r=nr_stun_server_add_client(isock->stun_server,pctx->label,
pcomp->stream->r2l_user,&pcomp->stream->r2l_pass,nr_ice_component_stun_server_cb,pcomp)) {
ABORT(r);
}
isock=STAILQ_NEXT(isock,entry);
}
}
pcomp->state = NR_ICE_COMPONENT_RUNNING;
_status=0;
abort:
return(_status);
}
/* Fires when we have an incoming candidate that doesn't correspond to an existing
remote peer. This is either pre-answer or just spurious. Store it in the
component for use when we see the actual answer, at which point we need
to do the procedures from S 7.2.1 in nr_ice_component_stun_server_cb.
*/
static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error)
{
int r, _status;
nr_ice_component *comp = (nr_ice_component *)cb_arg;
nr_ice_pre_answer_request *par = 0;
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/comp(%d): Received STUN request pre-answer from %s",
comp->ctx->label, comp->stream->label, comp->component_id, req->src_addr.as_string);
if (r=nr_ice_pre_answer_request_create(sock, req, &par))
ABORT(r);
*dont_free = 1;
STAILQ_INSERT_TAIL(&comp->pre_answer_reqs, par, entry);
_status=0;
abort:
return 0;
}
int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair)
{
int r,_status;

View File

@ -39,8 +39,19 @@ using namespace std;
extern "C" {
#endif /* __cplusplus */
typedef struct nr_ice_pre_answer_request_ {
nr_stun_server_request req;
char *username;
nr_transport_addr local_addr;
STAILQ_ENTRY(nr_ice_pre_answer_request_) entry;
} nr_ice_pre_answer_request;
typedef STAILQ_HEAD(nr_ice_pre_answer_request_head_, nr_ice_pre_answer_request_) nr_ice_pre_answer_request_head;
struct nr_ice_component_ {
int state;
#define NR_ICE_COMPONENT_UNPAIRED 0
#define NR_ICE_COMPONENT_RUNNING 1
#define NR_ICE_COMPONENT_NOMINATED 2
#define NR_ICE_COMPONENT_FAILED 3
@ -52,6 +63,7 @@ struct nr_ice_component_ {
nr_ice_socket_head sockets;
nr_ice_candidate_head candidates;
int candidate_ct;
nr_ice_pre_answer_request_head pre_answer_reqs;
int valid_pairs;
struct nr_ice_cand_pair_ *nominated; /* Highest priority nomninated pair */
@ -71,6 +83,7 @@ int nr_ice_component_destroy(nr_ice_component **componentp);
int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component);
int nr_ice_component_prune_candidates(nr_ice_ctx *ctx, nr_ice_component *comp);
int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp);
int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced);
int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);
int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);
int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp);

View File

@ -39,6 +39,7 @@ static char *RCSSTRING __UNUSED__="$Id: ice_media_stream.c,v 1.2 2008/04/28 17:5
#include <nr_api.h>
#include <r_assoc.h>
#include <async_timer.h>
#include "ice_util.h"
#include "ice_ctx.h"
static char *nr_ice_media_stream_states[]={"INVALID",
@ -109,6 +110,10 @@ int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp)
RFREE(stream->ufrag);
RFREE(stream->pwd);
RFREE(stream->r2l_user);
RFREE(stream->l2r_user);
r_data_zfree(&stream->r2l_pass);
r_data_zfree(&stream->l2r_pass);
if(stream->timer)
NR_async_timer_cancel(stream->timer);
@ -277,8 +282,7 @@ int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_strea
};
if (pstream->ice_state == NR_ICE_MEDIA_STREAM_UNPAIRED) {
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): unfreezing stream %s",pstream->pctx->label,pstream->label);
pstream->ice_state = NR_ICE_MEDIA_STREAM_CHECKS_FROZEN;
nr_ice_media_stream_set_state(pstream, NR_ICE_MEDIA_STREAM_CHECKS_FROZEN);
}
_status=0;
@ -286,6 +290,35 @@ int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_strea
return(_status);
}
int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, nr_ice_media_stream *pstream, int *serviced)
{
nr_ice_component *pcomp;
int r,_status;
char *user = 0;
char *lufrag, *rufrag;
if (serviced)
*serviced = 0;
pcomp=STAILQ_FIRST(&pstream->components);
while(pcomp){
int serviced_inner=0;
/* Flush all the pre-answer requests */
if(r=nr_ice_component_service_pre_answer_requests(pctx, pcomp, pstream->r2l_user, &serviced_inner))
ABORT(r);
if (serviced)
*serviced += serviced_inner;
pcomp=STAILQ_NEXT(pcomp,entry);
}
_status=0;
abort:
RFREE(user);
return(_status);
}
/* S 5.8 -- run the highest priority WAITING pair or if not available
FROZEN pair */
static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg)

View File

@ -50,7 +50,10 @@ struct nr_ice_media_stream_ {
char *ufrag; /* ICE username */
char *pwd; /* ICE password */
char *r2l_user; /* The username for incoming requests */
char *l2r_user; /* The username for outgoing requests */
Data r2l_pass; /* The password for incoming requests */
Data l2r_pass; /* The password for outcoming requests */
int ice_state;
#define NR_ICE_MEDIA_STREAM_UNPAIRED 1
@ -77,6 +80,7 @@ int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attr
int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp);
int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream);
int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream, int *serviced);
int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation);
int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out);

View File

@ -39,6 +39,8 @@ static char *RCSSTRING __UNUSED__="$Id: ice_peer_ctx.c,v 1.2 2008/04/28 17:59:01
#include <nr_api.h>
#include "ice_ctx.h"
#include "ice_peer_ctx.h"
#include "ice_media_stream.h"
#include "ice_util.h"
#include "nr_crypto.h"
#include "async_timer.h"
@ -98,6 +100,8 @@ int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_
{
nr_ice_media_stream *pstream=0;
nr_ice_component *comp,*comp2;
char *lufrag,*rufrag;
char *lpwd,*rpwd;
int r,_status;
/*
@ -122,6 +126,25 @@ int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_
if (r=nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct))
ABORT(r);
/* Now that we have the ufrag and password, compute all the username/password
pairs */
lufrag=stream->ufrag?stream->ufrag:pctx->ctx->ufrag;
lpwd=stream->pwd?stream->pwd:pctx->ctx->pwd;
assert(lufrag);
assert(lpwd);
rufrag=pstream->ufrag?pstream->ufrag:pctx->peer_ufrag;
rpwd=pstream->pwd?pstream->pwd:pctx->peer_pwd;
if (!rufrag || !rpwd)
ABORT(R_BAD_DATA);
if(r=nr_concat_strings(&pstream->r2l_user,lufrag,":",rufrag,NULL))
ABORT(r);
if(r=nr_concat_strings(&pstream->l2r_user,rufrag,":",lufrag,NULL))
ABORT(r);
if(r=r_data_make(&pstream->r2l_pass, (UCHAR *)lpwd, strlen(lpwd)))
ABORT(r);
if(r=r_data_make(&pstream->l2r_pass, (UCHAR *)rpwd, strlen(rpwd)))
ABORT(r);
STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry);
@ -207,6 +230,8 @@ int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_
int r,_status;
int needs_pairing = 0;
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) parsing trickle ICE candidate %s",pctx->ctx->label,pctx->label,candidate);
pstream=STAILQ_FIRST(&pctx->peer_streams);
while(pstream) {
if (pstream->local_stream == stream)
@ -279,6 +304,9 @@ int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx)
nr_ice_media_stream *stream;
int r,_status;
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) pairing candidates",pctx->ctx->label,pctx->label);
if(STAILQ_EMPTY(&pctx->peer_streams)) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) received no media stream attribributes",pctx->ctx->label,pctx->label);
ABORT(R_FAILED);
@ -356,6 +384,7 @@ int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first)
{
int r,_status;
nr_ice_media_stream *stream;
int started = 0;
stream=STAILQ_FIRST(&pctx->peer_streams);
if(!stream)
@ -366,6 +395,16 @@ int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first)
break;
if(!allow_non_first){
/* This test applies if:
1. allow_non_first is 0 (i.e., non-trickle ICE)
2. the first stream has an empty check list.
But in the non-trickle ICE case, the other side should have provided
some candidates or ICE is pretty much not going to work and we're
just going to fail. Hence R_FAILED as opposed to R_NOT_FOUND and
immediate termination here.
*/
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label);
ABORT(R_FAILED);
}
@ -374,15 +413,36 @@ int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first)
}
if (!stream) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) no streams with non-empty check lists",pctx->ctx->label,pctx->label);
ABORT(R_NOT_FOUND);
r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with non-empty check lists",pctx->ctx->label,pctx->label);
}
if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) {
else if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) {
if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream))
ABORT(r);
if(r=nr_ice_media_stream_start_checks(pctx,stream))
ABORT(r);
++started;
}
stream=STAILQ_FIRST(&pctx->peer_streams);
while (stream) {
int serviced = 0;
if (r=nr_ice_media_stream_service_pre_answer_requests(pctx, stream->local_stream, stream, &serviced))
ABORT(r);
if (serviced) {
++started;
}
else {
r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with pre-answer requests",pctx->ctx->label,pctx->label);
}
stream=STAILQ_NEXT(stream, entry);
}
if (!started) {
r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no checks to start",pctx->ctx->label,pctx->label);
ABORT(R_NOT_FOUND);
}
_status=0;

View File

@ -239,6 +239,9 @@ nr_stun_build_use_candidate(nr_stun_client_ice_use_candidate_params *params, nr_
if ((r=nr_stun_message_add_priority_attribute(req, params->priority)))
ABORT(r);
if ((r=nr_stun_message_add_ice_controlling_attribute(req, params->tiebreaker)))
ABORT(r);
*msg = req;
_status=0;
@ -274,6 +277,9 @@ nr_stun_build_req_ice(nr_stun_client_ice_binding_request_params *params, nr_stun
if ((r=nr_stun_message_add_ice_controlled_attribute(req, params->tiebreaker)))
ABORT(r);
break;
default:
assert(0);
ABORT(R_INTERNAL);
}
*msg = req;

View File

@ -79,6 +79,7 @@ typedef struct nr_stun_client_ice_use_candidate_params_ {
char *username;
Data password;
UINT4 priority;
UINT8 tiebreaker;
} nr_stun_client_ice_use_candidate_params;
int nr_stun_build_use_candidate(nr_stun_client_ice_use_candidate_params *params, nr_stun_message **msg);

View File

@ -84,17 +84,18 @@ int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp)
nr_stun_server_destroy_client(clnt1);
}
nr_stun_server_destroy_client(ctx->default_client);
RFREE(ctx->label);
RFREE(ctx);
return(0);
}
int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, int (*stun_server_cb)(void *cb_arg, nr_stun_server_ctx *ctx,nr_socket *sock, nr_stun_server_request *req, int *error), void *cb_arg)
static int nr_stun_server_client_create(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg, nr_stun_server_client **clntp)
{
int r,_status;
nr_stun_server_client *clnt=0;
int r,_status;
if(!(clnt=RCALLOC(sizeof(nr_stun_server_client))))
ABORT(R_NO_MEMORY);
@ -108,16 +109,49 @@ int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char
if(r=r_data_copy(&clnt->password,pass))
ABORT(r);
clnt->stun_server_cb=stun_server_cb;
clnt->stun_server_cb=cb;
clnt->cb_arg=cb_arg;
*clntp = clnt;
_status=0;
abort:
if(_status){
nr_stun_server_destroy_client(clnt);
}
return(_status);
}
int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg)
{
int r,_status;
nr_stun_server_client *clnt;
if (r=nr_stun_server_client_create(ctx, client_label, user, pass, cb, cb_arg, &clnt))
ABORT(r);
STAILQ_INSERT_TAIL(&ctx->clients,clnt,entry);
_status=0;
abort:
if(_status){
nr_stun_server_destroy_client(clnt);
}
return(_status);
}
int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg)
{
int r,_status;
nr_stun_server_client *clnt;
assert(!ctx->default_client);
if (ctx->default_client)
ABORT(R_INTERNAL);
if (r=nr_stun_server_client_create(ctx, "default_client", ufrag, pass, cb, cb_arg, &clnt))
ABORT(r);
ctx->default_client = clnt;
_status=0;
abort:
return(_status);
}
@ -200,6 +234,7 @@ int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, cha
nr_stun_server_client *clnt = 0;
nr_stun_server_request info;
int error;
int dont_free;
r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Received(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
@ -291,7 +326,8 @@ int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, cha
info.response = res;
error = 0;
if (clnt->stun_server_cb(clnt->cb_arg,ctx,sock,&info,&error)) {
dont_free = 0;
if (clnt->stun_server_cb(clnt->cb_arg,ctx,sock,&info,&dont_free,&error)) {
if (error == 0)
error = 500;
@ -335,8 +371,10 @@ int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, cha
_status = 0;
}
nr_stun_message_destroy(&res);
nr_stun_message_destroy(&req);
if (!dont_free) {
nr_stun_message_destroy(&res);
nr_stun_message_destroy(&req);
}
return(_status);
}
@ -375,6 +413,9 @@ static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock
static int nr_stun_server_destroy_client(nr_stun_server_client *clnt)
{
if (!clnt)
return 0;
RFREE(clnt->label);
RFREE(clnt->username);
r_data_zfree(&clnt->password);
@ -399,6 +440,20 @@ int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr
sizeof(attr->u.username)))
break;
}
if (!clnt && ctx->default_client) {
/* If we can't find a specific client see if this matches the default,
which means that the username starts with our ufrag.
*/
char *colon = strchr(attr->u.username, ':');
if (colon && !strncmp(ctx->default_client->username,
attr->u.username,
colon - attr->u.username)) {
clnt = ctx->default_client;
r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Falling back to default client, username=: %s",ctx->label,attr->u.username);
}
}
if (!clnt) {
r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Request from unknown user: %s",ctx->label,attr->u.username);
ABORT(R_NOT_FOUND);

View File

@ -47,11 +47,13 @@ typedef struct nr_stun_server_request_{
nr_stun_message *response;
} nr_stun_server_request;
typedef int (*nr_stun_server_cb)(void *cb_arg, struct nr_stun_server_ctx_ *ctx,nr_socket *sock,nr_stun_server_request *req, int *dont_free, int *error);
struct nr_stun_server_client_ {
char *label;
char *username;
Data password;
int (*stun_server_cb)(void *cb_arg, nr_stun_server_ctx *ctx,nr_socket *sock, nr_stun_server_request *req, int *error);
nr_stun_server_cb stun_server_cb;
void *cb_arg;
char nonce[NR_STUN_MAX_NONCE_BYTES+1]; /* +1 for \0 */
STAILQ_ENTRY(nr_stun_server_client_) entry;
@ -64,13 +66,15 @@ struct nr_stun_server_ctx_ {
nr_socket *sock;
nr_transport_addr my_addr;
nr_stun_server_client_head clients;
nr_stun_server_client *default_client;
};
int nr_stun_server_ctx_create(char *label, nr_socket *sock, nr_stun_server_ctx **ctxp);
int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp);
int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, int (*stun_server_cb)(void *cb_arg, nr_stun_server_ctx *ctx,nr_socket *sock,nr_stun_server_request *req, int *error), void *cb_arg);
int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg);
int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg);
int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg);
int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule);
int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **clnt);

View File

@ -2283,12 +2283,15 @@ TEST_F(SignalingTest, missingUfrag)
// FSM. This may change in the future.
a1_.CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV);
a1_.SetLocal(TestObserver::OFFER, offer, true);
// Really we should detect failure at the SetRemote point,
// since without a ufrag, we aren't going to be successful.
// But for now, this isn't detected till SIPCC tries to impose
// the parameters on the ICE stack in SetLocal. Bug 892161.
a2_.SetRemote(TestObserver::OFFER, offer, true);
a2_.CreateAnswer(constraints, offer, OFFER_AV | ANSWER_AV);
a2_.SetLocal(TestObserver::ANSWER, a2_.answer(), true);
a1_.SetRemote(TestObserver::ANSWER, a2_.answer(), true);
// We don't check anything in particular for success here -- simply not
// crashing by now is enough to declare success.
a2_.SetLocal(TestObserver::ANSWER, a2_.answer(),
true, sipcc::PeerConnectionImpl::kSignalingHaveRemoteOffer);
// Since things have now failed, just stop.
}
} // End namespace test.