ice.c 133 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
/*
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

29 30
#include <inttypes.h>

jehan's avatar
jehan committed
31
#include "mediastreamer2/stun.h"
Ghislain MARY's avatar
Ghislain MARY committed
32
#include "mediastreamer2/ice.h"
33
#include "ortp/ortp.h"
Ghislain MARY's avatar
Ghislain MARY committed
34 35


36 37 38
#define ICE_MAX_NB_CANDIDATES		10
#define ICE_MAX_NB_CANDIDATE_PAIRS	(ICE_MAX_NB_CANDIDATES*ICE_MAX_NB_CANDIDATES)

39 40
#define ICE_MIN_COMPONENTID		1
#define ICE_MAX_COMPONENTID		256
41
#define ICE_INVALID_COMPONENTID		0
Ghislain MARY's avatar
Ghislain MARY committed
42 43
#define ICE_MAX_UFRAG_LEN		256
#define ICE_MAX_PWD_LEN			256
44 45
#define ICE_DEFAULT_TA_DURATION		40	/* In milliseconds */
#define ICE_DEFAULT_RTO_DURATION	200	/* In milliseconds */
Ghislain MARY's avatar
Ghislain MARY committed
46
#define ICE_DEFAULT_KEEPALIVE_TIMEOUT   15	/* In seconds */
Simon Morlat's avatar
Simon Morlat committed
47
#define ICE_GATHERING_CANDIDATES_TIMEOUT	5000	/* In milliseconds */
48
#define ICE_NOMINATION_DELAY		1000	/* In milliseconds */
49
#define ICE_MAX_RETRANSMISSIONS		7
50
#define ICE_MAX_STUN_REQUEST_RETRANSMISSIONS	7
51

52

53 54 55 56 57
typedef struct _Type_ComponentID {
	IceCandidateType type;
	uint16_t componentID;
} Type_ComponentID;

58 59
typedef struct _Foundation_Pair_Priority_ComponentID {
	const IcePairFoundation *foundation;
60 61 62
	IceCandidatePair *pair;
	uint64_t priority;
	uint16_t componentID;
63
} Foundation_Pair_Priority_ComponentID;
64

65 66
typedef struct _CheckList_RtpSession {
	IceCheckList *cl;
67
	const RtpSession *rtp_session;
68 69
} CheckList_RtpSession;

70 71
typedef struct _CheckList_RtpSession_Time {
	IceCheckList *cl;
72
	const RtpSession *rtp_session;
73
	MSTimeSpec time;
74 75
} CheckList_RtpSession_Time;

76 77 78 79 80
typedef struct _CheckList_Bool {
	IceCheckList *cl;
	bool_t result;
} CheckList_Bool;

81
typedef struct _CheckList_MSListPtr {
82
	const IceCheckList *cl;
83 84 85
	MSList **list;
} CheckList_MSListPtr;

86 87 88 89 90
typedef struct _LocalCandidate_RemoteCandidate {
	IceCandidate *local;
	IceCandidate *remote;
} LocalCandidate_RemoteCandidate;

91
typedef struct _Addr_Ports {
92 93
	char *rtp_addr;
	char *rtcp_addr;
94 95 96 97 98
	int addr_len;
	int *rtp_port;
	int *rtcp_port;
} Addr_Ports;

99
typedef struct _Time_Bool {
100
	MSTimeSpec time;
101 102 103
	bool_t result;
} Time_Bool;

104 105 106 107 108
typedef struct _Session_Index {
	IceSession *session;
	int index;
} Session_Index;

109 110 111 112 113 114
typedef struct _LosingRemoteCandidate_InProgress_Failed {
	const IceCandidate *losing_remote_candidate;
	bool_t in_progress_candidates;
	bool_t failed_candidates;
} LosingRemoteCandidate_InProgress_Failed;

115 116 117 118 119
typedef struct _StunRequestRoundTripTime {
	int nb_responses;
	int sum;
} StunRequestRoundTripTime;

120

121 122
static MSTimeSpec ice_current_time(void);
static MSTimeSpec ice_add_ms(MSTimeSpec orig, uint32_t ms);
123
static int32_t ice_compare_time(MSTimeSpec ts1, MSTimeSpec ts2);
124
static char * ice_inet_ntoa(struct sockaddr *addr, int addrlen, char *dest, int destlen);
Ghislain MARY's avatar
Ghislain MARY committed
125
static void transactionID2string(const UInt96 *tr_id, char *tr_id_str);
126
static void ice_send_stun_server_binding_request(RtpTransport *rtptp, const struct sockaddr *server, socklen_t addrlen, IceStunServerCheck *check);
127
static int ice_compare_transport_addresses(const IceTransportAddress *ta1, const IceTransportAddress *ta2);
128
static int ice_compare_pair_priorities(const IceCandidatePair *p1, const IceCandidatePair *p2);
129 130
static int ice_compare_pairs(const IceCandidatePair *p1, const IceCandidatePair *p2);
static int ice_compare_candidates(const IceCandidate *c1, const IceCandidate *c2);
131
static int ice_find_host_candidate(const IceCandidate *candidate, const uint16_t *componentID);
132
static int ice_find_candidate_from_type_and_componentID(const IceCandidate *candidate, const Type_ComponentID *tc);
133
static int ice_find_use_candidate_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
134
static int ice_find_nominated_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
135
static int ice_find_selected_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
136
static void ice_find_selected_valid_pair_for_componentID(const uint16_t *componentID, CheckList_Bool *cb);
137
static int ice_find_pair_in_valid_list(IceValidCandidatePair *valid_pair, IceCandidatePair *pair);
138
static void ice_pair_set_state(IceCandidatePair *pair, IceCandidatePairState state);
139
static void ice_compute_candidate_foundation(IceCandidate *candidate, IceCheckList *cl);
Ghislain MARY's avatar
Ghislain MARY committed
140
static void ice_set_credentials(char **ufrag, char **pwd, const char *ufrag_str, const char *pwd_str);
141
static void ice_conclude_processing(IceCheckList* cl, RtpSession* rtp_session);
Ghislain MARY's avatar
Ghislain MARY committed
142 143


144 145 146 147
/******************************************************************************
 * CONSTANTS DEFINITIONS                                                      *
 *****************************************************************************/

148 149
uint32_t stun_magic_cookie = 0x2112A442;

Ghislain MARY's avatar
Ghislain MARY committed
150 151 152 153 154
static const char * const role_values[] = {
	"Controlling",	/* IR_Controlling */
	"Controlled",	/* IR_Controlled */
};

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
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 */
};

172 173 174 175 176 177 178 179 180 181
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
182 183 184
 * SESSION INITIALISATION AND DEINITIALISATION                                *
 *****************************************************************************/

185 186
static uint64_t generate_tie_breaker(void)
{
187
	return (((uint64_t)ortp_random()) << 32) | (((uint64_t)ortp_random()) & 0xffffffff);
188 189 190 191
}

static char * generate_ufrag(void)
{
Simon Morlat's avatar
Simon Morlat committed
192
	return ms_strdup_printf("%08x", (int)ortp_random());
193 194 195 196
}

static char * generate_pwd(void)
{
Simon Morlat's avatar
Simon Morlat committed
197
	return ms_strdup_printf("%08x%08x%08x", (int)ortp_random(), (int)ortp_random(), (int)ortp_random());
198 199
}

Ghislain MARY's avatar
Ghislain MARY committed
200 201
static void ice_session_init(IceSession *session)
{
Ghislain MARY's avatar
Ghislain MARY committed
202
	session->state = IS_Stopped;
Ghislain MARY's avatar
Ghislain MARY committed
203
	session->role = IR_Controlling;
204
	session->tie_breaker = generate_tie_breaker();
205
	session->ta = ICE_DEFAULT_TA_DURATION;
Ghislain MARY's avatar
Ghislain MARY committed
206
	session->keepalive_timeout = ICE_DEFAULT_KEEPALIVE_TIMEOUT;
Ghislain MARY's avatar
Ghislain MARY committed
207
	session->max_connectivity_checks = ICE_MAX_NB_CANDIDATE_PAIRS;
208 209
	session->local_ufrag = generate_ufrag();
	session->local_pwd = generate_pwd();
Ghislain MARY's avatar
Ghislain MARY committed
210 211
	session->remote_ufrag = NULL;
	session->remote_pwd = NULL;
212
	session->send_event = FALSE;
213 214
	session->gathering_start_ts.tv_sec = session->gathering_start_ts.tv_nsec = -1;
	session->gathering_end_ts.tv_sec = session->gathering_end_ts.tv_nsec = -1;
215
	session->check_message_integrity=TRUE;
Ghislain MARY's avatar
Ghislain MARY committed
216 217 218 219
}

IceSession * ice_session_new(void)
{
220
	IceSession *session = ms_new0(IceSession, 1);
Ghislain MARY's avatar
Ghislain MARY committed
221
	if (session == NULL) {
222 223 224
		ms_error("ice: Memory allocation of ICE session failed");
		return NULL;
	}
Ghislain MARY's avatar
Ghislain MARY committed
225 226 227 228 229 230
	ice_session_init(session);
	return session;
}

void ice_session_destroy(IceSession *session)
{
231
	int i;
232
	if (session != NULL) {
233 234 235 236 237 238
		for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
			if (session->streams[i] != NULL) {
				ice_check_list_destroy(session->streams[i]);
				session->streams[i] = NULL;
			}
		}
239 240 241 242 243 244
		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_free(session);
	}
Ghislain MARY's avatar
Ghislain MARY committed
245 246
}

247 248 249
void ice_session_enable_message_integrity_check(IceSession *session,bool_t enable) {
	session->check_message_integrity=enable;
}
Ghislain MARY's avatar
Ghislain MARY committed
250 251 252

/******************************************************************************
 * CHECK LIST INITIALISATION AND DEINITIALISATION                             *
253
 *****************************************************************************/
254

255 256
static void ice_check_list_init(IceCheckList *cl)
{
Ghislain MARY's avatar
Ghislain MARY committed
257
	cl->session = NULL;
258
	cl->rtp_session = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
259
	cl->remote_ufrag = cl->remote_pwd = NULL;
260
	cl->stun_server_checks = NULL;
261
	cl->local_candidates = cl->remote_candidates = cl->pairs = cl->losing_pairs = cl->triggered_checks_queue = cl->check_list = cl->valid_list = cl->transaction_list = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
262
	cl->local_componentIDs = cl->remote_componentIDs = cl->foundations = NULL;
263
	cl->state = ICL_Running;
264
	cl->foundation_generator = 1;
Ghislain MARY's avatar
Ghislain MARY committed
265
	cl->mismatch = FALSE;
266
	cl->gathering_candidates = FALSE;
267
	cl->gathering_finished = FALSE;
268 269 270 271 272
	cl->nomination_delay_running = FALSE;
	memset(&cl->ta_time, 0, sizeof(cl->ta_time));
	memset(&cl->keepalive_time, 0, sizeof(cl->keepalive_time));
	memset(&cl->gathering_start_time, 0, sizeof(cl->gathering_start_time));
	memset(&cl->nomination_delay_start_time, 0, sizeof(cl->nomination_delay_start_time));
273 274
}

275
IceCheckList * ice_check_list_new(void)
276
{
277
	IceCheckList *cl = ms_new0(IceCheckList, 1);
278 279 280 281 282
	if (cl == NULL) {
		ms_error("ice_check_list_new: Memory allocation failed");
		return NULL;
	}
	ice_check_list_init(cl);
283 284 285
	return cl;
}

286
static void ice_compute_pair_priority(IceCandidatePair *pair, const IceRole *role)
287 288
{
	/* Use formula defined in 5.7.2 to compute pair priority. */
Ghislain MARY's avatar
Ghislain MARY committed
289 290
	uint64_t G = 0;
	uint64_t D = 0;
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306

	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)
{
307
	IceCandidatePair *pair = ms_new0(IceCandidatePair, 1);
308 309
	pair->local = local_candidate;
	pair->remote = remote_candidate;
310
	pair->state = ICP_Frozen;
311 312
	pair->is_default = FALSE;
	pair->is_nominated = FALSE;
313
	pair->use_candidate = FALSE;
314
	pair->wait_transaction_timeout = FALSE;
315 316 317 318 319 320
	if ((pair->local->is_default == TRUE) && (pair->remote->is_default == TRUE)) pair->is_default = TRUE;
	else pair->is_default = FALSE;
	pair->rto = ICE_DEFAULT_RTO_DURATION;
	pair->retransmissions = 0;
	pair->role = cl->session->role;
	ice_compute_pair_priority(pair, &cl->session->role);
321
	pair->retry_with_dummy_message_integrity=!cl->session->check_message_integrity;
322 323 324
	return pair;
}

325 326 327 328 329
static void ice_free_stun_server_check_transaction(IceStunServerCheckTransaction *transaction)
{
	ms_free(transaction);
}

330 331
static void ice_free_stun_server_check(IceStunServerCheck *check)
{
332
	ms_list_for_each(check->transactions, (void (*)(void*))ice_free_stun_server_check_transaction);
333 334 335
	ms_free(check);
}

336 337 338 339 340
static void ice_free_transaction(IceTransaction *transaction)
{
	ms_free(transaction);
}

341 342 343 344 345
static void ice_free_pair_foundation(IcePairFoundation *foundation)
{
	ms_free(foundation);
}

346 347 348 349 350
static void ice_free_valid_pair(IceValidCandidatePair *valid_pair)
{
	ms_free(valid_pair);
}

351
static void ice_free_candidate_pair(IceCandidatePair *pair, IceCheckList *cl)
352
{
353 354 355 356 357 358
	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);
359
		cl->valid_list = ms_list_remove_link(cl->valid_list, elem);
360
	}
361 362 363
	ms_free(pair);
}

364 365 366 367 368
static void ice_free_candidate(IceCandidate *candidate)
{
	ms_free(candidate);
}

369 370
void ice_check_list_destroy(IceCheckList *cl)
{
Ghislain MARY's avatar
Ghislain MARY committed
371 372
	if (cl->remote_ufrag) ms_free(cl->remote_ufrag);
	if (cl->remote_pwd) ms_free(cl->remote_pwd);
373
	ms_list_for_each(cl->stun_server_checks, (void (*)(void*))ice_free_stun_server_check);
374
	ms_list_for_each(cl->transaction_list, (void (*)(void*))ice_free_transaction);
375
	ms_list_for_each(cl->foundations, (void (*)(void*))ice_free_pair_foundation);
376
	ms_list_for_each2(cl->pairs, (void (*)(void*,void*))ice_free_candidate_pair, cl);
377
	ms_list_for_each(cl->valid_list, (void (*)(void*))ice_free_valid_pair);
378 379
	ms_list_for_each(cl->remote_candidates, (void (*)(void*))ice_free_candidate);
	ms_list_for_each(cl->local_candidates, (void (*)(void*))ice_free_candidate);
380
	ms_list_free(cl->stun_server_checks);
381
	ms_list_free(cl->transaction_list);
382
	ms_list_free(cl->foundations);
Ghislain MARY's avatar
Ghislain MARY committed
383 384
	ms_list_free(cl->local_componentIDs);
	ms_list_free(cl->remote_componentIDs);
385
	ms_list_free(cl->valid_list);
386
	ms_list_free(cl->check_list);
387
	ms_list_free(cl->triggered_checks_queue);
388
	ms_list_free(cl->losing_pairs);
389 390 391
	ms_list_free(cl->pairs);
	ms_list_free(cl->remote_candidates);
	ms_list_free(cl->local_candidates);
392
	memset(cl, 0, sizeof(IceCheckList));
393 394 395 396
	ms_free(cl);
}


397 398 399 400
/******************************************************************************
 * CANDIDATE ACCESSORS                                                        *
 *****************************************************************************/

401
const char *ice_candidate_type(const IceCandidate *candidate)
402 403 404 405
{
	return candidate_type_values[candidate->type];
}

406 407 408 409 410 411 412 413 414 415 416 417
/******************************************************************************
 * CANDIDATE PAIR ACCESSORS                                                   *
 *****************************************************************************/

static void ice_pair_set_state(IceCandidatePair *pair, IceCandidatePairState state)
{
	if (pair->state != state) {
		pair->state = state;
	}
}


418 419 420 421
/******************************************************************************
 * CHECK LIST ACCESSORS                                                       *
 *****************************************************************************/

422
IceCheckListState ice_check_list_state(const IceCheckList* cl)
423 424 425 426
{
	return cl->state;
}

427
static IceCheckList * ice_find_check_list_from_state(const IceSession *session, IceCheckListState state)
428
{
429 430
	int i;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
Simon Morlat's avatar
Simon Morlat committed
431
		if (session->streams[i] && ice_check_list_state(session->streams[i]) == state) return session->streams[i];
432 433
	}
	return NULL;
434 435
}

436 437
void ice_check_list_set_state(IceCheckList *cl, IceCheckListState state)
{
438 439
	if (cl->state != state) {
		cl->state = state;
440 441
		if (ice_find_check_list_from_state(cl->session, ICL_Running) == NULL) {
			if (ice_find_check_list_from_state(cl->session, ICL_Failed) != NULL) {
442 443 444 445 446 447 448
				/* Set the state of the session to Failed if at least one check list is in the Failed state. */
				cl->session->state = IS_Failed;
			} else {
				/* All the check lists are in the Completed state, set the state of the session to Completed. */
				cl->session->state = IS_Completed;
			}
		}
449
	}
450 451
}

452 453 454 455 456
void ice_check_list_set_rtp_session(IceCheckList *cl, RtpSession *rtp_session)
{
	cl->rtp_session = rtp_session;
}

457
const char * ice_check_list_local_ufrag(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
458 459 460 461 462
{
	/* Do not handle media specific ufrag for the moment, so use the session local ufrag. */
	return cl->session->local_ufrag;
}

463
const char * ice_check_list_local_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
464 465 466 467 468
{
	/* Do not handle media specific pwd for the moment, so use the session local pwd. */
	return cl->session->local_pwd;
}

469
const char * ice_check_list_remote_ufrag(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
470 471 472 473 474
{
	if (cl->remote_ufrag) return cl->remote_ufrag;
	else return cl->session->remote_ufrag;
}

475
const char * ice_check_list_remote_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
476 477 478 479 480
{
	if (cl->remote_pwd) return cl->remote_pwd;
	else return cl->session->remote_pwd;
}

481
static int ice_find_default_local_candidate(const IceCandidate *candidate, const uint16_t *componentID)
482
{
483
	return !((candidate->componentID == *componentID) && (candidate->is_default == TRUE));
484 485
}

486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
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;
}

504 505 506 507 508
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);
}

509 510 511 512 513 514 515 516 517 518
const char* ice_check_list_get_remote_ufrag(const IceCheckList *cl)
{
	return cl->remote_ufrag;
}

const char* ice_check_list_get_remote_pwd(const IceCheckList *cl)
{
	return cl->remote_pwd;
}

519
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)
520 521
{
	IceCandidate *candidate = NULL;
522 523 524 525 526 527 528 529 530 531 532 533 534
	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;
535 536 537 538
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
539 540 541 542
	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;
543 544
}

545
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
546 547
{
	IceCandidate *candidate = NULL;
548 549 550 551 552 553
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
554
	rtp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
555 556
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
557
	rtcp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
558 559 560 561 562

	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;
563 564 565 566
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
567 568 569 570 571
	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
572 573
}

574
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)
575 576 577 578 579 580 581 582
{
	IceCandidate *candidate = NULL;
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
583
	rtp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
584 585
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
586
	rtcp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
587 588 589 590 591 592 593 594 595 596 597 598 599

	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;
}

600 601 602 603 604 605 606 607 608
static int ice_find_host_pair_identical_to_reflexive_pair(const IceCandidatePair *p1, const IceCandidatePair *p2)
{
	return !((ice_compare_transport_addresses(&p1->local->taddr, &p2->local->taddr) == 0)
		&& (p1->local->componentID == p2->local->componentID)
		&& (ice_compare_transport_addresses(&p1->remote->taddr, &p2->remote->taddr) == 0)
		&& (p1->remote->componentID == p2->remote->componentID)
		&& (p1->remote->type == ICT_HostCandidate));
}

609 610
IceCandidateType ice_check_list_selected_valid_candidate_type(const IceCheckList *cl)
{
611 612
	IceCandidatePair *pair = NULL;
	IceCandidateType type = ICT_RelayedCandidate;
613 614 615 616
	MSList *elem;
	uint16_t componentID = 1;

	elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
	if (elem == NULL) return type;
	pair = ((IceValidCandidatePair *)elem->data)->valid;
	type = pair->remote->type;
	/**
	 * If the pair is reflexive, check if there is a pair with the same addresses and componentID that is of host type to
	 * report host connection instead of reflexive connection. This might happen if the ICE checks discover reflexives
	 * candidates before the signaling layer has communicated the host candidates to the other peer.
	 */
	if ((type == ICT_ServerReflexiveCandidate) || (type == ICT_PeerReflexiveCandidate)) {
		elem = ms_list_find_custom(cl->pairs, (MSCompareFunc)ice_find_host_pair_identical_to_reflexive_pair, pair);
		if (elem != NULL) {
			type = ((IceCandidatePair *)elem->data)->remote->type;
		}
	}
	return type;
632 633
}

634 635 636 637 638 639 640 641 642 643 644 645 646 647
void ice_check_list_check_completed(IceCheckList *cl)
{
	CheckList_Bool cb;

	if (cl->state != ICL_Completed) {
		cb.cl = cl;
		cb.result = TRUE;
		ms_list_for_each2(cl->local_componentIDs, (void (*)(void*,void*))ice_find_selected_valid_pair_for_componentID, &cb);
		if (cb.result == TRUE) {
			ice_check_list_set_state(cl, ICL_Completed);
		}
	}
}

648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
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;
}

671 672 673 674 675 676 677 678 679 680 681
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
682 683 684 685 686
bool_t ice_check_list_is_mismatch(const IceCheckList *cl)
{
	return cl->mismatch;
}

Ghislain MARY's avatar
Ghislain MARY committed
687 688 689 690 691

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

692 693
IceCheckList * ice_session_check_list(const IceSession *session, unsigned int n)
{
694 695
	if (n >= ICE_SESSION_MAX_CHECK_LISTS) return NULL;
	return session->streams[n];
696 697
}

698
const char * ice_session_local_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
699 700 701 702
{
	return session->local_ufrag;
}

703
const char * ice_session_local_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
704 705 706 707
{
	return session->local_pwd;
}

708
const char * ice_session_remote_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
709 710 711 712
{
	return session->remote_ufrag;
}

713
const char * ice_session_remote_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
714 715 716 717
{
	return session->remote_pwd;
}

718 719 720 721 722 723 724
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)
{
725 726 727 728 729
	int i;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if (session->streams[i] != NULL)
			ice_check_list_compute_pair_priorities(session->streams[i]);
	}
730 731
}

732 733 734 735 736 737
IceSessionState ice_session_state(const IceSession *session)
{
	return session->state;
}

IceRole ice_session_role(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
738 739 740 741
{
	return session->role;
}

Ghislain MARY's avatar
Ghislain MARY committed
742 743
void ice_session_set_role(IceSession *session, IceRole role)
{
744 745 746 747 748
	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
749 750
}

Ghislain MARY's avatar
Ghislain MARY committed
751 752 753 754 755
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);
}

756 757 758 759 760 761 762 763
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
764 765 766 767 768
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
769 770 771 772 773
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
774 775 776 777 778 779
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
780 781 782 783 784

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

785 786
int ice_session_nb_check_lists(IceSession *session)
{
787 788 789 790 791 792
	int i;
	int nb = 0;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if (session->streams[i] != NULL) nb++;
	}
	return nb;
793 794 795 796
}

bool_t ice_session_has_completed_check_list(const IceSession *session)
{
797 798 799 800 801 802
	int i;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if ((session->streams[i] != NULL) && (ice_check_list_state(session->streams[i]) == ICL_Completed))
			return TRUE;
	}
	return FALSE;
803 804
}

805
void ice_session_add_check_list(IceSession *session, IceCheckList *cl, unsigned int idx)
806
{
807 808 809 810 811 812 813 814 815
	if (idx >= ICE_SESSION_MAX_CHECK_LISTS) {
		ms_error("ice_session_add_check_list: Wrong idx parameter");
		return;
	}
	if (session->streams[idx] != NULL) {
		ms_error("ice_session_add_check_list: Existing check list at index %u, remove it first", idx);
		return;
	}
	session->streams[idx] = cl;
Ghislain MARY's avatar
Ghislain MARY committed
816
	cl->session = session;
817 818 819
	if (cl->state == ICL_Running) {
		session->state = IS_Running;
	}
820 821
}

822 823
void ice_session_remove_check_list(IceSession *session, IceCheckList *cl)
{
824
	int i;
825 826
	bool_t keep_session_state = FALSE;

827 828 829 830 831 832 833
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if ((session->streams[i] != NULL) && (session->streams[i] == cl)) {
			ice_check_list_destroy(cl);
			session->streams[i] = NULL;
			break;
		}
	}
834 835 836 837 838 839 840 841 842 843

	// If all remaining check lists have completed set the session state to completed
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if ((session->streams[i] != NULL) && (ice_check_list_state(session->streams[i]) != ICL_Completed)) {
			keep_session_state = TRUE;
		}
	}
	if (!keep_session_state) {
		session->state = IS_Completed;
	}
844 845 846 847 848 849 850 851 852 853 854
}

void ice_session_remove_check_list_from_idx(IceSession *session, unsigned int idx) {
	if (idx >= ICE_SESSION_MAX_CHECK_LISTS) {
		ms_error("ice_session_remove_check_list_from_idx: Wrong idx parameter");
		return;
	}
	if (session->streams[idx] != NULL) {
		ice_check_list_destroy(session->streams[idx]);
		session->streams[idx] = NULL;
	}
855 856
}

Ghislain MARY's avatar
Ghislain MARY committed
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877
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)
{
878 879 880 881 882
	int i;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if (session->streams[i] != NULL)
			ice_check_list_check_mismatch(session->streams[i]);
	}
Ghislain MARY's avatar
Ghislain MARY committed
883 884
}

885

886 887 888 889
/******************************************************************************
 * CANDIDATES GATHERING                                                       *
 *****************************************************************************/

890 891 892
bool_t ice_check_list_candidates_gathered(const IceCheckList *cl)
{
	return cl->gathering_finished;
893 894 895 896
}

bool_t ice_session_candidates_gathered(const IceSession *session)
{
897 898 899 900 901 902
	int i;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if ((session->streams[i] != NULL) && (ice_check_list_candidates_gathered(session->streams[i]) != TRUE))
			return FALSE;
	}
	return TRUE;
903 904
}

905
static bool_t ice_check_list_gathering_needed(const IceCheckList *cl)
906
{
907 908 909
	if (cl->gathering_finished == FALSE)
		return TRUE;
	return FALSE;
910 911
}

912
static void ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si)
913 914
{
	IceStunServerCheck *check;
915
	RtpTransport *rtptp=NULL;
916
	MSTimeSpec curtime = ice_current_time();
917

918
	if ((cl->rtp_session != NULL) && (cl->gathering_candidates == FALSE) && (cl->state != ICL_Completed) && (ice_check_list_candidates_gathered(cl) == FALSE)) {
919
		cl->gathering_candidates = TRUE;
920
		cl->gathering_start_time = curtime;
921 922
		rtp_session_get_transports(cl->rtp_session,&rtptp,NULL);
		if (rtptp) {
923
			check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
924
			check->rtptp = rtptp;
925
			check->srcport = rtp_session_get_local_port(cl->rtp_session);
926
			if (si->index == 0) {
927
				check->next_transmission_time = ice_add_ms(curtime, ICE_DEFAULT_RTO_DURATION);
928
				ice_send_stun_server_binding_request(rtptp, (struct sockaddr *)&cl->session->ss, cl->session->ss_len, check);
929
			} else {
930
				check->next_transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION);
931 932
			}
			cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
jehan's avatar
jehan committed
933 934
		} else {
			ms_error("ice: no rtp socket found for session [%p]",cl->rtp_session);
935
		}
936 937 938
		rtptp=NULL;
		rtp_session_get_transports(cl->rtp_session,NULL,&rtptp);
		if (rtptp) {
939
			check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
940
			check->rtptp = rtptp;
941
			check->srcport = rtp_session_get_local_rtcp_port(cl->rtp_session);
942
			check->next_transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION + ICE_DEFAULT_TA_DURATION);
943
			cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
jehan's avatar
jehan committed
944 945
		}else {
			ms_message("ice: no rtcp socket found for session [%p]",cl->rtp_session);
946
		}
947
		si->index++;
jehan's avatar
jehan committed
948 949
	} else {
		ms_message("ice: candidate gathering skipped for rtp session [%p] with check list [%p] in state [%s]",cl->rtp_session,cl,ice_check_list_state_to_string(cl->state));
950 951 952
	}
}

953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
static bool_t ice_session_gathering_needed(const IceSession *session) {
	int i;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if ((session->streams[i] != NULL) && (ice_check_list_gathering_needed(session->streams[i]) == TRUE))
			return TRUE;
	}
	return FALSE;
}

static IceCheckList * ice_session_first_check_list(const IceSession *session) {
	int i;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if (session->streams[i] != NULL)
			return session->streams[i];
	}
	return NULL;
}

void ice_session_gather_candidates(IceSession *session, const struct sockaddr* ss, socklen_t ss_len)
972
{
973
	Session_Index si;
974
	OrtpEvent *ev;
975 976
	int i;

977
	memcpy(&session->ss,ss,ss_len);
978
	session->ss_len = ss_len;
979 980
	si.session = session;
	si.index = 0;
981
	ms_get_cur_time(&session->gathering_start_ts);
982 983 984 985 986
	if (ice_session_gathering_needed(session) == TRUE) {
		for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
			if (session->streams[i] != NULL)
				ice_check_list_gather_candidates(session->streams[i], &si);
		}
987 988 989 990 991
	} else {
		/* Notify end of gathering since it has already been done. */
		ev = ortp_event_new(ORTP_EVENT_ICE_GATHERING_FINISHED);
		ortp_event_get_data(ev)->info.ice_processing_successful = TRUE;
		session->gathering_end_ts = session->gathering_start_ts;
992
		rtp_session_dispatch_event(ice_session_first_check_list(session)->rtp_session, ev);
993
	}
994 995
}

996 997 998 999 1000 1001 1002
int ice_session_gathering_duration(IceSession *session)
{
	if ((session->gathering_start_ts.tv_sec == -1) || (session->gathering_end_ts.tv_sec == -1)) return -1;
	return ((session->gathering_end_ts.tv_sec - session->gathering_start_ts.tv_sec) * 1000.0)
		+ ((session->gathering_end_ts.tv_nsec - session->gathering_start_ts.tv_nsec) / 1000000.0);
}

1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
static void ice_transaction_sum_gathering_round_trip_time(const IceStunServerCheckTransaction *transaction, StunRequestRoundTripTime *rtt)
{
	if ((transaction->response_time.tv_sec != 0) && (transaction->response_time.tv_nsec != 0)) {
		rtt->nb_responses++;
		rtt->sum += ice_compare_time(transaction->response_time, transaction->request_time);
	}
}

static void ice_stun_server_check_sum_gathering_round_trip_time(const IceStunServerCheck *check, StunRequestRoundTripTime *rtt)
{
	ms_list_for_each2(check->transactions, (void (*)(void*,void*))ice_transaction_sum_gathering_round_trip_time, rtt);
}

static void ice_check_list_sum_gathering_round_trip_times(const IceCheckList *cl, StunRequestRoundTripTime *rtt)
{
	ms_list_for_each2(cl->stun_server_checks, (void (*)(void*,void*))ice_stun_server_check_sum_gathering_round_trip_time, rtt);
}

int ice_session_average_gathering_round_trip_time(IceSession *session)
{
	StunRequestRoundTripTime rtt;
1024
	int i;
1025 1026 1027

	if ((session->gathering_start_ts.tv_sec == -1) || (session->gathering_end_ts.tv_sec == -1)) return -1;
	memset(&rtt, 0, sizeof(rtt));
1028 1029 1030 1031
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if (session->streams[i] != NULL)
			ice_check_list_sum_gathering_round_trip_times(session->streams[i], &rtt);
	}
1032 1033 1034 1035
	if (rtt.nb_responses == 0) return -1;
	return (rtt.sum / rtt.nb_responses);
}

1036

1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
/******************************************************************************
 * 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;

1052 1053
	if (cl->state != ICL_Completed) return;

1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
	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)
{
1065 1066 1067 1068 1069
	int i;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if (session->streams[i] != NULL)
			ice_check_list_select_candidates(session->streams[i]);
	}
1070 1071 1072
}


1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098
/******************************************************************************
 * TRANSACTION HANDLING                                                       *
 *****************************************************************************/

static IceTransaction * ice_create_transaction(IceCheckList *cl, IceCandidatePair *pair, const UInt96 *tr_id)
{
	IceTransaction *transaction = ms_new0(IceTransaction, 1);
	transaction->pair = pair;
	memcpy(&transaction->transactionID, tr_id, sizeof(transaction->transactionID));
	cl->transaction_list = ms_list_prepend(cl->transaction_list, transaction);
	return transaction;
}

static int ice_find_transaction_from_pair(const IceTransaction *transaction, const IceCandidatePair *pair)
{
	return (transaction->pair != pair);
}

static IceTransaction * ice_find_transaction(const IceCheckList *cl, const IceCandidatePair *pair)
{
	MSList *elem = ms_list_find_custom(cl->transaction_list, (MSCompareFunc)ice_find_transaction_from_pair, pair);
	if (elem == NULL) return NULL;
	return (IceTransaction *)elem->data;
}


1099 1100 1101 1102
/******************************************************************************
 * STUN PACKETS HANDLING                                                      *
 *****************************************************************************/

1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
static int ice_send_message_to_socket(const RtpTransport * rtpt,char* buf,size_t len, const struct sockaddr *to, socklen_t tolen) {
	/*rtp_session_get_transports(rtp_session,&rtpt,NULL);*/
	return meta_rtp_transport_modifier_inject_packet_to(rtpt
														, NULL
														,rtp_session_create_packet_raw((const uint8_t *)buf, len)
														, 0
														,to
														,tolen);
}

static int ice_send_message_to_stun_addr(const RtpTransport * rtpt,char* buff,size_t len, StunAddress4 *dest) {
	struct sockaddr_in dest_addr;
	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.sin_addr.s_addr = htonl(dest->addr);
	dest_addr.sin_port = htons(dest->port);
	dest_addr.sin_family = AF_INET;
	return ice_send_message_to_socket(rtpt, buff, len,(struct sockaddr *)&dest_addr, sizeof(dest_addr));
}

static void ice_send_stun_server_binding_request(RtpTransport *rtptp, const struct sockaddr *server, socklen_t addrlen, IceStunServerCheck *check)
1123 1124 1125 1126 1127 1128
{
	StunMessage msg;
	StunAtrString username;
	StunAtrString password;
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
1129
	char tr_id_str[25];
1130 1131 1132 1133

	memset(&msg, 0, sizeof(StunMessage));
	memset(&username,0,sizeof(username));
	memset(&password,0,sizeof(password));
1134
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, 0);
1135 1136
	len = stunEncodeMessage(&msg, buf, len, &password);
	if (len > 0) {
1137 1138 1139 1140
		IceStunServerCheckTransaction *transaction = ms_new0(IceStunServerCheckTransaction, 1);
		transaction->request_time = ice_current_time();
		memcpy(&transaction->transactionID, &msg.msgHdr.tr_id, sizeof(transaction->transactionID));
		check->transactions = ms_list_append(check->transactions, transaction);
1141
		transactionID2string(&msg.msgHdr.tr_id, tr_id_str);
1142
		ms_message("ice: Send STUN binding request from port %u [%s]", check->srcport, tr_id_str);
1143
		ice_send_message_to_socket(rtptp, buf, len, server, addrlen);
jehan's avatar
jehan committed
1144 1145
	} else {
		ms_error("ice: encoding stun binding request from port %u [%s] failed", check->srcport, tr_id_str);
1146 1147 1148 1149 1150
	}
}

static int ice_parse_stun_server_binding_response(const StunMessage *msg, char *addr, int addr_len, int *port)
{
1151
	struct sockaddr_in addr_in;
1152
	memset(&addr_in,0,sizeof(addr_in));
1153 1154 1155

	if (msg->hasXorMappedAddress) {
		*port = msg->xorMappedAddress.ipv4.port;
1156
		addr_in.sin_addr.s_addr = htonl(msg->xorMappedAddress.ipv4.addr);
1157 1158
	} else if (msg->hasMappedAddress) {
		*port = msg->mappedAddress.ipv4.port;
1159
		addr_in.sin_addr.s_addr = htonl(msg->mappedAddress.ipv4.addr);
1160 1161
	} else return -1;

1162 1163 1164
	addr_in.sin_family = AF_INET;
	addr_in.sin_port = htons(*port);
	ice_inet_ntoa((struct sockaddr *)&addr_in, sizeof(addr_in), addr, addr_len);
1165 1166 1167
	return 0;
}

1168
/* Send a STUN binding request for ICE connectivity checks according to 7.1.2. */
1169
static void ice_send_binding_request(IceCheckList *cl, IceCandidatePair *pair, const RtpSession *rtp_session)
1170 1171 1172
{
	StunMessage msg;
	StunAddress4 dest;
1173 1174
	StunAtrString username;
	StunAtrString password;
1175
	IceTransaction *transaction;
1176 1177
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
1178
	RtpTransport *rtptp;
Ghislain MARY's avatar
Ghislain MARY committed
1179
	char tr_id_str[25];
1180

1181 1182
	transaction = ice_find_transaction(cl, pair);

1183
	if (pair->state == ICP_InProgress) {
1184 1185 1186 1187
		if (transaction == NULL) {
			ms_error("ice: No transaction found for InProgress pair");
			return;
		}
1188 1189 1190 1191
		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;
1192 1193 1194 1195
			if (pair->use_candidate == FALSE) {
				ice_pair_set_state(pair, ICP_Waiting);
				ice_check_list_queue_triggered_check(cl, pair);
			}
1196 1197
			return;
		}
1198 1199 1200 1201 1202 1203 1204 1205 1206
		/* 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;
	}
1207
	pair->transmission_time = ice_current_time();
1208

1209
	if (pair->local->componentID == 1) {
1210
		rtp_session_get_transports(rtp_session,&rtptp,NULL);
1211
	} else if (pair->local->componentID == 2) {
1212
		rtp_session_get_transports(rtp_session,NULL,&rtptp);
1213 1214
	} else return;

1215 1216 1217 1218 1219
	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);

1220 1221
	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
1222
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, 1);
1223
	msg.hasMessageIntegrity = TRUE;
1224
	msg.hasFingerprint = TRUE;
1225 1226 1227 1228 1229 1230

	/* 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. */
1231
	if ((cl->session->role == IR_Controlling) && (pair->use_candidate == TRUE)) {
1232 1233 1234
		msg.hasUseCandidate = TRUE;
	}

1235
	/* Include the ICE-CONTROLLING or ICE-CONTROLLED attribute depending on the role of the agent, as defined in 7.1.2.2. */
1236
	switch (cl->session->role) {
1237 1238
		case IR_Controlling:
			msg.hasIceControlling = TRUE;
1239
			msg.iceControlling.value = cl->session->tie_breaker;
1240 1241 1242
			break;
		case IR_Controlled:
			msg.hasIceControlled = TRUE;
1243
			msg.iceControlled.value = cl->session->tie_breaker;
1244 1245
			break;
	}
1246

1247 1248
	/* Keep the same transaction ID for retransmission. */
	if (pair->state == ICP_InProgress) {
1249 1250 1251
		memcpy(&msg.msgHdr.tr_id, &transaction->transactionID, sizeof(msg.msgHdr.tr_id));
	} else {
		transaction = ice_create_transaction(cl, pair, &msg.msgHdr.tr_id);
1252 1253
	}

1254 1255 1256
	/*for backward compatibility*/
	msg.hasDummyMessageIntegrity=pair->use_dummy_hmac;

1257
	len = stunEncodeMessage(&msg, buf, len, &password);
1258
	if (len > 0) {
1259
		transactionID2string(&transaction->transactionID, tr_id_str);
Ghislain MARY's avatar
Ghislain MARY committed
1260 1261 1262 1263 1264 1265 1266 1267 1268
		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);
		}
1269 1270

		ice_send_message_to_stun_addr(rtptp, buf, len, &dest);
1271

1272
		if (pair->state != ICP_InProgress) {
1273 1274 1275
			/* First transmission of the request, initialize the retransmission timer. */
			pair->rto = ICE_DEFAULT_RTO_DURATION;
			pair->retransmissions = 0;
1276 1277
			/* Save the role of the agent. */
			pair->role = cl->session->role;
1278 1279 1280
			/* Change the state of the pair. */
			ice_pair_set_state(pair, ICP_InProgress);
		}
1281 1282 1283
	}
}

1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
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;
}


1295
static int ice_get_transport_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data, RtpTransport **rtptp) {
1296
	if (evt_data->info.socket_type == OrtpRTPSocket) {
1297 1298
		rtp_session_get_transports(rtp_session, rtptp, NULL);
		return 0;
1299
	} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
1300 1301
		rtp_session_get_transports(rtp_session, NULL,rtptp);
		return 0;
1302 1303 1304 1305
	}
	return -1;
}

1306
static int ice_get_recv_port_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data)
1307 1308
{
	if (evt_data->info.socket_type == OrtpRTPSocket) {
1309
		return rtp_session_get_local_port(rtp_session);
1310
	} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
1311
		return rtp_session_get_local_rtcp_port(rtp_session);
1312 1313 1314
	} else return -1;
}

1315
static void ice_send_binding_response(IceCheckList *cl, const RtpSession *rtp_session, const OrtpEventData *evt_data, const StunMessage *msg, const StunAddress4 *dest)
1316 1317 1318 1319 1320
{
	StunMessage response;
	StunAtrString password;
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
1321
	RtpTransport *rtptp;
Ghislain MARY's avatar
Ghislain MARY committed
1322
	int recvport = ice_get_recv_port_from_rtp_session(rtp_session, evt_data);
1323 1324
	struct sockaddr_in dest_addr;
	struct sockaddr_in source_addr;
Ghislain MARY's avatar
Ghislain MARY committed
1325
	char dest_addr_str[256];
1326
	char source_addr_str[256];
Ghislain MARY's avatar
Ghislain MARY committed
1327
	char tr_id_str[25];
1328

1329 1330 1331
	ice_get_transport_from_rtp_session(rtp_session, evt_data,&rtptp);
	if (!rtptp) return;

1332
	memset(&response, 0, sizeof(response));
1333
	memset(&password, 0, sizeof(password));
1334 1335
	memset(&dest_addr,0,sizeof(dest_addr));
	memset(&source_addr,0,sizeof(source_addr));
1336 1337 1338 1339 1340 1341 1342 1343 1344

	/* 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;