misc.c 43.2 KB
Newer Older
aymeric's avatar
aymeric committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

/*
linphone
Copyright (C) 2000  Simon MORLAT (simon.morlat@linphone.org)

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.
*/

#include "private.h"
22
#include "lpconfig.h"
aymeric's avatar
aymeric committed
23 24 25
#include "mediastreamer2/mediastream.h"
#include <stdlib.h>
#include <stdio.h>
26
#ifdef HAVE_SIGHANDLER_T
aymeric's avatar
aymeric committed
27
#include <signal.h>
28
#endif /*HAVE_SIGHANDLER_T*/
Simon Morlat's avatar
Simon Morlat committed
29 30 31 32

#include <string.h>
#if !defined(_WIN32_WCE)
#include <errno.h>
aymeric's avatar
aymeric committed
33 34
#include <sys/types.h>
#include <sys/stat.h>
Sylvain Berfini's avatar
Sylvain Berfini committed
35 36 37
#if _MSC_VER
#include <io.h>
#else
aymeric's avatar
aymeric committed
38
#include <unistd.h>
Sylvain Berfini's avatar
Sylvain Berfini committed
39
#endif
aymeric's avatar
aymeric committed
40
#include <fcntl.h>
Jehan Monnier's avatar
Jehan Monnier committed
41
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
42

43
#undef snprintf
smorlat's avatar
smorlat committed
44
#include <ortp/stun.h>
aymeric's avatar
aymeric committed
45

46 47 48 49
#ifdef HAVE_GETIFADDRS
#include <net/if.h>
#include <ifaddrs.h>
#endif
50
#include <math.h>
51 52 53 54
#if _MSC_VER
#define snprintf _snprintf
#define popen _popen
#define pclose _pclose
Sylvain Berfini's avatar
Sylvain Berfini committed
55
#endif
56 57

#if !defined(WIN32)
aymeric's avatar
aymeric committed
58 59 60 61 62 63 64

static char lock_name[80];
static char lock_set=0;
/* put a lock file in /tmp. this is called when linphone runs as a daemon*/
int set_lock_file()
{
	FILE *lockfile;
65

aymeric's avatar
aymeric committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
	snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
	lockfile=fopen(lock_name,"w");
	if (lockfile==NULL)
	{
		printf("Failed to create lock file.\n");
		return(-1);
	}
	fprintf(lockfile,"%i",getpid());
	fclose(lockfile);
	lock_set=1;
	return(0);
}

/* looks if there is a lock file. If presents return its content (the pid of the already running linphone), if not found, returns -1*/
int get_lock_file()
{
	int pid;
	FILE *lockfile;
84

aymeric's avatar
aymeric committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
	snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
	lockfile=fopen(lock_name,"r");
	if (lockfile==NULL)
		return(-1);
	if (fscanf(lockfile,"%i",&pid)!=1){
		ms_warning("Could not read pid in lock file.");
		fclose(lockfile);
		return -1;
	}
	fclose(lockfile);
	return pid;
}

/* remove the lock file if it was set*/
int remove_lock_file()
{
	int err=0;
	if (lock_set)
	{
		err=unlink(lock_name);
		lock_set=0;
	}
	return(err);
}

#endif

char *int2str(int number)
{
	char *numstr=ms_malloc(10);
jehan's avatar
jehan committed
115
	snprintf(numstr,10,"%i",number);
aymeric's avatar
aymeric committed
116 117 118 119 120
	return numstr;
}

void check_sound_device(LinphoneCore *lc)
{
Simon Morlat's avatar
Simon Morlat committed
121
#ifdef _linux
Jehan Monnier's avatar
Jehan Monnier committed
122 123
	int fd=0;
	int len;
aymeric's avatar
aymeric committed
124 125 126 127 128 129 130
	int a;
	char *file=NULL;
	char *i810_audio=NULL;
	char *snd_pcm_oss=NULL;
	char *snd_mixer_oss=NULL;
	char *snd_pcm=NULL;
	fd=open("/proc/modules",O_RDONLY);
Simon Morlat's avatar
Simon Morlat committed
131

aymeric's avatar
aymeric committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	if (fd>0){
		/* read the entire /proc/modules file and check if sound conf seems correct */
		/*a=fstat(fd,&statbuf);
		if (a<0) ms_warning("Can't stat /proc/modules:%s.",strerror(errno));
		len=statbuf.st_size;
		if (len==0) ms_warning("/proc/modules has zero size!");
		*/
		/***** fstat does not work on /proc/modules for unknown reason *****/
		len=6000;
		file=ms_malloc(len+1);
		a=read(fd,file,len);
		if (a<len) file=ms_realloc(file,a+1);
		file[a]='\0';
		i810_audio=strstr(file,"i810_audio");
		if (i810_audio!=NULL){
			/* I'm sorry i put this warning in comments because
			 * i don't use yet the right driver !! */
/*			lc->vtable.display_warning(lc,_("You are currently using the i810_audio driver.\nThis driver is buggy and so does not work with Linphone.\nWe suggest that you replace it by its equivalent ALSA driver,\neither with packages from your distribution, or by downloading\nALSA drivers at http://www.alsa-project.org."));*/
			goto end;
		}
		snd_pcm=strstr(file,"snd-pcm");
		if (snd_pcm!=NULL){
			snd_pcm_oss=strstr(file,"snd-pcm-oss");
			snd_mixer_oss=strstr(file,"snd-mixer-oss");
			if (snd_pcm_oss==NULL){
				lc->vtable.display_warning(lc,_("Your computer appears to be using ALSA sound drivers.\nThis is the best choice. However the pcm oss emulation module\nis missing and linphone needs it. Please execute\n'modprobe snd-pcm-oss' as root to load it."));
			}
			if (snd_mixer_oss==NULL){
				lc->vtable.display_warning(lc,_("Your computer appears to be using ALSA sound drivers.\nThis is the best choice. However the mixer oss emulation module\nis missing and linphone needs it. Please execute\n 'modprobe snd-mixer-oss' as root to load it."));
			}
		}
	}else {
Simon Morlat's avatar
Simon Morlat committed
164

aymeric's avatar
aymeric committed
165 166 167 168
		ms_warning("Could not open /proc/modules.");
	}
	/* now check general volume. Some user forget to rise it and then complain that linphone is
	not working */
169
	/* but some other users complain that linphone should not change levels...
aymeric's avatar
aymeric committed
170 171 172 173 174 175 176 177 178 179 180
	if (lc->sound_conf.sndcard!=NULL){
		a=snd_card_get_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL);
		if (a<50){
			ms_warning("General level is quite low (%i). Linphone rises it up for you.",a);
			snd_card_set_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL,80);
		}
	}
	*/
	end:
	if (file!=NULL) ms_free(file);
	if (fd>0) close(fd);
Simon Morlat's avatar
Simon Morlat committed
181
#endif
aymeric's avatar
aymeric committed
182 183 184 185 186 187
}

#define UDP_HDR_SZ 8
#define RTP_HDR_SZ 12
#define IP4_HDR_SZ 20   /*20 is the minimum, but there may be some options*/

188
static void payload_type_set_enable(PayloadType *pt,int value)
aymeric's avatar
aymeric committed
189 190
{
	if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
191
	else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
aymeric's avatar
aymeric committed
192 193
}

194
static bool_t payload_type_enabled(const PayloadType *pt) {
aymeric's avatar
aymeric committed
195 196 197
	return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
}

198 199
bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const PayloadType *pt){
	if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){
200 201 202 203
		return payload_type_enabled(pt);
	}
	ms_error("Getting enablement status of codec not in audio or video list of PayloadType !");
	return FALSE;
aymeric's avatar
aymeric committed
204
}
205 206 207 208

int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadType *pt, bool_t enabled){
	if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){
		payload_type_set_enable(pt,enabled);
209
		_linphone_core_codec_config_write(lc);
210 211 212 213
		return 0;
	}
	ms_error("Enabling codec not in audio or video list of PayloadType !");
	return -1;
aymeric's avatar
aymeric committed
214 215
}

216
int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){
217 218 219
       return payload_type_get_number(pt);
}

220 221 222
const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){
	if (ms_filter_codec_supported(pt->mime_type)){
		MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type);
223 224 225 226 227
#ifdef ENABLE_NLS
		return dgettext("mediastreamer",desc->text);
#else
		return desc->text;
#endif
228 229
	}
	return NULL;
aymeric's avatar
aymeric committed
230 231
}

232

233 234 235 236 237
/*this function makes a special case for speex/8000.
This codec is variable bitrate. The 8kbit/s mode is interesting when having a low upload bandwidth, but its quality
is not very good. We 'd better use its 15kbt/s mode when we have enough bandwidth*/
static int get_codec_bitrate(LinphoneCore *lc, const PayloadType *pt){
	int upload_bw=linphone_core_get_upload_bandwidth(lc);
238
	if (bandwidth_is_greater(upload_bw,129) || (bandwidth_is_greater(upload_bw,33) && !linphone_core_video_enabled(lc)) ) {
239 240 241 242 243 244 245
		if (strcmp(pt->mime_type,"speex")==0 && pt->clock_rate==8000){
			return 15000;
		}
	}
	return pt->normal_bitrate;
}

246 247 248 249
/*
 *((codec-birate*ptime/8) + RTP header + UDP header + IP header)*8/ptime;
 *ptime=1/npacket
 */
250
static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
aymeric's avatar
aymeric committed
251 252 253
	double npacket=50;
	double packet_size;
	int bitrate;
254 255 256 257 258
	if (strcmp(payload_type_get_mime(&payload_type_aaceld_44k), payload_type_get_mime(pt))==0) {
		/*special case of aac 44K because ptime= 10ms*/
		npacket=100;
	}
		
259
	bitrate=get_codec_bitrate(lc,pt);
260
	packet_size= (((double)bitrate)/(npacket*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
aymeric's avatar
aymeric committed
261 262 263
	return packet_size*8.0*npacket;
}

Simon Morlat's avatar
Simon Morlat committed
264
void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
265
	call->audio_bw=(int)(ceil(get_audio_payload_bandwidth(call->core,pt)/1000.0)); /*rounding codec bandwidth should be avoid, specially for AMR*/
Simon Morlat's avatar
Simon Morlat committed
266
	ms_message("Audio bandwidth for this call is %i",call->audio_bw);
aymeric's avatar
aymeric committed
267 268
}

269
void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
aymeric's avatar
aymeric committed
270 271 272 273 274
	const MSList *elem;
	PayloadType *max=NULL;
	for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
		PayloadType *pt=(PayloadType*)elem->data;
		if (payload_type_enabled(pt)){
275
			int pt_bitrate=get_codec_bitrate(lc,pt);
aymeric's avatar
aymeric committed
276
			if (max==NULL) max=pt;
277
			else if (max->normal_bitrate<pt_bitrate){
aymeric's avatar
aymeric committed
278 279 280 281 282
				max=pt;
			}
		}
	}
	if (max) {
Simon Morlat's avatar
Simon Morlat committed
283
		lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
aymeric's avatar
aymeric committed
284 285 286
	}
}

287
bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt,  int bandwidth_limit)
Simon Morlat's avatar
Simon Morlat committed
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
{
	double codec_band;
	bool_t ret=FALSE;
	
	switch (pt->type){
		case PAYLOAD_AUDIO_CONTINUOUS:
		case PAYLOAD_AUDIO_PACKETIZED:
			codec_band=get_audio_payload_bandwidth(lc,pt);
			ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
			/*hack to avoid using uwb codecs when having low bitrate and video*/
			if (bandwidth_is_greater(199,bandwidth_limit)){
				if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
					ret=FALSE;
				}
			}
			//ms_message("Payload %s: %g",pt->mime_type,codec_band);
			break;
		case PAYLOAD_VIDEO:
			if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
				ret=TRUE;
			}
			else ret=FALSE;
			break;
	}
	return ret;
}

aymeric's avatar
aymeric committed
315 316 317 318
/* return TRUE if codec can be used with bandwidth, FALSE else*/
bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
{
	double codec_band;
Simon Morlat's avatar
Simon Morlat committed
319
	int allowed_bw,video_bw;
aymeric's avatar
aymeric committed
320
	bool_t ret=FALSE;
321 322

	linphone_core_update_allocated_audio_bandwidth(lc);
Simon Morlat's avatar
Simon Morlat committed
323
	allowed_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
aymeric's avatar
aymeric committed
324
					linphone_core_get_upload_bandwidth(lc));
Simon Morlat's avatar
Simon Morlat committed
325 326 327 328
	if (allowed_bw==0) {
		allowed_bw=-1;
		video_bw=1500; /*around 1.5 Mbit/s*/
	}else
329
		video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
aymeric's avatar
aymeric committed
330 331 332 333

	switch (pt->type){
		case PAYLOAD_AUDIO_CONTINUOUS:
		case PAYLOAD_AUDIO_PACKETIZED:
334
			codec_band=get_audio_payload_bandwidth(lc,pt);
Simon Morlat's avatar
Simon Morlat committed
335
			ret=bandwidth_is_greater(allowed_bw*1000,codec_band);
336
			/*hack to avoid using uwb codecs when having low bitrate and video*/
Simon Morlat's avatar
Simon Morlat committed
337
			if (bandwidth_is_greater(199,allowed_bw)){
338 339 340 341
				if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
					ret=FALSE;
				}
			}
aymeric's avatar
aymeric committed
342 343 344
			//ms_message("Payload %s: %g",pt->mime_type,codec_band);
			break;
		case PAYLOAD_VIDEO:
345
			if (video_bw>0){
Simon Morlat's avatar
Simon Morlat committed
346
				pt->normal_bitrate=video_bw*1000;
347 348
				ret=TRUE;
			}
aymeric's avatar
aymeric committed
349 350 351 352 353 354 355
			else ret=FALSE;
			break;
	}
	return ret;
}

bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
356
#if !defined(_WIN32_WCE)
aymeric's avatar
aymeric committed
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
	FILE *f=popen(command,"r");
	if (f!=NULL){
		int err;
		*result=ms_malloc(4096);
		err=fread(*result,1,4096-1,f);
		if (err<0){
			ms_warning("Error reading command output:%s",strerror(errno));
			ms_free(result);
			return FALSE;
		}
		(*result)[err]=0;
		err=pclose(f);
		if (command_ret!=NULL) *command_ret=err;
		return TRUE;
	}
372
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
373 374 375
	return FALSE;
}

smorlat's avatar
smorlat committed
376 377 378 379 380 381 382 383 384 385 386 387 388 389
static ortp_socket_t create_socket(int local_port){
	struct sockaddr_in laddr;
	ortp_socket_t sock;
	int optval;
	sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
	if (sock<0) {
		ms_error("Fail to create socket");
		return -1;
	}
	memset (&laddr,0,sizeof(laddr));
	laddr.sin_family=AF_INET;
	laddr.sin_addr.s_addr=INADDR_ANY;
	laddr.sin_port=htons(local_port);
	if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
smorlat's avatar
smorlat committed
390
		ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
smorlat's avatar
smorlat committed
391 392 393 394 395 396 397 398 399 400 401 402
		close_socket(sock);
		return -1;
	}
	optval=1;
	if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
				(SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
		ms_warning("Fail to set SO_REUSEADDR");
	}
	set_non_blocking_socket(sock);
	return sock;
}

smorlat's avatar
smorlat committed
403
static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
smorlat's avatar
smorlat committed
404 405 406 407 408 409 410 411 412
	char buf[STUN_MAX_MESSAGE_SIZE];
	int len = STUN_MAX_MESSAGE_SIZE;
	StunAtrString username;
   	StunAtrString password;
	StunMessage req;
	int err;
	memset(&req, 0, sizeof(StunMessage));
	memset(&username,0,sizeof(username));
	memset(&password,0,sizeof(password));
smorlat's avatar
smorlat committed
413
	stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
aymeric's avatar
aymeric committed
414
	len = stunEncodeMessage( &req, buf, len, &password);
smorlat's avatar
smorlat committed
415 416 417 418 419 420 421 422 423 424 425 426
	if (len<=0){
		ms_error("Fail to encode stun message.");
		return -1;
	}
	err=sendto(sock,buf,len,0,server,addrlen);
	if (err<0){
		ms_error("sendto failed: %s",strerror(errno));
		return -1;
	}
	return 0;
}

427 428
int linphone_parse_host_port(const char *input, char *host, size_t hostlen, int *port){
	char tmphost[NI_MAXHOST]={0};
429
	char *p1, *p2;
430 431 432
	
	if ((sscanf(input, "[%64[^]]]:%d", tmphost, port) == 2) || (sscanf(input, "[%64[^]]]", tmphost) == 1)) {
		
433
	} else {
434 435 436 437 438 439 440
		p1 = strchr(input, ':');
		p2 = strrchr(input, ':');
		if (p1 && p2 && (p1 != p2)) {/* an ipv6 address without port*/
			strncpy(tmphost, input, sizeof(tmphost) - 1);
		} else if (sscanf(input, "%[^:]:%d", tmphost, port) != 2) {
			/*no port*/
			strncpy(tmphost, input, sizeof(tmphost) - 1);
441 442
		}
	}
443 444 445 446 447 448 449 450 451 452 453 454 455
	strncpy(host,tmphost,hostlen);
	return 0;
}

int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen, int default_port){
	struct addrinfo hints,*res=NULL;
	char port[6];
	char host[NI_MAXHOST];
	int port_int=default_port;
	int ret;
	
	linphone_parse_host_port(server,host,sizeof(host),&port_int);
	
456
	snprintf(port, sizeof(port), "%d", port_int);
smorlat's avatar
smorlat committed
457
	memset(&hints,0,sizeof(hints));
458
	hints.ai_family=AF_UNSPEC;
smorlat's avatar
smorlat committed
459 460 461 462 463 464 465 466 467 468 469 470 471 472
	hints.ai_socktype=SOCK_DGRAM;
	hints.ai_protocol=IPPROTO_UDP;
	ret=getaddrinfo(host,port,&hints,&res);
	if (ret!=0){
		ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
		return -1;
	}
	if (!res) return -1;
	memcpy(ss,res->ai_addr,res->ai_addrlen);
	*socklen=res->ai_addrlen;
	freeaddrinfo(res);
	return 0;
}

smorlat's avatar
smorlat committed
473
static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
smorlat's avatar
smorlat committed
474 475 476 477 478 479
	char buf[STUN_MAX_MESSAGE_SIZE];
   	int len = STUN_MAX_MESSAGE_SIZE;
	StunMessage resp;
	len=recv(sock,buf,len,0);
	if (len>0){
		struct in_addr ia;
aymeric's avatar
aymeric committed
480 481
		stunParseMessage(buf,len, &resp );
		*id=resp.msgHdr.tr_id.octet[0];
482 483 484 485 486 487 488
		if (resp.hasXorMappedAddress){
			*port = resp.xorMappedAddress.ipv4.port;
			ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
		}else if (resp.hasMappedAddress){
			*port = resp.mappedAddress.ipv4.port;
			ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
		}else return -1;
smorlat's avatar
smorlat committed
489 490 491 492 493
		strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
	}
	return len;
}

Simon Morlat's avatar
Simon Morlat committed
494
/* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
495
int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
smorlat's avatar
smorlat committed
496
	const char *server=linphone_core_get_stun_server(lc);
497 498
	StunCandidate *ac=&call->ac;
	StunCandidate *vc=&call->vc;
Simon Morlat's avatar
Simon Morlat committed
499
	
smorlat's avatar
smorlat committed
500 501
	if (lc->sip_conf.ipv6_enabled){
		ms_warning("stun support is not implemented for ipv6");
Simon Morlat's avatar
Simon Morlat committed
502
		return -1;
smorlat's avatar
smorlat committed
503 504
	}
	if (server!=NULL){
505
		const struct addrinfo *ai=linphone_core_get_stun_server_addrinfo(lc);
smorlat's avatar
smorlat committed
506
		ortp_socket_t sock1=-1, sock2=-1;
507
		int loops=0;
smorlat's avatar
smorlat committed
508 509
		bool_t video_enabled=linphone_core_video_enabled(lc);
		bool_t got_audio,got_video;
smorlat's avatar
smorlat committed
510
		bool_t cone_audio=FALSE,cone_video=FALSE;
smorlat's avatar
smorlat committed
511
		struct timeval init,cur;
Simon Morlat's avatar
Simon Morlat committed
512 513
		double elapsed;
		int ret=0;
514
		
515 516
		if (ai==NULL){
			ms_error("Could not obtain stun server addrinfo.");
Simon Morlat's avatar
Simon Morlat committed
517
			return -1;
smorlat's avatar
smorlat committed
518 519 520
		}
		if (lc->vtable.display_status!=NULL)
			lc->vtable.display_status(lc,_("Stun lookup in progress..."));
521

smorlat's avatar
smorlat committed
522
		/*create the two audio and video RTP sockets, and send STUN message to our stun server */
523
		sock1=create_socket(call->audio_port);
Simon Morlat's avatar
Simon Morlat committed
524
		if (sock1==-1) return -1;
smorlat's avatar
smorlat committed
525
		if (video_enabled){
526
			sock2=create_socket(call->video_port);
Simon Morlat's avatar
Simon Morlat committed
527
			if (sock2==-1) return -1;
smorlat's avatar
smorlat committed
528
		}
smorlat's avatar
smorlat committed
529 530
		got_audio=FALSE;
		got_video=FALSE;
531
		ortp_gettimeofday(&init,NULL);
smorlat's avatar
smorlat committed
532
		do{
Simon Morlat's avatar
Simon Morlat committed
533
			
smorlat's avatar
smorlat committed
534
			int id;
535 536
			if (loops%20==0){
				ms_message("Sending stun requests...");
537 538
				sendStunRequest(sock1,ai->ai_addr,ai->ai_addrlen,11,TRUE);
				sendStunRequest(sock1,ai->ai_addr,ai->ai_addrlen,1,FALSE);
539
				if (sock2!=-1){
540 541
					sendStunRequest(sock2,ai->ai_addr,ai->ai_addrlen,22,TRUE);
					sendStunRequest(sock2,ai->ai_addr,ai->ai_addrlen,2,FALSE);
542 543
				}
			}
544
			ms_usleep(10000);
smorlat's avatar
smorlat committed
545

546 547
			if (recvStunResponse(sock1,ac->addr,
						&ac->port,&id)>0){
smorlat's avatar
smorlat committed
548
				ms_message("STUN test result: local audio port maps to %s:%i",
549 550
						ac->addr,
						ac->port);
smorlat's avatar
smorlat committed
551 552
				if (id==11)
					cone_audio=TRUE;
smorlat's avatar
smorlat committed
553 554
				got_audio=TRUE;
			}
555 556
			if (recvStunResponse(sock2,vc->addr,
							&vc->port,&id)>0){
smorlat's avatar
smorlat committed
557
				ms_message("STUN test result: local video port maps to %s:%i",
558 559
					vc->addr,
					vc->port);
smorlat's avatar
smorlat committed
560 561
				if (id==22)
					cone_video=TRUE;
smorlat's avatar
smorlat committed
562
				got_video=TRUE;
smorlat's avatar
smorlat committed
563
			}
564
			ortp_gettimeofday(&cur,NULL);
smorlat's avatar
smorlat committed
565
			elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
566 567
			if (elapsed>2000)  {
				ms_message("Stun responses timeout, going ahead.");
Simon Morlat's avatar
Simon Morlat committed
568
				ret=-1;
569 570 571 572
				break;
			}
			loops++;
		}while(!(got_audio && (got_video||sock2==-1)  ) );
Simon Morlat's avatar
Simon Morlat committed
573
		if (ret==0) ret=(int)elapsed;
smorlat's avatar
smorlat committed
574 575
		if (!got_audio){
			ms_error("No stun server response for audio port.");
smorlat's avatar
smorlat committed
576 577
		}else{
			if (!cone_audio) {
578
				ms_message("NAT is symmetric for audio port");
smorlat's avatar
smorlat committed
579
			}
smorlat's avatar
smorlat committed
580
		}
581
		if (sock2!=-1){
smorlat's avatar
smorlat committed
582 583 584 585
			if (!got_video){
				ms_error("No stun server response for video port.");
			}else{
				if (!cone_video) {
586
					ms_message("NAT is symmetric for video port.");
smorlat's avatar
smorlat committed
587 588
				}
			}
smorlat's avatar
smorlat committed
589
		}
smorlat's avatar
smorlat committed
590
		close_socket(sock1);
591
		if (sock2!=-1) close_socket(sock2);
Simon Morlat's avatar
Simon Morlat committed
592
		return ret;
smorlat's avatar
smorlat committed
593
	}
Simon Morlat's avatar
Simon Morlat committed
594
	return -1;
smorlat's avatar
smorlat committed
595 596
}

597 598 599 600 601 602 603 604 605 606
int linphone_core_get_edge_bw(LinphoneCore *lc){
	int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
	return edge_bw;
}

int linphone_core_get_edge_ptime(LinphoneCore *lc){
	int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
	return edge_ptime;
}

Simon Morlat's avatar
Simon Morlat committed
607
void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
Sylvain Berfini's avatar
Sylvain Berfini committed
608
	int threshold;
609
	if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
610
		ms_message("Stun server ping time is %i ms",ping_time_ms);
Sylvain Berfini's avatar
Sylvain Berfini committed
611
		threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
Simon Morlat's avatar
Simon Morlat committed
612 613
		
		if (ping_time_ms>threshold){
614
			/* we might be in a 2G network*/
jehan's avatar
jehan committed
615
			params->low_bandwidth=TRUE;
Simon Morlat's avatar
Simon Morlat committed
616 617
		}/*else use default settings */
	}
618 619 620 621 622
	if (params->low_bandwidth){
		params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc);
		params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc);
		params->has_video=FALSE;
	}
Simon Morlat's avatar
Simon Morlat committed
623 624
}

625 626 627 628 629 630 631 632 633 634 635
static void stun_server_resolved(LinphoneCore *lc, const char *name, struct addrinfo *addrinfo){
	if (lc->net_conf.stun_addrinfo){
		freeaddrinfo(lc->net_conf.stun_addrinfo);
		lc->net_conf.stun_addrinfo=NULL;
	}
	if (addrinfo){
		ms_message("Stun server resolution successful.");
	}else{
		ms_warning("Stun server resolution failed.");
	}
	lc->net_conf.stun_addrinfo=addrinfo;
Simon Morlat's avatar
Simon Morlat committed
636
	lc->net_conf.stun_res=NULL;
637
}
Simon Morlat's avatar
Simon Morlat committed
638

639 640 641 642 643 644
void linphone_core_resolve_stun_server(LinphoneCore *lc){
	const char *server=lc->net_conf.stun_server;
	if (lc->sal && server){
		char host[NI_MAXHOST];
		int port=3478;
		linphone_parse_host_port(server,host,sizeof(host),&port);
Simon Morlat's avatar
Simon Morlat committed
645
		lc->net_conf.stun_res=sal_resolve_a(lc->sal,host,port,AF_UNSPEC,(SalResolverCallback)stun_server_resolved,lc);
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
	}
}

/*
 * This function returns the addrinfo representation of the stun server address.
 * It is critical not to block for a long time if it can't be resolved, otherwise this stucks the main thread when making a call.
 * On the contrary, a fully asynchronous call initiation is complex to develop.
 * The compromise is then:
 * - have a cache of the stun server addrinfo
 * - this cached value is returned when it is non-null
 * - an asynchronous resolution is asked each time this function is called to ensure frequent refreshes of the cached value.
 * - if no cached value exists, block for a short time; this case must be unprobable because the resolution will be asked each time the stun server value is 
 * changed.
**/
const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc){
	const char *server=linphone_core_get_stun_server(lc);
	if (server){
		int wait_ms=0;
		int wait_limit=1000;
		linphone_core_resolve_stun_server(lc);
Simon Morlat's avatar
Simon Morlat committed
666
		while (!lc->net_conf.stun_addrinfo && lc->net_conf.stun_res!=NULL && wait_ms<wait_limit){
667 668 669 670 671 672 673
			sal_iterate(lc->sal);
			ms_usleep(50000);
			wait_ms+=50;
		}
	}
	return lc->net_conf.stun_addrinfo;
}
Simon Morlat's avatar
Simon Morlat committed
674

675
int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
676
{
677
	char local_addr[64];
678
	const struct addrinfo *ai;
679 680
	IceCheckList *audio_check_list;
	IceCheckList *video_check_list;
681 682
	const char *server = linphone_core_get_stun_server(lc);

683 684 685
	if ((server == NULL) || (call->ice_session == NULL)) return -1;
	audio_check_list = ice_session_check_list(call->ice_session, 0);
	video_check_list = ice_session_check_list(call->ice_session, 1);
686
	if (audio_check_list == NULL) return -1;
687

688 689
	if (lc->sip_conf.ipv6_enabled){
		ms_warning("stun support is not implemented for ipv6");
690
		return -1;
691
	}
692 693 694
	ai=linphone_core_get_stun_server_addrinfo(lc);
	if (ai==NULL){
		ms_warning("Fail to resolve STUN server for ICE gathering.");
695
		return -1;
696 697 698 699
	}
	if (lc->vtable.display_status != NULL)
		lc->vtable.display_status(lc, _("ICE local candidates gathering in progress..."));

700
	/* Gather local host candidates. */
701
	if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) {
702
		ms_error("Fail to get local ip");
703
		return -1;
704
	}
705 706 707 708 709 710 711
	if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) {
		ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL);
		ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL);
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
	}
	if (call->params.has_video && (video_check_list != NULL)
		&& (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) {
712 713
		ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL);
		ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL);
714
		call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
715
	}
716

jehan's avatar
jehan committed
717
	ms_message("ICE: gathering candidate from [%s]",server);
718
	/* Gather local srflx candidates. */
719
	ice_session_gather_candidates(call->ice_session, ai->ai_addr, ai->ai_addrlen);
720
	return 0;
721 722
}

723 724 725 726
void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
{
	IceCheckList *audio_check_list;
	IceCheckList *video_check_list;
Ghislain MARY's avatar
Ghislain MARY committed
727
	IceSessionState session_state;
728 729 730 731 732 733

	if (call->ice_session == NULL) return;
	audio_check_list = ice_session_check_list(call->ice_session, 0);
	video_check_list = ice_session_check_list(call->ice_session, 1);
	if (audio_check_list == NULL) return;

Ghislain MARY's avatar
Ghislain MARY committed
734 735
	session_state = ice_session_state(call->ice_session);
	if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
736 737
		if (ice_check_list_state(audio_check_list) == ICL_Completed) {
			switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
Ghislain MARY's avatar
Ghislain MARY committed
738
				case ICT_HostCandidate:
739
					call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
Ghislain MARY's avatar
Ghislain MARY committed
740 741 742
					break;
				case ICT_ServerReflexiveCandidate:
				case ICT_PeerReflexiveCandidate:
743
					call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
Ghislain MARY's avatar
Ghislain MARY committed
744 745
					break;
				case ICT_RelayedCandidate:
746
					call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
Ghislain MARY's avatar
Ghislain MARY committed
747 748
					break;
			}
749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
		} else {
			call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
		}
		if (call->params.has_video && (video_check_list != NULL)) {
			if (ice_check_list_state(video_check_list) == ICL_Completed) {
				switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
					case ICT_HostCandidate:
						call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection;
						break;
					case ICT_ServerReflexiveCandidate:
					case ICT_PeerReflexiveCandidate:
						call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
						break;
					case ICT_RelayedCandidate:
						call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
						break;
				}
			} else {
				call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
			}
Ghislain MARY's avatar
Ghislain MARY committed
769
		}
770 771 772 773 774
	} else if (session_state == IS_Running) {
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
		if (call->params.has_video && (video_check_list != NULL)) {
			call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
		}
Ghislain MARY's avatar
Ghislain MARY committed
775 776 777 778 779
	} else {
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
		if (call->params.has_video && (video_check_list != NULL)) {
			call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
		}
780 781 782
	}
}

783 784
void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
{
785
	const char *rtp_addr, *rtcp_addr;
786
	IceSessionState session_state = ice_session_state(session);
787
	int nb_candidates;
788
	int i, j;
789
	bool_t result;
790

791 792
	if (session_state == IS_Completed) {
		desc->ice_completed = TRUE;
Ghislain MARY's avatar
Ghislain MARY committed
793 794 795 796 797 798
		result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL);
		if (result == TRUE) {
			strncpy(desc->addr, rtp_addr, sizeof(desc->addr));
		} else {
			ms_warning("If ICE has completed successfully, rtp_addr should be set!");
		}
799 800 801 802
	}
	else {
		desc->ice_completed = FALSE;
	}
803 804
	strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd));
	strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag));
805
	for (i = 0; i < desc->n_active_streams; i++) {
806 807
		SalStreamDescription *stream = &desc->streams[i];
		IceCheckList *cl = ice_session_check_list(session, i);
808
		nb_candidates = 0;
809
		if (cl == NULL) continue;
810
		if (ice_check_list_state(cl) == ICL_Completed) {
811
			stream->ice_completed = TRUE;
812 813 814 815 816 817
			result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
		} else {
			stream->ice_completed = FALSE;
			result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
		}
		if (result == TRUE) {
818 819 820
			strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
			strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
		} else {
821 822
			memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
			memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
823
		}
824 825 826 827 828 829 830 831
		if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd)))
			strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd));
		else
			memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
		if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag)))
			strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag));
		else
			memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
832
		stream->ice_mismatch = ice_check_list_is_mismatch(cl);
833
		if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
834
			memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
Ghislain MARY's avatar
Ghislain MARY committed
835
			for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
836
				SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
837 838 839 840 841 842 843 844 845 846 847 848
				IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j);
				const char *default_addr = NULL;
				int default_port = 0;
				if (ice_candidate->componentID == 1) {
					default_addr = stream->rtp_addr;
					default_port = stream->rtp_port;
				} else if (ice_candidate->componentID == 2) {
					default_addr = stream->rtcp_addr;
					default_port = stream->rtcp_port;
				} else continue;
				if (default_addr[0] == '\0') default_addr = desc->addr;
				/* Only include the candidates matching the default destination for each component of the stream if the state is Completed as specified in RFC5245 section 9.1.2.2. */
849
				if ((ice_check_list_state(cl) == ICL_Completed)
850 851 852 853 854 855 856 857 858 859 860 861
					&& !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0)))
					continue;
				strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation));
				sal_candidate->componentID = ice_candidate->componentID;
				sal_candidate->priority = ice_candidate->priority;
				strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type));
				strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr));
				sal_candidate->port = ice_candidate->taddr.port;
				if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) {
					strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr));
					sal_candidate->rport = ice_candidate->base->taddr.port;
				}
862
				nb_candidates++;
863 864
			}
		}
865
		if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
866 867
			int rtp_port, rtcp_port;
			memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
Ghislain MARY's avatar
Ghislain MARY committed
868
			if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) {
869 870 871 872 873 874 875
				strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr));
				stream->ice_remote_candidates[0].port = rtp_port;
				strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr));
				stream->ice_remote_candidates[1].port = rtcp_port;
			} else {
				ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
			}
876 877 878 879 880
		} else {
			for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
				stream->ice_remote_candidates[j].addr[0] = '\0';
				stream->ice_remote_candidates[j].port = 0;
			}
881 882 883 884
		}
	}
}

885 886 887 888 889 890 891 892 893 894 895 896
static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
{
	if (componentID == 1) {
		*addr = stream->rtp_addr;
		*port = stream->rtp_port;
	} else if (componentID == 2) {
		*addr = stream->rtcp_addr;
		*port = stream->rtcp_port;
	} else return;
	if ((*addr)[0] == '\0') *addr = md->addr;
}

897 898
void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
{
899 900
	bool_t ice_restarted = FALSE;

901 902
	if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
		int i, j;
903 904

		/* Check for ICE restart and set remote credentials. */
905 906 907 908
		if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
			ice_session_restart(call->ice_session);
			ice_restarted = TRUE;
		} else {
909
			for (i = 0; i < md->n_total_streams; i++) {
910 911 912 913 914 915 916 917 918
				const SalStreamDescription *stream = &md->streams[i];
				IceCheckList *cl = ice_session_check_list(call->ice_session, i);
				if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
					ice_session_restart(call->ice_session);
					ice_restarted = TRUE;
					break;
				}
			}
		}
919 920 921
		if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) {
			ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
		} else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) {
922 923 924 925
			if (ice_restarted == FALSE) {
				ice_session_restart(call->ice_session);
				ice_restarted = TRUE;
			}
926 927
			ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
		}
928
		for (i = 0; i < md->n_total_streams; i++) {
929 930 931 932
			const SalStreamDescription *stream = &md->streams[i];
			IceCheckList *cl = ice_session_check_list(call->ice_session, i);
			if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
				if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) {
933 934 935 936
					if (ice_restarted == FALSE) {
						ice_session_restart(call->ice_session);
						ice_restarted = TRUE;
					}
937 938 939 940 941 942 943
					ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
					break;
				}
			}
		}

		/* Create ICE check lists if needed and parse ICE attributes. */
944
		for (i = 0; i < md->n_total_streams; i++) {
945 946
			const SalStreamDescription *stream = &md->streams[i];
			IceCheckList *cl = ice_session_check_list(call->ice_session, i);
947
			if ((cl == NULL) && (i < md->n_active_streams)) {
948 949 950 951
				cl = ice_check_list_new();
				ice_session_add_check_list(call->ice_session, cl);
				switch (stream->type) {
					case SalAudio:
Ghislain MARY's avatar
Ghislain MARY committed
952
						if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = cl;
953 954
						break;
					case SalVideo:
Ghislain MARY's avatar
Ghislain MARY committed
955
						if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl;
956 957 958 959 960
						break;
					default:
						break;
				}
			}
961
			if (stream->ice_mismatch == TRUE) {
962
				ice_check_list_set_state(cl, ICL_Failed);
963
			} else if (stream->rtp_port == 0) {
Ghislain MARY's avatar
Ghislain MARY committed
964
				ice_session_remove_check_list(call->ice_session, cl);
Simon Morlat's avatar
Simon Morlat committed
965 966 967 968 969 970
#ifdef VIDEO_ENABLED
				if (stream->type==SalVideo && call->videostream){
					video_stream_stop(call->videostream);
					call->videostream=NULL;
				}
#endif
971 972 973 974 975 976 977 978 979
			} else {
				if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
					ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
				for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
					const SalIceCandidate *candidate = &stream->ice_candidates[j];
					bool_t default_candidate = FALSE;
					const char *addr = NULL;
					int port = 0;
					if (candidate->addr[0] == '\0') break;
980 981
					if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
					get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
982 983 984 985 986
					if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0))
						default_candidate = TRUE;
					ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID,
						candidate->priority, candidate->foundation, default_candidate);
				}
987
				if (ice_restarted == FALSE) {
988
					bool_t losing_pairs_added = FALSE;
989 990 991 992 993 994 995
					for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
						const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j];
						const char *addr = NULL;
						int port = 0;
						int componentID = j + 1;
						if (candidate->addr[0] == '\0') break;
						get_default_addr_and_port(componentID, md, stream, &addr, &port);
996 997 998 999
						if (j == 0) {
							/* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */
							ice_check_list_unselect_valid_pairs(cl);
						}
1000 1001
						ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
						losing_pairs_added = TRUE;
1002
					}
1003
					if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
1004
				}
1005 1006
			}
		}
1007
		for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) {
1008 1009
			ice_session_remove_check_list(