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

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)

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
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 */
Simon Morlat's avatar
Simon Morlat committed
46
#define ICE_GATHERING_CANDIDATES_TIMEOUT	5000	/* In milliseconds */
47
#define ICE_NOMINATION_DELAY		1000	/* In milliseconds */
48
#define ICE_MAX_RETRANSMISSIONS		7
49
#define ICE_MAX_STUN_REQUEST_RETRANSMISSIONS	7
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;
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 {
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 115 116 117 118
typedef struct _StunRequestRoundTripTime {
	int nb_responses;
	int sum;
} StunRequestRoundTripTime;

119

120 121
static MSTimeSpec ice_current_time(void);
static MSTimeSpec ice_add_ms(MSTimeSpec orig, uint32_t ms);
122
static int32_t ice_compare_time(MSTimeSpec ts1, MSTimeSpec ts2);
123
static char * ice_inet_ntoa(struct sockaddr *addr, int addrlen, char *dest, int destlen);
Ghislain MARY's avatar
Ghislain MARY committed
124
static void transactionID2string(const UInt96 *tr_id, char *tr_id_str);
125
static void ice_send_stun_server_binding_request(ortp_socket_t sock, const struct sockaddr *server, socklen_t addrlen, IceStunServerCheck *check);
126
static int ice_compare_transport_addresses(const IceTransportAddress *ta1, const IceTransportAddress *ta2);
127
static int ice_compare_pair_priorities(const IceCandidatePair *p1, const IceCandidatePair *p2);
128 129
static int ice_compare_pairs(const IceCandidatePair *p1, const IceCandidatePair *p2);
static int ice_compare_candidates(const IceCandidate *c1, const IceCandidate *c2);
130
static int ice_find_host_candidate(const IceCandidate *candidate, const uint16_t *componentID);
131
static int ice_find_candidate_from_type_and_componentID(const IceCandidate *candidate, const Type_ComponentID *tc);
132
static int ice_find_use_candidate_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
133
static int ice_find_nominated_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
134
static int ice_find_selected_valid_pair_from_componentID(const IceValidCandidatePair* valid_pair, const uint16_t* componentID);
135
static void ice_find_selected_valid_pair_for_componentID(const uint16_t *componentID, CheckList_Bool *cb);
136
static int ice_find_running_check_list(const IceCheckList *cl);
137
static int ice_find_pair_in_valid_list(IceValidCandidatePair *valid_pair, IceCandidatePair *pair);
138
static void ice_pair_set_state(IceCandidatePair *pair, IceCandidatePairState state);
139
static void ice_compute_candidate_foundation(IceCandidate *candidate, IceCheckList *cl);
Ghislain MARY's avatar
Ghislain MARY committed
140
static void ice_set_credentials(char **ufrag, char **pwd, const char *ufrag_str, const char *pwd_str);
141
static void ice_conclude_processing(IceCheckList* cl, RtpSession* rtp_session);
Ghislain MARY's avatar
Ghislain MARY committed
142 143


144 145 146 147
/******************************************************************************
 * CONSTANTS DEFINITIONS                                                      *
 *****************************************************************************/

148 149
uint32_t stun_magic_cookie = 0x2112A442;

Ghislain MARY's avatar
Ghislain MARY committed
150 151 152 153 154
static const char * const role_values[] = {
	"Controlling",	/* IR_Controlling */
	"Controlled",	/* IR_Controlled */
};

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
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 */
};

172 173 174 175 176 177 178 179 180 181
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
182 183 184
 * SESSION INITIALISATION AND DEINITIALISATION                                *
 *****************************************************************************/

185 186 187 188 189 190 191 192
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);
193
	sprintf(ufrag, "%08x", (int)random());
194 195 196 197 198 199 200
	ufrag[8] = '\0';
	return ufrag;
}

static char * generate_pwd(void)
{
	char *pwd = ms_malloc(25);
201
	sprintf(pwd, "%08x%08x%08x", (int)random(), (int)random(), (int)random());
202 203 204 205
	pwd[24] = '\0';
	return pwd;
}

Ghislain MARY's avatar
Ghislain MARY committed
206 207 208
static void ice_session_init(IceSession *session)
{
	session->streams = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
209
	session->state = IS_Stopped;
Ghislain MARY's avatar
Ghislain MARY committed
210
	session->role = IR_Controlling;
211
	session->tie_breaker = generate_tie_breaker();
212
	session->ta = ICE_DEFAULT_TA_DURATION;
Ghislain MARY's avatar
Ghislain MARY committed
213
	session->keepalive_timeout = ICE_DEFAULT_KEEPALIVE_TIMEOUT;
Ghislain MARY's avatar
Ghislain MARY committed
214
	session->max_connectivity_checks = ICE_MAX_NB_CANDIDATE_PAIRS;
215 216
	session->local_ufrag = generate_ufrag();
	session->local_pwd = generate_pwd();
Ghislain MARY's avatar
Ghislain MARY committed
217 218
	session->remote_ufrag = NULL;
	session->remote_pwd = NULL;
219
	memset(&session->event_time, 0, sizeof(session->event_time));
220
	session->send_event = FALSE;
221 222
	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
223 224 225 226 227 228
}

IceSession * ice_session_new(void)
{
	IceSession *session = ms_new(IceSession, 1);
	if (session == NULL) {
229 230 231
		ms_error("ice: Memory allocation of ICE session failed");
		return NULL;
	}
Ghislain MARY's avatar
Ghislain MARY committed
232 233 234 235 236 237
	ice_session_init(session);
	return session;
}

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


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

254 255
static void ice_check_list_init(IceCheckList *cl)
{
Ghislain MARY's avatar
Ghislain MARY committed
256
	cl->session = NULL;
257
	cl->rtp_session = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
258
	cl->remote_ufrag = cl->remote_pwd = NULL;
259
	cl->stun_server_checks = NULL;
260
	cl->local_candidates = cl->remote_candidates = cl->pairs = cl->losing_pairs = cl->triggered_checks_queue = cl->check_list = cl->valid_list = cl->transaction_list = NULL;
Ghislain MARY's avatar
Ghislain MARY committed
261
	cl->local_componentIDs = cl->remote_componentIDs = cl->foundations = NULL;
262
	cl->state = ICL_Running;
263
	cl->foundation_generator = 1;
Ghislain MARY's avatar
Ghislain MARY committed
264
	cl->mismatch = FALSE;
265
	cl->gathering_candidates = FALSE;
266
	cl->gathering_finished = FALSE;
267 268 269 270 271
	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));
272 273
}

274
IceCheckList * ice_check_list_new(void)
275 276 277 278 279 280 281
{
	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);
282 283 284
	return cl;
}

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

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

323 324 325 326 327
static void ice_free_stun_server_check_transaction(IceStunServerCheckTransaction *transaction)
{
	ms_free(transaction);
}

328 329
static void ice_free_stun_server_check(IceStunServerCheck *check)
{
330
	ms_list_for_each(check->transactions, (void (*)(void*))ice_free_stun_server_check_transaction);
331 332 333
	ms_free(check);
}

334 335 336 337 338
static void ice_free_transaction(IceTransaction *transaction)
{
	ms_free(transaction);
}

339 340 341 342 343
static void ice_free_pair_foundation(IcePairFoundation *foundation)
{
	ms_free(foundation);
}

344 345 346 347 348
static void ice_free_valid_pair(IceValidCandidatePair *valid_pair)
{
	ms_free(valid_pair);
}

349
static void ice_free_candidate_pair(IceCandidatePair *pair, IceCheckList *cl)
350
{
351 352 353 354 355 356
	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);
357
		cl->valid_list = ms_list_remove_link(cl->valid_list, elem);
358
	}
359 360 361
	ms_free(pair);
}

362 363 364 365 366
static void ice_free_candidate(IceCandidate *candidate)
{
	ms_free(candidate);
}

367 368
void ice_check_list_destroy(IceCheckList *cl)
{
Ghislain MARY's avatar
Ghislain MARY committed
369 370
	if (cl->remote_ufrag) ms_free(cl->remote_ufrag);
	if (cl->remote_pwd) ms_free(cl->remote_pwd);
371
	ms_list_for_each(cl->stun_server_checks, (void (*)(void*))ice_free_stun_server_check);
372
	ms_list_for_each(cl->transaction_list, (void (*)(void*))ice_free_transaction);
373
	ms_list_for_each(cl->foundations, (void (*)(void*))ice_free_pair_foundation);
374
	ms_list_for_each2(cl->pairs, (void (*)(void*,void*))ice_free_candidate_pair, cl);
375
	ms_list_for_each(cl->valid_list, (void (*)(void*))ice_free_valid_pair);
376 377
	ms_list_for_each(cl->remote_candidates, (void (*)(void*))ice_free_candidate);
	ms_list_for_each(cl->local_candidates, (void (*)(void*))ice_free_candidate);
378
	ms_list_free(cl->stun_server_checks);
379
	ms_list_free(cl->transaction_list);
380
	ms_list_free(cl->foundations);
Ghislain MARY's avatar
Ghislain MARY committed
381 382
	ms_list_free(cl->local_componentIDs);
	ms_list_free(cl->remote_componentIDs);
383
	ms_list_free(cl->valid_list);
384
	ms_list_free(cl->check_list);
385
	ms_list_free(cl->triggered_checks_queue);
386
	ms_list_free(cl->losing_pairs);
387 388 389
	ms_list_free(cl->pairs);
	ms_list_free(cl->remote_candidates);
	ms_list_free(cl->local_candidates);
390
	memset(cl, 0, sizeof(IceCheckList));
391 392 393 394
	ms_free(cl);
}


395 396 397 398
/******************************************************************************
 * CANDIDATE ACCESSORS                                                        *
 *****************************************************************************/

399
const char *ice_candidate_type(const IceCandidate *candidate)
400 401 402 403
{
	return candidate_type_values[candidate->type];
}

404 405 406 407 408 409 410 411 412 413 414 415
/******************************************************************************
 * CANDIDATE PAIR ACCESSORS                                                   *
 *****************************************************************************/

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


416 417 418 419
/******************************************************************************
 * CHECK LIST ACCESSORS                                                       *
 *****************************************************************************/

420
IceCheckListState ice_check_list_state(const IceCheckList* cl)
421 422 423 424
{
	return cl->state;
}

425
static int ice_find_check_list_from_state(const IceCheckList *cl, const IceCheckListState *state)
426
{
427
	return (cl->state != *state);
428 429
}

430 431
void ice_check_list_set_state(IceCheckList *cl, IceCheckListState state)
{
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
	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;
			}
		}
447
	}
448 449
}

450 451 452 453 454
void ice_check_list_set_rtp_session(IceCheckList *cl, RtpSession *rtp_session)
{
	cl->rtp_session = rtp_session;
}

455
const char * ice_check_list_local_ufrag(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
456 457 458 459 460
{
	/* Do not handle media specific ufrag for the moment, so use the session local ufrag. */
	return cl->session->local_ufrag;
}

461
const char * ice_check_list_local_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
462 463 464 465 466
{
	/* Do not handle media specific pwd for the moment, so use the session local pwd. */
	return cl->session->local_pwd;
}

467
const char * ice_check_list_remote_ufrag(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
468 469 470 471 472
{
	if (cl->remote_ufrag) return cl->remote_ufrag;
	else return cl->session->remote_ufrag;
}

473
const char * ice_check_list_remote_pwd(const IceCheckList* cl)
Ghislain MARY's avatar
Ghislain MARY committed
474 475 476 477 478
{
	if (cl->remote_pwd) return cl->remote_pwd;
	else return cl->session->remote_pwd;
}

479
static int ice_find_default_local_candidate(const IceCandidate *candidate, const uint16_t *componentID)
480
{
481
	return !((candidate->componentID == *componentID) && (candidate->is_default == TRUE));
482 483
}

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
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;
}

502 503 504 505 506
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);
}

507
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)
508 509
{
	IceCandidate *candidate = NULL;
510 511 512 513 514 515 516 517 518 519 520 521 522
	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;
523 524 525 526
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
527 528 529 530
	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;
531 532
}

533
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
534 535
{
	IceCandidate *candidate = NULL;
536 537 538 539 540 541
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
542
	rtp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
543 544
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
545
	rtcp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
546 547 548 549 550

	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;
551 552 553 554
	if (rtcp_elem == NULL) {
		if ((rtcp_addr != NULL) || (rtcp_port != NULL)) return FALSE;
		else return TRUE;
	}
555 556 557 558 559
	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
560 561
}

562
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)
563 564 565 566 567 568 569 570
{
	IceCandidate *candidate = NULL;
	IceValidCandidatePair *valid_pair = NULL;
	uint16_t componentID;
	MSList *rtp_elem;
	MSList *rtcp_elem;

	componentID = 1;
571
	rtp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
572 573
	if (rtp_elem == NULL) return FALSE;
	componentID = 2;
574
	rtcp_elem = ms_list_find_custom(cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, &componentID);
575 576 577 578 579 580 581 582 583 584 585 586 587

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

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

598 599 600 601 602 603 604 605 606 607 608 609 610 611
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);
		}
	}
}

612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
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;
}

635 636 637 638 639 640 641 642 643 644 645
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
646 647 648 649 650
bool_t ice_check_list_is_mismatch(const IceCheckList *cl)
{
	return cl->mismatch;
}

Ghislain MARY's avatar
Ghislain MARY committed
651 652 653 654 655

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

656 657 658 659 660
IceCheckList * ice_session_check_list(const IceSession *session, unsigned int n)
{
	return (IceCheckList *)ms_list_nth_data(session->streams, n);
}

661
const char * ice_session_local_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
662 663 664 665
{
	return session->local_ufrag;
}

666
const char * ice_session_local_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
667 668 669 670
{
	return session->local_pwd;
}

671
const char * ice_session_remote_ufrag(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
672 673 674 675
{
	return session->remote_ufrag;
}

676
const char * ice_session_remote_pwd(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
677 678 679 680
{
	return session->remote_pwd;
}

681 682 683 684 685 686 687 688 689 690
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);
}

691 692 693 694 695 696
IceSessionState ice_session_state(const IceSession *session)
{
	return session->state;
}

IceRole ice_session_role(const IceSession *session)
Ghislain MARY's avatar
Ghislain MARY committed
697 698 699 700
{
	return session->role;
}

Ghislain MARY's avatar
Ghislain MARY committed
701 702
void ice_session_set_role(IceSession *session, IceRole role)
{
703 704 705 706 707
	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
708 709
}

Ghislain MARY's avatar
Ghislain MARY committed
710 711 712 713 714
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);
}

715 716 717 718 719 720 721 722
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
723 724 725 726 727
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
728 729 730 731 732
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
733 734 735 736 737 738
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
739 740 741 742 743

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

744 745 746 747 748
int ice_session_nb_check_lists(IceSession *session)
{
	return ms_list_size(session->streams);
}

749 750 751 752 753 754 755 756 757 758 759 760
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
761
void ice_session_add_check_list(IceSession *session, IceCheckList *cl)
762
{
Ghislain MARY's avatar
Ghislain MARY committed
763 764
	session->streams = ms_list_append(session->streams, cl);
	cl->session = session;
765 766 767
	if (cl->state == ICL_Running) {
		session->state = IS_Running;
	}
768 769
}

770 771
void ice_session_remove_check_list(IceSession *session, IceCheckList *cl)
{
772
	if (cl == NULL) return;
773 774 775 776
	session->streams = ms_list_remove(session->streams, cl);
	ice_check_list_destroy(cl);
}

Ghislain MARY's avatar
Ghislain MARY committed
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
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);
}

801

802 803 804 805
/******************************************************************************
 * CANDIDATES GATHERING                                                       *
 *****************************************************************************/

806
static void ice_check_list_candidates_gathered_result_ptr(const IceCheckList *cl, bool_t *result)
807
{
808 809 810 811 812 813
	if (cl->gathering_finished == FALSE) *result = FALSE;
}

bool_t ice_check_list_candidates_gathered(const IceCheckList *cl)
{
	return cl->gathering_finished;
814 815 816 817 818
}

bool_t ice_session_candidates_gathered(const IceSession *session)
{
	bool_t result = TRUE;
819
	ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_candidates_gathered_result_ptr, &result);
820 821 822
	return result;
}

823 824 825 826 827
static void ice_check_list_gathering_needed(const IceCheckList *cl, bool_t *gathering_needed)
{
	if (cl->gathering_finished == FALSE) *gathering_needed = TRUE;
}

828
static void ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si)
829 830 831
{
	IceStunServerCheck *check;
	ortp_socket_t sock = -1;
832
	MSTimeSpec curtime = ice_current_time();
833

834
	if ((cl->rtp_session != NULL) && (cl->gathering_candidates == FALSE) && (cl->state != ICL_Completed) && (ice_check_list_candidates_gathered(cl) == FALSE)) {
835
		cl->gathering_candidates = TRUE;
836
		cl->gathering_start_time = curtime;
837 838 839 840
		sock = rtp_session_get_rtp_socket(cl->rtp_session);
		if (sock > 0) {
			check = (IceStunServerCheck *)ms_new0(IceStunServerCheck, 1);
			check->sock = sock;
841
			check->srcport = rtp_session_get_local_port(cl->rtp_session);
842
			if (si->index == 0) {
843 844
				check->next_transmission_time = ice_add_ms(curtime, ICE_DEFAULT_RTO_DURATION);
				ice_send_stun_server_binding_request(sock, (struct sockaddr *)&cl->session->ss, cl->session->ss_len, check);
845
			} else {
846
				check->next_transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION);
847 848 849 850 851 852 853
			}
			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;
854
			check->srcport = rtp_session_get_local_rtcp_port(cl->rtp_session);
855
			check->next_transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION + ICE_DEFAULT_TA_DURATION);
856 857
			cl->stun_server_checks = ms_list_append(cl->stun_server_checks, check);
		}
858
		si->index++;
859 860 861
	}
}

862
void ice_session_gather_candidates(IceSession *session,const struct sockaddr* ss, socklen_t ss_len)
863
{
864
	Session_Index si;
865 866
	OrtpEvent *ev;
	bool_t gathering_needed = FALSE;
867
	memcpy(&session->ss,ss,ss_len);
868
	session->ss_len = ss_len;
869 870
	si.session = session;
	si.index = 0;
871
	ms_get_cur_time(&session->gathering_start_ts);
872 873 874 875 876 877 878 879 880 881
	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);
	}
882 883
}

884 885 886 887 888 889 890
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);
}

891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
static void ice_transaction_sum_gathering_round_trip_time(const IceStunServerCheckTransaction *transaction, StunRequestRoundTripTime *rtt)
{
	if ((transaction->response_time.tv_sec != 0) && (transaction->response_time.tv_nsec != 0)) {
		rtt->nb_responses++;
		rtt->sum += ice_compare_time(transaction->response_time, transaction->request_time);
	}
}

static void ice_stun_server_check_sum_gathering_round_trip_time(const IceStunServerCheck *check, StunRequestRoundTripTime *rtt)
{
	ms_list_for_each2(check->transactions, (void (*)(void*,void*))ice_transaction_sum_gathering_round_trip_time, rtt);
}

static void ice_check_list_sum_gathering_round_trip_times(const IceCheckList *cl, StunRequestRoundTripTime *rtt)
{
	ms_list_for_each2(cl->stun_server_checks, (void (*)(void*,void*))ice_stun_server_check_sum_gathering_round_trip_time, rtt);
}

int ice_session_average_gathering_round_trip_time(IceSession *session)
{
	StunRequestRoundTripTime rtt;

	if ((session->gathering_start_ts.tv_sec == -1) || (session->gathering_end_ts.tv_sec == -1)) return -1;
	memset(&rtt, 0, sizeof(rtt));
	ms_list_for_each2(session->streams, (void (*)(void*,void*))ice_check_list_sum_gathering_round_trip_times, &rtt);
	if (rtt.nb_responses == 0) return -1;
	return (rtt.sum / rtt.nb_responses);
}

920

921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
/******************************************************************************
 * 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;

936 937
	if (cl->state != ICL_Completed) return;

938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
	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);
}


953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978
/******************************************************************************
 * TRANSACTION HANDLING                                                       *
 *****************************************************************************/

static IceTransaction * ice_create_transaction(IceCheckList *cl, IceCandidatePair *pair, const UInt96 *tr_id)
{
	IceTransaction *transaction = ms_new0(IceTransaction, 1);
	transaction->pair = pair;
	memcpy(&transaction->transactionID, tr_id, sizeof(transaction->transactionID));
	cl->transaction_list = ms_list_prepend(cl->transaction_list, transaction);
	return transaction;
}

static int ice_find_transaction_from_pair(const IceTransaction *transaction, const IceCandidatePair *pair)
{
	return (transaction->pair != pair);
}

static IceTransaction * ice_find_transaction(const IceCheckList *cl, const IceCandidatePair *pair)
{
	MSList *elem = ms_list_find_custom(cl->transaction_list, (MSCompareFunc)ice_find_transaction_from_pair, pair);
	if (elem == NULL) return NULL;
	return (IceTransaction *)elem->data;
}


979 980 981 982
/******************************************************************************
 * STUN PACKETS HANDLING                                                      *
 *****************************************************************************/

983
static void ice_send_stun_server_binding_request(ortp_socket_t sock, const struct sockaddr *server, socklen_t addrlen, IceStunServerCheck *check)
984 985 986 987 988 989 990
{
	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;
991
	char tr_id_str[25];
992 993 994 995

	memset(&msg, 0, sizeof(StunMessage));
	memset(&username,0,sizeof(username));
	memset(&password,0,sizeof(password));
996
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, check->sock);
997 998
	len = stunEncodeMessage(&msg, buf, len, &password);
	if (len > 0) {
999 1000 1001 1002
		IceStunServerCheckTransaction *transaction = ms_new0(IceStunServerCheckTransaction, 1);
		transaction->request_time = ice_current_time();
		memcpy(&transaction->transactionID, &msg.msgHdr.tr_id, sizeof(transaction->transactionID));
		check->transactions = ms_list_append(check->transactions, transaction);
1003
		transactionID2string(&msg.msgHdr.tr_id, tr_id_str);
1004
		ms_message("ice: Send STUN binding request from port %u [%s]", check->srcport, tr_id_str);
1005 1006 1007 1008 1009 1010
		sendMessage(sock, buf, len, htonl(servaddr->sin_addr.s_addr), htons(servaddr->sin_port));
	}
}

static int ice_parse_stun_server_binding_response(const StunMessage *msg, char *addr, int addr_len, int *port)
{
1011
	struct sockaddr_in addr_in;
1012 1013 1014

	if (msg->hasXorMappedAddress) {
		*port = msg->xorMappedAddress.ipv4.port;
1015
		addr_in.sin_addr.s_addr = htonl(msg->xorMappedAddress.ipv4.addr);
1016 1017
	} else if (msg->hasMappedAddress) {
		*port = msg->mappedAddress.ipv4.port;
1018
		addr_in.sin_addr.s_addr = htonl(msg->mappedAddress.ipv4.addr);
1019 1020
	} else return -1;

1021 1022 1023
	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);
1024 1025 1026
	return 0;
}

1027
/* Send a STUN binding request for ICE connectivity checks according to 7.1.2. */
1028
static void ice_send_binding_request(IceCheckList *cl, IceCandidatePair *pair, const RtpSession *rtp_session)
1029 1030 1031
{
	StunMessage msg;
	StunAddress4 dest;
1032 1033
	StunAtrString username;
	StunAtrString password;
1034
	IceTransaction *transaction;
1035 1036 1037
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	int socket = 0;
Ghislain MARY's avatar
Ghislain MARY committed
1038
	char tr_id_str[25];
1039

1040 1041
	transaction = ice_find_transaction(cl, pair);

1042
	if (pair->state == ICP_InProgress) {
1043 1044 1045 1046
		if (transaction == NULL) {
			ms_error("ice: No transaction found for InProgress pair");
			return;
		}
1047 1048 1049 1050
		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;
1051 1052 1053 1054
			if (pair->use_candidate == FALSE) {
				ice_pair_set_state(pair, ICP_Waiting);
				ice_check_list_queue_triggered_check(cl, pair);
			}
1055 1056
			return;
		}
1057 1058 1059 1060 1061 1062 1063 1064 1065
		/* 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;
	}
1066
	pair->transmission_time = ice_current_time();
1067

1068
	if (pair->local->componentID == 1) {
1069
		socket = rtp_session_get_rtp_socket(rtp_session);
1070
	} else if (pair->local->componentID == 2) {
1071
		socket = rtp_session_get_rtcp_socket(rtp_session);
1072 1073
	} else return;

1074 1075 1076 1077 1078
	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);

1079 1080
	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
1081
	stunBuildReqSimple(&msg, &username, FALSE, FALSE, 1);
1082
	msg.hasMessageIntegrity = TRUE;
1083
	msg.hasFingerprint = TRUE;
1084 1085 1086 1087 1088 1089

	/* 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. */
1090
	if ((cl->session->role == IR_Controlling) && (pair->use_candidate == TRUE)) {
1091 1092 1093
		msg.hasUseCandidate = TRUE;
	}

1094
	/* Include the ICE-CONTROLLING or ICE-CONTROLLED attribute depending on the role of the agent, as defined in 7.1.2.2. */
1095
	switch (cl->session->role) {
1096 1097
		case IR_Controlling:
			msg.hasIceControlling = TRUE;
1098
			msg.iceControlling.value = cl->session->tie_breaker;
1099 1100 1101
			break;
		case IR_Controlled:
			msg.hasIceControlled = TRUE;
1102
			msg.iceControlled.value = cl->session->tie_breaker;
1103 1104
			break;
	}
1105

1106 1107
	/* Keep the same transaction ID for retransmission. */
	if (pair->state == ICP_InProgress) {
1108 1109 1110
		memcpy(&msg.msgHdr.tr_id, &transaction->transactionID, sizeof(msg.msgHdr.tr_id));
	} else {
		transaction = ice_create_transaction(cl, pair, &msg.msgHdr.tr_id);
1111 1112
	}

1113
	len = stunEncodeMessage(&msg, buf, len, &password);
1114
	if (len > 0) {
1115
		transactionID2string(&transaction->transactionID, tr_id_str);
Ghislain MARY's avatar
Ghislain MARY committed
1116 1117 1118 1119 1120 1121 1122 1123 1124
		if (pair->state == ICP_InProgress) {
			ms_message("ice: Retransmit (%d) binding request for pair %p: %s:%u:%s --> %s:%u:%s [%s]", pair->retransmissions, pair,
				pair->local->taddr.ip, pair->local->taddr.port, candidate_type_values[pair->local->type],
				pair->remote->taddr.ip, pair->remote->taddr.port, candidate_type_values[pair->remote->type], tr_id_str);
		} else {
			ms_message("ice: Send binding request for %s pair %p: %s:%u:%s --> %s:%u:%s [%s]", candidate_pair_state_values[pair->state], pair,
				pair->local->taddr.ip, pair->local->taddr.port, candidate_type_values[pair->local->type],
				pair->remote->taddr.ip, pair->remote->taddr.port, candidate_type_values[pair->remote->type], tr_id_str);
		}
1125
		sendMessage(socket, buf, len, dest.addr, dest.port);
1126

1127
		if (pair->state != ICP_InProgress) {
1128 1129 1130
			/* First transmission of the request, initialize the retransmission timer. */
			pair->rto = ICE_DEFAULT_RTO_DURATION;
			pair->retransmissions = 0;
1131 1132
			/* Save the role of the agent. */
			pair->role = cl->session->role;
1133 1134 1135
			/* Change the state of the pair. */
			ice_pair_set_state(pair, ICP_InProgress);
		}
1136 1137 1138
	}
}

1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
static int ice_get_componentID_from_rtp_session(const OrtpEventData *evt_data)
{
	if (evt_data->info.socket_type == OrtpRTPSocket) {
		return 1;
	} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
		return 2;
	}
	return -1;
}


1150
static int ice_get_socket_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data)
1151 1152 1153 1154 1155 1156 1157 1158 1159
{
	if (evt_data->info.socket_type == OrtpRTPSocket) {
		return rtp_session_get_rtp_socket(rtp_session);
	} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
		return rtp_session_get_rtcp_socket(rtp_session);
	}
	return -1;
}

1160
static int ice_get_recv_port_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data)
1161 1162
{
	if (evt_data->info.socket_type == OrtpRTPSocket) {
1163
		return rtp_session_get_local_port(rtp_session);
1164
	} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
1165
		return rtp_session_get_local_rtcp_port(rtp_session);
1166 1167 1168
	} else return -1;
}

1169
static void ice_send_binding_response(const RtpSession *rtp_session, const OrtpEventData *evt_data, const StunMessage *msg, const StunAddress4 *dest)
1170 1171 1172 1173 1174
{
	StunMessage response;
	StunAtrString password;
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
1175
	int socket = ice_get_socket_from_rtp_session(rtp_session, evt_data);
Ghislain MARY's avatar
Ghislain MARY committed
1176
	int recvport = ice_get_recv_port_from_rtp_session(rtp_session, evt_data);
1177 1178
	struct sockaddr_in dest_addr;
	struct sockaddr_in source_addr;
Ghislain MARY's avatar
Ghislain MARY committed
1179
	char dest_addr_str[256];
1180
	char source_addr_str[256];
Ghislain MARY's avatar
Ghislain MARY committed
1181
	char tr_id_str[25];
1182

1183
	if (socket < 0) return;
1184
	memset(&response, 0, sizeof(response));
1185
	memset(&password, 0, sizeof(password));
1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198

	/* Copy magic cookie and transaction ID from the request. */
	response.msgHdr.magic_cookie = ntohl(msg->msgHdr.magic_cookie);
	memcpy(&response.msgHdr.tr_id, &msg->msgHdr.tr_id, sizeof(response.msgHdr.tr_id));

	/* Create the binding response. */
	response.msgHdr.msgType = (STUN_METHOD_BINDING | STUN_SUCCESS_RESP);
	response.hasMessageIntegrity = TRUE;
	response.hasFingerprint = TRUE;
	response.hasUsername = TRUE;
	memcpy(response.username.value, msg->username.value, msg->username.sizeValue);
	response.username.sizeValue = msg->username.sizeValue;

1199 1200 1201 1202 1203
	/* Add the mapped address to the response. */
	response.hasXorMappedAddress = TRUE;
	response.xorMappedAddress.ipv4.port = dest->port ^ (stun_magic_cookie >> 16);
	response.xorMappedAddress.ipv4.addr = dest->addr ^ stun_magic_cookie;

1204 1205
	len = stunEncodeMessage(&response, buf, len, &password);
	if (len > 0) {
Ghislain MARY's avatar
Ghislain MARY committed
1206
		transactionID2string(&response.msgHdr.tr_id, tr_id_str);
1207 1208 1209 1210
		dest_addr.sin_addr.s_addr = htonl(dest->addr);
		dest_addr.sin_port = htons(dest->port);
		dest_addr.sin_family = AF_INET;
		ice_inet_ntoa((struct sockaddr *)&dest_addr, sizeof(dest_addr), dest_addr_str, sizeof(dest_addr_str));
1211
		source_addr.sin_addr.s_addr = evt_data->packet->recv_addr.addr.ipi_addr.s_addr;	// TODO: Handle IPv6
1212 1213 1214 1215
		source_addr.sin_port = htons(recvport);
		source_addr.sin_family = AF_INET;
		ice_inet_ntoa((struct sockaddr *)&source_addr, sizeof(source_addr), source_addr_str, sizeof(source_addr_str));
		ms_message("ice: Send binding response: %s:%u --> %s:%u [%s]", source_addr_str, recvport, dest_addr_str, dest->port, tr_id_str);
1216 1217 1218 1219
		sendMessage(socket, buf, len, dest->addr, dest->port);
	}
}

1220
static void ice_send_error_response(const RtpSession *rtp_session, const OrtpEventData *evt_data, const StunMessage *msg, uint8_t err_class, uint8_t err_num, const StunAddress4 *dest, const char *error)
1221 1222 1223 1224 1225
{
	StunMessage response;
	StunAtrString password;
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
1226
	int socket = ice_get_socket_from_rtp_session(rtp_session, evt_data);
Ghislain MARY's avatar
Ghislain MARY committed
1227
	int recvport = ice_get_recv_port_from_rtp_session(rtp_session, evt_data);
1228 1229 1230 1231
	struct sockaddr_in dest_addr;
	struct sockaddr_in source_addr;
	char dest_addr_str[256];
	char source_addr_str[256];
Ghislain MARY's avatar
Ghislain MARY committed
1232
	char tr_id_str[25];
1233

1234
	if (socket < 0) return;
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
	memset(&response, 0, sizeof(response));

	/* Copy magic cookie and transaction ID from the request. */
	response.msgHdr.magic_cookie = ntohl(msg->msgHdr.magic_cookie);
	memcpy(&response.msgHdr.tr_id, &msg->msgHdr.tr_id, sizeof(response.msgHdr.tr_id));

	/* Create the error response. */
	response.msgHdr.msgType = (STUN_METHOD_BINDING | STUN_ERR_RESP);
	response.hasErrorCode = TRUE;
	response.errorCode.errorClass = err_class;
	response.errorCode.number = err_num;
	strcpy(response.errorCode.reason, error);
	response.errorCode.sizeReason = strlen(error);
	response.hasFingerprint = TRUE;

	len = stunEncodeMessage(&response, buf, len, &password);
	if (len > 0) {
Ghislain MARY's avatar
Ghislain MARY committed
1252
		transactionID2string(&response.msgHdr.tr_id, tr_id_str);
1253 1254 1255 1256
		dest_addr.sin_addr.s_addr = htonl(dest->addr);
		dest_addr.sin_port = htons(dest->port);
		dest_addr.sin_family = AF_INET;
		ice_inet_ntoa((struct sockaddr *)&dest_addr, sizeof(dest_addr), dest_addr_str, sizeof(dest_addr_str));
1257
		source_addr.sin_addr.s_addr = evt_data->packet->recv_addr.addr.ipi_addr.s_addr;	// TODO: Handle IPv6
1258 1259 1260 1261
		source_addr.sin_port = htons(recvport);
		source_addr.sin_family = AF_INET;
		ice_inet_ntoa((struct sockaddr *)&source_addr, sizeof(source_addr), source_addr_str, sizeof(source_addr_str));
		ms_message("ice: Send error response: %s:%u --> %s:%u [%s]", source_addr_str, recvport, dest_addr_str, dest->port, tr_id_str);
1262 1263 1264 1265
		sendMessage(socket, buf, len, dest->addr, dest->port);
	}
}

1266
static void ice_send_indication(const IceCandidatePair *pair, const RtpSession *rtp_session)
Ghislain MARY's avatar
Ghislain MARY committed
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
{
	StunMessage indication;
	StunAddress4 dest;
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	int socket;

	if (pair->local->componentID == 1) {
		socket = rtp_session_get_rtp_socket(rtp_session);
	} else if (pair->local->componentID == 2) {
		socket = rtp_session_get_rtcp_socket(rtp_session);
	} else return;

	stunParseHostName(pair->remote->taddr.ip, &dest.addr, &dest.port, pair->remote->taddr.port);
	memset(&indication, 0, sizeof(indication));
	stunBuildReqSimple(&indication, NULL, FALSE, FALSE, 1);
	indication.msgHdr.msgType = (STUN_METHOD_BINDING|STUN_INDICATION);
	indication.hasFingerprint = TRUE;

	len = stunEncodeMessage(&indication, buf, len, NULL);
	if (len > 0) {
Ghislain MARY's avatar
Ghislain MARY committed
1288 1289 1290
		ms_message("ice: Send indication for pair %p: %s:%u:%s --> %s:%u:%s", pair,
			pair->local->taddr.ip, pair->local->taddr.port, candidate_type_values[pair->local->type],
			pair->remote->taddr.ip, pair->remote->taddr.port, candidate_type_values[pair->remote->type]);
Ghislain MARY's avatar
Ghislain MARY committed
1291 1292 1293 1294
		sendMessage(socket, buf, len, dest.addr, dest.port);
	}
}

1295
static void ice_send_keepalive_packet_for_componentID(const uint16_t *componentID, const CheckList_RtpSession *cr)
Ghislain MARY's avatar
Ghislain MARY committed
1296
{
1297
	MSList *elem = ms_list_find_custom(cr->cl->valid_list, (MSCompareFunc)ice_find_selected_valid_pair_from_componentID, componentID);
Ghislain MARY's avatar
Ghislain MARY committed
1298 1299 1300 1301 1302 1303
	if (elem != NULL) {
		IceValidCandidatePair *valid_pair = (IceValidCandidatePair *)elem->data;
		ice_send_indication(valid_pair->valid, cr->rtp_session);
	}
}

1304
static void ice_send_keepalive_packets(IceCheckList *cl, const RtpSession *rtp_session)
Ghislain MARY's avatar
Ghislain MARY committed
1305 1306 1307 1308
{
	CheckList_RtpSession cr;
	cr.cl = cl;
	cr.rtp_session = rtp_session;
Ghislain MARY's avatar
Ghislain MARY committed
1309
	ms_list_for_each2(cl->local_componentIDs, (void (*)(void*,void*))ice_send_keepalive_packet_for_componentID, &cr);
Ghislain MARY's avatar
Ghislain MARY committed
1310 1311
}

1312
static int ice_find_candidate_from_transport_address(const IceCandidate *candidate, const IceTransportAddress *taddr)
1313 1314 1315 1316
{
	return ice_compare_transport_addresses(&candidate->taddr, taddr);
}

1317 1318 1319 1320 1321
static int ice_find_candidate_from_ip_address(const IceCandidate *candidate, const char *ipaddr)
{
	return strcmp(candidate->taddr.ip, ipaddr);
}

1322
/* Check that the mandatory attributes of a connectivity check binding request are present. */
1323
static int ice_check_received_binding_request_attributes(const RtpSession *rtp_session, const OrtpEventData *evt_data, const StunMessage *msg, const StunAddress4 *remote_addr)
1324 1325 1326
{
	if (!msg->hasMessageIntegrity) {
		ms_warning("ice: Received binding request missing MESSAGE-INTEGRITY attribute");
1327
		ice_send_error_response(rtp_session, evt_data, msg, 4, 0, remote_addr, "Missing MESSAGE-INTEGRITY attribute");
1328 1329 1330 1331
		return -1;
	}
	if (!msg->hasUsername) {
		ms_warning("ice: Received binding request missing USERNAME attribute");
1332
		ice_send_error_response(rtp_session, evt_data, msg, 4, 0, remote_addr, "Missing USERNAME attribute");
1333 1334 1335 1336
		return -1;
	}
	if (!msg->hasFingerprint) {
		ms_warning("ice: Received binding request missing FINGERPRINT attribute");
1337
		ice_send_error_response(rtp_session, evt_data, msg, 4, 0, remote_addr, "Missing FINGERPRINT attribute");
1338 1339 1340 1341
		return -1;
	}
	if (!msg->hasPriority) {
		ms_warning("ice: Received binding request missing PRIORITY attribute");
1342
		ice_send_error_response(rtp_session, evt_data, msg, 4, 0, remote_addr, "Missing PRIORITY attribute");
1343 1344 1345 1346
		return -1;
	}
	if (!msg->hasIceControlling && !msg->hasIceControlled) {
		ms_warning("ice: Received binding request missing ICE-CONTROLLING or ICE-CONTROLLED attribute");
1347
		ice_send_error_response(rtp_session, evt_data ,msg, 4, 0, remote_addr, "Missing ICE-CONTROLLING or ICE-CONTROLLED attribute");
1348 1349 1350 1351 1352
		return -1;
	}
	return 0;
}

1353
static int ice_check_received_binding_request_integrity(const IceCheckList *cl, const RtpSession *rtp_session, const OrtpEventData *evt_data, const StunMessage *msg, const StunAddress4 *remote_addr)
1354 1355
{
	char hmac[20];
1356
	mblk_t *mp = evt_data->packet;
1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367

	/* Check the message integrity: first remove length of fingerprint... */
	char *lenpos = (char *)mp->b_rptr + sizeof(uint16_t);
	uint16_t newlen = htons(msg->msgHdr.msgLength - 8);
	memcpy(lenpos, &newlen, sizeof(uint16_t));
	stunCalculateIntegrity_shortterm(hmac, (char *)mp->b_rptr, mp->b_wptr - mp->b_rptr - 24 - 8, ice_check_list_local_pwd(cl));
	/* ... and then restore the length with fingerprint. */
	newlen = htons(msg->msgHdr.msgLength);
	memcpy(lenpos, &newlen, sizeof(uint16_t));
	if (memcmp(msg->messageIntegrity.hash, hmac, sizeof(hmac)) != 0) {
		ms_error("ice: Wrong MESSAGE-INTEGRITY in received binding request");