ice.c 109 KB
Newer Older
Ghislain MARY's avatar
Ghislain MARY committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006  Belledonne Communications

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


#if !defined(WIN32) && !defined(_WIN32_WCE)
#ifdef __APPLE__
#include <sys/types.h>
#endif
#include <sys/socket.h>
#include <netdb.h>
#endif

#include "mediastreamer2/msticker.h"
#include "mediastreamer2/ice.h"
31
#include "ortp/ortp.h"
Ghislain MARY's avatar
Ghislain MARY committed
32 33


34 35 36
#define ICE_MAX_NB_CANDIDATES		10
#define ICE_MAX_NB_CANDIDATE_PAIRS	(ICE_MAX_NB_CANDIDATES*ICE_MAX_NB_CANDIDATES)

Ghislain MARY's avatar
Ghislain MARY committed
37 38
#define ICE_MIN_COMPONENTID		1
#define ICE_MAX_COMPONENTID		256
39
#define ICE_INVALID_COMPONENTID		0
Ghislain MARY's avatar
Ghislain MARY committed
40 41
#define ICE_MAX_UFRAG_LEN		256
#define ICE_MAX_PWD_LEN			256
Ghislain MARY's avatar
Ghislain MARY committed
42 43
#define ICE_DEFAULT_TA_DURATION		40	/* In milliseconds */
#define ICE_DEFAULT_RTO_DURATION	200	/* In milliseconds */
Ghislain MARY's avatar
Ghislain MARY committed
44
#define ICE_DEFAULT_KEEPALIVE_TIMEOUT   15	/* In seconds */
45
#define ICE_GATHERING_CANDIDATES_TIMEOUT	2500	/* In milliseconds */
Ghislain MARY's avatar
Ghislain MARY committed
46
#define ICE_MAX_RETRANSMISSIONS		4
Ghislain MARY's avatar
Ghislain MARY committed
47

48

49 50 51 52 53
typedef struct _Type_ComponentID {
	IceCandidateType type;
	uint16_t componentID;
} Type_ComponentID;

54 55
typedef struct _Foundation_Pair_Priority_ComponentID {
	const IcePairFoundation *foundation;
56 57 58
	IceCandidatePair *pair;
	uint64_t priority;
	uint16_t componentID;
59
} Foundation_Pair_Priority_ComponentID;
60

61 62
typedef struct _CheckList_RtpSession {
	IceCheckList *cl;
63
	const RtpSession *rtp_session;
64 65
} CheckList_RtpSession;

66 67
typedef struct _CheckList_RtpSession_Time {
	IceCheckList *cl;
68
	const RtpSession *rtp_session;
69 70 71
	uint64_t time;
} CheckList_RtpSession_Time;

72 73 74 75 76
typedef struct _CheckList_Bool {
	IceCheckList *cl;
	bool_t result;
} CheckList_Bool;

77
typedef struct _CheckList_MSListPtr {
78
	const IceCheckList *cl;
79 80 81
	MSList **list;
} CheckList_MSListPtr;

82 83 84 85 86
typedef struct _LocalCandidate_RemoteCandidate {
	IceCandidate *local;
	IceCandidate *remote;
} LocalCandidate_RemoteCandidate;

87
typedef struct _Addr_Ports {
88 89
	char *rtp_addr;
	char *rtcp_addr;
90 91 92 93 94
	int addr_len;
	int *rtp_port;
	int *rtcp_port;
} Addr_Ports;

95 96 97 98 99
typedef struct _Time_Bool {
	uint64_t time;
	bool_t result;
} Time_Bool;

100 101 102 103 104
typedef struct _Session_Index {
	IceSession *session;
	int index;
} Session_Index;

105 106 107 108 109 110
typedef struct _LosingRemoteCandidate_InProgress_Failed {
	const IceCandidate *losing_remote_candidate;
	bool_t in_progress_candidates;
	bool_t failed_candidates;
} LosingRemoteCandidate_InProgress_Failed;

111

112
// WARNING: We need this function to push events in the rtp event queue but it should not be made public in oRTP.
113 114 115
extern void rtp_session_dispatch_event(RtpSession *session, OrtpEvent *ev);


Ghislain MARY's avatar
Ghislain MARY committed
116
static void transactionID2string(const UInt96 *tr_id, char *tr_id_str);
117
static void ice_send_stun_server_binding_request(ortp_socket_t sock, const struct sockaddr *server, socklen_t addrlen, UInt96 *transactionID, uint8_t nb_transmissions, int id);
118
static int ice_compare_transport_addresses(const IceTransportAddress *ta1, const IceTransportAddress *ta2);
119
static int ice_compare_pair_priorities(const IceCandidatePair *p1, const IceCandidatePair *p2);
120 121
static int ice_compare_pairs(const IceCandidatePair *p1, const IceCandidatePair *p2);
static int ice_compare_candidates(const IceCandidate *c1, const IceCandidate *c2);
122
static int ice_find_host_candidate(const IceCandidate *candidate, const uint16_t *componentID);
123
static int ice_find_nominated_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
124
static int ice_find_selected_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
125
static int ice_find_running_check_list(const IceCheckList *cl);
126
static int ice_find_pair_in_valid_list(IceValidCandidatePair *valid_pair, IceCandidatePair *pair);
127
static void ice_pair_set_state(IceCandidatePair *pair, IceCandidatePairState state);
128
static void ice_compute_candidate_foundation(IceCandidate *candidate, IceCheckList *cl);
Ghislain MARY's avatar
Ghislain MARY committed
129
static void ice_set_credentials(char **ufrag, char **pwd, const char *ufrag_str, const char *pwd_str);
130
static void ice_conclude_processing(IceCheckList* cl, RtpSession* rtp_session);
Ghislain MARY's avatar
Ghislain MARY committed
131 132


133 134 135 136
/******************************************************************************
 * CONSTANTS DEFINITIONS                                                      *
 *****************************************************************************/

137 138
uint32_t stun_magic_cookie = 0x2112A442;

Ghislain MARY's avatar
Ghislain MARY committed
139 140 141 142 143
static const char * const role_values[] = {
	"Controlling",	/* IR_Controlling */
	"Controlled",	/* IR_Controlled */
};

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
static const char * const candidate_type_values[] = {
	"host",		/* ICT_HostCandidate */
	"srflx",	/* ICT_ServerReflexiveCandidate */
	"prflx",	/* ICT_PeerReflexiveCandidate */
	"relay"		/* ICT_RelayedCandidate */
};

/**
 * ICE candidate type preference values as recommended in 4.1.1.2.
 */
static const uint8_t type_preference_values[] = {
	126,	/* ICT_HostCandidate */
	100,	/* ICT_ServerReflexiveCandidate */
	110,	/* ICT_PeerReflexiveCandidate */
	0	/* ICT_RelayedCandidate */
};

161 162 163 164 165 166 167 168 169 170
static const char * const candidate_pair_state_values[] = {
	"Waiting",	/* ICP_Waiting */
	"In-Progress",	/* ICP_InProgress */
	"Succeeded",	/* ICP_Succeeded */
	"Failed",	/* ICP_Failed */
	"Frozen"	/* ICP_Frozen */
};


/******************************************************************************
Ghislain MARY's avatar
Ghislain MARY committed
171 172 173
 * SESSION INITIALISATION AND DEINITIALISATION                                *
 *****************************************************************************/

Ghislain MARY's avatar
Ghislain MARY committed
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
static uint64_t generate_tie_breaker(void)
{
	return (((uint64_t)random()) << 32) | (((uint64_t)random()) & 0xffffffff);
}

static char * generate_ufrag(void)
{
	char *ufrag = ms_malloc(9);
	sprintf(ufrag, "%08lx", random());
	ufrag[8] = '\0';
	return ufrag;
}

static char * generate_pwd(void)
{
	char *pwd = ms_malloc(25);
	sprintf(pwd, "%08lx%08lx%08lx", random(), random(), random());
	pwd[24] = '\0';
	return pwd;
}

Ghislain MARY's avatar
Ghislain MARY committed
195 196 197
static void ice_session_init(IceSession *session)
{
	session->streams = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
198
	session->state = IS_Stopped;
Ghislain MARY's avatar
Ghislain MARY committed
199
	session->role = IR_Controlling;
Ghislain MARY's avatar
Ghislain MARY committed
200
	session->tie_breaker = generate_tie_breaker();
201
	session->ta = ICE_DEFAULT_TA_DURATION;
Ghislain MARY's avatar
Ghislain MARY committed
202
	session->keepalive_timeout = ICE_DEFAULT_KEEPALIVE_TIMEOUT;
Ghislain MARY's avatar
Ghislain MARY committed
203
	session->max_connectivity_checks = ICE_MAX_NB_CANDIDATE_PAIRS;
Ghislain MARY's avatar
Ghislain MARY committed
204 205
	session->local_ufrag = generate_ufrag();
	session->local_pwd = generate_pwd();
Ghislain MARY's avatar
Ghislain MARY committed
206 207
	session->remote_ufrag = NULL;
	session->remote_pwd = NULL;
208 209
	session->event_time = 0;
	session->send_event = FALSE;
Ghislain MARY's avatar
Ghislain MARY committed
210 211 212 213
}

IceSession * ice_session_new(void)
{
214
	MSTickerParams params;
Ghislain MARY's avatar
Ghislain MARY committed
215 216
	IceSession *session = ms_new(IceSession, 1);
	if (session == NULL) {
217 218 219 220 221 222 223 224 225
		ms_error("ice: Memory allocation of ICE session failed");
		return NULL;
	}
	params.name = "ICE Ticker";
	params.prio = MS_TICKER_PRIO_NORMAL;
	session->ticker = ms_ticker_new_with_params(&params);
	if (session->ticker == NULL) {
		ms_error("ice: Creation of ICE ticker failed");
		ice_session_destroy(session);
Ghislain MARY's avatar
Ghislain MARY committed
226 227 228 229 230 231 232 233
		return NULL;
	}
	ice_session_init(session);
	return session;
}

void ice_session_destroy(IceSession *session)
{
234 235 236 237 238 239 240 241 242 243
	if (session != NULL) {
		ms_list_for_each(session->streams, (void (*)(void*))ice_check_list_destroy);
		if (session->ticker) ms_ticker_destroy(session->ticker);
		if (session->local_ufrag) ms_free(session->local_ufrag);
		if (session->local_pwd) ms_free(session->local_pwd);
		if (session->remote_ufrag) ms_free(session->remote_ufrag);
		if (session->remote_pwd) ms_free(session->remote_pwd);
		ms_list_free(session->streams);
		ms_free(session);
	}
Ghislain MARY's avatar
Ghislain MARY committed
244 245 246 247 248
}


/******************************************************************************
 * CHECK LIST INITIALISATION AND DEINITIALISATION                             *
249
 *****************************************************************************/
250

251 252
static void ice_check_list_init(IceCheckList *cl)
{
Ghislain MARY's avatar
Ghislain MARY committed
253
	cl->session = NULL;
254
	cl->rtp_session = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
255
	cl->remote_ufrag = cl->remote_pwd = NULL;
256
	cl->stun_server_checks = NULL;
257
	cl->local_candidates = cl->remote_candidates = cl->pairs = cl->losing_pairs = cl->triggered_checks_queue = cl->check_list = cl->valid_list = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
258
	cl->local_componentIDs = cl->remote_componentIDs = cl->foundations = NULL;
259
	cl->state = ICL_Running;
260
	cl->ta_time = 0;
Ghislain MARY's avatar
Ghislain MARY committed
261
	cl->keepalive_time = 0;
262
	cl->foundation_generator = 1;
Ghislain MARY's avatar
Ghislain MARY committed
263
	cl->mismatch = FALSE;
264
	cl->gathering_candidates = FALSE;
265 266
}

267
IceCheckList * ice_check_list_new(void)
268 269 270 271 272 273 274
{
	IceCheckList *cl = ms_new(IceCheckList, 1);
	if (cl == NULL) {
		ms_error("ice_check_list_new: Memory allocation failed");
		return NULL;
	}
	ice_check_list_init(cl);
275 276 277
	return cl;
}

278
static void ice_compute_pair_priority(IceCandidatePair *pair, const IceRole *role)
279 280
{
	/* Use formula defined in 5.7.2 to compute pair priority. */
Ghislain MARY's avatar
Ghislain MARY committed
281 282
	uint64_t G = 0;
	uint64_t D = 0;
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

	switch (*role) {
		case IR_Controlling:
			G = pair->local->priority;
			D = pair->remote->priority;
			break;
		case IR_Controlled:
			G = pair->remote->priority;
			D = pair->local->priority;
			break;
	}
	pair->priority = (MIN(G, D) << 32) | (MAX(G, D) << 1) | (G > D ? 1 : 0);
}

static IceCandidatePair *ice_pair_new(IceCheckList *cl, IceCandidate* local_candidate, IceCandidate *remote_candidate)
{
299
	IceCandidatePair *pair = ms_new0(IceCandidatePair, 1);
300 301
	pair->local = local_candidate;
	pair->remote = remote_candidate;
302
	pair->state = ICP_Frozen;
303 304
	pair->is_default = FALSE;
	pair->is_nominated = FALSE;
305
	pair->use_candidate = FALSE;
306
	pair->wait_transaction_timeout = FALSE;
307 308 309 310 311 312 313 314 315 316
	if ((pair->local->is_default == TRUE) && (pair->remote->is_default == TRUE)) pair->is_default = TRUE;
	else pair->is_default = FALSE;
	memset(&pair->transactionID, 0, sizeof(pair->transactionID));
	pair->rto = ICE_DEFAULT_RTO_DURATION;
	pair->retransmissions = 0;
	pair->role = cl->session->role;
	ice_compute_pair_priority(pair, &cl->session->role);
	return pair;
}

317 318 319 320 321
static void ice_free_stun_server_check(IceStunServerCheck *check)
{
	ms_free(check);
}

322 323 324 325 326
static void ice_free_pair_foundation(IcePairFoundation *foundation)
{
	ms_free(foundation);
}

327 328 329 330 331
static void ice_free_valid_pair(IceValidCandidatePair *valid_pair)
{
	ms_free(valid_pair);
}

332
static void ice_free_candidate_pair(IceCandidatePair *pair, IceCheckList *cl)
Ghislain MARY's avatar
Ghislain MARY committed
333
{
334 335 336 337 338 339
	MSList *elem;
	while ((elem = ms_list_find(cl->check_list, pair)) != NULL) {
		cl->check_list = ms_list_remove(cl->check_list, pair);
	}
	while ((elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_pair_in_valid_list, pair)) != NULL) {
		ice_free_valid_pair(elem->data);
340
		cl->valid_list = ms_list_remove_link(cl->valid_list, elem);
341
	}
Ghislain MARY's avatar
Ghislain MARY committed
342 343 344
	ms_free(pair);
}

345 346 347 348 349
static void ice_free_candidate(IceCandidate *candidate)
{
	ms_free(candidate);
}

350 351
void ice_check_list_destroy(IceCheckList *cl)
{
Ghislain MARY's avatar
Ghislain MARY committed
352 353
	if (cl->remote_ufrag) ms_free(cl->remote_ufrag);
	if (cl->remote_pwd) ms_free(cl->remote_pwd);
354
	ms_list_for_each(cl->stun_server_checks, (void (*)(void*))ice_free_stun_server_check);
355
	ms_list_for_each(cl->foundations, (void (*)(void*))ice_free_pair_foundation);
356
	ms_list_for_each2(cl->pairs, (void (*)(void*,void*))ice_free_candidate_pair, cl);
357
	ms_list_for_each(cl->valid_list, (void (*)(void*))ice_free_valid_pair);
358 359
	ms_list_for_each(cl->remote_candidates, (void (*)(void*))ice_free_candidate);
	ms_list_for_each(cl->local_candidates, (void (*)(void*))ice_free_candidate);
360
	ms_list_free(cl->stun_server_checks);
361
	ms_list_free(cl->foundations);
Ghislain MARY's avatar
Ghislain MARY committed
362 363
	ms_list_free(cl->local_componentIDs);
	ms_list_free(cl->remote_componentIDs);
Ghislain MARY's avatar
Ghislain MARY committed
364
	ms_list_free(cl->valid_list);
365
	ms_list_free(cl->check_list);
366
	ms_list_free(cl->triggered_checks_queue);
367
	ms_list_free(cl->losing_pairs);
368 369 370 371 372 373 374
	ms_list_free(cl->pairs);
	ms_list_free(cl->remote_candidates);
	ms_list_free(cl->local_candidates);
	ms_free(cl);
}


375 376 377 378
/******************************************************************************
 * CANDIDATE ACCESSORS                                                        *
 *****************************************************************************/

379
const char *ice_candidate_type(const IceCandidate *candidate)
380 381 382 383
{
	return candidate_type_values[candidate->type];
}

384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
/******************************************************************************
 * CANDIDATE PAIR ACCESSORS                                                   *
 *****************************************************************************/

static void ice_pair_set_state(IceCandidatePair *pair, IceCandidatePairState state)
{
	if (pair->state != state) {
		pair->state = state;
		switch (state) {
			case ICP_Failed:
			case ICP_Waiting:
				memset(&pair->transactionID, 0, sizeof(pair->transactionID));
				break;
			case ICP_InProgress:
			case ICP_Succeeded:
			case ICP_Frozen:
				break;
		}
	}
}


406 407 408 409
/******************************************************************************
 * CHECK LIST ACCESSORS                                                       *
 *****************************************************************************/

410
IceCheckListState ice_check_list_state(const IceCheckList* cl)
411 412 413 414
{
	return cl->state;
}

415 416 417 418 419
static int ice_find_non_failed_check_list(const IceCheckList *cl)
{
	return (cl->state == ICL_Failed);
}

420 421 422
void ice_check_list_set_state(IceCheckList *cl, IceCheckListState state)
{
	cl->state = state;
423
	if (ms_list_find_custom(cl->session->streams, (MSCompareFunc)ice_find_non_failed_check_list, NULL) == NULL) {
424 425 426
		/* Set the state of the session to Failed if all the check lists are in the Failed state. */
		cl->session->state = IS_Failed;
	}
427 428
}

429 430 431 432 433
void ice_check_list_set_rtp_session(IceCheckList *cl, RtpSession *rtp_session)
{
	cl->rtp_session = rtp_session;
}

434
const char * ice_check_list_local_ufrag(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
435 436 437 438 439
{
	/* Do not handle media specific ufrag for the moment, so use the session local ufrag. */
	return cl->session->local_ufrag;
}

440
const char * ice_check_list_local_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
441 442 443 444 445
{
	/* Do not handle media specific pwd for the moment, so use the session local pwd. */
	return cl->session->local_pwd;
}

446
const char * ice_check_list_remote_ufrag(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
447 448 449 450 451
{
	if (cl->remote_ufrag) return cl->remote_ufrag;
	else return cl->session->remote_ufrag;
}

452
const char * ice_check_list_remote_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
453 454 455 456 457
{
	if (cl->remote_pwd) return cl->remote_pwd;
	else return cl->session->remote_pwd;
}

458
static int ice_find_default_local_candidate(const IceCandidate *candidate, const uint16_t *componentID)
459
{
460
	return !((candidate->componentID == *componentID) && (candidate->is_default == TRUE));
461 462
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
bool_t ice_check_list_remote_credentials_changed(IceCheckList *cl, const char *ufrag, const char *pwd)
{
	const char *old_ufrag;
	const char *old_pwd;
	if ((cl->remote_ufrag == NULL) || (cl->remote_pwd == NULL)) {
		if (cl->remote_ufrag == NULL) old_ufrag = cl->session->remote_ufrag;
		else old_ufrag = cl->remote_ufrag;
		if ((strlen(ufrag) != strlen(old_ufrag)) || (strcmp(ufrag, old_ufrag) != 0)) return TRUE;
		if (cl->remote_pwd == NULL) old_pwd = cl->session->remote_pwd;
		else old_pwd = cl->remote_pwd;
		if ((strlen(pwd) != strlen(old_pwd)) || (strcmp(pwd, old_pwd) != 0)) return TRUE;
		return FALSE;
	}
	if (strlen(ufrag) != strlen(cl->remote_ufrag) || (strcmp(ufrag, cl->remote_ufrag) != 0)) return TRUE;
	if (strlen(pwd) != strlen(cl->remote_pwd) || (strcmp(pwd, cl->remote_pwd) != 0)) return TRUE;
	return FALSE;
}

481 482 483 484 485
void ice_check_list_set_remote_credentials(IceCheckList *cl, const char *ufrag, const char *pwd)
{
	ice_set_credentials(&cl->remote_ufrag, &cl->remote_pwd, ufrag, pwd);
}

486
bool_t ice_check_list_default_local_candidate(const IceCheckList *cl, const char **rtp_addr, int *rtp_port, const char **rtcp_addr, int *rtcp_port)
487 488
{
	IceCandidate *candidate = NULL;
489 490 491 492 493 494 495 496 497 498 499 500 501
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
	rtp_elem = ms_list_find_custom(cl->local_candidates, (MSCompareFunc)ice_find_default_local_candidate, &componentID);
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
	rtcp_elem = ms_list_find_custom(cl->local_candidates, (MSCompareFunc)ice_find_default_local_candidate, &componentID);

	candidate = (IceCandidate *)rtp_elem->data;
	if (rtp_addr != NULL) *rtp_addr = candidate->taddr.ip;
	if (rtp_port != NULL) *rtp_port = candidate->taddr.port;
502 503 504 505
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
506 507 508 509
	candidate = (IceCandidate *)rtcp_elem->data;
	if (rtcp_addr != NULL) *rtcp_addr = candidate->taddr.ip;
	if (rtcp_port != NULL) *rtcp_port = candidate->taddr.port;
	return TRUE;
510 511
}

512
bool_t ice_check_list_selected_valid_local_candidate(const IceCheckList *cl, const char **rtp_addr, int *rtp_port, const char **rtcp_addr, int *rtcp_port)
Ghislain MARY's avatar
Ghislain MARY committed
513 514
{
	IceCandidate *candidate = NULL;
515 516 517 518 519 520
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
521
	rtp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
522 523
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
524
	rtcp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
525 526 527 528 529

	valid_pair = (IceValidCandidatePair *)rtp_elem->data;
	candidate = valid_pair->valid->local;
	if (rtp_addr != NULL) *rtp_addr = candidate->taddr.ip;
	if (rtp_port != NULL) *rtp_port = candidate->taddr.port;
530 531 532 533
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
534 535 536 537 538
	valid_pair = (IceValidCandidatePair *)rtcp_elem->data;
	candidate = valid_pair->valid->local;
	if (rtcp_addr != NULL) *rtcp_addr = candidate->taddr.ip;
	if (rtcp_port != NULL) *rtcp_port = candidate->taddr.port;
	return TRUE;
Ghislain MARY's avatar
Ghislain MARY committed
539 540
}

541
bool_t ice_check_list_selected_valid_remote_candidate(const IceCheckList *cl, const char **rtp_addr, int *rtp_port, const char **rtcp_addr, int *rtcp_port)
542 543 544 545 546 547 548 549
{
	IceCandidate *candidate = NULL;
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
550
	rtp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
551 552
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
553
	rtcp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
554 555 556 557 558 559 560 561 562 563 564 565 566

	valid_pair = (IceValidCandidatePair *)rtp_elem->data;
	candidate = valid_pair->valid->remote;
	if (rtp_addr != NULL) *rtp_addr = candidate->taddr.ip;
	if (rtp_port != NULL) *rtp_port = candidate->taddr.port;
	if (rtcp_elem == NULL) return FALSE;
	valid_pair = (IceValidCandidatePair *)rtcp_elem->data;
	candidate = valid_pair->valid->remote;
	if (rtcp_addr != NULL) *rtcp_addr = candidate->taddr.ip;
	if (rtcp_port != NULL) *rtcp_port = candidate->taddr.port;
	return TRUE;
}

567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
static void ice_check_list_queue_triggered_check(IceCheckList *cl, IceCandidatePair *pair)
{
	MSList *elem = ms_list_find(cl->triggered_checks_queue, pair);
	if (elem != NULL) {
		/* The pair is already in the triggered checks queue, do not add it again. */
	} else {
		cl->triggered_checks_queue = ms_list_append(cl->triggered_checks_queue, pair);
	}
}

static IceCandidatePair * ice_check_list_pop_triggered_check(IceCheckList *cl)
{
	IceCandidatePair *pair;

	if (ms_list_size(cl->triggered_checks_queue) == 0) return NULL;
	pair = ms_list_nth_data(cl->triggered_checks_queue, 0);
	if (pair != NULL) {
		/* Remove the first element in the triggered checks queue. */
		cl->triggered_checks_queue = ms_list_remove_link(cl->triggered_checks_queue, cl->triggered_checks_queue);
	}
	return pair;
}

590 591 592 593 594 595 596 597 598 599 600
static int ice_find_non_frozen_pair(const IceCandidatePair *pair, const void *dummy)
{
	return (pair->state == ICP_Frozen);
}

static bool_t ice_check_list_is_frozen(const IceCheckList *cl)
{
	MSList *elem = ms_list_find_custom(cl->check_list, (MSCompareFunc)ice_find_non_frozen_pair, NULL);
	return (elem == NULL);
}

Ghislain MARY's avatar
Ghislain MARY committed
601 602 603 604 605
bool_t ice_check_list_is_mismatch(const IceCheckList *cl)
{
	return cl->mismatch;
}

Ghislain MARY's avatar
Ghislain MARY committed
606 607 608 609 610

/******************************************************************************
 * SESSION ACCESSORS                                                          *
 *****************************************************************************/

611 612 613 614 615
IceCheckList * ice_session_check_list(const IceSession *session, unsigned int n)
{
	return (IceCheckList *)ms_list_nth_data(session->streams, n);
}

616
const char * ice_session_local_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
617 618 619 620
{
	return session->local_ufrag;
}

621
const char * ice_session_local_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
622 623 624 625
{
	return session->local_pwd;
}

626
const char * ice_session_remote_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
627 628 629 630
{
	return session->remote_ufrag;
}

631
const char * ice_session_remote_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
632 633 634 635
{
	return session->remote_pwd;
}

636 637 638 639 640 641 642 643 644 645
static void ice_check_list_compute_pair_priorities(IceCheckList *cl)
{
	ms_list_for_each2(cl->pairs, (void (*)(void*,void*))ice_compute_pair_priority, &cl->session->role);
}

static void ice_session_compute_pair_priorities(IceSession *session)
{
	ms_list_for_each(session->streams, (void (*)(void*))ice_check_list_compute_pair_priorities);
}

646 647 648 649 650 651
IceSessionState ice_session_state(const IceSession *session)
{
	return session->state;
}

IceRole ice_session_role(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
652 653 654 655
{
	return session->role;
}

Ghislain MARY's avatar
Ghislain MARY committed
656 657
void ice_session_set_role(IceSession *session, IceRole role)
{
658 659 660 661 662
	if (session->role != role) {
		/* Compute new candidate pair priorities if the role changes. */
		session->role = role;
		ice_session_compute_pair_priorities(session);
	}
Ghislain MARY's avatar
Ghislain MARY committed
663 664
}

Ghislain MARY's avatar
Ghislain MARY committed
665 666 667 668 669
void ice_session_set_local_credentials(IceSession *session, const char *ufrag, const char *pwd)
{
	ice_set_credentials(&session->local_ufrag, &session->local_pwd, ufrag, pwd);
}

670 671 672 673 674 675 676 677
bool_t ice_session_remote_credentials_changed(IceSession *session, const char *ufrag, const char *pwd)
{
	if ((session->remote_ufrag == NULL) || (session->remote_pwd == NULL)) return TRUE;
	if (strlen(ufrag) != strlen(session->remote_ufrag) || (strcmp(ufrag, session->remote_ufrag) != 0)) return TRUE;
	if (strlen(pwd) != strlen(session->remote_pwd) || (strcmp(pwd, session->remote_pwd) != 0)) return TRUE;
	return FALSE;
}

Ghislain MARY's avatar
Ghislain MARY committed
678 679 680 681 682
void ice_session_set_remote_credentials(IceSession *session, const char *ufrag, const char *pwd)
{
	ice_set_credentials(&session->remote_ufrag, &session->remote_pwd, ufrag, pwd);
}

Ghislain MARY's avatar
Ghislain MARY committed
683 684 685 686 687
void ice_session_set_max_connectivity_checks(IceSession *session, uint8_t max_connectivity_checks)
{
	session->max_connectivity_checks = max_connectivity_checks;
}

Ghislain MARY's avatar
Ghislain MARY committed
688 689 690 691 692 693
void ice_session_set_keepalive_timeout(IceSession *session, uint8_t timeout)
{
	if (timeout < ICE_DEFAULT_KEEPALIVE_TIMEOUT) timeout = ICE_DEFAULT_KEEPALIVE_TIMEOUT;
	session->keepalive_timeout = timeout;
}

Ghislain MARY's avatar
Ghislain MARY committed
694 695 696 697 698

/******************************************************************************
 * SESSION HANDLING                                                           *
 *****************************************************************************/

699 700 701 702 703
int ice_session_nb_check_lists(IceSession *session)
{
	return ms_list_size(session->streams);
}

Ghislain MARY's avatar
Ghislain MARY committed
704
void ice_session_add_check_list(IceSession *session, IceCheckList *cl)
705
{
Ghislain MARY's avatar
Ghislain MARY committed
706 707
	session->streams = ms_list_append(session->streams, cl);
	cl->session = session;
708 709 710
	if (cl->state == ICL_Running) {
		session->state = IS_Running;
	}
711 712
}

713 714
void ice_session_remove_check_list(IceSession *session, IceCheckList *cl)
{
715
	if (cl == NULL) return;
716 717 718 719
	session->streams = ms_list_remove(session->streams, cl);
	ice_check_list_destroy(cl);
}

Ghislain MARY's avatar
Ghislain MARY committed
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
static int ice_find_default_candidate_from_componentID(const IceCandidate *candidate, const uint16_t *componentID)
{
	return !((candidate->is_default == TRUE) && (candidate->componentID == *componentID));
}

static void ice_find_default_remote_candidate_for_componentID(const uint16_t *componentID, IceCheckList *cl)
{
	MSList *elem = ms_list_find_custom(cl->remote_candidates, (MSCompareFunc)ice_find_default_candidate_from_componentID, componentID);
	if (elem == NULL) {
		cl->mismatch = TRUE;
		cl->state = ICL_Failed;
	}
}

static void ice_check_list_check_mismatch(IceCheckList *cl)
{
	ms_list_for_each2(cl->remote_componentIDs, (void (*)(void*,void*))ice_find_default_remote_candidate_for_componentID, cl);
}

void ice_session_check_mismatch(IceSession *session)
{
	ms_list_for_each(session->streams, (void (*)(void*))ice_check_list_check_mismatch);
}

744

745 746 747 748
/******************************************************************************
 * CANDIDATES GATHERING                                                       *
 *****************************************************************************/

749 750 751 752 753 754 755 756 757 758 759 760
static void ice_check_list_candidates_gathered(const IceCheckList *cl, bool_t *result)
{
	if (ms_list_size(cl->local_candidates) == 0) *result = FALSE;
}

bool_t ice_session_candidates_gathered(const IceSession *session)
{
	bool_t result = TRUE;
	ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_candidates_gathered, &result);
	return result;
}

761
static void ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si)
762 763 764
{
	IceStunServerCheck *check;
	ortp_socket_t sock = -1;
765
	uint64_t curtime = si->session->ticker->time;
766

767
	if ((cl->rtp_session != NULL) && (cl->gathering_candidates == FALSE) && (cl->state != ICL_Completed)) {
768
		cl->gathering_candidates = TRUE;
769
		cl->gathering_start_time = curtime;
770 771 772 773
		sock = rtp_session_get_rtp_socket(cl->rtp_session);
		if (sock > 0) {
			check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
			check->sock = sock;
774
			if (si->index == 0) {
775 776
				check->transmission_time = curtime + ICE_DEFAULT_RTO_DURATION;
				check->nb_transmissions = 1;
777 778
				ice_send_stun_server_binding_request(sock, (struct sockaddr *)&cl->session->ss, cl->session->ss_len,
					&check->transactionID, check->nb_transmissions, check->sock);
779
			} else {
780
				check->transmission_time = curtime + 2 * si->index * ICE_DEFAULT_TA_DURATION;
781 782 783 784 785 786 787
			}
			cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
		}
		sock = rtp_session_get_rtcp_socket(cl->rtp_session);
		if (sock > 0) {
			check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
			check->sock = sock;
788
			check->transmission_time = curtime + 2 * si->index * ICE_DEFAULT_TA_DURATION + ICE_DEFAULT_TA_DURATION;
789 790
			cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
		}
791
		si->index++;
792 793 794 795 796
	}
}

void ice_session_gather_candidates(IceSession *session, struct sockaddr_storage ss, socklen_t ss_len)
{
797
	Session_Index si;
798 799
	session->ss = ss;
	session->ss_len = ss_len;
800 801 802
	si.session = session;
	si.index = 0;
	ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_gather_candidates, &si);
803 804 805
}


806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
/******************************************************************************
 * CANDIDATES SELECTION                                                       *
 *****************************************************************************/

static void ice_unselect_valid_pair(IceValidCandidatePair *valid_pair)
{
	valid_pair->selected = FALSE;
}

static void ice_check_list_select_candidates(IceCheckList *cl)
{
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *elem;

	ms_list_for_each(cl->valid_list, (void (*)(void*))ice_unselect_valid_pair);
	for (componentID = 1; componentID <= 2; componentID++) {
		elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_nominated_valid_pair_from_componentID, &componentID);
		if (elem == NULL) continue;
		valid_pair = (IceValidCandidatePair *)elem->data;
		valid_pair->selected = TRUE;
	}
}

void ice_session_select_candidates(IceSession *session)
{
	ms_list_for_each(session->streams, (void (*)(void*))ice_check_list_select_candidates);
}


836 837 838 839
/******************************************************************************
 * STUN PACKETS HANDLING                                                      *
 *****************************************************************************/

840
static void ice_send_stun_server_binding_request(ortp_socket_t sock, const struct sockaddr *server, socklen_t addrlen, UInt96 *transactionID, uint8_t nb_transmissions, int id)
841 842 843 844 845 846 847 848 849 850 851 852
{
	StunMessage msg;
	StunAtrString username;
	StunAtrString password;
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	const struct sockaddr_in *servaddr = (const struct sockaddr_in *)server;

	memset(&msg, 0, sizeof(StunMessage));
	memset(&username,0,sizeof(username));
	memset(&password,0,sizeof(password));
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, id);
853 854 855 856
	if (nb_transmissions > 1) {
		/* Keep the same transaction ID for retransmissions. */
		memcpy(&msg.msgHdr.tr_id, transactionID, sizeof(msg.msgHdr.tr_id));
	}
857 858 859
	len = stunEncodeMessage(&msg, buf, len, &password);
	if (len > 0) {
		sendMessage(sock, buf, len, htonl(servaddr->sin_addr.s_addr), htons(servaddr->sin_port));
860
		memcpy(transactionID, &msg.msgHdr.tr_id, sizeof(*transactionID));
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
	}
}

static int ice_parse_stun_server_binding_response(const StunMessage *msg, char *addr, int addr_len, int *port)
{
	struct in_addr ia;

	if (msg->hasXorMappedAddress) {
		*port = msg->xorMappedAddress.ipv4.port;
		ia.s_addr = htonl(msg->xorMappedAddress.ipv4.addr);
	} else if (msg->hasMappedAddress) {
		*port = msg->mappedAddress.ipv4.port;
		ia.s_addr = htonl(msg->mappedAddress.ipv4.addr);
	} else return -1;

	strncpy(addr, inet_ntoa(ia), addr_len);
	return 0;
}

880
/* Send a STUN binding request for ICE connectivity checks according to 7.1.2. */
881
static void ice_send_binding_request(IceCheckList *cl, IceCandidatePair *pair, const RtpSession *rtp_session)
882 883 884
{
	StunMessage msg;
	StunAddress4 dest;
885 886
	StunAtrString username;
	StunAtrString password;
887 888 889
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	int socket = 0;
Ghislain MARY's avatar
Ghislain MARY committed
890
	char tr_id_str[25];
891

892
	if (pair->state == ICP_InProgress) {
893 894 895 896
		if (pair->wait_transaction_timeout == TRUE) {
			/* Special case where a binding response triggers a binding request for an InProgress pair. */
			/* In this case we wait for the transmission timeout before creating a new binding request for the pair. */
			pair->wait_transaction_timeout = FALSE;
897 898 899 900
			if (pair->use_candidate == FALSE) {
				ice_pair_set_state(pair, ICP_Waiting);
				ice_check_list_queue_triggered_check(cl, pair);
			}
901 902
			return;
		}
903 904 905 906 907 908 909 910 911
		/* This is a retransmission: update the number of retransmissions, the retransmission timer value, and the transmission time. */
		pair->retransmissions++;
		if (pair->retransmissions > ICE_MAX_RETRANSMISSIONS) {
			/* Too much retransmissions, stop sending connectivity checks for this pair. */
			ice_pair_set_state(pair, ICP_Failed);
			return;
		}
		pair->rto = pair->rto << 1;
	}
912
	pair->transmission_time = cl->session->ticker->time;
913

914
	if (pair->local->componentID == 1) {
915
		socket = rtp_session_get_rtp_socket(rtp_session);
916
	} else if (pair->local->componentID == 2) {
917
		socket = rtp_session_get_rtcp_socket(rtp_session);
918 919
	} else return;

920 921 922 923 924
	snprintf(username.value, sizeof(username.value) - 1, "%s:%s", ice_check_list_remote_ufrag(cl), ice_check_list_local_ufrag(cl));
	username.sizeValue = strlen(username.value);
	snprintf(password.value, sizeof(password.value) - 1, "%s", ice_check_list_remote_pwd(cl));
	password.sizeValue = strlen(password.value);

925 926
	stunParseHostName(pair->remote->taddr.ip, &dest.addr, &dest.port, pair->remote->taddr.port);
	memset(&msg, 0, sizeof(msg));
Ghislain MARY's avatar
Ghislain MARY committed
927
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, 1);
928
	msg.hasMessageIntegrity = TRUE;
929
	msg.hasFingerprint = TRUE;
930 931 932 933 934 935

	/* Set the PRIORITY attribute as defined in 7.1.2.1. */
	msg.hasPriority = TRUE;
	msg.priority.priority = (pair->local->priority & 0x00ffffff) | (type_preference_values[ICT_PeerReflexiveCandidate] << 24);

	/* Include the USE-CANDIDATE attribute if the pair is nominated and the agent has the controlling role, as defined in 7.1.2.1. */
936
	if ((cl->session->role == IR_Controlling) && (pair->use_candidate == TRUE)) {
937 938 939
		msg.hasUseCandidate = TRUE;
	}

940
	/* Include the ICE-CONTROLLING or ICE-CONTROLLED attribute depending on the role of the agent, as defined in 7.1.2.2. */
941
	switch (cl->session->role) {
942 943
		case IR_Controlling:
			msg.hasIceControlling = TRUE;
944
			msg.iceControlling.value = cl->session->tie_breaker;
945 946 947
			break;
		case IR_Controlled:
			msg.hasIceControlled = TRUE;
948
			msg.iceControlled.value = cl->session->tie_breaker;
949 950
			break;
	}
951

952 953 954 955 956
	/* Keep the same transaction ID for retransmission. */
	if (pair->state == ICP_InProgress) {
		memcpy(&msg.msgHdr.tr_id, &pair->transactionID, sizeof(msg.msgHdr.tr_id));
	}

957
	len = stunEncodeMessage(&msg, buf, len, &password);
958 959 960
	if (len > 0) {
		/* Save the generated transaction ID to match the response to the request, and send the request. */
		memcpy(&pair->transactionID, &msg.msgHdr.tr_id, sizeof(pair->transactionID));
Ghislain MARY's avatar
Ghislain MARY committed
961 962 963 964 965 966 967 968 969 970
		transactionID2string(&pair->transactionID, tr_id_str);
		if (pair->state == ICP_InProgress) {
			ms_message("ice: Retransmit (%d) binding request for pair %p: %s:%u:%s --> %s:%u:%s [%s]", pair->retransmissions, pair,
				pair->local->taddr.ip, pair->local->taddr.port, candidate_type_values[pair->local->type],
				pair->remote->taddr.ip, pair->remote->taddr.port, candidate_type_values[pair->remote->type], tr_id_str);
		} else {
			ms_message("ice: Send binding request for %s pair %p: %s:%u:%s --> %s:%u:%s [%s]", candidate_pair_state_values[pair->state], pair,
				pair->local->taddr.ip, pair->local->taddr.port, candidate_type_values[pair->local->type],
				pair->remote->taddr.ip, pair->remote->taddr.port, candidate_type_values[pair->remote->type], tr_id_str);
		}
971
		sendMessage(socket, buf, len, dest.addr, dest.port);
Ghislain MARY's avatar
Ghislain MARY committed
972

973
		if (pair->state != ICP_InProgress) {
974 975 976
			/* First transmission of the request, initialize the retransmission timer. */
			pair->rto = ICE_DEFAULT_RTO_DURATION;
			pair->retransmissions = 0;
977 978
			/* Save the role of the agent. */
			pair->role = cl->session->role;
979 980 981
			/* Change the state of the pair. */
			ice_pair_set_state(pair, ICP_InProgress);
		}
982 983 984
	}
}

985 986 987 988 989 990 991 992 993 994 995
static int ice_get_componentID_from_rtp_session(const OrtpEventData *evt_data)
{
	if (evt_data->info.socket_type == OrtpRTPSocket) {
		return 1;
	} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
		return 2;
	}
	return -1;
}


996
static int ice_get_socket_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data)
997 998 999 1000 1001 1002 1003 1004 1005
{
	if (evt_data->info.socket_type == OrtpRTPSocket) {
		return rtp_session_get_rtp_socket(rtp_session);
	} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
		return rtp_session_get_rtcp_socket(rtp_session);
	}
	return -1;
}

1006
static int ice_get_recv_port_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data)
1007 1008 1009 1010 1011 1012 1013 1014
{
	if (evt_data->info.socket_type == OrtpRTPSocket) {
		return rtp_session->rtp.loc_port;
	} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
		return rtp_session->rtp.loc_port + 1;
	} else return -1;
}

1015
static void ice_send_binding_response(const RtpSession *rtp_session, const OrtpEventData *evt_data, const StunMessage *msg, const StunAddress4 *dest)
1016 1017 1018 1019 1020
{
	StunMessage response;
	StunAtrString password;
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
1021
	int socket = ice_get_socket_from_rtp_session(rtp_session, evt_data);
Ghislain MARY's avatar
Ghislain MARY committed
1022 1023 1024 1025
	int recvport = ice_get_recv_port_from_rtp_session(rtp_session, evt_data);
	struct in_addr dest_addr;
	char dest_addr_str[256];
	char tr_id_str[25];
1026

1027
	if (socket < 0) return;
1028
	memset(&response, 0, sizeof(response));
1029
	memset(&password, 0, sizeof(password));
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

	/* Copy magic cookie and transaction ID from the request. */
	response.msgHdr.magic_cookie = ntohl(msg->msgHdr.magic_cookie);
	memcpy(&response.msgHdr.tr_id, &msg->msgHdr.tr_id, sizeof(response.msgHdr.tr_id));

	/* Create the binding response. */
	response.msgHdr.msgType = (STUN_METHOD_BINDING | STUN_SUCCESS_RESP);
	response.hasMessageIntegrity = TRUE;
	response.hasFingerprint = TRUE;
	response.hasUsername = TRUE;
	memcpy(response.username.value, msg->username.