ice.c 121 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 */
47
#define ICE_NOMINATION_DELAY		1000	/* In milliseconds */
48
#define ICE_MAX_RETRANSMISSIONS		7
49
#define ICE_MAX_STUN_REQUEST_RETRANSMISSIONS	7
Ghislain MARY's avatar
Ghislain MARY committed
50

51

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

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

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

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

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

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

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

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

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

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

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

114

Ghislain MARY's avatar
Ghislain MARY committed
115 116 117
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);
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, int srcport, 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_candidate_from_type_and_componentID(const IceCandidate *candidate, const Type_ComponentID *tc);
127
static int ice_find_use_candidate_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
128
static int ice_find_nominated_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
129
static int ice_find_selected_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
130
static void ice_find_selected_valid_pair_for_componentID(const uint16_t *componentID, CheckList_Bool *cb);
131
static int ice_find_running_check_list(const IceCheckList *cl);
132
static int ice_find_pair_in_valid_list(IceValidCandidatePair *valid_pair, IceCandidatePair *pair);
133
static void ice_pair_set_state(IceCandidatePair *pair, IceCandidatePairState state);
134
static void ice_compute_candidate_foundation(IceCandidate *candidate, IceCheckList *cl);
Ghislain MARY's avatar
Ghislain MARY committed
135
static void ice_set_credentials(char **ufrag, char **pwd, const char *ufrag_str, const char *pwd_str);
136
static void ice_conclude_processing(IceCheckList* cl, RtpSession* rtp_session);
Ghislain MARY's avatar
Ghislain MARY committed
137 138


139 140 141 142
/******************************************************************************
 * CONSTANTS DEFINITIONS                                                      *
 *****************************************************************************/

143 144
uint32_t stun_magic_cookie = 0x2112A442;

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

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

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

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

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

void ice_session_destroy(IceSession *session)
{
233 234 235 236 237 238 239 240 241
	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
242 243 244 245 246
}


/******************************************************************************
 * CHECK LIST INITIALISATION AND DEINITIALISATION                             *
247
 *****************************************************************************/
248

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

269
IceCheckList * ice_check_list_new(void)
270 271 272 273 274 275 276
{
	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);
277 278 279
	return cl;
}

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

	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)
{
301
	IceCandidatePair *pair = ms_new0(IceCandidatePair, 1);
302 303
	pair->local = local_candidate;
	pair->remote = remote_candidate;
304
	pair->state = ICP_Frozen;
305 306
	pair->is_default = FALSE;
	pair->is_nominated = FALSE;
307
	pair->use_candidate = FALSE;
308
	pair->wait_transaction_timeout = FALSE;
309 310 311 312 313 314 315 316 317 318
	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;
}

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

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

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

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

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

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


377 378 379 380
/******************************************************************************
 * CANDIDATE ACCESSORS                                                        *
 *****************************************************************************/

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

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
/******************************************************************************
 * 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;
		}
	}
}


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

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

417
static int ice_find_check_list_from_state(const IceCheckList *cl, const IceCheckListState *state)
418
{
419
	return (cl->state != *state);
420 421
}

422 423
void ice_check_list_set_state(IceCheckList *cl, IceCheckListState state)
{
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
	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;
			}
		}
439
	}
440 441
}

442 443 444 445 446
void ice_check_list_set_rtp_session(IceCheckList *cl, RtpSession *rtp_session)
{
	cl->rtp_session = rtp_session;
}

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

453
const char * ice_check_list_local_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
454 455 456 457 458
{
	/* Do not handle media specific pwd for the moment, so use the session local pwd. */
	return cl->session->local_pwd;
}

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

465
const char * ice_check_list_remote_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
466 467 468 469 470
{
	if (cl->remote_pwd) return cl->remote_pwd;
	else return cl->session->remote_pwd;
}

471
static int ice_find_default_local_candidate(const IceCandidate *candidate, const uint16_t *componentID)
472
{
473
	return !((candidate->componentID == *componentID) && (candidate->is_default == TRUE));
474 475
}

476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
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;
}

494 495 496 497 498
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);
}

499
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)
500 501
{
	IceCandidate *candidate = NULL;
502 503 504 505 506 507 508 509 510 511 512 513 514
	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;
515 516 517 518
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
519 520 521 522
	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;
523 524
}

525
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
526 527
{
	IceCandidate *candidate = NULL;
528 529 530 531 532 533
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
534
	rtp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
535 536
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
537
	rtcp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
538 539 540 541 542

	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;
543 544 545 546
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
547 548 549 550 551
	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
552 553
}

554
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)
555 556 557 558 559 560 561 562
{
	IceCandidate *candidate = NULL;
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
563
	rtp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
564 565
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
566
	rtcp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
567 568 569 570 571 572 573 574 575 576 577 578 579

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

580 581 582 583 584 585 586 587 588 589
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;
}

590 591 592 593 594 595 596 597 598 599 600 601 602 603
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);
		}
	}
}

604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
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;
}

627 628 629 630 631 632 633 634 635 636 637
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
638 639 640 641 642
bool_t ice_check_list_is_mismatch(const IceCheckList *cl)
{
	return cl->mismatch;
}

Ghislain MARY's avatar
Ghislain MARY committed
643 644 645 646 647

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

648 649 650 651 652
IceCheckList * ice_session_check_list(const IceSession *session, unsigned int n)
{
	return (IceCheckList *)ms_list_nth_data(session->streams, n);
}

653
const char * ice_session_local_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
654 655 656 657
{
	return session->local_ufrag;
}

658
const char * ice_session_local_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
659 660 661 662
{
	return session->local_pwd;
}

663
const char * ice_session_remote_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
664 665 666 667
{
	return session->remote_ufrag;
}

668
const char * ice_session_remote_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
669 670 671 672
{
	return session->remote_pwd;
}

673 674 675 676 677 678 679 680 681 682
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);
}

683 684 685 686 687 688
IceSessionState ice_session_state(const IceSession *session)
{
	return session->state;
}

IceRole ice_session_role(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
689 690 691 692
{
	return session->role;
}

Ghislain MARY's avatar
Ghislain MARY committed
693 694
void ice_session_set_role(IceSession *session, IceRole role)
{
695 696 697 698 699
	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
700 701
}

Ghislain MARY's avatar
Ghislain MARY committed
702 703 704 705 706
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);
}

707 708 709 710 711 712 713 714
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
715 716 717 718 719
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
720 721 722 723 724
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
725 726 727 728 729 730
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
731 732 733 734 735

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

736 737 738 739 740
int ice_session_nb_check_lists(IceSession *session)
{
	return ms_list_size(session->streams);
}

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

762 763
void ice_session_remove_check_list(IceSession *session, IceCheckList *cl)
{
764
	if (cl == NULL) return;
765 766 767 768
	session->streams = ms_list_remove(session->streams, cl);
	ice_check_list_destroy(cl);
}

Ghislain MARY's avatar
Ghislain MARY committed
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
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);
}

793

794 795 796 797
/******************************************************************************
 * CANDIDATES GATHERING                                                       *
 *****************************************************************************/

798
static void ice_check_list_candidates_gathered_result_ptr(const IceCheckList *cl, bool_t *result)
799
{
800 801 802 803 804 805
	if (cl->gathering_finished == FALSE) *result = FALSE;
}

bool_t ice_check_list_candidates_gathered(const IceCheckList *cl)
{
	return cl->gathering_finished;
806 807 808 809 810
}

bool_t ice_session_candidates_gathered(const IceSession *session)
{
	bool_t result = TRUE;
811
	ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_candidates_gathered_result_ptr, &result);
812 813 814
	return result;
}

815 816 817 818 819
static void ice_check_list_gathering_needed(const IceCheckList *cl, bool_t *gathering_needed)
{
	if (cl->gathering_finished == FALSE) *gathering_needed = TRUE;
}

820
static void ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si)
821 822 823
{
	IceStunServerCheck *check;
	ortp_socket_t sock = -1;
Ghislain MARY's avatar
Ghislain MARY committed
824
	MSTimeSpec curtime = ice_current_time();
825

826
	if ((cl->rtp_session != NULL) && (cl->gathering_candidates == FALSE) && (cl->state != ICL_Completed) && (ice_check_list_candidates_gathered(cl) == FALSE)) {
827
		cl->gathering_candidates = TRUE;
828
		cl->gathering_start_time = curtime;
829 830 831 832
		sock = rtp_session_get_rtp_socket(cl->rtp_session);
		if (sock > 0) {
			check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
			check->sock = sock;
833
			check->srcport = rtp_session_get_local_port(cl->rtp_session);
834
			if (si->index == 0) {
Ghislain MARY's avatar
Ghislain MARY committed
835
				check->transmission_time = ice_add_ms(curtime, ICE_DEFAULT_RTO_DURATION);
836
				check->nb_transmissions = 1;
837
				ice_send_stun_server_binding_request(sock, (struct sockaddr *)&cl->session->ss, cl->session->ss_len, check->srcport,
838
					&check->transactionID, check->nb_transmissions, check->sock);
839
			} else {
Ghislain MARY's avatar
Ghislain MARY committed
840
				check->transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION);
841 842 843 844 845 846 847
			}
			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;
848
			check->srcport = rtp_session_get_local_port(cl->rtp_session) + 1;
Ghislain MARY's avatar
Ghislain MARY committed
849
			check->transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION + ICE_DEFAULT_TA_DURATION);
850 851
			cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
		}
852
		si->index++;
853 854 855 856 857
	}
}

void ice_session_gather_candidates(IceSession *session, struct sockaddr_storage ss, socklen_t ss_len)
{
858
	Session_Index si;
859 860
	OrtpEvent *ev;
	bool_t gathering_needed = FALSE;
861 862
	session->ss = ss;
	session->ss_len = ss_len;
863 864
	si.session = session;
	si.index = 0;
865
	ms_get_cur_time(&session->gathering_start_ts);
866 867 868 869 870 871 872 873 874 875
	ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_gathering_needed, &gathering_needed);
	if (gathering_needed == TRUE) {
		ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_gather_candidates, &si);
	} 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;
		rtp_session_dispatch_event(ice_session_check_list(session, 0)->rtp_session, ev);
	}
876 877
}

878 879 880 881 882 883 884
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);
}

885

886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
/******************************************************************************
 * 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;

901 902
	if (cl->state != ICL_Completed) return;

903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
	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);
}


918 919 920 921
/******************************************************************************
 * STUN PACKETS HANDLING                                                      *
 *****************************************************************************/

922
static void ice_send_stun_server_binding_request(ortp_socket_t sock, const struct sockaddr *server, socklen_t addrlen, int srcport, UInt96 *transactionID, uint8_t nb_transmissions, int id)
923 924 925 926 927 928 929
{
	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;
930
	char tr_id_str[25];
931 932 933 934 935

	memset(&msg, 0, sizeof(StunMessage));
	memset(&username,0,sizeof(username));
	memset(&password,0,sizeof(password));
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, id);
936 937 938 939
	if (nb_transmissions > 1) {
		/* Keep the same transaction ID for retransmissions. */
		memcpy(&msg.msgHdr.tr_id, transactionID, sizeof(msg.msgHdr.tr_id));
	}
940 941
	len = stunEncodeMessage(&msg, buf, len, &password);
	if (len > 0) {
942 943 944 945 946 947
		transactionID2string(&msg.msgHdr.tr_id, tr_id_str);
		if (nb_transmissions > 1) {
			ms_message("ice: Retransmit STUN binding request from port %u [%s]", srcport, tr_id_str);
		} else {
			ms_message("ice: Send STUN binding request from port %u [%s]", srcport, tr_id_str);
		}
948
		sendMessage(sock, buf, len, htonl(servaddr->sin_addr.s_addr), htons(servaddr->sin_port));
949
		memcpy(transactionID, &msg.msgHdr.tr_id, sizeof(*transactionID));
950 951 952 953 954
	}
}

static int ice_parse_stun_server_binding_response(const StunMessage *msg, char *addr, int addr_len, int *port)
{
955
	struct sockaddr_in addr_in;
956 957 958

	if (msg->hasXorMappedAddress) {
		*port = msg->xorMappedAddress.ipv4.port;
959
		addr_in.sin_addr.s_addr = htonl(msg->xorMappedAddress.ipv4.addr);
960 961
	} else if (msg->hasMappedAddress) {
		*port = msg->mappedAddress.ipv4.port;
962
		addr_in.sin_addr.s_addr = htonl(msg->mappedAddress.ipv4.addr);
963 964
	} else return -1;

965 966 967
	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);
968 969 970
	return 0;
}

971
/* Send a STUN binding request for ICE connectivity checks according to 7.1.2. */
972
static void ice_send_binding_request(IceCheckList *cl, IceCandidatePair *pair, const RtpSession *rtp_session)
973 974 975
{
	StunMessage msg;
	StunAddress4 dest;
976 977
	StunAtrString username;
	StunAtrString password;
978 979 980
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	int socket = 0;
Ghislain MARY's avatar
Ghislain MARY committed
981
	char tr_id_str[25];
982

983
	if (pair->state == ICP_InProgress) {
984 985 986 987
		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;
988 989 990 991
			if (pair->use_candidate == FALSE) {
				ice_pair_set_state(pair, ICP_Waiting);
				ice_check_list_queue_triggered_check(cl, pair);
			}
992 993
			return;
		}
994 995 996 997 998 999 1000 1001 1002
		/* 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
1003
	pair->transmission_time = ice_current_time();
1004

1005
	if (pair->local->componentID == 1) {
1006
		socket = rtp_session_get_rtp_socket(rtp_session);
1007
	} else if (pair->local->componentID == 2) {
1008
		socket = rtp_session_get_rtcp_socket(rtp_session);
1009 1010
	} else return;

1011 1012 1013 1014 1015
	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);

1016 1017
	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
1018
	stunBuildReqSimple(&