ice.c 117 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
#include "mediastreamer2/ice.h"
32
#include "ortp/ortp.h"
Ghislain MARY's avatar
Ghislain MARY committed
33 34


35 36 37
#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
38 39
#define ICE_MIN_COMPONENTID		1
#define ICE_MAX_COMPONENTID		256
40
#define ICE_INVALID_COMPONENTID		0
Ghislain MARY's avatar
Ghislain MARY committed
41 42
#define ICE_MAX_UFRAG_LEN		256
#define ICE_MAX_PWD_LEN			256
Ghislain MARY's avatar
Ghislain MARY committed
43 44
#define ICE_DEFAULT_TA_DURATION		40	/* In milliseconds */
#define ICE_DEFAULT_RTO_DURATION	200	/* In milliseconds */
Ghislain MARY's avatar
Ghislain MARY committed
45
#define ICE_DEFAULT_KEEPALIVE_TIMEOUT   15	/* In seconds */
46
#define ICE_GATHERING_CANDIDATES_TIMEOUT	2500	/* In milliseconds */
Ghislain MARY's avatar
Ghislain MARY committed
47
#define ICE_MAX_RETRANSMISSIONS		4
Ghislain MARY's avatar
Ghislain MARY committed
48

49

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

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

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

67 68
typedef struct _CheckList_RtpSession_Time {
	IceCheckList *cl;
69
	const RtpSession *rtp_session;
Ghislain MARY's avatar
Ghislain MARY committed
70
	MSTimeSpec time;
71 72
} CheckList_RtpSession_Time;

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

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

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

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

96
typedef struct _Time_Bool {
Ghislain MARY's avatar
Ghislain MARY committed
97
	MSTimeSpec time;
98 99 100
	bool_t result;
} Time_Bool;

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

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

112

Ghislain MARY's avatar
Ghislain MARY committed
113 114 115
static MSTimeSpec ice_current_time(void);
static MSTimeSpec ice_add_ms(MSTimeSpec orig, uint32_t ms);
static uint32_t ice_compare_time(MSTimeSpec ts1, MSTimeSpec ts2);
116
static char * ice_inet_ntoa(struct sockaddr *addr, int addrlen, char *dest, int destlen);
Ghislain MARY's avatar
Ghislain MARY committed
117
static void transactionID2string(const UInt96 *tr_id, char *tr_id_str);
118
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);
119
static int ice_compare_transport_addresses(const IceTransportAddress *ta1, const IceTransportAddress *ta2);
120
static int ice_compare_pair_priorities(const IceCandidatePair *p1, const IceCandidatePair *p2);
121 122
static int ice_compare_pairs(const IceCandidatePair *p1, const IceCandidatePair *p2);
static int ice_compare_candidates(const IceCandidate *c1, const IceCandidate *c2);
123
static int ice_find_host_candidate(const IceCandidate *candidate, const uint16_t *componentID);
124
static int ice_find_candidate_from_type_and_componentID(const IceCandidate *candidate, const Type_ComponentID *tc);
125
static int ice_find_use_candidate_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, 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 void ice_find_selected_valid_pair_for_componentID(const uint16_t *componentID, CheckList_Bool *cb);
129
static int ice_find_running_check_list(const IceCheckList *cl);
130
static int ice_find_pair_in_valid_list(IceValidCandidatePair *valid_pair, IceCandidatePair *pair);
131
static void ice_pair_set_state(IceCandidatePair *pair, IceCandidatePairState state);
132
static void ice_compute_candidate_foundation(IceCandidate *candidate, IceCheckList *cl);
Ghislain MARY's avatar
Ghislain MARY committed
133
static void ice_set_credentials(char **ufrag, char **pwd, const char *ufrag_str, const char *pwd_str);
134
static void ice_conclude_processing(IceCheckList* cl, RtpSession* rtp_session);
Ghislain MARY's avatar
Ghislain MARY committed
135 136


137 138 139 140
/******************************************************************************
 * CONSTANTS DEFINITIONS                                                      *
 *****************************************************************************/

141 142
uint32_t stun_magic_cookie = 0x2112A442;

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

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

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

Ghislain MARY's avatar
Ghislain MARY committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
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
199 200 201
static void ice_session_init(IceSession *session)
{
	session->streams = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
202
	session->state = IS_Stopped;
Ghislain MARY's avatar
Ghislain MARY committed
203
	session->role = IR_Controlling;
Ghislain MARY's avatar
Ghislain MARY committed
204
	session->tie_breaker = generate_tie_breaker();
205
	session->ta = ICE_DEFAULT_TA_DURATION;
Ghislain MARY's avatar
Ghislain MARY committed
206
	session->keepalive_timeout = ICE_DEFAULT_KEEPALIVE_TIMEOUT;
Ghislain MARY's avatar
Ghislain MARY committed
207
	session->max_connectivity_checks = ICE_MAX_NB_CANDIDATE_PAIRS;
Ghislain MARY's avatar
Ghislain MARY committed
208 209
	session->local_ufrag = generate_ufrag();
	session->local_pwd = generate_pwd();
Ghislain MARY's avatar
Ghislain MARY committed
210 211
	session->remote_ufrag = NULL;
	session->remote_pwd = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
212
	memset(&session->event_time, 0, sizeof(session->event_time));
213
	session->send_event = FALSE;
214 215
	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;
Ghislain MARY's avatar
Ghislain MARY committed
216 217 218 219 220 221
}

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

void ice_session_destroy(IceSession *session)
{
231 232 233 234 235 236 237 238 239
	if (session != NULL) {
		ms_list_for_each(session->streams, (void (*)(void*))ice_check_list_destroy);
		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
240 241 242 243 244
}


/******************************************************************************
 * CHECK LIST INITIALISATION AND DEINITIALISATION                             *
245
 *****************************************************************************/
246

247 248
static void ice_check_list_init(IceCheckList *cl)
{
Ghislain MARY's avatar
Ghislain MARY committed
249
	cl->session = NULL;
250
	cl->rtp_session = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
251
	cl->remote_ufrag = cl->remote_pwd = NULL;
252
	cl->stun_server_checks = NULL;
253
	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
254
	cl->local_componentIDs = cl->remote_componentIDs = cl->foundations = NULL;
255
	cl->state = ICL_Running;
Ghislain MARY's avatar
Ghislain MARY committed
256 257
	memset(&cl->ta_time, 0, sizeof(cl->ta_time));
	memset(&cl->keepalive_time, 0, sizeof(cl->keepalive_time));
258
	cl->foundation_generator = 1;
Ghislain MARY's avatar
Ghislain MARY committed
259
	cl->mismatch = FALSE;
260
	cl->gathering_candidates = FALSE;
261 262
}

263
IceCheckList * ice_check_list_new(void)
264 265 266 267 268 269 270
{
	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);
271 272 273
	return cl;
}

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

	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)
{
295
	IceCandidatePair *pair = ms_new0(IceCandidatePair, 1);
296 297
	pair->local = local_candidate;
	pair->remote = remote_candidate;
298
	pair->state = ICP_Frozen;
299 300
	pair->is_default = FALSE;
	pair->is_nominated = FALSE;
301
	pair->use_candidate = FALSE;
302
	pair->wait_transaction_timeout = FALSE;
303 304 305 306 307 308 309 310 311 312
	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;
}

313 314 315 316 317
static void ice_free_stun_server_check(IceStunServerCheck *check)
{
	ms_free(check);
}

318 319 320 321 322
static void ice_free_pair_foundation(IcePairFoundation *foundation)
{
	ms_free(foundation);
}

323 324 325 326 327
static void ice_free_valid_pair(IceValidCandidatePair *valid_pair)
{
	ms_free(valid_pair);
}

328
static void ice_free_candidate_pair(IceCandidatePair *pair, IceCheckList *cl)
Ghislain MARY's avatar
Ghislain MARY committed
329
{
330 331 332 333 334 335
	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);
336
		cl->valid_list = ms_list_remove_link(cl->valid_list, elem);
337
	}
Ghislain MARY's avatar
Ghislain MARY committed
338 339 340
	ms_free(pair);
}

341 342 343 344 345
static void ice_free_candidate(IceCandidate *candidate)
{
	ms_free(candidate);
}

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


371 372 373 374
/******************************************************************************
 * CANDIDATE ACCESSORS                                                        *
 *****************************************************************************/

375
const char *ice_candidate_type(const IceCandidate *candidate)
376 377 378 379
{
	return candidate_type_values[candidate->type];
}

380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
/******************************************************************************
 * 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;
		}
	}
}


402 403 404 405
/******************************************************************************
 * CHECK LIST ACCESSORS                                                       *
 *****************************************************************************/

406
IceCheckListState ice_check_list_state(const IceCheckList* cl)
407 408 409 410
{
	return cl->state;
}

411
static int ice_find_check_list_from_state(const IceCheckList *cl, const IceCheckListState *state)
412
{
413
	return (cl->state == *state);
414 415
}

416 417
void ice_check_list_set_state(IceCheckList *cl, IceCheckListState state)
{
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
	IceCheckListState check_state;

	if (cl->state != state) {
		cl->state = state;
		check_state = ICL_Running;
		if (ms_list_find_custom(cl->session->streams, (MSCompareFunc)ice_find_check_list_from_state, &check_state) == NULL) {
			check_state = ICL_Failed;
			if (ms_list_find_custom(cl->session->streams, (MSCompareFunc)ice_find_check_list_from_state, &check_state) != NULL) {
				/* 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;
			}
		}
433
	}
434 435
}

436 437 438 439 440
void ice_check_list_set_rtp_session(IceCheckList *cl, RtpSession *rtp_session)
{
	cl->rtp_session = rtp_session;
}

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

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

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

459
const char * ice_check_list_remote_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
460 461 462 463 464
{
	if (cl->remote_pwd) return cl->remote_pwd;
	else return cl->session->remote_pwd;
}

465
static int ice_find_default_local_candidate(const IceCandidate *candidate, const uint16_t *componentID)
466
{
467
	return !((candidate->componentID == *componentID) && (candidate->is_default == TRUE));
468 469
}

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
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;
}

488 489 490 491 492
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);
}

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

519
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
520 521
{
	IceCandidate *candidate = NULL;
522 523 524 525 526 527
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
528
	rtp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
529 530
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
531
	rtcp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
532 533 534 535 536

	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;
537 538 539 540
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
541 542 543 544 545
	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
546 547
}

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

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

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

574 575 576 577 578 579 580 581 582 583
IceCandidateType ice_check_list_selected_valid_candidate_type(const IceCheckList *cl)
{
	MSList *elem;
	uint16_t componentID = 1;

	elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
	if (elem == NULL) return ICT_RelayedCandidate;
	return ((IceValidCandidatePair *)elem->data)->valid->remote->type;
}

584 585 586 587 588 589 590 591 592 593 594 595 596 597
void ice_check_list_check_completed(IceCheckList *cl)
{
	CheckList_Bool cb;

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

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
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;
}

621 622 623 624 625 626 627 628 629 630 631
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
632 633 634 635 636
bool_t ice_check_list_is_mismatch(const IceCheckList *cl)
{
	return cl->mismatch;
}

Ghislain MARY's avatar
Ghislain MARY committed
637 638 639 640 641

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

642 643 644 645 646
IceCheckList * ice_session_check_list(const IceSession *session, unsigned int n)
{
	return (IceCheckList *)ms_list_nth_data(session->streams, n);
}

647
const char * ice_session_local_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
648 649 650 651
{
	return session->local_ufrag;
}

652
const char * ice_session_local_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
653 654 655 656
{
	return session->local_pwd;
}

657
const char * ice_session_remote_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
658 659 660 661
{
	return session->remote_ufrag;
}

662
const char * ice_session_remote_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
663 664 665 666
{
	return session->remote_pwd;
}

667 668 669 670 671 672 673 674 675 676
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);
}

677 678 679 680 681 682
IceSessionState ice_session_state(const IceSession *session)
{
	return session->state;
}

IceRole ice_session_role(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
683 684 685 686
{
	return session->role;
}

Ghislain MARY's avatar
Ghislain MARY committed
687 688
void ice_session_set_role(IceSession *session, IceRole role)
{
689 690 691 692 693
	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
694 695
}

Ghislain MARY's avatar
Ghislain MARY committed
696 697 698 699 700
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);
}

701 702 703 704 705 706 707 708
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
709 710 711 712 713
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
714 715 716 717 718
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
719 720 721 722 723 724
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
725 726 727 728 729

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

730 731 732 733 734
int ice_session_nb_check_lists(IceSession *session)
{
	return ms_list_size(session->streams);
}

735 736 737 738 739 740 741 742 743 744 745 746
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
747
void ice_session_add_check_list(IceSession *session, IceCheckList *cl)
748
{
Ghislain MARY's avatar
Ghislain MARY committed
749 750
	session->streams = ms_list_append(session->streams, cl);
	cl->session = session;
751 752 753
	if (cl->state == ICL_Running) {
		session->state = IS_Running;
	}
754 755
}

756 757
void ice_session_remove_check_list(IceSession *session, IceCheckList *cl)
{
758
	if (cl == NULL) return;
759 760 761 762
	session->streams = ms_list_remove(session->streams, cl);
	ice_check_list_destroy(cl);
}

Ghislain MARY's avatar
Ghislain MARY committed
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
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);
}

787

788 789 790 791
/******************************************************************************
 * CANDIDATES GATHERING                                                       *
 *****************************************************************************/

792
static void ice_check_list_candidates_gathered_result_ptr(const IceCheckList *cl, bool_t *result)
793
{
794 795 796 797 798 799
	if (cl->gathering_finished == FALSE) *result = FALSE;
}

bool_t ice_check_list_candidates_gathered(const IceCheckList *cl)
{
	return cl->gathering_finished;
800 801 802 803 804
}

bool_t ice_session_candidates_gathered(const IceSession *session)
{
	bool_t result = TRUE;
805
	ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_candidates_gathered_result_ptr, &result);
806 807 808
	return result;
}

809
static void ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si)
810 811 812
{
	IceStunServerCheck *check;
	ortp_socket_t sock = -1;
Ghislain MARY's avatar
Ghislain MARY committed
813
	MSTimeSpec curtime = ice_current_time();
814

815
	if ((cl->rtp_session != NULL) && (cl->gathering_candidates == FALSE) && (cl->state != ICL_Completed) && (ice_check_list_candidates_gathered(cl) == FALSE)) {
816
		cl->gathering_candidates = TRUE;
817
		cl->gathering_start_time = curtime;
818 819 820 821
		sock = rtp_session_get_rtp_socket(cl->rtp_session);
		if (sock > 0) {
			check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
			check->sock = sock;
822
			if (si->index == 0) {
Ghislain MARY's avatar
Ghislain MARY committed
823
				check->transmission_time = ice_add_ms(curtime, ICE_DEFAULT_RTO_DURATION);
824
				check->nb_transmissions = 1;
825 826
				ice_send_stun_server_binding_request(sock, (struct sockaddr *)&cl->session->ss, cl->session->ss_len,
					&check->transactionID, check->nb_transmissions, check->sock);
827
			} else {
Ghislain MARY's avatar
Ghislain MARY committed
828
				check->transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION);
829 830 831 832 833 834 835
			}
			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;
Ghislain MARY's avatar
Ghislain MARY committed
836
			check->transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION + ICE_DEFAULT_TA_DURATION);
837 838
			cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
		}
839
		si->index++;
840 841 842 843 844
	}
}

void ice_session_gather_candidates(IceSession *session, struct sockaddr_storage ss, socklen_t ss_len)
{
845
	Session_Index si;
846 847
	session->ss = ss;
	session->ss_len = ss_len;
848 849
	si.session = session;
	si.index = 0;
850
	ms_get_cur_time(&session->gathering_start_ts);
851
	ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_gather_candidates, &si);
852 853
}

854 855 856 857 858 859 860
int ice_session_gathering_duration(IceSession *session)
{
	if ((session->gathering_start_ts.tv_sec == -1) || (session->gathering_end_ts.tv_sec == -1)) return -1;
	return ((session->gathering_end_ts.tv_sec - session->gathering_start_ts.tv_sec) * 1000.0)
		+ ((session->gathering_end_ts.tv_nsec - session->gathering_start_ts.tv_nsec) / 1000000.0);
}

861

862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
/******************************************************************************
 * 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;

877 878
	if (cl->state != ICL_Completed) return;

879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
	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);
}


894 895 896 897
/******************************************************************************
 * STUN PACKETS HANDLING                                                      *
 *****************************************************************************/

898
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)
899 900 901 902 903 904 905 906 907 908 909 910
{
	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);
911 912 913 914
	if (nb_transmissions > 1) {
		/* Keep the same transaction ID for retransmissions. */
		memcpy(&msg.msgHdr.tr_id, transactionID, sizeof(msg.msgHdr.tr_id));
	}
915 916 917
	len = stunEncodeMessage(&msg, buf, len, &password);
	if (len > 0) {
		sendMessage(sock, buf, len, htonl(servaddr->sin_addr.s_addr), htons(servaddr->sin_port));
918
		memcpy(transactionID, &msg.msgHdr.tr_id, sizeof(*transactionID));
919 920 921 922 923
	}
}

static int ice_parse_stun_server_binding_response(const StunMessage *msg, char *addr, int addr_len, int *port)
{
924
	struct sockaddr_in addr_in;
925 926 927

	if (msg->hasXorMappedAddress) {
		*port = msg->xorMappedAddress.ipv4.port;
928
		addr_in.sin_addr.s_addr = htonl(msg->xorMappedAddress.ipv4.addr);
929 930
	} else if (msg->hasMappedAddress) {
		*port = msg->mappedAddress.ipv4.port;
931
		addr_in.sin_addr.s_addr = htonl(msg->mappedAddress.ipv4.addr);
932 933
	} else return -1;

934 935 936
	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);
937 938 939
	return 0;
}

940
/* Send a STUN binding request for ICE connectivity checks according to 7.1.2. */
941
static void ice_send_binding_request(IceCheckList *cl, IceCandidatePair *pair, const RtpSession *rtp_session)
942 943 944
{
	StunMessage msg;
	StunAddress4 dest;
945 946
	StunAtrString username;
	StunAtrString password;
947 948 949
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	int socket = 0;
Ghislain MARY's avatar
Ghislain MARY committed
950
	char tr_id_str[25];
951

952
	if (pair->state == ICP_InProgress) {
953 954 955 956
		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;
957 958 959 960
			if (pair->use_candidate == FALSE) {
				ice_pair_set_state(pair, ICP_Waiting);
				ice_check_list_queue_triggered_check(cl, pair);
			}
961 962
			return;
		}
963 964 965 966 967 968 969 970 971
		/* 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;
	}
Ghislain MARY's avatar
Ghislain MARY committed
972
	pair->transmission_time = ice_current_time();
973

974
	if (pair->local->componentID == 1) {
975
		socket = rtp_session_get_rtp_socket(rtp_session);
976
	} else if (pair->local->componentID == 2) {
977
		socket = rtp_session_get_rtcp_socket(rtp_session);
978 979
	} else return;

980 981 982 983 984
	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);

985 986
	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
987
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, 1);
988
	msg.hasMessageIntegrity = TRUE;
989
	msg.hasFingerprint = TRUE;
990 991 992 993 994 995

	/* 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. */
996
	if ((cl->session->role == IR_Controlling) && (pair->use_candidate == TRUE)) {
997 998 999
		msg.hasUseCandidate = TRUE;
	}

1000
	/* Include the ICE-CONTROLLING or ICE-CONTROLLED attribute depending on the role of the agent, as defined in 7.1.2.2. */
1001
	switch (cl->session->role) {
1002 1003
		case IR_Controlling:
			msg.hasIceControlling = TRUE;
1004
			msg.iceControlling.value = cl->session->tie_breaker;
1005 1006 1007
			break;
		case IR_Controlled:
			msg.hasIceControlled = TRUE;
1008
			msg.iceControlled.value = cl->session->tie_breaker;
1009 1010
			break;
	}
1011

1012 1013 1014 1015 1016
	/* Keep the same transaction ID for retransmission. */
	if (pair->state == ICP_InProgress) {
		memcpy(&msg.msgHdr.tr_id, &pair