ice.c 119 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
48
#define ICE_MAX_STUN_REQUEST_RETRANSMISSIONS	7
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;
Ghislain MARY's avatar
Ghislain MARY committed
71
	MSTimeSpec time;
72 73
} 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
typedef struct _Time_Bool {
Ghislain MARY's avatar
Ghislain MARY committed
98
	MSTimeSpec time;
99 100 101
	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

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


138 139 140 141
/******************************************************************************
 * CONSTANTS DEFINITIONS                                                      *
 *****************************************************************************/

142 143
uint32_t stun_magic_cookie = 0x2112A442;

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

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

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

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

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

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


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

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

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

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

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

315 316 317 318 319
static void ice_free_stun_server_check(IceStunServerCheck *check)
{
	ms_free(check);
}

320 321 322 323 324
static void ice_free_pair_foundation(IcePairFoundation *foundation)
{
	ms_free(foundation);
}

325 326 327 328 329
static void ice_free_valid_pair(IceValidCandidatePair *valid_pair)
{
	ms_free(valid_pair);
}

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

343 344 345 346 347
static void ice_free_candidate(IceCandidate *candidate)
{
	ms_free(candidate);
}

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


373 374 375 376
/******************************************************************************
 * CANDIDATE ACCESSORS                                                        *
 *****************************************************************************/

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

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


404 405 406 407
/******************************************************************************
 * CHECK LIST ACCESSORS                                                       *
 *****************************************************************************/

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

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

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

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

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

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

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

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

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

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

490 491 492 493 494
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);
}

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

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

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

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

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

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

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

576 577 578 579 580 581 582 583 584 585
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;
}

586 587 588 589 590 591 592 593 594 595 596 597 598 599
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);
		}
	}
}

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

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

Ghislain MARY's avatar
Ghislain MARY committed
639 640 641 642 643

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

789

790 791 792 793
/******************************************************************************
 * CANDIDATES GATHERING                                                       *
 *****************************************************************************/

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

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

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

811 812 813 814 815
static void ice_check_list_gathering_needed(const IceCheckList *cl, bool_t *gathering_needed)
{
	if (cl->gathering_finished == FALSE) *gathering_needed = TRUE;
}

816
static void ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si)
817 818 819
{
	IceStunServerCheck *check;
	ortp_socket_t sock = -1;
Ghislain MARY's avatar
Ghislain MARY committed
820
	MSTimeSpec curtime = ice_current_time();
821

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

void ice_session_gather_candidates(IceSession *session, struct sockaddr_storage ss, socklen_t ss_len)
{
854
	Session_Index si;
855 856
	OrtpEvent *ev;
	bool_t gathering_needed = FALSE;
857 858
	session->ss = ss;
	session->ss_len = ss_len;
859 860
	si.session = session;
	si.index = 0;
861
	ms_get_cur_time(&session->gathering_start_ts);
862 863 864 865 866 867 868 869 870 871
	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);
	}
872 873
}

874 875 876 877 878 879 880
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);
}

881

882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
/******************************************************************************
 * 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;

897 898
	if (cl->state != ICL_Completed) return;

899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
	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);
}


914 915 916 917
/******************************************************************************
 * STUN PACKETS HANDLING                                                      *
 *****************************************************************************/

918
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)
919 920 921 922 923 924 925
{
	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;
926
	char tr_id_str[25];
927 928 929 930 931

	memset(&msg, 0, sizeof(StunMessage));
	memset(&username,0,sizeof(username));
	memset(&password,0,sizeof(password));
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, id);
932 933 934 935
	if (nb_transmissions > 1) {
		/* Keep the same transaction ID for retransmissions. */
		memcpy(&msg.msgHdr.tr_id, transactionID, sizeof(msg.msgHdr.tr_id));
	}
936 937
	len = stunEncodeMessage(&msg, buf, len, &password);
	if (len > 0) {
938 939 940 941 942 943
		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);
		}
944
		sendMessage(sock, buf, len, htonl(servaddr->sin_addr.s_addr), htons(servaddr->sin_port));
945
		memcpy(transactionID, &msg.msgHdr.tr_id, sizeof(*transactionID));
946 947 948 949 950
	}
}

static int ice_parse_stun_server_binding_response(const StunMessage *msg, char *addr, int addr_len, int *port)
{
951
	struct sockaddr_in addr_in;
952 953 954

	if (msg->hasXorMappedAddress) {
		*port = msg->xorMappedAddress.ipv4.port;
955
		addr_in.sin_addr.s_addr = htonl(msg->xorMappedAddress.ipv4.addr);
956 957
	} else if (msg->hasMappedAddress) {
		*port = msg->mappedAddress.ipv4.port;
958
		addr_in.sin_addr.s_addr = htonl(msg->mappedAddress.ipv4.addr);
959 960
	} else return -1;

961 962 963
	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);
964 965 966
	return 0;
}

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

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

1001
	if (pair->local->componentID == 1) {
1002
		socket = rtp_session_get_rtp_socket(rtp_session);
1003
	} else if (pair->local->componentID == 2) {
1004
		socket = rtp_session_get_rtcp_socket(rtp_session);
1005 1006
	} else return;

1007 1008 1009 1010 1011
	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);

1012 1013
	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
1014
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, 1);
1015
	msg.hasMessageIntegrity = TRUE;
1016
	msg.hasFingerprint =<