misc.c 65.5 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

/*
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
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
aymeric's avatar
aymeric committed
19 20 21
*/

#include "private.h"
22
#include "linphone/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>
35 36 37
#if _MSC_VER
#include <io.h>
#else
aymeric's avatar
aymeric committed
38
#include <unistd.h>
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
jehan's avatar
jehan committed
44
#include <mediastreamer2/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
55
#endif
56

aymeric's avatar
aymeric committed
57 58 59 60 61

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

62
static void clear_ice_check_list(LinphoneCall *call, IceCheckList *removed);
aymeric's avatar
aymeric committed
63

64
bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const LinphonePayloadType *pt){
65
	if (bctbx_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || bctbx_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt) || bctbx_list_find(lc->codecs_conf.text_codecs, (PayloadType*)pt)){
66 67 68 69
		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
70
}
71

72
bool_t linphone_core_payload_type_is_vbr(LinphoneCore *lc, const LinphonePayloadType *pt){
73 74 75 76
	if (pt->type==PAYLOAD_VIDEO) return TRUE;
	return !!(pt->flags & PAYLOAD_TYPE_IS_VBR);
}

77
int linphone_core_enable_payload_type(LinphoneCore *lc, LinphonePayloadType *pt, bool_t enabled){
78
	if (bctbx_list_find(lc->codecs_conf.audio_codecs,pt) || bctbx_list_find(lc->codecs_conf.video_codecs,pt) || bctbx_list_find(lc->codecs_conf.text_codecs,pt)){
79
		payload_type_set_enable(pt,enabled);
80
		_linphone_core_codec_config_write(lc);
81
		linphone_core_update_allocated_audio_bandwidth(lc);
82 83 84 85
		return 0;
	}
	ms_error("Enabling codec not in audio or video list of PayloadType !");
	return -1;
aymeric's avatar
aymeric committed
86 87
}

88
int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){
89
	return payload_type_get_number(pt);
90 91
}

92 93 94 95
void linphone_core_set_payload_type_number(LinphoneCore *lc, PayloadType *pt, int number){
	payload_type_set_number(pt,number);
}

96
const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){
97 98 99
	//if (ms_filter_codec_supported(pt->mime_type)){
	if (ms_factory_codec_supported(lc->factory, pt->mime_type)){
		MSFilterDesc *desc=ms_factory_get_encoder(lc->factory, pt->mime_type);
100
#ifdef ENABLE_NLS
101 102 103 104
		return dgettext("mediastreamer",desc->text);
#else
		return desc->text;
#endif
105 106
	}
	return NULL;
aymeric's avatar
aymeric committed
107 108
}

109
void linphone_core_set_payload_type_bitrate(LinphoneCore *lc, LinphonePayloadType *pt, int bitrate){
110
	if (bctbx_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || bctbx_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt) || bctbx_list_find(lc->codecs_conf.text_codecs, (PayloadType*)pt)){
111
		if (pt->type==PAYLOAD_VIDEO || pt->flags & PAYLOAD_TYPE_IS_VBR){
112 113
			pt->normal_bitrate=bitrate*1000;
			pt->flags|=PAYLOAD_TYPE_BITRATE_OVERRIDE;
114
			linphone_core_update_allocated_audio_bandwidth(lc);
115 116
		}else{
			ms_error("Cannot set an explicit bitrate for codec %s/%i, because it is not VBR.",pt->mime_type,pt->clock_rate);
117
			return;
118
		}
119 120
	} else {
		ms_error("linphone_core_set_payload_type_bitrate() payload type not in audio or video list !");
121 122 123
	}
}

124

125 126 127 128
/*
 *((codec-birate*ptime/8) + RTP header + UDP header + IP header)*8/ptime;
 *ptime=1/npacket
 */
129 130

static double get_audio_payload_bandwidth_from_codec_bitrate(const PayloadType *pt){
aymeric's avatar
aymeric committed
131 132 133
	double npacket=50;
	double packet_size;
	int bitrate;
134

135 136 137
	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;
138 139
	}else if (strcmp(payload_type_get_mime(&payload_type_ilbc), payload_type_get_mime(pt))==0) {
		npacket=1000/30.0;
140
	}
141

142
	bitrate=pt->normal_bitrate;
143
	packet_size= (((double)bitrate)/(npacket*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
aymeric's avatar
aymeric committed
144 145 146
	return packet_size*8.0*npacket;
}

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
typedef struct vbr_codec_bitrate{
	int max_avail_bitrate;
	int min_rate;
	int recomended_bitrate;
}vbr_codec_bitrate_t;

static vbr_codec_bitrate_t defauls_vbr[]={
	//{ 100, 44100, 100 },
	{ 64, 44100, 50 },
	{ 64, 16000, 40 },
	{ 32, 16000, 32 },
	{ 32, 8000, 32 },
	{ 0 , 8000, 24 },
	{ 0 , 0, 0 }
};

static int lookup_vbr_typical_bitrate(int maxbw, int clock_rate){
	vbr_codec_bitrate_t *it;
	if (maxbw<=0) maxbw=defauls_vbr[0].max_avail_bitrate;
	for(it=defauls_vbr;it->min_rate!=0;it++){
		if (maxbw>=it->max_avail_bitrate && clock_rate>=it->min_rate)
			return it->recomended_bitrate;
	}
	ms_error("lookup_vbr_typical_bitrate(): should not happen.");
	return 32;
}

static int get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt, int maxbw){
	if (linphone_core_payload_type_is_vbr(lc,pt)){
		if (pt->flags & PAYLOAD_TYPE_BITRATE_OVERRIDE){
177
			ms_debug("PayloadType %s/%i has bitrate override",pt->mime_type,pt->clock_rate);
178 179 180 181 182 183
			return pt->normal_bitrate/1000;
		}
		return lookup_vbr_typical_bitrate(maxbw,pt->clock_rate);
	}else return (int)ceil(get_audio_payload_bandwidth_from_codec_bitrate(pt)/1000.0);/*rounding codec bandwidth should be avoid, specially for AMR*/
}

184
int linphone_core_get_payload_type_bitrate(LinphoneCore *lc, const LinphonePayloadType *pt){
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
	int maxbw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
					linphone_core_get_upload_bandwidth(lc));
	if (pt->type==PAYLOAD_AUDIO_CONTINUOUS || pt->type==PAYLOAD_AUDIO_PACKETIZED){
		return get_audio_payload_bandwidth(lc,pt,maxbw);
	}else if (pt->type==PAYLOAD_VIDEO){
		int video_bw;
		if (maxbw<=0) {
			video_bw=1500; /*default bitrate for video stream when no bandwidth limit is set, around 1.5 Mbit/s*/
		}else{
			video_bw=get_remaining_bandwidth_for_video(maxbw,lc->audio_bw);
		}
		return video_bw;
	}
	return 0;
}

void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt, int maxbw){
	call->audio_bw=get_audio_payload_bandwidth(call->core,pt,maxbw);
Simon Morlat's avatar
Simon Morlat committed
203
	ms_message("Audio bandwidth for this call is %i",call->audio_bw);
aymeric's avatar
aymeric committed
204 205
}

206
void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
207
	const bctbx_list_t *elem;
208 209 210
	int maxbw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
					linphone_core_get_upload_bandwidth(lc));
	int max_codec_bitrate=0;
211

aymeric's avatar
aymeric committed
212 213 214
	for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
		PayloadType *pt=(PayloadType*)elem->data;
		if (payload_type_enabled(pt)){
215 216 217 218 219
			int pt_bitrate=get_audio_payload_bandwidth(lc,pt,maxbw);
			if (max_codec_bitrate==0) {
				max_codec_bitrate=pt_bitrate;
			}else if (max_codec_bitrate<pt_bitrate){
				max_codec_bitrate=pt_bitrate;
aymeric's avatar
aymeric committed
220 221 222
			}
		}
	}
223 224
	if (max_codec_bitrate) {
		lc->audio_bw=max_codec_bitrate;
aymeric's avatar
aymeric committed
225 226 227
	}
}

228
bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, const PayloadType *pt, int bandwidth_limit){
229
	double codec_band;
230
	const int video_enablement_limit = 99;
231
	bool_t ret=FALSE;
232

233 234 235
	switch (pt->type){
		case PAYLOAD_AUDIO_CONTINUOUS:
		case PAYLOAD_AUDIO_PACKETIZED:
236
			codec_band=get_audio_payload_bandwidth(lc,pt,bandwidth_limit);
237
			ret=bandwidth_is_greater(bandwidth_limit,(int)codec_band);
238
			/*ms_message("Payload %s: codec_bandwidth=%g, bandwidth_limit=%i",pt->mime_type,codec_band,bandwidth_limit);*/
239 240
			break;
		case PAYLOAD_VIDEO:
241
			if (bandwidth_limit<=0 || bandwidth_limit >= video_enablement_limit) {/* infinite or greater than video_enablement_limit*/
242 243 244 245
				ret=TRUE;
			}
			else ret=FALSE;
			break;
Sylvain Berfini's avatar
Sylvain Berfini committed
246 247 248
		case PAYLOAD_TEXT:
			ret=TRUE;
			break;
249 250 251 252
	}
	return ret;
}

253
bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, const PayloadType *pt){
254 255 256
	int maxbw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
					linphone_core_get_upload_bandwidth(lc));
	bool_t ret=linphone_core_is_payload_type_usable_for_bandwidth(lc, pt, maxbw);
257
	if ((pt->type==PAYLOAD_AUDIO_CONTINUOUS || pt->type==PAYLOAD_AUDIO_PACKETIZED)
258
		&& lc->sound_conf.capt_sndcard
259 260 261 262
		&& !(ms_snd_card_get_capabilities(lc->sound_conf.capt_sndcard) & MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER)
		&& linphone_core_echo_cancellation_enabled(lc)
		&& (pt->clock_rate!=16000 && pt->clock_rate!=8000)
		&& strcasecmp(pt->mime_type,"opus")!=0
263
		&& ms_factory_lookup_filter_by_name(lc->factory, "MSWebRTCAEC")!=NULL){
264 265 266 267 268
		ms_warning("Payload type %s/%i cannot be used because software echo cancellation is required but is unable to operate at this rate.",
			   pt->mime_type,pt->clock_rate);
		ret=FALSE;
	}
	return ret;
aymeric's avatar
aymeric committed
269 270 271
}

bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
272
#if !defined(_WIN32_WCE) && !defined(LINPHONE_WINDOWS_UNIVERSAL)
aymeric's avatar
aymeric committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
	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;
	}
288
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
289 290 291
	return FALSE;
}

smorlat's avatar
smorlat committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305
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
306
		ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
smorlat's avatar
smorlat committed
307 308 309 310 311 312 313 314 315 316 317 318
		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;
}

Ghislain MARY's avatar
Ghislain MARY committed
319 320
static int send_stun_request(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t change_addr){
	char *buf = NULL;
321
	size_t len;
Ghislain MARY's avatar
Ghislain MARY committed
322 323 324 325 326 327 328 329 330
	int err = 0;
	MSStunMessage *req = ms_stun_binding_request_create();
	UInt96 tr_id = ms_stun_message_get_tr_id(req);
	tr_id.octet[0] = id;
	ms_stun_message_set_tr_id(req, tr_id);
	ms_stun_message_enable_change_ip(req, change_addr);
	ms_stun_message_enable_change_port(req, change_addr);
	len = ms_stun_message_encode(req, &buf);
	if (len <= 0) {
smorlat's avatar
smorlat committed
331
		ms_error("Fail to encode stun message.");
Ghislain MARY's avatar
Ghislain MARY committed
332 333
		err = -1;
	} else {
334
		err = bctbx_sendto(sock, buf, len, 0, server, addrlen);
Ghislain MARY's avatar
Ghislain MARY committed
335 336 337 338
		if (err < 0) {
			ms_error("sendto failed: %s",strerror(errno));
			err = -1;
		}
smorlat's avatar
smorlat committed
339
	}
Ghislain MARY's avatar
Ghislain MARY committed
340 341 342
	if (buf != NULL) ms_free(buf);
	ms_free(req);
	return err;
smorlat's avatar
smorlat committed
343 344
}

345 346
int linphone_parse_host_port(const char *input, char *host, size_t hostlen, int *port){
	char tmphost[NI_MAXHOST]={0};
347
	char *p1, *p2;
348

349
	if ((sscanf(input, "[%64[^]]]:%d", tmphost, port) == 2) || (sscanf(input, "[%64[^]]]", tmphost) == 1)) {
350

351
	} else {
352 353 354 355 356 357 358
		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);
359 360
		}
	}
361 362 363 364 365 366 367 368 369 370
	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;
371

372
	linphone_parse_host_port(server,host,sizeof(host),&port_int);
373

374
	snprintf(port, sizeof(port), "%d", port_int);
smorlat's avatar
smorlat committed
375
	memset(&hints,0,sizeof(hints));
376
	hints.ai_family=AF_UNSPEC;
smorlat's avatar
smorlat committed
377 378 379 380 381 382 383 384 385
	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);
386
	*socklen=(socklen_t)res->ai_addrlen;
smorlat's avatar
smorlat committed
387 388 389 390
	freeaddrinfo(res);
	return 0;
}

Ghislain MARY's avatar
Ghislain MARY committed
391 392 393 394 395 396 397
static int recv_stun_response(ortp_socket_t sock, char *ipaddr, int *port, int *id) {
	char buf[MS_STUN_MAX_MESSAGE_SIZE];
	int len = MS_STUN_MAX_MESSAGE_SIZE;
	MSStunMessage *resp;

	len = recv(sock, buf, len, 0);
	if (len > 0) {
smorlat's avatar
smorlat committed
398
		struct in_addr ia;
Ghislain MARY's avatar
Ghislain MARY committed
399
		resp = ms_stun_message_create_from_buffer_parsing((uint8_t *)buf, (ssize_t)len);
Ghislain MARY's avatar
Ghislain MARY committed
400 401 402 403 404 405
		if (resp != NULL) {
			const MSStunAddress *stun_addr;
			UInt96 tr_id = ms_stun_message_get_tr_id(resp);
			*id = tr_id.octet[0];
			stun_addr = ms_stun_message_get_xor_mapped_address(resp);
			if (stun_addr != NULL) {
406 407
				*port = stun_addr->ip.v4.port;
				ia.s_addr = htonl(stun_addr->ip.v4.addr);
Ghislain MARY's avatar
Ghislain MARY committed
408 409 410
			} else {
				stun_addr = ms_stun_message_get_mapped_address(resp);
				if (stun_addr != NULL) {
411 412
					*port = stun_addr->ip.v4.port;
					ia.s_addr = htonl(stun_addr->ip.v4.addr);
Ghislain MARY's avatar
Ghislain MARY committed
413 414 415 416
				} else len = -1;
			}
			if (len > 0) strncpy(ipaddr, inet_ntoa(ia), LINPHONE_IPADDR_SIZE);
		}
smorlat's avatar
smorlat committed
417 418 419 420
	}
	return len;
}

421
/* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
422
int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
smorlat's avatar
smorlat committed
423
	const char *server=linphone_core_get_stun_server(lc);
424 425
	StunCandidate *ac=&call->ac;
	StunCandidate *vc=&call->vc;
426
	StunCandidate *tc=&call->tc;
427

smorlat's avatar
smorlat committed
428 429
	if (lc->sip_conf.ipv6_enabled){
		ms_warning("stun support is not implemented for ipv6");
430
		return -1;
smorlat's avatar
smorlat committed
431
	}
432
	if (call->media_ports[call->main_audio_stream_index].rtp_port==-1){
433 434 435
		ms_warning("Stun-only support not available for system random port");
		return -1;
	}
smorlat's avatar
smorlat committed
436
	if (server!=NULL){
437
		const struct addrinfo *ai=linphone_core_get_stun_server_addrinfo(lc);
438
		ortp_socket_t sock1=-1, sock2=-1, sock3=-1;
439
		int loops=0;
smorlat's avatar
smorlat committed
440
		bool_t video_enabled=linphone_core_video_enabled(lc);
441 442
		bool_t got_audio,got_video,got_text;
		bool_t cone_audio=FALSE,cone_video=FALSE,cone_text=FALSE;
smorlat's avatar
smorlat committed
443
		struct timeval init,cur;
444 445
		double elapsed;
		int ret=0;
446

447 448
		if (ai==NULL){
			ms_error("Could not obtain stun server addrinfo.");
449
			return -1;
smorlat's avatar
smorlat committed
450
		}
451
		linphone_core_notify_display_status(lc,_("Stun lookup in progress..."));
452

smorlat's avatar
smorlat committed
453
		/*create the two audio and video RTP sockets, and send STUN message to our stun server */
454
		sock1=create_socket(call->media_ports[call->main_audio_stream_index].rtp_port);
455
		if (sock1==-1) return -1;
smorlat's avatar
smorlat committed
456
		if (video_enabled){
457
			sock2=create_socket(call->media_ports[call->main_video_stream_index].rtp_port);
458
			if (sock2==-1) return -1;
smorlat's avatar
smorlat committed
459
		}
Sylvain Berfini's avatar
Sylvain Berfini committed
460 461 462
		sock3=create_socket(call->media_ports[call->main_text_stream_index].rtp_port);
		if (sock3==-1) return -1;
		
smorlat's avatar
smorlat committed
463 464
		got_audio=FALSE;
		got_video=FALSE;
465
		got_text=FALSE;
466
		ortp_gettimeofday(&init,NULL);
smorlat's avatar
smorlat committed
467
		do{
468

smorlat's avatar
smorlat committed
469
			int id;
470 471
			if (loops%20==0){
				ms_message("Sending stun requests...");
Ghislain MARY's avatar
Ghislain MARY committed
472 473
				send_stun_request((int)sock1,ai->ai_addr,(socklen_t)ai->ai_addrlen,11,TRUE);
				send_stun_request((int)sock1,ai->ai_addr,(socklen_t)ai->ai_addrlen,1,FALSE);
474
				if (sock2!=-1){
Ghislain MARY's avatar
Ghislain MARY committed
475 476
					send_stun_request((int)sock2,ai->ai_addr,(socklen_t)ai->ai_addrlen,22,TRUE);
					send_stun_request((int)sock2,ai->ai_addr,(socklen_t)ai->ai_addrlen,2,FALSE);
477
				}
478
				if (sock3!=-1){
Ghislain MARY's avatar
Ghislain MARY committed
479 480
					send_stun_request((int)sock3,ai->ai_addr,(socklen_t)ai->ai_addrlen,33,TRUE);
					send_stun_request((int)sock3,ai->ai_addr,(socklen_t)ai->ai_addrlen,3,FALSE);
481
				}
482
			}
483
			ms_usleep(10000);
smorlat's avatar
smorlat committed
484

Ghislain MARY's avatar
Ghislain MARY committed
485 486 487
			if (recv_stun_response(sock1, ac->addr, &ac->port, &id) > 0) {
				ms_message("STUN test result: local audio port maps to %s:%i", ac->addr, ac->port);
				if (id==11) cone_audio=TRUE;
smorlat's avatar
smorlat committed
488 489
				got_audio=TRUE;
			}
Ghislain MARY's avatar
Ghislain MARY committed
490 491 492
			if (recv_stun_response(sock2, vc->addr, &vc->port, &id) > 0) {
				ms_message("STUN test result: local video port maps to %s:%i", vc->addr, vc->port);
				if (id==22) cone_video=TRUE;
smorlat's avatar
smorlat committed
493
				got_video=TRUE;
smorlat's avatar
smorlat committed
494
			}
Ghislain MARY's avatar
Ghislain MARY committed
495 496 497
			if (recv_stun_response(sock3, tc->addr, &tc->port, &id)>0) {
				ms_message("STUN test result: local text port maps to %s:%i", tc->addr, tc->port);
				if (id==33) cone_text=TRUE;
498 499
				got_text=TRUE;
			}
500
			ortp_gettimeofday(&cur,NULL);
smorlat's avatar
smorlat committed
501
			elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
502 503
			if (elapsed>2000)  {
				ms_message("Stun responses timeout, going ahead.");
504
				ret=-1;
505 506 507
				break;
			}
			loops++;
508
		}while(!(got_audio && (got_video||sock2==-1) && (got_text||sock3==-1)  ) );
509
		if (ret==0) ret=(int)elapsed;
smorlat's avatar
smorlat committed
510 511
		if (!got_audio){
			ms_error("No stun server response for audio port.");
smorlat's avatar
smorlat committed
512 513
		}else{
			if (!cone_audio) {
514
				ms_message("NAT is symmetric for audio port");
smorlat's avatar
smorlat committed
515
			}
smorlat's avatar
smorlat committed
516
		}
517
		if (sock2!=-1){
smorlat's avatar
smorlat committed
518 519 520 521
			if (!got_video){
				ms_error("No stun server response for video port.");
			}else{
				if (!cone_video) {
522
					ms_message("NAT is symmetric for video port.");
smorlat's avatar
smorlat committed
523 524
				}
			}
smorlat's avatar
smorlat committed
525
		}
526 527 528 529 530 531 532 533 534
		if (sock3!=-1){
			if (!got_text){
				ms_error("No stun server response for text port.");
			}else{
				if (!cone_text) {
					ms_message("NAT is symmetric for text port.");
				}
			}
		}
smorlat's avatar
smorlat committed
535
		close_socket(sock1);
536
		if (sock2!=-1) close_socket(sock2);
537
		if (sock3!=-1) close_socket(sock3);
538
		return ret;
smorlat's avatar
smorlat committed
539
	}
540
	return -1;
smorlat's avatar
smorlat committed
541 542
}

543 544 545 546 547 548 549 550 551 552
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;
}

553
void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
554
	int threshold;
555
	if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
556
		ms_message("Stun server ping time is %i ms",ping_time_ms);
557
		threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
558

559
		if (ping_time_ms>threshold){
560
			/* we might be in a 2G network*/
jehan's avatar
jehan committed
561
			params->low_bandwidth=TRUE;
562 563
		}/*else use default settings */
	}
564 565 566 567 568
	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;
	}
569 570 571
}


572
void linphone_core_resolve_stun_server(LinphoneCore *lc){
573 574 575
	if (lc->nat_policy != NULL) {
		linphone_nat_policy_resolve_stun_server(lc->nat_policy);
	} else {
576
		ms_error("linphone_core_resolve_stun_server(): called without nat_policy, this should not happen.");
577 578 579 580
	}
}

const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc){
581 582 583
	if (lc->nat_policy != NULL) {
		return linphone_nat_policy_get_stun_server_addrinfo(lc->nat_policy);
	} else {
584
		ms_error("linphone_core_get_stun_server_addrinfo(): called without nat_policy, this should not happen.");
585
	}
586
	return NULL;
587
}
588

589 590 591 592
void linphone_core_enable_forced_ice_relay(LinphoneCore *lc, bool_t enable) {
	lc->forced_ice_relay = enable;
}

593 594 595 596
void linphone_core_enable_short_turn_refresh(LinphoneCore *lc, bool_t enable) {
	lc->short_turn_refresh = enable;
}

597 598
static void stun_auth_requested_cb(LinphoneCall *call, const char *realm, const char *nonce, const char **username, const char **password, const char **ha1) {
	LinphoneProxyConfig *proxy = NULL;
599
	const LinphoneNatPolicy *nat_policy = NULL;
600 601 602
	const LinphoneAddress *addr = NULL;
	const LinphoneAuthInfo *auth_info = NULL;
	LinphoneCore *lc = call->core;
Ghislain MARY's avatar
Ghislain MARY committed
603
	const char *user = NULL;
604

605
	// Get the username from the nat policy or the proxy config
606 607 608
	if (call->dest_proxy != NULL) proxy = call->dest_proxy;
	else proxy = linphone_core_get_default_proxy_config(call->core);
	if (proxy == NULL) return;
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
	nat_policy = linphone_proxy_config_get_nat_policy(proxy);
	if (nat_policy != NULL) {
		user = linphone_nat_policy_get_stun_server_username(nat_policy);
	} else {
		nat_policy = linphone_core_get_nat_policy(call->core);
		if (nat_policy != NULL) {
			user = linphone_nat_policy_get_stun_server_username(nat_policy);
		}
	}
	if (user == NULL) {
		/* If the username has not been found in the nat_policy, take the username from the currently used proxy config. */
		addr = linphone_proxy_config_get_identity_address(proxy);
		if (addr == NULL) return;
		user = linphone_address_get_username(addr);
	}
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
	if (user == NULL) return;

	auth_info = linphone_core_find_auth_info(lc, realm, user, NULL);
	if (auth_info != NULL) {
		const char *hash = linphone_auth_info_get_ha1(auth_info);
		if (hash != NULL) {
			*ha1 = hash;
		} else {
			*password = linphone_auth_info_get_passwd(auth_info);
		}
		*username = user;
	} else {
		ms_warning("No auth info found for STUN auth request");
	}
}

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
static void linphone_core_add_local_ice_candidates(LinphoneCall *call, int family, const char *addr, IceCheckList *audio_cl, IceCheckList *video_cl, IceCheckList *text_cl) {
	if ((ice_check_list_state(audio_cl) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_cl) == FALSE)) {
		ice_add_local_candidate(audio_cl, "host", family, addr, call->media_ports[call->main_audio_stream_index].rtp_port, 1, NULL);
		ice_add_local_candidate(audio_cl, "host", family, addr, call->media_ports[call->main_audio_stream_index].rtcp_port, 2, NULL);
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
	}
	if (linphone_core_video_enabled(call->core) && (video_cl != NULL)
		&& (ice_check_list_state(video_cl) != ICL_Completed) && (ice_check_list_candidates_gathered(video_cl) == FALSE)) {
		ice_add_local_candidate(video_cl, "host", family, addr, call->media_ports[call->main_video_stream_index].rtp_port, 1, NULL);
		ice_add_local_candidate(video_cl, "host", family, addr, call->media_ports[call->main_video_stream_index].rtcp_port, 2, NULL);
		call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
	}
	if (call->params->realtimetext_enabled && (text_cl != NULL)
		&& (ice_check_list_state(text_cl) != ICL_Completed) && (ice_check_list_candidates_gathered(text_cl) == FALSE)) {
		ice_add_local_candidate(text_cl, "host", family, addr, call->media_ports[call->main_text_stream_index].rtp_port, 1, NULL);
		ice_add_local_candidate(text_cl, "host", family, addr, call->media_ports[call->main_text_stream_index].rtcp_port, 2, NULL);
		call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateInProgress;
	}
}

static const struct addrinfo * get_preferred_stun_server_addrinfo(const struct addrinfo *ai) {
	char ip[NI_MAXHOST];
	const struct addrinfo *preferred_ai = NULL;

	while (ai != NULL) {
		bctbx_addrinfo_to_printable_ip_address(ai, ip, sizeof(ip));
		if (ai->ai_family == AF_INET) {
			preferred_ai = ai;
			break;
		}
		else if (ai->ai_family == AF_INET6) {
			struct sockaddr_storage ss;
			socklen_t sslen = sizeof(ss);
			bctbx_sockaddr_ipv6_to_ipv4(ai->ai_addr, (struct sockaddr *)&ss, &sslen);
			if ((ss.ss_family == AF_INET) && (preferred_ai == NULL)) preferred_ai = ai;
		}
		ai = ai->ai_next;
	}

	bctbx_addrinfo_to_printable_ip_address(preferred_ai, ip, sizeof(ip));
	return preferred_ai;
}

683 684 685 686 687
/* Return values:
 * 1 :  STUN gathering is started
 * 0 :  no STUN gathering is started, but it's ok to proceed with ICE anyway (with local candidates only or because STUN gathering was already done before)
 * -1: no gathering started and something went wrong with local candidates. There is no way to start the ICE session.
 */
688
int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call){
689
	char local_addr[64];
690
	const struct addrinfo *ai = NULL;
691 692 693
	IceCheckList *audio_cl;
	IceCheckList *video_cl;
	IceCheckList *text_cl;
694 695 696 697 698 699
	LinphoneNatPolicy *nat_policy = NULL;
	const char *server = NULL;

	if (call->dest_proxy != NULL) nat_policy = linphone_proxy_config_get_nat_policy(call->dest_proxy);
	if (nat_policy == NULL) nat_policy = linphone_core_get_nat_policy(lc);
	if (nat_policy != NULL) server = linphone_nat_policy_get_stun_server(nat_policy);
700

701
	if (call->ice_session == NULL) return -1;
702 703 704
	audio_cl = ice_session_check_list(call->ice_session, call->main_audio_stream_index);
	video_cl = ice_session_check_list(call->ice_session, call->main_video_stream_index);
	text_cl = ice_session_check_list(call->ice_session, call->main_text_stream_index);
705
	if ((audio_cl == NULL) && (video_cl == NULL) && (text_cl == NULL)) return -1;
706

707 708
	if ((nat_policy != NULL) && (server != NULL) && (server[0] != '\0')) {
		ai=linphone_nat_policy_get_stun_server_addrinfo(nat_policy);
709 710
		if (ai==NULL){
			ms_warning("Fail to resolve STUN server for ICE gathering, continuing without stun.");
711 712
		} else {
			ai = get_preferred_stun_server_addrinfo(ai);
713 714 715
		}
	}else{
		ms_warning("Ice is used without stun server.");
716
	}
717
	linphone_core_notify_display_status(lc, _("ICE local candidates gathering in progress..."));
718

719
	ice_session_enable_forced_relay(call->ice_session, lc->forced_ice_relay);
720
	ice_session_enable_short_turn_refresh(call->ice_session, lc->short_turn_refresh);
721

722
	/* Gather local host candidates. */
723 724 725 726 727 728 729
	if (call->af == AF_INET6) {
		if (linphone_core_get_local_ip_for(AF_INET6, NULL, local_addr) < 0) {
			ms_error("Fail to get local IPv6");
			return -1;
		} else {
			linphone_core_add_local_ice_candidates(call, AF_INET6, local_addr, audio_cl, video_cl, text_cl);
		}
730
	}
731 732 733 734 735 736 737
	if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) {
		if (call->af != AF_INET6) {
			ms_error("Fail to get local IPv4");
			return -1;
		}
	} else {
		linphone_core_add_local_ice_candidates(call, AF_INET, local_addr, audio_cl, video_cl, text_cl);
738
	}
739 740
	if ((ai != NULL) && (nat_policy != NULL)
		&& (linphone_nat_policy_stun_enabled(nat_policy) || linphone_nat_policy_turn_enabled(nat_policy))) {
741
		bool_t gathering_in_progress;
742
		ms_message("ICE: gathering candidate from [%s] using %s", server, linphone_nat_policy_turn_enabled(nat_policy) ? "TURN" : "STUN");
743
		/* Gather local srflx candidates. */
744
		ice_session_enable_turn(call->ice_session, linphone_nat_policy_turn_enabled(nat_policy));
745
		ice_session_set_stun_auth_requested_cb(call->ice_session, (MSStunAuthRequestedCb)stun_auth_requested_cb, call);
746 747
		gathering_in_progress = ice_session_gather_candidates(call->ice_session, ai->ai_addr, (socklen_t)ai->ai_addrlen);
		return (gathering_in_progress == FALSE) ? 0 : 1;
748 749 750 751 752
	} else {
		ms_message("ICE: bypass candidates gathering");
		ice_session_compute_candidates_foundations(call->ice_session);
		ice_session_eliminate_redundant_candidates(call->ice_session);
		ice_session_choose_default_candidates(call->ice_session);
753
	}
754
	return 0;
755 756
}

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
const char *linphone_ice_state_to_string(LinphoneIceState state){
	switch(state){
		case LinphoneIceStateFailed:
			return "IceStateFailed";
		case LinphoneIceStateHostConnection:
			return "IceStateHostConnection";
		case LinphoneIceStateInProgress:
			return "IceStateInProgress";
		case LinphoneIceStateNotActivated:
			return "IceStateNotActivated";
		case LinphoneIceStateReflexiveConnection:
			return "IceStateReflexiveConnection";
		case LinphoneIceStateRelayConnection:
			return "IceStateRelayConnection";
	}
	return "invalid";
}

775 776 777 778
void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
{
	IceCheckList *audio_check_list;
	IceCheckList *video_check_list;
779
	IceCheckList *text_check_list;
Ghislain MARY's avatar
Ghislain MARY committed
780
	IceSessionState session_state;
781 782

	if (call->ice_session == NULL) return;
783 784
	audio_check_list = ice_session_check_list(call->ice_session, call->main_audio_stream_index);
	video_check_list = ice_session_check_list(call->ice_session, call->main_video_stream_index);
785
	text_check_list = ice_session_check_list(call->ice_session, call->main_text_stream_index);
786
	if ((audio_check_list == NULL) && (video_check_list == NULL) && (text_check_list == NULL)) return;
787

Ghislain MARY's avatar
Ghislain MARY committed
788 789
	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))) {
790 791 792 793 794 795 796 797 798 799 800 801 802
		if (call->params->has_audio && (audio_check_list != NULL)) {
			if (ice_check_list_state(audio_check_list) == ICL_Completed) {
				switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
					case ICT_HostCandidate:
						call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
						break;
					case ICT_ServerReflexiveCandidate:
					case ICT_PeerReflexiveCandidate:
						call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
						break;
					case ICT_RelayedCandidate:
						call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
						break;
803 804 805 806
					case ICT_CandidateInvalid:
					case ICT_CandidateTypeMax:
						/*shall not happen*/
						break;
807 808 809
				}
			} else {
				call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
Ghislain MARY's avatar
Ghislain MARY committed
810
			}
811 812
		}else call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateNotActivated;
		
813
		if (call->params->has_video && (video_check_list != NULL)) {
814 815 816 817 818 819 820 821 822 823 824 825
			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;
826 827 828 829
					case ICT_CandidateInvalid:
					case ICT_CandidateTypeMax:
						/*shall not happen*/
						break;
830 831 832 833
				}
			} else {
				call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
			}
Simon Morlat's avatar
Simon Morlat committed
834
		}else call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateNotActivated;
835 836 837 838 839 840 841 842 843 844 845 846 847 848
		
		if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
			if (ice_check_list_state(text_check_list) == ICL_Completed) {
				switch (ice_check_list_selected_valid_candidate_type(text_check_list)) {
					case ICT_HostCandidate:
						call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateHostConnection;
						break;
					case ICT_ServerReflexiveCandidate:
					case ICT_PeerReflexiveCandidate:
						call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateReflexiveConnection;
						break;
					case ICT_RelayedCandidate:
						call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateRelayConnection;
						break;
849 850 851 852
					case ICT_CandidateInvalid:
					case ICT_CandidateTypeMax:
						/*shall not happen*/
						break;
853 854 855 856 857
				}
			} else {
				call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateFailed;
			}
		}else call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateNotActivated;
858 859
	} else if (session_state == IS_Running) {
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
860
		if (call->params->has_video && (video_check_list != NULL)) {
861 862
			call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
		}
863 864 865
		if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
			call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateInProgress;
		}
Ghislain MARY's avatar
Ghislain MARY committed
866 867
	} else {
		call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
868
		if (call->params->has_video && (video_check_list != NULL)) {
Ghislain MARY's avatar
Ghislain MARY committed
869 870
			call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
		}
871 872 873
		if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
			call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateFailed;
		}
874
	}
875 876
	ms_message("Call [%p] New ICE state: audio: [%s]    video: [%s]    text: [%s]", call,
		   linphone_ice_state_to_string(call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state), linphone_ice_state_to_string(call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state), linphone_ice_state_to_string(call->stats[LINPHONE_CALL_STATS_TEXT].ice_state));
877 878
}

Simon Morlat's avatar
Simon Morlat committed
879
void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call, SalMediaDescription *desc) {
880
	int i;
881
	IceSession *session = call->ice_session;
882

883 884 885
	if (session == NULL) return;
	if (ice_session_state(session) == IS_Completed) return;

886
	for (i = 0; i < desc->nb_streams; i++) {
887 888 889
		IceCheckList *cl = ice_session_check_list(session, i);
		if (!sal_stream_description_active(&desc->streams[i]) && cl) {
			ice_session_remove_check_list(session, cl);
890
			clear_ice_check_list(call, cl);
891 892
		}
	}
893 894

	linphone_core_update_ice_state_in_call_stats(call);
895 896
}

897
void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session, bool_t use_nortpproxy) {
898 899
	IceCandidate *rtp_candidate = NULL;
	IceCandidate *rtcp_candidate = NULL;
900
	IceSessionState session_state = ice_session_state(session);
901
	int nb_candidates;
902
	int i;
903
	int j;
904
	bool_t result = FALSE;
905

906
	if (session_state == IS_Completed) {
907 908 909 910 911 912 913 914 915 916 917
		IceCheckList *first_cl = NULL;
		for (i = 0; i < desc->nb_streams; i++) {
			IceCheckList *cl = ice_session_check_list(session, i);
			if (cl != NULL) {
				first_cl = cl;
				break;
			}
		}
		if (first_cl != NULL) {
			result = ice_check_list_selected_valid_local_candidate(first_cl, &rtp_candidate, NULL);
		}
918
		if (result == TRUE) {
919
			strncpy(desc->addr, rtp_candidate->taddr.ip, sizeof(desc->addr));
920
		} else {
Ghislain MARY's avatar
Ghislain MARY committed
921
			ms_warning("If ICE has completed successfully, rtp_candidate should be set!");
922
		}
923
	}
924
	
925 926
	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));
927
	for (i = 0; i < desc->nb_streams; i++) {
928 929
		SalStreamDescription *stream = &desc->streams[i];
		IceCheckList *cl = ice_session_check_list(session, i);
930
		nb_candidates = 0;
Ghislain MARY's avatar
Ghislain MARY committed
931
		rtp_candidate = rtcp_candidate = NULL;
932
		if (!sal_stream_description_active(stream) || (cl == NULL)) continue;
933
		if (ice_check_list_state(cl) == ICL_Completed) {
934
			if (use_nortpproxy) stream->set_nortpproxy = TRUE;
935
			result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_candidate, &rtcp_candidate);
936
		} else {
937
			stream->set_nortpproxy = FALSE;
938
			result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_candidate, &rtcp_candidate);
939 940
		}
		if (result == TRUE) {
941 942 943 944
			strncpy(stream->rtp_addr, rtp_candidate->taddr.ip, sizeof(stream->rtp_addr));
			strncpy(stream->rtcp_addr, rtcp_candidate->taddr.ip, sizeof(stream->rtcp_addr));
			stream->rtp_port = rtp_candidate->taddr.port;
			stream->rtcp_port = rtcp_candidate->taddr.port;
945
		} else {
946 947
			memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
			memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
948
		}
949 950 951 952 953 954 955 956
		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));
957
		stream->ice_mismatch = ice_check_list_is_mismatch(cl);
958
		if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
959
			memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
960
			for (j = 0; j < MIN((int)bctbx_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
961
				SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
962
				IceCandidate *ice_candidate = bctbx_list_nth_data(cl->local_candidates, j);
963 964 965 966 967 968 969 970 971 972 973
				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. */
974
				if ((ice_check_list_state(cl) == ICL_Completed)
975 976 977 978 979 980 981 982 983 984 985 986
					&& !((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;
				}
987
				nb_candidates++;
988 989
			}
		}
990
		if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
991
			memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
992 993 994 995 996
			if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_candidate, &rtcp_candidate) == TRUE) {
				strncpy(stream->ice_remote_candidates[0].addr, rtp_candidate->taddr.ip, sizeof(stream->ice_remote_candidates[0].addr));
				stream->ice_remote_candidates[0].port = rtp_candidate->taddr.port;
				strncpy(stream->ice_remote_candidates[1].addr, rtcp_candidate->taddr.ip, sizeof(stream->ice_remote_candidates[1].addr));
				stream->ice_remote_candidates[1].port = rtcp_candidate->taddr.port;
997 998 999
			} else {
				ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
			}
1000 1001 1002 1003 1004
		} 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;
			}
1005 1006 1007 1008
		}
	}
}

1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
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;
}

1021 1022 1023 1024 1025
static void clear_ice_check_list(LinphoneCall *call, IceCheckList *removed){
	if (call->audiostream && call->audiostream->ms.ice_check_list==removed)
		call->audiostream->ms.ice_check_list=NULL;
	if (call->videostream && call->videostream->ms.ice_check_list==removed)
		call->videostream->ms.ice_check_list=NULL;
1026 1027
	if (call->textstream && call->textstream->ms.ice_check_list==removed)
		call->textstream->ms.ice_check_list=NULL;
1028 1029
}

1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
void linphone_call_clear_unused_ice_candidates(LinphoneCall *call, const SalMediaDescription *md){
	int i;
	
	if (!call->localdesc) return;
	for (i = 0; i < md->nb_streams; i++) {
		const SalStreamDescription *local_stream = &call->localdesc->streams[i];
		const SalStreamDescription *stream = &md->streams[i];
		IceCheckList *cl = ice_session_check_list(call->ice_session, i);
		if (!cl || !local_stream) continue;
		
		if (stream->rtcp_mux && local_stream->rtcp_mux){
			ice_check_list_remove_rtcp_candidates(cl);
		}
	}
}

1046
bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md){
1047 1048
	int i;

1049
	for (i = 0; md && i < md->nb_streams; i++) {
1050
		if (md->streams[i].type == SalVideo && md->streams[i].rtp_port!=0)
1051 1052 1053 1054 1055
			return TRUE;
	}
	return FALSE;
}

1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
	unsigned int ret=0;
	const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
	if (features){
		char tmp[256]={0};
		char name[256];
		char *p,*n;
		strncpy(tmp,features,sizeof(tmp)-1);
		for(p=tmp;*p!='\0';p++){
			if (*p==' ') continue;
			n=strchr(p,'|');
			if (n) *n='\0';
			sscanf(p,"%s",name);
			ms_message("Found audio feature %s",name);
			if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
			else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
			else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
			else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
			else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
			else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
			else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
Simon Morlat's avatar
Simon Morlat committed
1077
			else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1078 1079
			else if (strcasecmp(name,"LOCAL_PLAYING")==0) ret|=AUDIO_STREAM_FEATURE_LOCAL_PLAYING;
			else if (strcasecmp(name,"REMOTE_PLAYING")==0) ret|=AUDIO_STREAM_FEATURE_REMOTE_PLAYING;
1080 1081 1082 1083 1084 1085 1086
			else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
			else if (strcasecmp(name,"NONE")==0) ret=0;
			else ms_error("Unsupported audio feature %s requested in config file.",name);
			if (!n) break;
			p=n;
		}
	}else ret=AUDIO_STREAM_FEATURE_ALL;
1087

Simon Morlat's avatar
Simon Morlat committed
1088 1089 1090 1091 1092
	if (ret==AUDIO_STREAM_FEATURE_ALL){
		/*since call recording is specified before creation of the stream in linphonecore,
		* it will be requested on demand. It is not necessary to include it all the time*/
		ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
	}
1093 1094 1095
	return ret;
}

1096 1097 1098
bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc){
	return lp_config_get_int(lc->config,"sound","tone_indications",1);
}
1099

1100
int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
1101
	return bctbx_get_local_ip_for(type, dest, 5060, result, LINPHONE_IPADDR_SIZE);
1102
}
1103

1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
void linphone_core_get_local_ip(LinphoneCore *lc, int af, const char *dest, char *result) {
	if (af == AF_UNSPEC) {
		if (linphone_core_ipv6_enabled(lc)) {
			bool_t has_ipv6 = linphone_core_get_local_ip_for(AF_INET6, dest, result) == 0;
			if (strcmp(result, "::1") != 0)
				return; /*this machine has real ipv6 connectivity*/
			if ((linphone_core_get_local_ip_for(AF_INET, dest, result) == 0) && (strcmp(result, "127.0.0.1") != 0))
				return; /*this machine has only ipv4 connectivity*/
			if (has_ipv6) {
				/*this machine has only local loopback for both ipv4 and ipv6, so prefer ipv6*/
				strncpy(result, "::1", LINPHONE_IPADDR_SIZE);
				return;
			}
		}
		/*in all other cases use IPv4*/
		af = AF_INET;
	}
	linphone_core_get_local_ip_for(af, dest, result);
}

1124 1125 1126
SalReason linphone_reason_to_sal(LinphoneReason reason){
	switch(reason){
		case LinphoneReasonNone:
1127
			return SalReasonNone;
1128
		case LinphoneReasonNoResponse:
1129
			return SalReasonRequestTimeout;
1130
		case LinphoneReasonForbidden:
1131 1132 1133 1134 1135
			return SalReasonForbidden;
		case LinphoneReasonDeclined:
			return SalReasonDeclined;
		case LinphoneReasonNotFound:
			return SalReasonNotFound;
1136
		case LinphoneReasonTemporarilyUnavailable:
1137 1138 1139
			return SalReasonTemporarilyUnavailable;
		case LinphoneReasonBusy:
			return SalReasonBusy;
1140 1141
		case LinphoneReasonNotAcceptable:
			return SalReasonNotAcceptable;
1142 1143
		case LinphoneReasonIOError:
			return SalReasonServiceUnavailable;
jehan's avatar
jehan committed
1144
		case LinphoneReasonDoNotDisturb:
jehan's avatar
jehan committed
1145
			return SalReasonDoNotDisturb;
jehan's avatar
jehan committed
1146 1147
		case LinphoneReasonUnauthorized:
			return SalReasonUnauthorized;
1148 1149
		case LinphoneReasonUnsupportedContent:
			return SalReasonUnsupportedContent;
1150 1151
		case LinphoneReasonNoMatch:
			return SalReasonNoMatch;
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
		case LinphoneReasonMovedPermanently:
			return SalReasonMovedPermanently;
		case LinphoneReasonGone:
			return SalReasonGone;
		case LinphoneReasonAddressIncomplete:
			return SalReasonAddressIncomplete;
		case LinphoneReasonNotImplemented:
			return SalReasonNotImplemented;
		case LinphoneReasonBadGateway:
			return SalReasonBadGateway;
		case LinphoneReasonServerTimeout:
			return SalReasonServerTimeout;
		case LinphoneReasonNotAnswered:
			return SalReasonRequestTimeout;
1166 1167
		case LinphoneReasonUnknown:
			return SalReasonUnknown;
1168 1169 1170 1171 1172 1173 1174
	}
	return SalReasonUnknown;
}

LinphoneReason linphone_reason_from_sal(SalReason r){
	LinphoneReason ret=LinphoneReasonNone;
	switch(r){
1175
		case SalReasonNone:
1176 1177
			ret=LinphoneReasonNone;
			break;
1178 1179 1180 1181
		case SalReasonIOError:
			ret=LinphoneReasonIOError;
			break;
		case SalReasonUnknown:
1182
		case SalReasonInternalError:
1183 1184
			ret=LinphoneReasonUnknown;
			break;
1185 1186 1187 1188 1189 1190 1191
		case SalReasonBusy:
			ret=LinphoneReasonBusy;
			break;
		case SalReasonDeclined:
			ret=LinphoneReasonDeclined;
			break;
		case SalReasonDoNotDisturb:
jehan's avatar
jehan committed
1192
			ret=LinphoneReasonDoNotDisturb;
1193 1194 1195 1196
			break;
		case SalReasonForbidden:
			ret=LinphoneReasonBadCredentials;
			break;
1197 1198
		case SalReasonNotAcceptable:
			ret=LinphoneReasonNotAcceptable;
1199 1200 1201 1202 1203 1204 1205 1206
			break;
		case SalReasonNotFound:
			ret=LinphoneReasonNotFound;
			break;
		case SalReasonRedirect:
			ret=LinphoneReasonNone;
			break;
		case SalReasonTemporarilyUnavailable:
1207
			ret=LinphoneReasonTemporarilyUnavailable;
1208 1209 1210
			break;
		case SalReasonServiceUnavailable:
			ret=LinphoneReasonIOError;
1211 1212
			break;
		case SalReasonRequestPending:
1213
			ret=LinphoneReasonTemporarilyUnavailable; /*might not be exactly the perfect matching, but better than LinphoneReasonNone*/
1214
			break;
jehan's avatar
jehan committed
1215 1216
		case SalReasonUnauthorized:
			ret=LinphoneReasonUnauthorized;
1217
			break;
1218 1219
		case SalReasonUnsupportedContent:
			ret=LinphoneReasonUnsupportedContent;
jehan's avatar
jehan committed
1220
		break;
1221 1222 1223
		case SalReasonNoMatch:
			ret=LinphoneReasonNoMatch;
		break;
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244
		case SalReasonRequestTimeout:
			ret=LinphoneReasonNotAnswered;
		break;
		case SalReasonMovedPermanently:
			ret=LinphoneReasonMovedPermanently;
		break;
		case SalReasonGone:
			ret=LinphoneReasonGone;
		break;
		case SalReasonAddressIncomplete:
			ret=LinphoneReasonAddressIncomplete;
		break;
		case SalReasonNotImplemented:
			ret=LinphoneReasonNotImplemented;
		break;
		case SalReasonBadGateway:
			ret=LinphoneReasonBadGateway;
		break;
		case SalReasonServerTimeout:
			ret=LinphoneReasonServerTimeout;
		break;
1245 1246 1247 1248
	}
	return ret;
}