misc.c 66.4 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"
Ghislain MARY's avatar
Ghislain MARY committed
23
#include "linphone/wrapper_utils.h"
aymeric's avatar
aymeric committed
24 25 26
#include "mediastreamer2/mediastream.h"
#include <stdlib.h>
#include <stdio.h>
27
#include <string.h>
28
#ifdef HAVE_SIGHANDLER_T
aymeric's avatar
aymeric committed
29
#include <signal.h>
30
#endif /*HAVE_SIGHANDLER_T*/
Simon Morlat's avatar
Simon Morlat committed
31 32 33 34

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

45
#undef snprintf
jehan's avatar
jehan committed
46
#include <mediastreamer2/stun.h>
aymeric's avatar
aymeric committed
47

48 49 50 51
#ifdef HAVE_GETIFADDRS
#include <net/if.h>
#include <ifaddrs.h>
#endif
52
#include <math.h>
53 54 55 56
#if _MSC_VER
#define snprintf _snprintf
#define popen _popen
#define pclose _pclose
57
#endif
58

aymeric's avatar
aymeric committed
59 60 61 62 63

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

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

66

67 68 69 70
/*
 *((codec-birate*ptime/8) + RTP header + UDP header + IP header)*8/ptime;
 *ptime=1/npacket
 */
71 72

static double get_audio_payload_bandwidth_from_codec_bitrate(const PayloadType *pt){
aymeric's avatar
aymeric committed
73 74 75
	double npacket=50;
	double packet_size;
	int bitrate;
76

77 78 79
	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;
80 81
	}else if (strcmp(payload_type_get_mime(&payload_type_ilbc), payload_type_get_mime(pt))==0) {
		npacket=1000/30.0;
82
	}
83

84
	bitrate=pt->normal_bitrate;
85
	packet_size= (((double)bitrate)/(npacket*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
aymeric's avatar
aymeric committed
86 87 88
	return packet_size*8.0*npacket;
}

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

116 117
int get_audio_payload_bandwidth(const LinphoneCore *lc, const PayloadType *pt, int maxbw) {
	if (payload_type_is_vbr(pt)) {
118
		if (pt->flags & PAYLOAD_TYPE_BITRATE_OVERRIDE){
119
			ms_debug("PayloadType %s/%i has bitrate override",pt->mime_type,pt->clock_rate);
120 121 122 123 124 125 126 127
			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*/
}

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
128
	ms_message("Audio bandwidth for this call is %i",call->audio_bw);
aymeric's avatar
aymeric committed
129 130
}

131
void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
132
	const bctbx_list_t *elem;
133 134 135
	int maxbw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
					linphone_core_get_upload_bandwidth(lc));
	int max_codec_bitrate=0;
136

aymeric's avatar
aymeric committed
137 138 139
	for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
		PayloadType *pt=(PayloadType*)elem->data;
		if (payload_type_enabled(pt)){
140 141 142 143 144
			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
145 146 147
			}
		}
	}
148 149
	if (max_codec_bitrate) {
		lc->audio_bw=max_codec_bitrate;
aymeric's avatar
aymeric committed
150 151 152
	}
}

153
bool_t linphone_core_is_payload_type_usable_for_bandwidth(const LinphoneCore *lc, const PayloadType *pt, int bandwidth_limit){
154
	double codec_band;
155
	const int video_enablement_limit = 99;
156
	bool_t ret=FALSE;
157

158 159 160
	switch (pt->type){
		case PAYLOAD_AUDIO_CONTINUOUS:
		case PAYLOAD_AUDIO_PACKETIZED:
161
			codec_band=get_audio_payload_bandwidth(lc,pt,bandwidth_limit);
162
			ret=bandwidth_is_greater(bandwidth_limit,(int)codec_band);
163
			/*ms_message("Payload %s: codec_bandwidth=%g, bandwidth_limit=%i",pt->mime_type,codec_band,bandwidth_limit);*/
164 165
			break;
		case PAYLOAD_VIDEO:
166
			if (bandwidth_limit<=0 || bandwidth_limit >= video_enablement_limit) {/* infinite or greater than video_enablement_limit*/
167 168 169 170
				ret=TRUE;
			}
			else ret=FALSE;
			break;
Sylvain Berfini's avatar
Sylvain Berfini committed
171 172 173
		case PAYLOAD_TEXT:
			ret=TRUE;
			break;
174 175 176 177
	}
	return ret;
}

aymeric's avatar
aymeric committed
178
bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
179
#if !defined(_WIN32_WCE) && !defined(LINPHONE_WINDOWS_UNIVERSAL)
aymeric's avatar
aymeric committed
180 181 182
	FILE *f=popen(command,"r");
	if (f!=NULL){
		int err;
183
		*result=reinterpret_cast<char *>(ms_malloc(4096));
Ghislain MARY's avatar
Ghislain MARY committed
184
		err=(int)fread(*result,1,4096-1,f);
aymeric's avatar
aymeric committed
185 186 187 188 189 190 191 192 193 194
		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;
	}
195
#endif /*_WIN32_WCE*/
aymeric's avatar
aymeric committed
196 197 198
	return FALSE;
}

smorlat's avatar
smorlat committed
199 200 201 202 203 204 205 206 207 208 209 210 211 212
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
213
		ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
smorlat's avatar
smorlat committed
214 215 216 217 218 219 220 221 222 223 224 225
		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
226 227
static int send_stun_request(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t change_addr){
	char *buf = NULL;
228
	size_t len;
Ghislain MARY's avatar
Ghislain MARY committed
229 230 231 232 233 234 235 236 237
	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
238
		ms_error("Fail to encode stun message.");
Ghislain MARY's avatar
Ghislain MARY committed
239 240
		err = -1;
	} else {
241
		err = bctbx_sendto(sock, buf, len, 0, server, addrlen);
Ghislain MARY's avatar
Ghislain MARY committed
242 243 244 245
		if (err < 0) {
			ms_error("sendto failed: %s",strerror(errno));
			err = -1;
		}
smorlat's avatar
smorlat committed
246
	}
Ghislain MARY's avatar
Ghislain MARY committed
247 248 249
	if (buf != NULL) ms_free(buf);
	ms_free(req);
	return err;
smorlat's avatar
smorlat committed
250 251
}

252 253
int linphone_parse_host_port(const char *input, char *host, size_t hostlen, int *port){
	char tmphost[NI_MAXHOST]={0};
254

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

257
	} else {
Wescoeur's avatar
Wescoeur committed
258 259
		const char *p1 = strchr(input, ':');
		const char *p2 = strrchr(input, ':');
260 261 262 263 264
		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);
265 266
		}
	}
267 268 269 270 271 272 273 274 275 276
	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;
277

278
	linphone_parse_host_port(server,host,sizeof(host),&port_int);
279

280
	snprintf(port, sizeof(port), "%d", port_int);
smorlat's avatar
smorlat committed
281
	memset(&hints,0,sizeof(hints));
282
	hints.ai_family=AF_UNSPEC;
smorlat's avatar
smorlat committed
283 284 285 286 287 288 289 290 291
	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);
292
	*socklen=(socklen_t)res->ai_addrlen;
smorlat's avatar
smorlat committed
293 294 295 296
	freeaddrinfo(res);
	return 0;
}

Ghislain MARY's avatar
Ghislain MARY committed
297 298 299 300 301 302 303
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
304
		struct in_addr ia;
Ghislain MARY's avatar
Ghislain MARY committed
305
		resp = ms_stun_message_create_from_buffer_parsing((uint8_t *)buf, (ssize_t)len);
Ghislain MARY's avatar
Ghislain MARY committed
306 307 308 309 310 311
		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) {
312 313
				*port = stun_addr->ip.v4.port;
				ia.s_addr = htonl(stun_addr->ip.v4.addr);
Ghislain MARY's avatar
Ghislain MARY committed
314 315 316
			} else {
				stun_addr = ms_stun_message_get_mapped_address(resp);
				if (stun_addr != NULL) {
317 318
					*port = stun_addr->ip.v4.port;
					ia.s_addr = htonl(stun_addr->ip.v4.addr);
Ghislain MARY's avatar
Ghislain MARY committed
319 320 321 322
				} else len = -1;
			}
			if (len > 0) strncpy(ipaddr, inet_ntoa(ia), LINPHONE_IPADDR_SIZE);
		}
smorlat's avatar
smorlat committed
323 324 325 326
	}
	return len;
}

327
/* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
328
int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
smorlat's avatar
smorlat committed
329
	const char *server=linphone_core_get_stun_server(lc);
330 331
	StunCandidate *ac=&call->ac;
	StunCandidate *vc=&call->vc;
332
	StunCandidate *tc=&call->tc;
333

smorlat's avatar
smorlat committed
334 335
	if (lc->sip_conf.ipv6_enabled){
		ms_warning("stun support is not implemented for ipv6");
336
		return -1;
smorlat's avatar
smorlat committed
337
	}
338
	if (call->media_ports[call->main_audio_stream_index].rtp_port==-1){
339 340 341
		ms_warning("Stun-only support not available for system random port");
		return -1;
	}
smorlat's avatar
smorlat committed
342
	if (server!=NULL){
343
		const struct addrinfo *ai=linphone_core_get_stun_server_addrinfo(lc);
344
		ortp_socket_t sock1=-1, sock2=-1, sock3=-1;
345
		int loops=0;
smorlat's avatar
smorlat committed
346
		bool_t video_enabled=linphone_core_video_enabled(lc);
347 348
		bool_t got_audio,got_video,got_text;
		bool_t cone_audio=FALSE,cone_video=FALSE,cone_text=FALSE;
smorlat's avatar
smorlat committed
349
		struct timeval init,cur;
350 351
		double elapsed;
		int ret=0;
352

353 354
		if (ai==NULL){
			ms_error("Could not obtain stun server addrinfo.");
355
			return -1;
smorlat's avatar
smorlat committed
356
		}
357
		linphone_core_notify_display_status(lc,_("Stun lookup in progress..."));
358

smorlat's avatar
smorlat committed
359
		/*create the two audio and video RTP sockets, and send STUN message to our stun server */
360
		sock1=create_socket(call->media_ports[call->main_audio_stream_index].rtp_port);
361
		if (sock1==-1) return -1;
smorlat's avatar
smorlat committed
362
		if (video_enabled){
363
			sock2=create_socket(call->media_ports[call->main_video_stream_index].rtp_port);
364
			if (sock2==-1) return -1;
smorlat's avatar
smorlat committed
365
		}
Sylvain Berfini's avatar
Sylvain Berfini committed
366 367
		sock3=create_socket(call->media_ports[call->main_text_stream_index].rtp_port);
		if (sock3==-1) return -1;
Wescoeur's avatar
Wescoeur committed
368

smorlat's avatar
smorlat committed
369 370
		got_audio=FALSE;
		got_video=FALSE;
371
		got_text=FALSE;
372
		ortp_gettimeofday(&init,NULL);
smorlat's avatar
smorlat committed
373
		do{
374

smorlat's avatar
smorlat committed
375
			int id;
376 377
			if (loops%20==0){
				ms_message("Sending stun requests...");
Ghislain MARY's avatar
Ghislain MARY committed
378 379
				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);
380
				if (sock2!=-1){
Ghislain MARY's avatar
Ghislain MARY committed
381 382
					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);
383
				}
384
				if (sock3!=-1){
Ghislain MARY's avatar
Ghislain MARY committed
385 386
					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);
387
				}
388
			}
389
			ms_usleep(10000);
smorlat's avatar
smorlat committed
390

Ghislain MARY's avatar
Ghislain MARY committed
391 392 393
			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
394 395
				got_audio=TRUE;
			}
Ghislain MARY's avatar
Ghislain MARY committed
396 397 398
			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
399
				got_video=TRUE;
smorlat's avatar
smorlat committed
400
			}
Ghislain MARY's avatar
Ghislain MARY committed
401 402 403
			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;
404 405
				got_text=TRUE;
			}
406
			ortp_gettimeofday(&cur,NULL);
smorlat's avatar
smorlat committed
407
			elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
408 409
			if (elapsed>2000)  {
				ms_message("Stun responses timeout, going ahead.");
410
				ret=-1;
411 412 413
				break;
			}
			loops++;
414
		}while(!(got_audio && (got_video||sock2==-1) && (got_text||sock3==-1)  ) );
415
		if (ret==0) ret=(int)elapsed;
smorlat's avatar
smorlat committed
416 417
		if (!got_audio){
			ms_error("No stun server response for audio port.");
smorlat's avatar
smorlat committed
418 419
		}else{
			if (!cone_audio) {
420
				ms_message("NAT is symmetric for audio port");
smorlat's avatar
smorlat committed
421
			}
smorlat's avatar
smorlat committed
422
		}
423
		if (sock2!=-1){
smorlat's avatar
smorlat committed
424 425 426 427
			if (!got_video){
				ms_error("No stun server response for video port.");
			}else{
				if (!cone_video) {
428
					ms_message("NAT is symmetric for video port.");
smorlat's avatar
smorlat committed
429 430
				}
			}
smorlat's avatar
smorlat committed
431
		}
432 433 434 435 436 437 438 439 440
		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
441
		close_socket(sock1);
442
		if (sock2!=-1) close_socket(sock2);
443
		if (sock3!=-1) close_socket(sock3);
444
		return ret;
smorlat's avatar
smorlat committed
445
	}
446
	return -1;
smorlat's avatar
smorlat committed
447 448
}

449 450 451 452 453 454 455 456 457 458
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;
}

459
void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
460
	int threshold;
461
	if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
462
		ms_message("Stun server ping time is %i ms",ping_time_ms);
463
		threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
464

465
		if (ping_time_ms>threshold){
466
			/* we might be in a 2G network*/
jehan's avatar
jehan committed
467
			params->low_bandwidth=TRUE;
468 469
		}/*else use default settings */
	}
470 471 472 473 474
	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;
	}
475 476 477
}


478
void linphone_core_resolve_stun_server(LinphoneCore *lc){
479 480 481
	if (lc->nat_policy != NULL) {
		linphone_nat_policy_resolve_stun_server(lc->nat_policy);
	} else {
482
		ms_error("linphone_core_resolve_stun_server(): called without nat_policy, this should not happen.");
483 484 485 486
	}
}

const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc){
487 488 489
	if (lc->nat_policy != NULL) {
		return linphone_nat_policy_get_stun_server_addrinfo(lc->nat_policy);
	} else {
490
		ms_error("linphone_core_get_stun_server_addrinfo(): called without nat_policy, this should not happen.");
491
	}
492
	return NULL;
493
}
494

495 496 497 498
void linphone_core_enable_forced_ice_relay(LinphoneCore *lc, bool_t enable) {
	lc->forced_ice_relay = enable;
}

499 500 501 502
void linphone_core_enable_short_turn_refresh(LinphoneCore *lc, bool_t enable) {
	lc->short_turn_refresh = enable;
}

503 504
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;
505
	const LinphoneNatPolicy *nat_policy = NULL;
506 507 508
	const LinphoneAddress *addr = NULL;
	const LinphoneAuthInfo *auth_info = NULL;
	LinphoneCore *lc = call->core;
Ghislain MARY's avatar
Ghislain MARY committed
509
	const char *user = NULL;
510

511
	// Get the username from the nat policy or the proxy config
512 513 514
	if (call->dest_proxy != NULL) proxy = call->dest_proxy;
	else proxy = linphone_core_get_default_proxy_config(call->core);
	if (proxy == NULL) return;
515 516 517 518
	nat_policy = linphone_proxy_config_get_nat_policy(proxy);
	if (nat_policy != NULL) {
		user = linphone_nat_policy_get_stun_server_username(nat_policy);
	} else {
519
		nat_policy = call->nat_policy;
520 521 522 523 524 525 526 527 528 529
		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);
	}
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
	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");
	}
}

546 547 548 549
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);
550
		call->audio_stats->ice_state = LinphoneIceStateInProgress;
551 552 553 554 555
	}
	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);
556
		call->video_stats->ice_state = LinphoneIceStateInProgress;
557 558 559 560 561
	}
	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);
562
		call->text_stats->ice_state = LinphoneIceStateInProgress;
563 564 565
	}
}

566
static const struct addrinfo * find_nat64_addrinfo(const struct addrinfo *ai) {
567
	while (ai != NULL) {
568
		if (ai->ai_family == AF_INET6) {
569 570
			struct sockaddr_storage ss;
			socklen_t sslen = sizeof(ss);
571
			bctbx_sockaddr_remove_nat64_mapping(ai->ai_addr, (struct sockaddr *)&ss, &sslen);
572
			if (ss.ss_family == AF_INET) break;
573 574 575
		}
		ai = ai->ai_next;
	}
576 577
	return ai;
}
578

579 580 581
static const struct addrinfo * find_ipv4_addrinfo(const struct addrinfo *ai) {
	while (ai != NULL) {
		if (ai->ai_family == AF_INET) break;
582
		if (ai->ai_family == AF_INET6 && ai->ai_flags & AI_V4MAPPED) break;
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
		ai = ai->ai_next;
	}
	return ai;
}

static const struct addrinfo * find_ipv6_addrinfo(const struct addrinfo *ai) {
	while (ai != NULL) {
		if (ai->ai_family == AF_INET6) break;
		ai = ai->ai_next;
	}
	return ai;
}

/**
 * Choose the preferred IP address to use to contact the STUN server from the list of IP addresses
 * the DNS resolution returned. If a NAT64 address is present, use it, otherwise if an IPv4 address
 * is present, use it, otherwise use an IPv6 address if it is present.
 */
static const struct addrinfo * get_preferred_stun_server_addrinfo(const struct addrinfo *ai) {
	const struct addrinfo *preferred_ai = find_nat64_addrinfo(ai);
	if (!preferred_ai) preferred_ai = find_ipv4_addrinfo(ai);
	if (!preferred_ai) preferred_ai = find_ipv6_addrinfo(ai);
605 606 607
	return preferred_ai;
}

608 609 610 611 612
/* 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.
 */
613
int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call){
614
	char local_addr[64];
615
	const struct addrinfo *ai = NULL;
616 617 618
	IceCheckList *audio_cl;
	IceCheckList *video_cl;
	IceCheckList *text_cl;
619
	LinphoneNatPolicy *nat_policy = call->nat_policy;
620

621
	if (call->ice_session == NULL) return -1;
622 623 624
	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);
625
	if ((audio_cl == NULL) && (video_cl == NULL) && (text_cl == NULL)) return -1;
626

627
	if ((nat_policy != NULL) && linphone_nat_policy_stun_server_activated(nat_policy)) {
628
		ai=linphone_nat_policy_get_stun_server_addrinfo(nat_policy);
629 630
		if (ai==NULL){
			ms_warning("Fail to resolve STUN server for ICE gathering, continuing without stun.");
631 632
		} else {
			ai = get_preferred_stun_server_addrinfo(ai);
633 634 635
		}
	}else{
		ms_warning("Ice is used without stun server.");
636
	}
637
	linphone_core_notify_display_status(lc, _("ICE local candidates gathering in progress..."));
638

639
	ice_session_enable_forced_relay(call->ice_session, lc->forced_ice_relay);
640
	ice_session_enable_short_turn_refresh(call->ice_session, lc->short_turn_refresh);
641

642
	/* Gather local host candidates. */
643 644 645 646 647 648 649
	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);
		}
650
	}
651 652 653 654 655 656 657
	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);
658
	}
659
	if ((ai != NULL) && (nat_policy != NULL) && linphone_nat_policy_stun_server_activated(nat_policy)) {
660
		bool_t gathering_in_progress;
661
		const char *server = linphone_nat_policy_get_stun_server(nat_policy);
662
		ms_message("ICE: gathering candidate from [%s] using %s", server, linphone_nat_policy_turn_enabled(nat_policy) ? "TURN" : "STUN");
663
		/* Gather local srflx candidates. */
664
		ice_session_enable_turn(call->ice_session, linphone_nat_policy_turn_enabled(nat_policy));
665
		ice_session_set_stun_auth_requested_cb(call->ice_session, (MSStunAuthRequestedCb)stun_auth_requested_cb, call);
666 667
		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;
668 669 670 671 672
	} 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);
673
	}
674
	return 0;
675 676
}

677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
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";
}

695
void linphone_call_update_ice_state_in_call_stats(LinphoneCall *call) {
696 697
	IceCheckList *audio_check_list;
	IceCheckList *video_check_list;
698
	IceCheckList *text_check_list;
Ghislain MARY's avatar
Ghislain MARY committed
699
	IceSessionState session_state;
700

701 702 703 704 705 706
	if (call->ice_session == NULL) {
		call->audio_stats->ice_state = LinphoneIceStateNotActivated;
		call->video_stats->ice_state = LinphoneIceStateNotActivated;
		call->text_stats->ice_state = LinphoneIceStateNotActivated;
		return;
	}
707 708
	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);
709
	text_check_list = ice_session_check_list(call->ice_session, call->main_text_stream_index);
710
	if ((audio_check_list == NULL) && (video_check_list == NULL) && (text_check_list == NULL)) return;
711

Ghislain MARY's avatar
Ghislain MARY committed
712 713
	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))) {
714 715 716 717
		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:
718
						call->audio_stats->ice_state = LinphoneIceStateHostConnection;
719 720 721
						break;
					case ICT_ServerReflexiveCandidate:
					case ICT_PeerReflexiveCandidate:
722
						call->audio_stats->ice_state = LinphoneIceStateReflexiveConnection;
723 724
						break;
					case ICT_RelayedCandidate:
725
						call->audio_stats->ice_state = LinphoneIceStateRelayConnection;
726
						break;
727 728 729 730
					case ICT_CandidateInvalid:
					case ICT_CandidateTypeMax:
						/*shall not happen*/
						break;
731 732
				}
			} else {
733
				call->audio_stats->ice_state = LinphoneIceStateFailed;
Ghislain MARY's avatar
Ghislain MARY committed
734
			}
735
		}else call->audio_stats->ice_state = LinphoneIceStateNotActivated;
Wescoeur's avatar
Wescoeur committed
736

737
		if (call->params->has_video && (video_check_list != NULL)) {
738 739 740
			if (ice_check_list_state(video_check_list) == ICL_Completed) {
				switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
					case ICT_HostCandidate:
741
						call->video_stats->ice_state = LinphoneIceStateHostConnection;
742 743 744
						break;
					case ICT_ServerReflexiveCandidate:
					case ICT_PeerReflexiveCandidate:
745
						call->video_stats->ice_state = LinphoneIceStateReflexiveConnection;
746 747
						break;
					case ICT_RelayedCandidate:
748
						call->video_stats->ice_state = LinphoneIceStateRelayConnection;
749
						break;
750 751 752 753
					case ICT_CandidateInvalid:
					case ICT_CandidateTypeMax:
						/*shall not happen*/
						break;
754 755
				}
			} else {
756
				call->video_stats->ice_state = LinphoneIceStateFailed;
757
			}
758
		}else call->video_stats->ice_state = LinphoneIceStateNotActivated;
Wescoeur's avatar
Wescoeur committed
759

760 761 762 763
		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:
764
						call->text_stats->ice_state = LinphoneIceStateHostConnection;
765 766 767
						break;
					case ICT_ServerReflexiveCandidate:
					case ICT_PeerReflexiveCandidate:
768
						call->text_stats->ice_state = LinphoneIceStateReflexiveConnection;
769 770
						break;
					case ICT_RelayedCandidate:
771
						call->text_stats->ice_state = LinphoneIceStateRelayConnection;
772
						break;
773 774 775 776
					case ICT_CandidateInvalid:
					case ICT_CandidateTypeMax:
						/*shall not happen*/
						break;
777 778
				}
			} else {
779
				call->text_stats->ice_state = LinphoneIceStateFailed;
780
			}
781
		}else call->text_stats->ice_state = LinphoneIceStateNotActivated;
782
	} else if (session_state == IS_Running) {
783
		call->audio_stats->ice_state = LinphoneIceStateInProgress;
784
		if (call->params->has_video && (video_check_list != NULL)) {
785
			call->video_stats->ice_state = LinphoneIceStateInProgress;
786
		}
787
		if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
788
			call->text_stats->ice_state = LinphoneIceStateInProgress;
789
		}
Ghislain MARY's avatar
Ghislain MARY committed
790
	} else {
791
		call->audio_stats->ice_state = LinphoneIceStateFailed;
792
		if (call->params->has_video && (video_check_list != NULL)) {
793
			call->video_stats->ice_state = LinphoneIceStateFailed;
Ghislain MARY's avatar
Ghislain MARY committed
794
		}
795
		if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
796
			call->text_stats->ice_state = LinphoneIceStateFailed;
797
		}
798
	}
799
	ms_message("Call [%p] New ICE state: audio: [%s]    video: [%s]    text: [%s]", call,
800
		   linphone_ice_state_to_string(call->audio_stats->ice_state), linphone_ice_state_to_string(call->video_stats->ice_state), linphone_ice_state_to_string(call->text_stats->ice_state));
801 802
}

Simon Morlat's avatar
Simon Morlat committed
803
void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call, SalMediaDescription *desc) {
804
	int i;
805
	IceSession *session = call->ice_session;
806

807 808 809
	if (session == NULL) return;
	if (ice_session_state(session) == IS_Completed) return;

810
	for (i = 0; i < desc->nb_streams; i++) {
811 812 813
		IceCheckList *cl = ice_session_check_list(session, i);
		if (!sal_stream_description_active(&desc->streams[i]) && cl) {
			ice_session_remove_check_list(session, cl);
814
			clear_ice_check_list(call, cl);
815 816
		}
	}
817

818
	linphone_call_update_ice_state_in_call_stats(call);
819 820
}

821

822
void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session, bool_t use_nortpproxy) {
823 824
	IceCandidate *rtp_candidate = NULL;
	IceCandidate *rtcp_candidate = NULL;
825
	IceSessionState session_state = ice_session_state(session);
826
	int nb_candidates;
827
	int i;
828
	int j;
829
	bool_t result = FALSE;
830

831
	if (session_state == IS_Completed) {
832 833 834 835 836 837 838 839 840 841 842
		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);
		}
843
		if (result == TRUE) {
844
			strncpy(desc->addr, rtp_candidate->taddr.ip, sizeof(desc->addr));
845
		} else {
Ghislain MARY's avatar
Ghislain MARY committed
846
			ms_warning("If ICE has completed successfully, rtp_candidate should be set!");
847
			ice_dump_valid_list(first_cl);
848
		}
849
	}
Wescoeur's avatar
Wescoeur committed
850

851 852
	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));
853
	for (i = 0; i < desc->nb_streams; i++) {
854 855
		SalStreamDescription *stream = &desc->streams[i];
		IceCheckList *cl = ice_session_check_list(session, i);
856
		nb_candidates = 0;
Ghislain MARY's avatar
Ghislain MARY committed
857
		rtp_candidate = rtcp_candidate = NULL;
858
		if (!sal_stream_description_active(stream) || (cl == NULL)) continue;
859
		if (ice_check_list_state(cl) == ICL_Completed) {
860
			if (use_nortpproxy) stream->set_nortpproxy = TRUE;
861
			result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_candidate, &rtcp_candidate);
862
		} else {
863
			stream->set_nortpproxy = FALSE;
864
			result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_candidate, &rtcp_candidate);
865 866
		}
		if (result == TRUE) {
867 868 869 870
			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;
871
		} else {
872 873
			memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
			memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
874
		}
875 876 877 878 879 880 881 882
		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));
883
		stream->ice_mismatch = ice_check_list_is_mismatch(cl);
884
		if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
885
			memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
886
			for (j = 0; j < MIN((int)bctbx_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
887
				SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
888
				IceCandidate *ice_candidate = reinterpret_cast<IceCandidate *>(bctbx_list_nth_data(cl->local_candidates, j));
889 890 891 892 893 894 895 896 897 898 899
				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. */
900
				if ((ice_check_list_state(cl) == ICL_Completed)
901 902 903 904 905 906 907 908 909 910 911 912
					&& !((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;
				}
913
				nb_candidates++;
914 915
			}
		}
916
		if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
917
			memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
918 919 920 921 922
			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;
923 924 925
			} else {
				ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
			}
926 927 928 929 930
		} 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;
			}
931 932 933 934
		}
	}
}

935 936 937 938 939 940 941 942 943 944 945 946
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;
}

947 948 949 950 951
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;
952 953
	if (call->textstream && call->textstream->ms.ice_check_list==removed)
		call->textstream->ms.ice_check_list=NULL;
954 955
}

956 957
void linphone_call_clear_unused_ice_candidates(LinphoneCall *call, const SalMediaDescription *md){
	int i;
Wescoeur's avatar
Wescoeur committed
958

959 960 961 962 963 964
	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;
Wescoeur's avatar
Wescoeur committed
965

966 967 968 969 970 971
		if (stream->rtcp_mux && local_stream->rtcp_mux){
			ice_check_list_remove_rtcp_candidates(cl);
		}
	}
}

972
bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md){
973 974
	int i;

975
	for (i = 0; md && i < md->nb_streams; i++) {
976
		if (md->streams[i].type == SalVideo && md->streams[i].rtp_port!=0)
977 978 979 980 981
			return TRUE;
	}
	return FALSE;
}

982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002
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
1003
			else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1004 1005
			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;
1006 1007 1008 1009 1010 1011 1012
			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;
1013

Simon Morlat's avatar
Simon Morlat committed
1014 1015 1016 1017 1018
	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;
	}
1019 1020 1021
	return ret;
}

1022 1023 1024
bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc){
	return lp_config_get_int(lc->config,"sound","tone_indications",1);
}
1025

1026
int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
1027
	return bctbx_get_local_ip_for(type, dest, 5060, result, LINPHONE_IPADDR_SIZE);
1028
}
1029

1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
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);
}

1050 1051 1052
SalReason linphone_reason_to_sal(LinphoneReason reason){
	switch(reason){
		case LinphoneReasonNone:
1053
			return SalReasonNone;
1054
		case LinphoneReasonNoResponse:
1055
			return SalReasonRequestTimeout;
1056
		case LinphoneReasonForbidden:
1057 1058 1059 1060 1061
			return SalReasonForbidden;
		case LinphoneReasonDeclined:
			return SalReasonDeclined;
		case LinphoneReasonNotFound:
			return SalReasonNotFound;
1062
		case LinphoneReasonTemporarilyUnavailable:
1063 1064 1065
			return SalReasonTemporarilyUnavailable;
		case LinphoneReasonBusy:
			return SalReasonBusy;
1066 1067
		case LinphoneReasonNotAcceptable:
			return SalReasonNotAcceptable;
1068 1069
		case LinphoneReasonIOError:
			return SalReasonServiceUnavailable;
jehan's avatar
jehan committed
1070
		case LinphoneReasonDoNotDisturb:
jehan's avatar
jehan committed
1071
			return SalReasonDoNotDisturb;
jehan's avatar
jehan committed
1072 1073
		case LinphoneReasonUnauthorized:
			return SalReasonUnauthorized;
1074 1075
		case LinphoneReasonUnsupportedContent:
			return SalReasonUnsupportedContent;
1076 1077
		case LinphoneReasonNoMatch:
			return SalReasonNoMatch;
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
		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;
1092 1093
		case LinphoneReasonUnknown:
			return SalReasonUnknown;
1094 1095 1096 1097 1098 1099 1100
	}
	return SalReasonUnknown;
}

LinphoneReason linphone_reason_from_sal(SalReason r){
	LinphoneReason ret=LinphoneReasonNone;
	switch(r){
1101
		case SalReasonNone:
1102 1103
			ret=LinphoneReasonNone;
			break;
1104 1105 1106 1107
		case SalReasonIOError:
			ret=LinphoneReasonIOError;
			break;
		case SalReasonUnknown:
1108
		case SalReasonInternalError:
1109 1110
			ret=LinphoneReasonUnknown;
			break;
1111 1112 1113 1114 1115 1116 1117
		case SalReasonBusy:
			ret=LinphoneReasonBusy;
			break;
		case SalReasonDeclined:
			ret=LinphoneReasonDeclined;
			break;
		case SalReasonDoNotDisturb:
jehan's avatar
jehan committed
1118
			ret=LinphoneReasonDoNotDisturb;
1119 1120 1121 1122
			break;
		case SalReasonForbidden:
			ret=LinphoneReasonBadCredentials;
			break;
1123 1124
		case SalReasonNotAcceptable:
			ret=LinphoneReasonNotAcceptable;
1125 1126 1127 1128 1129 1130 1131 1132
			break;
		case SalReasonNotFound:
			ret=LinphoneReasonNotFound;
			break;
		case SalReasonRedirect:
			ret=LinphoneReasonNone;
			break;
		case SalReasonTemporarilyUnavailable:
1133
			ret=LinphoneReasonTemporarilyUnavailable;
1134 1135 1136
			break;
		case SalReasonServiceUnavailable:
			ret=LinphoneReasonIOError;
1137 1138
			break;
		case SalReasonRequestPending:
1139
			ret=LinphoneReasonTemporarilyUnavailable; /*might not be exactly the perfect matching, but better than LinphoneReasonNone*/
1140
			break;
jehan's avatar
jehan committed
1141 1142
		case SalReasonUnauthorized:
			ret=LinphoneReasonUnauthorized;
1143
			break;
1144 1145
		case SalReasonUnsupportedContent:
			ret=LinphoneReasonUnsupportedContent;
jehan's avatar
jehan committed
1146
		break;
1147 1148 1149
		case SalReasonNoMatch:
			ret=LinphoneReasonNoMatch;
		break;
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
		case SalReasonRequestTimeout:
			ret=LinphoneReasonNotAnswered;
		break;
		case SalReasonMovedPermanently:
			ret=LinphoneReasonMovedPermanently;
		break;
		case SalReasonGone:
			ret=LinphoneReasonGone;
		break;
		case SalReasonAddressIncomplete: