ice.c 112 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

Ghislain MARY's avatar
Ghislain MARY committed
29 30
#include <inttypes.h>

Ghislain MARY's avatar
Ghislain MARY committed
31 32
#include "mediastreamer2/msticker.h"
#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)

Ghislain MARY's avatar
Ghislain MARY committed
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
Ghislain MARY's avatar
Ghislain MARY committed
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 */
47
#define ICE_GATHERING_CANDIDATES_TIMEOUT	2500	/* In milliseconds */
Ghislain MARY's avatar
Ghislain MARY committed
48
#define ICE_MAX_RETRANSMISSIONS		4
Ghislain MARY's avatar
Ghislain MARY committed
49

50

51 52 53 54 55
typedef struct _Type_ComponentID {
	IceCandidateType type;
	uint16_t componentID;
} Type_ComponentID;

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

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

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

74 75 76 77 78
typedef struct _CheckList_Bool {
	IceCheckList *cl;
	bool_t result;
} CheckList_Bool;

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

84 85 86 87 88
typedef struct _LocalCandidate_RemoteCandidate {
	IceCandidate *local;
	IceCandidate *remote;
} LocalCandidate_RemoteCandidate;

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

97 98 99 100 101
typedef struct _Time_Bool {
	uint64_t time;
	bool_t result;
} Time_Bool;

102 103 104 105 106
typedef struct _Session_Index {
	IceSession *session;
	int index;
} Session_Index;

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

113

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


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


136 137 138 139
/******************************************************************************
 * CONSTANTS DEFINITIONS                                                      *
 *****************************************************************************/

140 141
uint32_t stun_magic_cookie = 0x2112A442;

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

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
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 */
};

164 165 166 167 168 169 170 171 172 173
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
174 175 176
 * SESSION INITIALISATION AND DEINITIALISATION                                *
 *****************************************************************************/

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

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

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

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

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

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


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

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

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

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

	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)
{
302
	IceCandidatePair *pair = ms_new0(IceCandidatePair, 1);
303 304
	pair->local = local_candidate;
	pair->remote = remote_candidate;
305
	pair->state = ICP_Frozen;
306 307
	pair->is_default = FALSE;
	pair->is_nominated = FALSE;
308
	pair->use_candidate = FALSE;
309
	pair->wait_transaction_timeout = FALSE;
310 311 312 313 314 315 316 317 318 319
	if ((pair->local->is_default == TRUE) && (pair->remote->is_default == TRUE)) pair->is_default = TRUE;
	else pair->is_default = FALSE;
	memset(&pair->transactionID, 0, sizeof(pair->transactionID));
	pair->rto = ICE_DEFAULT_RTO_DURATION;
	pair->retransmissions = 0;
	pair->role = cl->session->role;
	ice_compute_pair_priority(pair, &cl->session->role);
	return pair;
}

320 321 322 323 324
static void ice_free_stun_server_check(IceStunServerCheck *check)
{
	ms_free(check);
}

325 326 327 328 329
static void ice_free_pair_foundation(IcePairFoundation *foundation)
{
	ms_free(foundation);
}

330 331 332 333 334
static void ice_free_valid_pair(IceValidCandidatePair *valid_pair)
{
	ms_free(valid_pair);
}

335
static void ice_free_candidate_pair(IceCandidatePair *pair, IceCheckList *cl)
Ghislain MARY's avatar
Ghislain MARY committed
336
{
337 338 339 340 341 342
	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);
343
		cl->valid_list = ms_list_remove_link(cl->valid_list, elem);
344
	}
Ghislain MARY's avatar
Ghislain MARY committed
345 346 347
	ms_free(pair);
}

348 349 350 351 352
static void ice_free_candidate(IceCandidate *candidate)
{
	ms_free(candidate);
}

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


378 379 380 381
/******************************************************************************
 * CANDIDATE ACCESSORS                                                        *
 *****************************************************************************/

382
const char *ice_candidate_type(const IceCandidate *candidate)
383 384 385 386
{
	return candidate_type_values[candidate->type];
}

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

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


409 410 411 412
/******************************************************************************
 * CHECK LIST ACCESSORS                                                       *
 *****************************************************************************/

413
IceCheckListState ice_check_list_state(const IceCheckList* cl)
414 415 416 417
{
	return cl->state;
}

418 419 420 421 422
static int ice_find_non_failed_check_list(const IceCheckList *cl)
{
	return (cl->state == ICL_Failed);
}

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

432 433 434 435 436
void ice_check_list_set_rtp_session(IceCheckList *cl, RtpSession *rtp_session)
{
	cl->rtp_session = rtp_session;
}

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

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

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

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

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

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
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;
}

484 485 486 487 488
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);
}

489
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)
490 491
{
	IceCandidate *candidate = NULL;
492 493 494 495 496 497 498 499 500 501 502 503 504
	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;
505 506 507 508
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
509 510 511 512
	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;
513 514
}

515
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
516 517
{
	IceCandidate *candidate = NULL;
518 519 520 521 522 523
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

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

	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;
533 534 535 536
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
537 538 539 540 541
	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
542 543
}

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

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

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

570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
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;
}

593 594 595 596 597 598 599 600 601 602 603
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
604 605 606 607 608
bool_t ice_check_list_is_mismatch(const IceCheckList *cl)
{
	return cl->mismatch;
}

Ghislain MARY's avatar
Ghislain MARY committed
609 610 611 612 613

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

614 615 616 617 618
IceCheckList * ice_session_check_list(const IceSession *session, unsigned int n)
{
	return (IceCheckList *)ms_list_nth_data(session->streams, n);
}

619
const char * ice_session_local_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
620 621 622 623
{
	return session->local_ufrag;
}

624
const char * ice_session_local_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
625 626 627 628
{
	return session->local_pwd;
}

629
const char * ice_session_remote_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
630 631 632 633
{
	return session->remote_ufrag;
}

634
const char * ice_session_remote_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
635 636 637 638
{
	return session->remote_pwd;
}

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

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

649 650 651 652 653 654
IceSessionState ice_session_state(const IceSession *session)
{
	return session->state;
}

IceRole ice_session_role(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
655 656 657 658
{
	return session->role;
}

Ghislain MARY's avatar
Ghislain MARY committed
659 660
void ice_session_set_role(IceSession *session, IceRole role)
{
661 662 663 664 665
	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
666 667
}

Ghislain MARY's avatar
Ghislain MARY committed
668 669 670 671 672
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);
}

673 674 675 676 677 678 679 680
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
681 682 683 684 685
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
686 687 688 689 690
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
691 692 693 694 695 696
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
697 698 699 700 701

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

702 703 704 705 706
int ice_session_nb_check_lists(IceSession *session)
{
	return ms_list_size(session->streams);
}

707 708 709 710 711 712 713 714 715 716 717 718
static int ice_find_completed_check_list(const IceCheckList *cl, const void *dummy)
{
	return (cl->state != ICL_Completed);
}

bool_t ice_session_has_completed_check_list(const IceSession *session)
{
	MSList *elem = ms_list_find_custom(session->streams, (MSCompareFunc)ice_find_completed_check_list, NULL);
	if (elem == NULL) return FALSE;
	else return TRUE;
}

Ghislain MARY's avatar
Ghislain MARY committed
719
void ice_session_add_check_list(IceSession *session, IceCheckList *cl)
720
{
Ghislain MARY's avatar
Ghislain MARY committed
721 722
	session->streams = ms_list_append(session->streams, cl);
	cl->session = session;
723 724 725
	if (cl->state == ICL_Running) {
		session->state = IS_Running;
	}
726 727
}

728 729
void ice_session_remove_check_list(IceSession *session, IceCheckList *cl)
{
730
	if (cl == NULL) return;
731 732 733 734
	session->streams = ms_list_remove(session->streams, cl);
	ice_check_list_destroy(cl);
}

Ghislain MARY's avatar
Ghislain MARY committed
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
static int ice_find_default_candidate_from_componentID(const IceCandidate *candidate, const uint16_t *componentID)
{
	return !((candidate->is_default == TRUE) && (candidate->componentID == *componentID));
}

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

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

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

759

760 761 762 763
/******************************************************************************
 * CANDIDATES GATHERING                                                       *
 *****************************************************************************/

764 765 766 767 768 769 770 771 772 773 774 775
static void ice_check_list_candidates_gathered(const IceCheckList *cl, bool_t *result)
{
	if (ms_list_size(cl->local_candidates) == 0) *result = FALSE;
}

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

776
static void ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si)
777 778 779
{
	IceStunServerCheck *check;
	ortp_socket_t sock = -1;
780
	uint64_t curtime = si->session->ticker->time;
781

782
	if ((cl->rtp_session != NULL) && (cl->gathering_candidates == FALSE) && (cl->state != ICL_Completed)) {
783
		cl->gathering_candidates = TRUE;
784
		cl->gathering_start_time = curtime;
785 786 787 788
		sock = rtp_session_get_rtp_socket(cl->rtp_session);
		if (sock > 0) {
			check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
			check->sock = sock;
789
			if (si->index == 0) {
790 791
				check->transmission_time = curtime + ICE_DEFAULT_RTO_DURATION;
				check->nb_transmissions = 1;
792 793
				ice_send_stun_server_binding_request(sock, (struct sockaddr *)&cl->session->ss, cl->session->ss_len,
					&check->transactionID, check->nb_transmissions, check->sock);
794
			} else {
795
				check->transmission_time = curtime + 2 * si->index * ICE_DEFAULT_TA_DURATION;
796 797 798 799 800 801 802
			}
			cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
		}
		sock = rtp_session_get_rtcp_socket(cl->rtp_session);
		if (sock > 0) {
			check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
			check->sock = sock;
803
			check->transmission_time = curtime + 2 * si->index * ICE_DEFAULT_TA_DURATION + ICE_DEFAULT_TA_DURATION;
804 805
			cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
		}
806
		si->index++;
807 808 809 810 811
	}
}

void ice_session_gather_candidates(IceSession *session, struct sockaddr_storage ss, socklen_t ss_len)
{
812
	Session_Index si;
813 814
	session->ss = ss;
	session->ss_len = ss_len;
815 816 817
	si.session = session;
	si.index = 0;
	ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_gather_candidates, &si);
818 819 820
}


821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
/******************************************************************************
 * CANDIDATES SELECTION                                                       *
 *****************************************************************************/

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

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

836 837
	if (cl->state != ICL_Completed) return;

838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
	ms_list_for_each(cl->valid_list, (void (*)(void*))ice_unselect_valid_pair);
	for (componentID = 1; componentID <= 2; componentID++) {
		elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_nominated_valid_pair_from_componentID, &componentID);
		if (elem == NULL) continue;
		valid_pair = (IceValidCandidatePair *)elem->data;
		valid_pair->selected = TRUE;
	}
}

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


853 854 855 856
/******************************************************************************
 * STUN PACKETS HANDLING                                                      *
 *****************************************************************************/

857
static void ice_send_stun_server_binding_request(ortp_socket_t sock, const struct sockaddr *server, socklen_t addrlen, UInt96 *transactionID, uint8_t nb_transmissions, int id)
858 859 860 861 862 863 864 865 866 867 868 869
{
	StunMessage msg;
	StunAtrString username;
	StunAtrString password;
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	const struct sockaddr_in *servaddr = (const struct sockaddr_in *)server;

	memset(&msg, 0, sizeof(StunMessage));
	memset(&username,0,sizeof(username));
	memset(&password,0,sizeof(password));
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, id);
870 871 872 873
	if (nb_transmissions > 1) {
		/* Keep the same transaction ID for retransmissions. */
		memcpy(&msg.msgHdr.tr_id, transactionID, sizeof(msg.msgHdr.tr_id));
	}
874 875 876
	len = stunEncodeMessage(&msg, buf, len, &password);
	if (len > 0) {
		sendMessage(sock, buf, len, htonl(servaddr->sin_addr.s_addr), htons(servaddr->sin_port));
877
		memcpy(transactionID, &msg.msgHdr.tr_id, sizeof(*transactionID));
878 879 880 881 882
	}
}

static int ice_parse_stun_server_binding_response(const StunMessage *msg, char *addr, int addr_len, int *port)
{
883
	struct sockaddr_in addr_in;
884 885 886

	if (msg->hasXorMappedAddress) {
		*port = msg->xorMappedAddress.ipv4.port;
887
		addr_in.sin_addr.s_addr = htonl(msg->xorMappedAddress.ipv4.addr);
888 889
	} else if (msg->hasMappedAddress) {
		*port = msg->mappedAddress.ipv4.port;
890
		addr_in.sin_addr.s_addr = htonl(msg->mappedAddress.ipv4.addr);
891 892
	} else return -1;

893 894 895
	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);
896 897 898
	return 0;
}

899
/* Send a STUN binding request for ICE connectivity checks according to 7.1.2. */
900
static void ice_send_binding_request(IceCheckList *cl, IceCandidatePair *pair, const RtpSession *rtp_session)
901 902 903
{
	StunMessage msg;
	StunAddress4 dest;
904 905
	StunAtrString username;
	StunAtrString password;
906 907 908
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	int socket = 0;
Ghislain MARY's avatar
Ghislain MARY committed
909
	char tr_id_str[25];
910

911
	if (pair->state == ICP_InProgress) {
912 913 914 915
		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;
916 917 918 919
			if (pair->use_candidate == FALSE) {
				ice_pair_set_state(pair, ICP_Waiting);
				ice_check_list_queue_triggered_check(cl, pair);
			}
920 921
			return;
		}
922 923 924 925 926 927 928 929 930
		/* 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;
	}
931
	pair->transmission_time = cl->session->ticker->time;
932

933
	if (pair->local->componentID == 1) {
934
		socket = rtp_session_get_rtp_socket(rtp_session);
935
	} else if (pair->local->componentID == 2) {
936
		socket = rtp_session_get_rtcp_socket(rtp_session);
937 938
	} else return;

939 940 941 942 943
	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);

944 945
	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
946
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, 1);
947
	msg.hasMessageIntegrity = TRUE;
948
	msg.hasFingerprint = TRUE;
949 950 951 952 953 954

	/* 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. */
955
	if ((cl->session->role == IR_Controlling) && (pair->use_candidate == TRUE)) {
956 957 958
		msg.hasUseCandidate = TRUE;
	}

959
	/* Include the ICE-CONTROLLING or ICE-CONTROLLED attribute depending on the role of the agent, as defined in 7.1.2.2. */
960
	switch (cl->session->role) {
961 962
		case IR_Controlling:
			msg.hasIceControlling = TRUE;
963
			msg.iceControlling.value = cl->session->tie_breaker;
964 965 966
			break;
		case IR_Controlled:
			msg.hasIceControlled = TRUE;
967
			msg.iceControlled.value = cl->session->tie_breaker;
968 969
			break;
	}
970

971 972 973 974 975
	/* Keep the same transaction ID for retransmission. */
	if (pair->state == ICP_InProgress) {
		memcpy(&msg.msgHdr.tr_id, &pair->transactionID, sizeof(msg.msgHdr.tr_id));
	}

976
	len = stunEncodeMessage(&msg, buf, len, &password);
977 978 979
	if (len > 0) {
		/* Save the generated transaction ID to match the response to the request, and send the request. */
		memcpy(&pair->transactionID, &msg.msgHdr.tr_id, sizeof(pair->transactionID));
Ghislain MARY's avatar
Ghislain MARY committed
980 981 982 983 984 985 986 987 988 989
		transactionID2string(&pair->transactionID, tr_id_str);
		if (pair->state == ICP_InProgress) {
			ms_message("ice: Retransmit (%d) binding request for pair %p: %s:%u:%s --> %s:%u:%s [%s]", pair->retransmissions, pair,
				pair->local->taddr.ip, pair->local->taddr.port, candidate_type_values[pair->local->type],
				pair->remote->taddr.ip, pair->remote->taddr.port, candidate_type_values[pair->remote->type], tr_id_str);
		} else {
			ms_message("ice: Send binding request for %s pair %p: %s:%u:%s --> %s:%u:%s [%s]", candidate_pair_state_values[pair->state], pair,
				pair->local->taddr.ip, pair->local->taddr.port, candidate_type_values[pair->local->type],
				pair->remote->taddr.ip, pair->remote->taddr.port, candidate_type_values[pair->remote->type], tr_id_str);
		}
990
		sendMessage(socket, buf, len, dest.addr, dest.port);
Ghislain MARY's avatar
Ghislain MARY committed
991

992
		if (pair->state != ICP_InProgress) {
993 994 995
			/* First transmission of the request, initialize the retransmission timer. */
			pair->rto = ICE_DEFAULT_RTO_DURATION;
			pair->retransmissions = 0;
996 997
			/* Save the role of the agent. */
			pair->role = cl->session->role;
998 999 1000
			/* Change the state of the pair. */
			ice_pair_set_state(pair, ICP_InProgress);
		}
1001 1002 1003
	}
}

1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
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;
}


1015
static int ice_get_socket_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data)
1016 1017 1018 1019 1020 1021 1022 1023 1024
{
	if (evt_data->info.socket_type == OrtpRTPSocket) {
		return rtp_session_get_rtp_socket(rtp_session);
	} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
		return rtp_session_get_rtcp_socket(rtp_session);
	}
	return -1;
}

1025
static int ice_get_recv_port_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data)
1026 1027 1028 1029 1030 <