ice.c 173 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
/*
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
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Ghislain MARY's avatar
Ghislain MARY committed
18 19 20
*/


21
#if !defined(_WIN32) && !defined(_WIN32_WCE)
Ghislain MARY's avatar
Ghislain MARY committed
22 23 24 25 26 27 28
#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"
34
#include <bctoolbox/port.h>
Ghislain MARY's avatar
Ghislain MARY committed
35 36


37 38
#define ICE_MAX_NB_CANDIDATES		16
#define ICE_MAX_NB_CANDIDATE_PAIRS	100
39

40 41 42
#define ICE_RTP_COMPONENT_ID	1
#define ICE_RTCP_COMPONENT_ID	2

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

56

57 58 59 60 61
typedef struct _TransportAddress_ComponentID {
	const IceTransportAddress *ta;
	uint16_t componentID;
} TransportAddress_ComponentID;

62 63 64 65 66
typedef struct _Type_ComponentID {
	IceCandidateType type;
	uint16_t componentID;
} Type_ComponentID;

67 68
typedef struct _Foundation_Pair_Priority_ComponentID {
	const IcePairFoundation *foundation;
69 70 71
	IceCandidatePair *pair;
	uint64_t priority;
	uint16_t componentID;
72
} Foundation_Pair_Priority_ComponentID;
73

74 75
typedef struct _CheckList_RtpSession {
	IceCheckList *cl;
76
	const RtpSession *rtp_session;
77 78
} CheckList_RtpSession;

79 80
typedef struct _CheckList_RtpSession_Time {
	IceCheckList *cl;
81
	const RtpSession *rtp_session;
82
	MSTimeSpec time;
83 84
} CheckList_RtpSession_Time;

85 86 87 88 89
typedef struct _CheckList_Bool {
	IceCheckList *cl;
	bool_t result;
} CheckList_Bool;

90
typedef struct _CheckList_MSListPtr {
91
	const IceCheckList *cl;
92
	bctbx_list_t **list;
93 94
} CheckList_MSListPtr;

95 96 97 98 99
typedef struct _LocalCandidate_RemoteCandidate {
	IceCandidate *local;
	IceCandidate *remote;
} LocalCandidate_RemoteCandidate;

100 101 102 103
typedef struct _TransportAddresses {
	IceTransportAddress **rtp_taddr;
	IceTransportAddress **rtcp_taddr;
} TransportAddresses;
104

105
typedef struct _Time_Bool {
106
	MSTimeSpec time;
107 108 109
	bool_t result;
} Time_Bool;

110 111 112 113 114
typedef struct _Session_Index {
	IceSession *session;
	int index;
} Session_Index;

115 116 117 118 119 120
typedef struct _LosingRemoteCandidate_InProgress_Failed {
	const IceCandidate *losing_remote_candidate;
	bool_t in_progress_candidates;
	bool_t failed_candidates;
} LosingRemoteCandidate_InProgress_Failed;

121 122 123 124 125
typedef struct _ComponentID_Family {
	uint16_t componentID;
	int family;
} ComponentID_Family;

126

127 128
static MSTimeSpec ice_current_time(void);
static MSTimeSpec ice_add_ms(MSTimeSpec orig, uint32_t ms);
129
static int32_t ice_compare_time(MSTimeSpec ts1, MSTimeSpec ts2);
Ghislain MARY's avatar
Ghislain MARY committed
130
static void transactionID2string(const UInt96 *tr_id, char *tr_id_str);
131
static IceStunServerRequest * ice_stun_server_request_new(IceCheckList *cl, MSTurnContext *turn_context, RtpTransport *rtptp, int family, const char *srcaddr, int srcport, uint16_t stun_method);
132 133 134
static void ice_stun_server_request_transaction_free(IceStunServerRequestTransaction *transaction);
static void ice_stun_server_request_free(IceStunServerRequest *request);
static IceStunServerRequestTransaction * ice_send_stun_server_request(IceStunServerRequest *request, const struct sockaddr *server, socklen_t addrlen);
135 136
static void ice_check_list_deallocate_rtp_turn_candidate(IceCheckList *cl);
static void ice_check_list_deallocate_rtcp_turn_candidate(IceCheckList *cl);
137
static void ice_check_list_deallocate_turn_candidates(IceCheckList *cl);
138
static int ice_compare_transport_addresses(const IceTransportAddress *ta1, const IceTransportAddress *ta2);
139
static int ice_compare_pair_priorities(const IceCandidatePair *p1, const IceCandidatePair *p2);
140 141
static int ice_compare_pairs(const IceCandidatePair *p1, const IceCandidatePair *p2);
static int ice_compare_candidates(const IceCandidate *c1, const IceCandidate *c2);
142
static int ice_find_host_candidate(const IceCandidate *candidate, const ComponentID_Family *cf);
143
static int ice_find_candidate_from_type_and_componentID(const IceCandidate *candidate, const Type_ComponentID *tc);
144
static int ice_find_use_candidate_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
145
static int ice_find_nominated_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
146
static int ice_find_selected_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
147
static void ice_find_selected_valid_pair_for_componentID(const uint16_t *componentID, CheckList_Bool *cb);
148
static int ice_find_pair_in_valid_list(IceValidCandidatePair *valid_pair, IceCandidatePair *pair);
149
static void ice_pair_set_state(IceCandidatePair *pair, IceCandidatePairState state);
150
static void ice_compute_candidate_foundation(IceCandidate *candidate, IceCheckList *cl);
Ghislain MARY's avatar
Ghislain MARY committed
151
static void ice_set_credentials(char **ufrag, char **pwd, const char *ufrag_str, const char *pwd_str);
152
static void ice_conclude_processing(IceCheckList* cl, RtpSession* rtp_session);
153
static void ice_check_list_stop_gathering(IceCheckList *cl);
154 155
static void ice_check_list_remove_stun_server_request(IceCheckList *cl, UInt96 *tr_id);
static IceStunServerRequest * ice_check_list_get_stun_server_request(IceCheckList *cl, UInt96 *tr_id);
156
static void ice_transport_address_to_printable_ip_address(const IceTransportAddress *taddr, char *printable_ip, size_t printable_ip_size);
157
static void ice_stun_server_request_add_transaction(IceStunServerRequest *request, IceStunServerRequestTransaction *transaction);
Ghislain MARY's avatar
Ghislain MARY committed
158 159


160 161 162 163
/******************************************************************************
 * CONSTANTS DEFINITIONS                                                      *
 *****************************************************************************/

Ghislain MARY's avatar
Ghislain MARY committed
164 165 166 167 168
static const char * const role_values[] = {
	"Controlling",	/* IR_Controlling */
	"Controlled",	/* IR_Controlled */
};

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
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 */
};

186 187 188 189 190 191 192 193 194 195
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
196 197 198
 * SESSION INITIALISATION AND DEINITIALISATION                                *
 *****************************************************************************/

199 200
static uint64_t generate_tie_breaker(void)
{
201
	return (((uint64_t)ortp_random()) << 32) | (((uint64_t)ortp_random()) & 0xffffffff);
202 203 204 205
}

static char * generate_ufrag(void)
{
Simon Morlat's avatar
Simon Morlat committed
206
	return ms_strdup_printf("%08x", (int)ortp_random());
207 208 209 210
}

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

Ghislain MARY's avatar
Ghislain MARY committed
214 215
static void ice_session_init(IceSession *session)
{
Ghislain MARY's avatar
Ghislain MARY committed
216
	session->state = IS_Stopped;
Ghislain MARY's avatar
Ghislain MARY committed
217
	session->role = IR_Controlling;
218
	session->tie_breaker = generate_tie_breaker();
219
	session->ta = ICE_DEFAULT_TA_DURATION;
Ghislain MARY's avatar
Ghislain MARY committed
220
	session->keepalive_timeout = ICE_DEFAULT_KEEPALIVE_TIMEOUT;
Ghislain MARY's avatar
Ghislain MARY committed
221
	session->max_connectivity_checks = ICE_MAX_NB_CANDIDATE_PAIRS;
222 223
	session->local_ufrag = generate_ufrag();
	session->local_pwd = generate_pwd();
Ghislain MARY's avatar
Ghislain MARY committed
224 225
	session->remote_ufrag = NULL;
	session->remote_pwd = NULL;
226
	session->send_event = FALSE;
227 228
	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;
229
	session->check_message_integrity=TRUE;
230 231 232 233
	session->default_types[0] = ICT_RelayedCandidate;
	session->default_types[1] = ICT_ServerReflexiveCandidate;
	session->default_types[2] = ICT_HostCandidate;
	session->default_types[2] = ICT_CandidateInvalid;
Ghislain MARY's avatar
Ghislain MARY committed
234 235 236 237
}

IceSession * ice_session_new(void)
{
238
	IceSession *session = ms_new0(IceSession, 1);
Ghislain MARY's avatar
Ghislain MARY committed
239
	if (session == NULL) {
240 241 242
		ms_error("ice: Memory allocation of ICE session failed");
		return NULL;
	}
Ghislain MARY's avatar
Ghislain MARY committed
243 244 245 246 247 248
	ice_session_init(session);
	return session;
}

void ice_session_destroy(IceSession *session)
{
249
	int i;
250
	if (session != NULL) {
251 252 253 254 255 256
		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;
			}
		}
257 258 259 260 261 262
		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
263 264
}

265 266 267 268
void ice_session_set_default_candidates_types(IceSession *session, const IceCandidateType types[ICT_CandidateTypeMax]){
	memcpy(session->default_types, types, sizeof(session->default_types));
}

269 270 271
void ice_session_enable_message_integrity_check(IceSession *session,bool_t enable) {
	session->check_message_integrity=enable;
}
Ghislain MARY's avatar
Ghislain MARY committed
272 273 274

/******************************************************************************
 * CHECK LIST INITIALISATION AND DEINITIALISATION                             *
275
 *****************************************************************************/
276

277 278
static void ice_check_list_init(IceCheckList *cl)
{
Ghislain MARY's avatar
Ghislain MARY committed
279
	cl->session = NULL;
280
	cl->rtp_session = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
281
	cl->remote_ufrag = cl->remote_pwd = NULL;
282
	cl->stun_server_requests = NULL;
283
	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
284
	cl->local_componentIDs = cl->remote_componentIDs = cl->foundations = NULL;
285
	cl->state = ICL_Running;
286
	cl->foundation_generator = 1;
Ghislain MARY's avatar
Ghislain MARY committed
287
	cl->mismatch = FALSE;
288
	cl->gathering_candidates = FALSE;
289
	cl->gathering_finished = FALSE;
290
	cl->nomination_delay_running = FALSE;
291
	cl->ta_time = ice_current_time();
292 293 294
	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));
295 296
}

297
IceCheckList * ice_check_list_new(void)
298
{
299
	IceCheckList *cl = ms_new0(IceCheckList, 1);
300 301 302 303 304
	if (cl == NULL) {
		ms_error("ice_check_list_new: Memory allocation failed");
		return NULL;
	}
	ice_check_list_init(cl);
305 306 307
	return cl;
}

308
static void ice_compute_pair_priority(IceCandidatePair *pair, const IceRole *role)
309 310
{
	/* Use formula defined in 5.7.2 to compute pair priority. */
Ghislain MARY's avatar
Ghislain MARY committed
311 312
	uint64_t G = 0;
	uint64_t D = 0;
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

	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)
{
329
	IceCandidatePair *pair = ms_new0(IceCandidatePair, 1);
330 331
	pair->local = local_candidate;
	pair->remote = remote_candidate;
332
	pair->state = ICP_Frozen;
333 334
	pair->is_default = FALSE;
	pair->is_nominated = FALSE;
335
	pair->use_candidate = FALSE;
336
	pair->wait_transaction_timeout = FALSE;
337 338 339 340 341 342
	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);
343
	pair->retry_with_dummy_message_integrity=!cl->session->check_message_integrity;
344 345 346
	return pair;
}

347 348 349 350 351
static void ice_free_transaction(IceTransaction *transaction)
{
	ms_free(transaction);
}

352 353 354 355 356
static void ice_free_pair_foundation(IcePairFoundation *foundation)
{
	ms_free(foundation);
}

357 358 359 360 361
static void ice_free_valid_pair(IceValidCandidatePair *valid_pair)
{
	ms_free(valid_pair);
}

362
static void ice_free_candidate_pair(IceCandidatePair *pair, IceCheckList *cl)
363
{
364 365 366
	bctbx_list_t *elem;
	while ((elem = bctbx_list_find(cl->check_list, pair)) != NULL) {
		cl->check_list = bctbx_list_remove(cl->check_list, pair);
367
	}
368
	while ((elem = bctbx_list_find_custom(cl->valid_list, (bctbx_compare_func)ice_find_pair_in_valid_list, pair)) != NULL) {
369
		ice_free_valid_pair(elem->data);
370
		cl->valid_list = bctbx_list_erase_link(cl->valid_list, elem);
371
	}
372 373 374
	ms_free(pair);
}

375 376 377 378 379
static void ice_free_candidate(IceCandidate *candidate)
{
	ms_free(candidate);
}

380 381
static void ice_check_list_destroy_turn_contexts(IceCheckList *cl) {
	ice_check_list_deallocate_turn_candidates(cl);
382 383 384 385 386 387 388 389
	if (cl->rtp_turn_context != NULL) {
		ms_turn_context_destroy(cl->rtp_turn_context);
		cl->rtp_turn_context = NULL;
	}
	if (cl->rtcp_turn_context != NULL) {
		ms_turn_context_destroy(cl->rtcp_turn_context);
		cl->rtcp_turn_context = NULL;
	}
390 391
}

392 393
void ice_check_list_destroy(IceCheckList *cl)
{
394
	ice_check_list_destroy_turn_contexts(cl);
Ghislain MARY's avatar
Ghislain MARY committed
395 396
	if (cl->remote_ufrag) ms_free(cl->remote_ufrag);
	if (cl->remote_pwd) ms_free(cl->remote_pwd);
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
	bctbx_list_for_each(cl->stun_server_requests, (void (*)(void*))ice_stun_server_request_free);
	bctbx_list_for_each(cl->transaction_list, (void (*)(void*))ice_free_transaction);
	bctbx_list_for_each(cl->foundations, (void (*)(void*))ice_free_pair_foundation);
	bctbx_list_for_each2(cl->pairs, (void (*)(void*,void*))ice_free_candidate_pair, cl);
	bctbx_list_for_each(cl->valid_list, (void (*)(void*))ice_free_valid_pair);
	bctbx_list_for_each(cl->remote_candidates, (void (*)(void*))ice_free_candidate);
	bctbx_list_for_each(cl->local_candidates, (void (*)(void*))ice_free_candidate);
	bctbx_list_free(cl->stun_server_requests);
	bctbx_list_free(cl->transaction_list);
	bctbx_list_free(cl->foundations);
	bctbx_list_free(cl->local_componentIDs);
	bctbx_list_free(cl->remote_componentIDs);
	bctbx_list_free(cl->valid_list);
	bctbx_list_free(cl->check_list);
	bctbx_list_free(cl->triggered_checks_queue);
	bctbx_list_free(cl->losing_pairs);
	bctbx_list_free(cl->pairs);
	bctbx_list_free(cl->remote_candidates);
	bctbx_list_free(cl->local_candidates);
416
	memset(cl, 0, sizeof(IceCheckList));
417 418 419 420
	ms_free(cl);
}


421

422 423 424 425
/******************************************************************************
 * CANDIDATE ACCESSORS                                                        *
 *****************************************************************************/

426
const char *ice_candidate_type(const IceCandidate *candidate)
427 428 429 430
{
	return candidate_type_values[candidate->type];
}

431 432 433 434 435 436 437 438 439 440 441 442
/******************************************************************************
 * CANDIDATE PAIR ACCESSORS                                                   *
 *****************************************************************************/

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


443 444 445 446
/******************************************************************************
 * CHECK LIST ACCESSORS                                                       *
 *****************************************************************************/

447
IceCheckListState ice_check_list_state(const IceCheckList* cl)
448 449 450 451
{
	return cl->state;
}

452
static IceCheckList * ice_find_check_list_from_state(const IceSession *session, IceCheckListState state)
453
{
454 455
	int i;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
Simon Morlat's avatar
Simon Morlat committed
456
		if (session->streams[i] && ice_check_list_state(session->streams[i]) == state) return session->streams[i];
457 458
	}
	return NULL;
459 460
}

461 462
void ice_check_list_set_state(IceCheckList *cl, IceCheckListState state)
{
463 464
	if (cl->state != state) {
		cl->state = state;
465 466
		if (ice_find_check_list_from_state(cl->session, ICL_Running) == NULL) {
			if (ice_find_check_list_from_state(cl->session, ICL_Failed) != NULL) {
467 468 469 470 471 472 473
				/* 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;
			}
		}
474
	}
475 476
}

477 478 479 480 481
void ice_check_list_set_rtp_session(IceCheckList *cl, RtpSession *rtp_session)
{
	cl->rtp_session = rtp_session;
}

482
const char * ice_check_list_local_ufrag(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
483 484 485 486 487
{
	/* Do not handle media specific ufrag for the moment, so use the session local ufrag. */
	return cl->session->local_ufrag;
}

488
const char * ice_check_list_local_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
489 490 491 492 493
{
	/* Do not handle media specific pwd for the moment, so use the session local pwd. */
	return cl->session->local_pwd;
}

494
const char * ice_check_list_remote_ufrag(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
495 496 497 498 499
{
	if (cl->remote_ufrag) return cl->remote_ufrag;
	else return cl->session->remote_ufrag;
}

500
const char * ice_check_list_remote_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
501 502 503 504 505
{
	if (cl->remote_pwd) return cl->remote_pwd;
	else return cl->session->remote_pwd;
}

506
static int ice_find_default_local_candidate(const IceCandidate *candidate, const uint16_t *componentID)
507
{
508
	return !((candidate->componentID == *componentID) && (candidate->is_default == TRUE));
509 510
}

511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
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;
}

529 530 531 532 533
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);
}

534 535 536 537 538 539 540 541 542 543
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;
}

544
bool_t ice_check_list_default_local_candidate(const IceCheckList *cl, IceCandidate **rtp_candidate, IceCandidate **rtcp_candidate) {
545
	uint16_t componentID;
546
	bctbx_list_t *elem;
547 548 549

	if (rtp_candidate != NULL) {
		componentID = 1;
550
		elem = bctbx_list_find_custom(cl->local_candidates, (bctbx_compare_func)ice_find_default_local_candidate, &componentID);
551 552 553 554 555
		if (elem == NULL) return FALSE;
		*rtp_candidate = (IceCandidate *)elem->data;
	}
	if (rtcp_candidate != NULL) {
		componentID = 2;
556
		elem = bctbx_list_find_custom(cl->local_candidates, (bctbx_compare_func)ice_find_default_local_candidate, &componentID);
Ghislain MARY's avatar
Ghislain MARY committed
557
		if (elem == NULL) return FALSE;
558 559
		*rtcp_candidate = (IceCandidate *)elem->data;
	}
560

561
	return TRUE;
Ghislain MARY's avatar
Ghislain MARY committed
562 563
}

564
bool_t ice_check_list_selected_valid_local_candidate(const IceCheckList *cl, IceCandidate **rtp_candidate, IceCandidate **rtcp_candidate) {
565 566
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
567
	bctbx_list_t *elem;
568 569 570

	if (rtp_candidate != NULL) {
		componentID = 1;
571
		elem = bctbx_list_find_custom(cl->valid_list, (bctbx_compare_func)ice_find_selected_valid_pair_from_componentID, &componentID);
572 573 574 575 576
		if (elem == NULL) return FALSE;
		valid_pair = (IceValidCandidatePair *)elem->data;
		*rtp_candidate = valid_pair->valid->local;
	}
	if (rtcp_candidate != NULL) {
577 578 579 580 581
		if (rtp_session_rtcp_mux_enabled(cl->rtp_session)) {
			componentID = 1;
		} else {
			componentID = 2;
		}
582
		elem = bctbx_list_find_custom(cl->valid_list, (bctbx_compare_func)ice_find_selected_valid_pair_from_componentID, &componentID);
583 584 585 586 587
		if (elem == NULL) return FALSE;
		valid_pair = (IceValidCandidatePair *)elem->data;
		*rtcp_candidate = valid_pair->valid->local;
	}

588 589 590
	return TRUE;
}

591
bool_t ice_check_list_selected_valid_remote_candidate(const IceCheckList *cl, IceCandidate **rtp_candidate, IceCandidate **rtcp_candidate) {
592 593
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
594
	bctbx_list_t *elem;
595 596 597

	if (rtp_candidate != NULL) {
		componentID = 1;
598
		elem = bctbx_list_find_custom(cl->valid_list, (bctbx_compare_func)ice_find_selected_valid_pair_from_componentID, &componentID);
599 600 601
		if (elem == NULL) return FALSE;
		valid_pair = (IceValidCandidatePair *)elem->data;
		*rtp_candidate = valid_pair->valid->remote;
602
	}
603 604 605 606 607 608
	if (rtcp_candidate != NULL) {
		if (rtp_session_rtcp_mux_enabled(cl->rtp_session)) {
			componentID = 1;
		} else {
			componentID = 2;
		}
609
		elem = bctbx_list_find_custom(cl->valid_list, (bctbx_compare_func)ice_find_selected_valid_pair_from_componentID, &componentID);
610 611 612 613 614
		if (elem == NULL) return FALSE;
		valid_pair = (IceValidCandidatePair *)elem->data;
		*rtcp_candidate = valid_pair->valid->remote;
	}

615 616 617
	return TRUE;
}

618 619 620 621 622 623 624 625 626
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));
}

627 628
IceCandidateType ice_check_list_selected_valid_candidate_type(const IceCheckList *cl)
{
629 630
	IceCandidatePair *pair = NULL;
	IceCandidateType type = ICT_RelayedCandidate;
631
	bctbx_list_t *elem;
632 633
	uint16_t componentID = 1;

634
	elem = bctbx_list_find_custom(cl->valid_list, (bctbx_compare_func)ice_find_selected_valid_pair_from_componentID, &componentID);
635 636
	if (elem == NULL) return type;
	pair = ((IceValidCandidatePair *)elem->data)->valid;
637
	if (pair->local->type == ICT_RelayedCandidate) return ICT_RelayedCandidate;
638 639 640 641 642 643 644
	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)) {
645
		elem = bctbx_list_find_custom(cl->pairs, (bctbx_compare_func)ice_find_host_pair_identical_to_reflexive_pair, pair);
646 647 648 649 650
		if (elem != NULL) {
			type = ((IceCandidatePair *)elem->data)->remote->type;
		}
	}
	return type;
651 652
}

653 654 655 656 657 658 659
void ice_check_list_check_completed(IceCheckList *cl)
{
	CheckList_Bool cb;

	if (cl->state != ICL_Completed) {
		cb.cl = cl;
		cb.result = TRUE;
660
		bctbx_list_for_each2(cl->local_componentIDs, (void (*)(void*,void*))ice_find_selected_valid_pair_for_componentID, &cb);
661 662 663 664 665 666
		if (cb.result == TRUE) {
			ice_check_list_set_state(cl, ICL_Completed);
		}
	}
}

667 668
static void ice_check_list_queue_triggered_check(IceCheckList *cl, IceCandidatePair *pair)
{
669
	bctbx_list_t *elem = bctbx_list_find(cl->triggered_checks_queue, pair);
670 671 672
	if (elem != NULL) {
		/* The pair is already in the triggered checks queue, do not add it again. */
	} else {
673
		cl->triggered_checks_queue = bctbx_list_append(cl->triggered_checks_queue, pair);
674 675 676 677 678 679 680
	}
}

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

681 682
	if (bctbx_list_size(cl->triggered_checks_queue) == 0) return NULL;
	pair = bctbx_list_nth_data(cl->triggered_checks_queue, 0);
683 684
	if (pair != NULL) {
		/* Remove the first element in the triggered checks queue. */
685
		cl->triggered_checks_queue = bctbx_list_erase_link(cl->triggered_checks_queue, cl->triggered_checks_queue);
686 687 688 689
	}
	return pair;
}

690 691 692 693 694 695 696
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)
{
697
	bctbx_list_t *elem = bctbx_list_find_custom(cl->check_list, (bctbx_compare_func)ice_find_non_frozen_pair, NULL);
698 699 700
	return (elem == NULL);
}

Ghislain MARY's avatar
Ghislain MARY committed
701 702 703 704 705
bool_t ice_check_list_is_mismatch(const IceCheckList *cl)
{
	return cl->mismatch;
}

Ghislain MARY's avatar
Ghislain MARY committed
706 707 708 709 710

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

711
IceCheckList * ice_session_check_list(const IceSession *session, int n)
712
{
François Grisez's avatar
François Grisez committed
713
	if (n >= ICE_SESSION_MAX_CHECK_LISTS) return NULL;
714
	return session->streams[n];
715 716
}

717
const char * ice_session_local_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
718 719 720 721
{
	return session->local_ufrag;
}

722
const char * ice_session_local_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
723 724 725 726
{
	return session->local_pwd;
}

727
const char * ice_session_remote_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
728 729 730 731
{
	return session->remote_ufrag;
}

732
const char * ice_session_remote_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
733 734 735 736
{
	return session->remote_pwd;
}

737 738
static void ice_check_list_compute_pair_priorities(IceCheckList *cl)
{
739
	bctbx_list_for_each2(cl->pairs, (void (*)(void*,void*))ice_compute_pair_priority, &cl->session->role);
740 741 742 743
}

static void ice_session_compute_pair_priorities(IceSession *session)
{
744 745 746 747 748
	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]);
	}
749 750
}

751 752 753 754 755 756
IceSessionState ice_session_state(const IceSession *session)
{
	return session->state;
}

IceRole ice_session_role(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
757 758 759 760
{
	return session->role;
}

Ghislain MARY's avatar
Ghislain MARY committed
761 762
void ice_session_set_role(IceSession *session, IceRole role)
{
763 764 765 766 767
	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
768 769
}

Ghislain MARY's avatar
Ghislain MARY committed
770 771 772 773 774
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);
}

775 776 777 778 779 780 781 782
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
783 784 785 786 787
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
788 789 790 791 792
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
793 794 795 796 797 798
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
799 800 801 802 803

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

804 805
int ice_session_nb_check_lists(IceSession *session)
{
806 807 808 809 810 811
	int i;
	int nb = 0;
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if (session->streams[i] != NULL) nb++;
	}
	return nb;
812 813 814 815
}

bool_t ice_session_has_completed_check_list(const IceSession *session)
{
816 817 818 819 820 821
	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;
822 823
}

824
void ice_session_add_check_list(IceSession *session, IceCheckList *cl, unsigned int idx)
825
{
826 827 828 829 830 831 832 833 834
	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
835
	cl->session = session;
836 837 838
	if (cl->state == ICL_Running) {
		session->state = IS_Running;
	}
839 840
}

841 842
void ice_session_remove_check_list(IceSession *session, IceCheckList *cl)
{
843
	int i;
844 845
	bool_t keep_session_state = FALSE;

846 847 848 849 850 851 852
	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;
		}
	}
853 854 855 856 857 858 859 860 861 862

	// 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;
	}
863 864 865 866 867 868 869 870 871 872 873
}

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

Ghislain MARY's avatar
Ghislain MARY committed
876 877 878 879 880 881 882
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)
{
883
	bctbx_list_t *elem = bctbx_list_find_custom(cl->remote_candidates, (bctbx_compare_func)ice_find_default_candidate_from_componentID, componentID);
Ghislain MARY's avatar
Ghislain MARY committed
884 885 886 887 888 889 890 891
	if (elem == NULL) {
		cl->mismatch = TRUE;
		cl->state = ICL_Failed;
	}
}

static void ice_check_list_check_mismatch(IceCheckList *cl)
{
892
	bctbx_list_for_each2(cl->remote_componentIDs, (void (*)(void*,void*))ice_find_default_remote_candidate_for_componentID, cl);
Ghislain MARY's avatar
Ghislain MARY committed
893 894 895 896
}

void ice_session_check_mismatch(IceSession *session)
{
897 898 899 900 901
	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
902 903
}

904

905 906 907 908
/******************************************************************************
 * CANDIDATES GATHERING                                                       *
 *****************************************************************************/

909 910 911
bool_t ice_check_list_candidates_gathered(const IceCheckList *cl)
{
	return cl->gathering_finished;
912 913 914 915
}

bool_t ice_session_candidates_gathered(const IceSession *session)
{
916 917 918 919 920 921
	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;
922 923
}

924
static bool_t ice_check_list_gathering_needed(const IceCheckList *cl)
925
{
926 927 928
	if (cl->gathering_finished == FALSE)
		return TRUE;
	return FALSE;
929 930
}

931
static void ice_check_list_add_stun_server_request(IceCheckList *cl, IceStunServerRequest *request) {
932
	cl->stun_server_requests = bctbx_list_append(cl->stun_server_requests, request);
933 934
}

935
static bool_t ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si)
936
{
937
	IceStunServerRequest *request;
938
	RtpTransport *rtptp=NULL;
939
	MSTimeSpec curtime = ice_current_time();
940 941
	char source_addr_str[64];
	int source_port = 0;
942

943
	if ((cl->rtp_session != NULL) && (cl->gathering_candidates == FALSE) && (cl->state != ICL_Completed) && (ice_check_list_candidates_gathered(cl) == FALSE)) {
944
		cl->gathering_candidates = TRUE;
945
		cl->gathering_start_time = curtime;
946 947
		rtp_session_get_transports(cl->rtp_session,&rtptp,NULL);
		if (rtptp) {
948
			struct sockaddr *sa = (struct sockaddr *)&cl->rtp_session->rtp.gs.loc_addr;
949 950 951
			if (cl->session->turn_enabled) {
				/* Define the RTP endpoint that will perform STUN encapsulation/decapsulation for TURN data */
				meta_rtp_transport_set_endpoint(rtptp, ms_turn_context_create_endpoint(cl->rtp_turn_context));
952
				ms_turn_context_set_server_addr(cl->rtp_turn_context, (struct sockaddr *)&cl->session->ss, cl->session->ss_len);
953
			}
954
			memset(source_addr_str, 0, sizeof(source_addr_str));
955 956
			bctbx_sockaddr_to_ip_address(sa, cl->rtp_session->rtp.gs.loc_addrlen, source_addr_str, sizeof(source_addr_str), &source_port);
			request = ice_stun_server_request_new(cl, cl->rtp_turn_context, rtptp, sa->sa_family, source_addr_str, source_port,
957
				cl->session->turn_enabled ? MS_TURN_METHOD_ALLOCATE : MS_STUN_METHOD_BINDING);
958
			request->gathering = TRUE;
959
			if (si->index == 0) {
960 961 962 963
				IceStunServerRequestTransaction *transaction = NULL;
				request->next_transmission_time = ice_add_ms(curtime, ICE_DEFAULT_RTO_DURATION);
				if (cl->session->turn_enabled) ms_turn_context_set_state(cl->rtp_turn_context, MS_TURN_CONTEXT_STATE_CREATING_ALLOCATION);
				transaction = ice_send_stun_server_request(request, (struct sockaddr *)&cl->session->ss, cl->session->ss_len);
964
				ice_stun_server_request_add_transaction(request, transaction);
965
			} else {
966
				request->next_transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION);
967
			}
968
			ice_check_list_add_stun_server_request(cl, request);
jehan's avatar
jehan committed
969 970
		} else {
			ms_error("ice: no rtp socket found for session [%p]",cl->rtp_session);
971
		}
972 973 974
		rtptp=NULL;
		rtp_session_get_transports(cl->rtp_session,NULL,&rtptp);
		if (rtptp) {
975
			struct sockaddr *sa = (struct sockaddr *)&cl->rtp_session->rtcp.gs.loc_addr;
976 977 978
			if (cl->session->turn_enabled) {
				/* Define the RTP endpoint that will perform STUN encapsulation/decapsulation for TURN data */
				meta_rtp_transport_set_endpoint(rtptp, ms_turn_context_create_endpoint(cl->rtcp_turn_context));
979
				ms_turn_context_set_server_addr(cl->rtcp_turn_context, (struct sockaddr *)&cl->session->ss, cl->session->ss_len);
980
			}
981
			memset(source_addr_str, 0, sizeof(source_addr_str));
982 983
			bctbx_sockaddr_to_ip_address(sa, cl->rtp_session->rtcp.gs.loc_addrlen, source_addr_str, sizeof(source_addr_str), &source_port);
			request = ice_stun_server_request_new(cl, cl->rtcp_turn_context, rtptp, sa->sa_family, source_addr_str, source_port,
984
				cl->session->turn_enabled ? MS_TURN_METHOD_ALLOCATE : MS_STUN_METHOD_BINDING);
985
			request->gathering = TRUE;
986
			request->next_transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION + ICE_DEFAULT_TA_DURATION);
987
			ice_check_list_add_stun_server_request(cl, request);
988
			if (cl->session->turn_enabled) ms_turn_context_set_state(cl->rtcp_turn_context, MS_TURN_CONTEXT_STATE_CREATING_ALLOCATION);
jehan's avatar
jehan committed
989 990
		}else {
			ms_message("ice: no rtcp socket found for session [%p]",cl->rtp_session);
991
		}
992
		si->index++;
jehan's avatar
jehan committed
993
	} else {
994 995 996
		if (cl->gathering_candidates == FALSE) {
			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));
		}
997
	}
998

999
	return cl->gathering_candidates;
1000 1001
}

1002
static void ice_check_list_deallocate_turn_candidate(IceCheckList *cl, MSTurnContext *turn_context, RtpTransport *rtptp, OrtpStream *stream) {
1003 1004
	IceStunServerRequest *request = NULL;
	IceStunServerRequestTransaction *transaction = NULL;
1005 1006
	char source_addr_str[64];
	int source_port = 0;
1007

1008 1009
	if ((turn_context != NULL) && (ms_turn_context_get_state(turn_context) >= MS_TURN_CONTEXT_STATE_ALLOCATION_CREATED)) {
		ms_turn_context_set_lifetime(turn_context, 0);
1010
		if (rtptp) {
1011
			struct sockaddr *sa = (struct sockaddr *)&stream->loc_addr;
1012
			memset(source_addr_str, 0, sizeof(source_addr_str));
1013
			bctbx_sockaddr_to_ip_address(sa, stream->loc_addrlen, source_addr_str, sizeof(source_addr_str), &source_port);
1014
			request = ice_stun_server_request_new(cl, turn_context, rtptp, sa->sa_family, source_addr_str, source_port, MS_TURN_METHOD_REFRESH);
1015 1016 1017
			transaction = ice_send_stun_server_request(request, (struct sockaddr *)&cl->session->ss, cl->session->ss_len);
			if (transaction != NULL) ice_stun_server_request_transaction_free(transaction);
			ice_stun_server_request_free(request);
1018
			meta_rtp_transport_set_endpoint(rtptp,NULL); /*endpoint is later freed*/
1019 1020 1021 1022 1023 1024
		} else {
			ms_error("ice: no rtp socket found for session [%p]", cl->rtp_session);
		}
	}
}

1025 1026 1027
static void ice_check_list_deallocate_rtp_turn_candidate(IceCheckList *cl) {
	RtpTransport *rtptp = NULL;
	rtp_session_get_transports(cl->rtp_session, &rtptp, NULL);
1028
	ice_check_list_deallocate_turn_candidate(cl, cl->rtp_turn_context, rtptp, &cl->rtp_session->rtp.gs);
1029 1030 1031 1032 1033
}

static void ice_check_list_deallocate_rtcp_turn_candidate(IceCheckList *cl) {
	RtpTransport *rtptp = NULL;
	rtp_session_get_transports(cl->rtp_session, NULL, &rtptp);
1034
	ice_check_list_deallocate_turn_candidate(cl, cl->rtcp_turn_context, rtptp, &cl->rtp_session->rtcp.gs);
1035 1036 1037 1038 1039 1040 1041
}

static void ice_check_list_deallocate_turn_candidates(IceCheckList *cl) {
	ice_check_list_deallocate_rtp_turn_candidate(cl);
	ice_check_list_deallocate_rtcp_turn_candidate(cl);
}

1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
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;
}

1060
bool_t ice_session_gather_candidates(IceSession *session, const struct sockaddr* ss, socklen_t ss_len)
1061
{
1062
	Session_Index si;
1063
	OrtpEvent *ev;
1064
	int i;
1065
	bool_t gathering_in_progress = FALSE;
1066

1067
	memcpy(&session->ss,ss,ss_len);
1068
	session->ss_len = ss_len;
1069 1070
	si.session = session;
	si.index = 0;
1071
	ms_get_cur_time(&session->gathering_start_ts);
1072 1073
	if (ice_session_gathering_needed(session) == TRUE) {
		for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
1074 1075 1076 1077
			if (session->streams[i] != NULL) {
				bool_t cl_gathering_in_progress = ice_check_list_gather_candidates(session->streams[i], &si);
				if (cl_gathering_in_progress == TRUE) gathering_in_progress = TRUE;
			}
1078
		}
1079 1080 1081 1082 1083
	} 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;
1084
		rtp_session_dispatch_event(ice_session_first_check_list(session)->rtp_session, ev);
1085
	}
1086 1087

	return gathering_in_progress;
1088 1089
}

1090 1091 1092
int ice_session_gathering_duration(IceSession *session)
{
	if ((session->gathering_start_ts.tv_sec == -1) || (session->gathering_end_ts.tv_sec == -1)) return -1;
1093 1094
	return (int)(((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));
1095 1096
}

1097
void ice_session_enable_forced_relay(IceSession *session, bool_t enable) {
1098 1099 1100
	session->forced_relay = enable;
}

1101 1102 1103 1104
void ice_session_enable_short_turn_refresh(IceSession *session, bool_t enable) {
	session->short_turn_refresh = enable;
}

Ghislain MARY's avatar
Ghislain MARY committed
1105
static void ice_check_list_create_turn_contexts(IceCheckList *cl) {
1106 1107 1108 1109 1110 1111
	if (!cl->rtp_turn_context){
		cl->rtp_turn_context = ms_turn_context_new(MS_TURN_CONTEXT_TYPE_RTP, cl->rtp_session);
	}
	if (!cl->rtcp_turn_context){
		cl->rtcp_turn_context = ms_turn_context_new(MS_TURN_CONTEXT_TYPE_RTCP, cl->rtp_session);
	}
1112 1113
}

Ghislain MARY's avatar
Ghislain MARY committed
1114
void ice_session_enable_turn(IceSession *session, bool_t enable) {
1115
	int i;
Ghislain MARY's avatar
Ghislain MARY committed
1116
	session->turn_enabled = enable;
1117
	if (!enable) return;
1118 1119 1120 1121
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
		if (session->streams[i] != NULL)
			ice_check_list_create_turn_contexts(session->streams[i]);
	}
Ghislain MARY's avatar
Ghislain MARY committed
1122 1123
}

1124 1125 1126 1127 1128 1129
void ice_session_set_stun_auth_requested_cb(IceSession *session, MSStunAuthRequestedCb cb, void *userdata)
{
	session->stun_auth_requested_cb = cb;
	session->stun_auth_requested_userdata = userdata;
}

1130
static void ice_transaction_sum_gathering_round_trip_time(const IceStunServerRequestTransaction *transaction, IceStunRequestRoundTripTime *rtt)
1131 1132 1133 1134 1135 1136 1137
{
	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);
	}
}

1138
static void ice_stun_server_check_sum_gathering_round_trip_time(const IceStunServerRequest *request, IceStunRequestRoundTripTime *rtt)
1139
{
1140
	if (request->gathering == TRUE) {
1141
		bctbx_list_for_each2(request->transactions, (void (*)(void*,void*))ice_transaction_sum_gathering_round_trip_time, rtt);
1142
	}
1143 1144
}

1145
static void ice_check_list_sum_gathering_round_trip_times(IceCheckList *cl)
1146
{
1147
	bctbx_list_for_each2(cl->stun_server_requests, (void (*)(void*,void*))ice_stun_server_check_sum_gathering_round_trip_time, &cl->rtt);
1148 1149 1150 1151
}

int ice_session_average_gathering_round_trip_time(IceSession *session)
{
1152
	IceStunRequestRoundTripTime rtt;
1153
	int i;
1154 1155 1156

	if ((session->gathering_start_ts.tv_sec == -1) || (session->gathering_end_ts.tv_sec == -1)) return -1;
	memset(&rtt, 0, sizeof(rtt));
1157
	for (i = 0; i < ICE_SESSION_MAX_CHECK_LISTS; i++) {
1158 1159 1160 1161
		if (session->streams[i] != NULL) {
			rtt.nb_responses += session->streams[i]->rtt.nb_responses;
			rtt.sum += session->streams[i]->rtt.sum;
		}
1162
	}
1163 1164 1165 1166
	if (rtt.nb_responses == 0) return -1;
	return (rtt.sum / rtt.nb_responses);
}

1167

1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
/******************************************************************************
 * 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;
1181
	bctbx_list_t *elem;
1182

1183 1184
	if (cl->state != ICL_Completed) return;

1185
	bctbx_list_for_each(cl->valid_list, (void (*)(void*))ice_unselect_valid_pair);
1186
	for (componentID = 1; componentID <= 2; componentID++) {
1187
		elem = bctbx_list_find_custom(cl->valid_list, (bctbx_compare_func)ice_find_nominated_valid_pair_from_componentID, &componentID);
1188 1189 1190 1191 1192 1193 1194 1195
		if (elem == NULL) continue;
		valid_pair = (IceValidCandidatePair *)elem->data;
		valid_pair->selected = TRUE;
	}
}

void ice_session_select_candidates(IceSession *session)
{
1196 1197 1198 1199 1200
	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]);
	}
1201 1202 1203
}


1204 1205 1206 1207
/******************************************************************************
 * TRANSACTION HANDLING                                                       *
 *****************************************************************************/

Ghislain MARY's avatar
Ghislain MARY committed
1208
static IceTransaction * ice_create_transaction(IceCheckList *cl, IceCandidatePair *pair, const UInt96 tr_id)
1209 1210 1211
{
	IceTransaction *transaction = ms_new0(IceTransaction, 1);
	transaction->pair = pair;
Ghislain MARY's avatar
Ghislain MARY committed
1212
	transaction->transactionID = tr_id;
1213
	cl->transaction_list = bctbx_list_prepend(cl->transaction_list, transaction);
1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
	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)
{
1224
	bctbx_list_t *elem = bctbx_list_find_custom(cl->transaction_list, (bctbx_compare_func)ice_find_transaction_from_pair, pair);
1225 1226 1227 1228 1229
	if (elem == NULL) return NULL;
	return (IceTransaction *)elem->data;
}


1230 1231 1232 1233
/******************************************************************************
 * STUN PACKETS HANDLING                                                      *
 *****************************************************************************/

1234 1235 1236 1237 1238 1239 1240
static IceStunServerRequestTransaction * ice_stun_server_request_transaction_new(UInt96 transactionID) {
	IceStunServerRequestTransaction *transaction = ms_new0(IceStunServerRequestTransaction, 1);
	transaction->request_time = ice_current_time();
	transaction->transactionID = transactionID;
	return transaction;
}

1241
static IceStunServerRequest * ice_stun_server_request_new(IceCheckList *cl, MSTurnContext *turn_context, RtpTransport *rtptp, int family, const char *srcaddr, int srcport, uint16_t stun_method) {
1242 1243 1244 1245
	IceStunServerRequest *request = (IceStunServerRequest *)ms_new0(IceStunServerRequest, 1);
	request->cl = cl;
	request->turn_context = turn_context;
	request->rtptp = rtptp;
1246
	request->