1 
2 /*
3 linphone
4 Copyright (C) 2000  Simon MORLAT (simon.morlat@linphone.org)
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20 
21 #include "private.h"
22 #include "linphone/lpconfig.h"
23 #include "linphone/wrapper_utils.h"
24 #include "mediastreamer2/mediastream.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #ifdef HAVE_SIGHANDLER_T
29 #include <signal.h>
30 #endif /*HAVE_SIGHANDLER_T*/
31 
32 #include <string.h>
33 #if !defined(_WIN32_WCE)
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #if _MSC_VER
38 #include <io.h>
39 #else
40 #include <unistd.h>
41 #endif
42 #include <fcntl.h>
43 #endif /*_WIN32_WCE*/
44 
45 #undef snprintf
46 #include <mediastreamer2/stun.h>
47 
48 #ifdef HAVE_GETIFADDRS
49 #include <net/if.h>
50 #include <ifaddrs.h>
51 #endif
52 #include <math.h>
53 #if _MSC_VER
54 #define snprintf _snprintf
55 #define popen _popen
56 #define pclose _pclose
57 #endif
58 
59 
60 #define UDP_HDR_SZ 8
61 #define RTP_HDR_SZ 12
62 #define IP4_HDR_SZ 20   /*20 is the minimum, but there may be some options*/
63 
64 static void clear_ice_check_list(LinphoneCall *call, IceCheckList *removed);
65 
66 
67 /*
68  *((codec-birate*ptime/8) + RTP header + UDP header + IP header)*8/ptime;
69  *ptime=1/npacket
70  */
71 
get_audio_payload_bandwidth_from_codec_bitrate(const PayloadType * pt)72 static double get_audio_payload_bandwidth_from_codec_bitrate(const PayloadType *pt){
73 	double npacket=50;
74 	double packet_size;
75 	int bitrate;
76 
77 	if (strcmp(payload_type_get_mime(&payload_type_aaceld_44k), payload_type_get_mime(pt))==0) {
78 		/*special case of aac 44K because ptime= 10ms*/
79 		npacket=100;
80 	}else if (strcmp(payload_type_get_mime(&payload_type_ilbc), payload_type_get_mime(pt))==0) {
81 		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;
86 	return packet_size*8.0*npacket;
87 }
88 
89 typedef struct vbr_codec_bitrate{
90 	int max_avail_bitrate;
91 	int min_rate;
92 	int recomended_bitrate;
93 }vbr_codec_bitrate_t;
94 
95 static vbr_codec_bitrate_t defauls_vbr[]={
96 	//{ 100, 44100, 100 },
97 	{ 64, 44100, 50 },
98 	{ 64, 16000, 40 },
99 	{ 32, 16000, 32 },
100 	{ 32, 8000, 32 },
101 	{ 0 , 8000, 24 },
102 	{ 0 , 0, 0 }
103 };
104 
lookup_vbr_typical_bitrate(int maxbw,int clock_rate)105 static int lookup_vbr_typical_bitrate(int maxbw, int clock_rate){
106 	vbr_codec_bitrate_t *it;
107 	if (maxbw<=0) maxbw=defauls_vbr[0].max_avail_bitrate;
108 	for(it=defauls_vbr;it->min_rate!=0;it++){
109 		if (maxbw>=it->max_avail_bitrate && clock_rate>=it->min_rate)
110 			return it->recomended_bitrate;
111 	}
112 	ms_error("lookup_vbr_typical_bitrate(): should not happen.");
113 	return 32;
114 }
115 
get_audio_payload_bandwidth(const LinphoneCore * lc,const PayloadType * pt,int maxbw)116 int get_audio_payload_bandwidth(const LinphoneCore *lc, const PayloadType *pt, int maxbw) {
117 	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 			return pt->normal_bitrate/1000;
121 		}
122 		return lookup_vbr_typical_bitrate(maxbw,pt->clock_rate);
123 	}else return (int)ceil(get_audio_payload_bandwidth_from_codec_bitrate(pt)/1000.0);/*rounding codec bandwidth should be avoid, specially for AMR*/
124 }
125 
linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall * call,const PayloadType * pt,int maxbw)126 void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt, int maxbw){
127 	call->audio_bw=get_audio_payload_bandwidth(call->core,pt,maxbw);
128 	ms_message("Audio bandwidth for this call is %i",call->audio_bw);
129 }
130 
linphone_core_update_allocated_audio_bandwidth(LinphoneCore * lc)131 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
132 	const bctbx_list_t *elem;
133 	int maxbw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
134 					linphone_core_get_upload_bandwidth(lc));
135 	int max_codec_bitrate=0;
136 
137 	for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
138 		PayloadType *pt=(PayloadType*)elem->data;
139 		if (payload_type_enabled(pt)){
140 			int pt_bitrate=get_audio_payload_bandwidth(lc,pt,maxbw);
141 			if (max_codec_bitrate==0) {
142 				max_codec_bitrate=pt_bitrate;
143 			}else if (max_codec_bitrate<pt_bitrate){
144 				max_codec_bitrate=pt_bitrate;
145 			}
146 		}
147 	}
148 	if (max_codec_bitrate) {
149 		lc->audio_bw=max_codec_bitrate;
150 	}
151 }
152 
linphone_core_is_payload_type_usable_for_bandwidth(const LinphoneCore * lc,const PayloadType * pt,int bandwidth_limit)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 	switch (pt->type){
159 		case PAYLOAD_AUDIO_CONTINUOUS:
160 		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 			break;
165 		case PAYLOAD_VIDEO:
166 			if (bandwidth_limit<=0 || bandwidth_limit >= video_enablement_limit) {/* infinite or greater than video_enablement_limit*/
167 				ret=TRUE;
168 			}
169 			else ret=FALSE;
170 			break;
171 		case PAYLOAD_TEXT:
172 			ret=TRUE;
173 			break;
174 	}
175 	return ret;
176 }
177 
lp_spawn_command_line_sync(const char * command,char ** result,int * command_ret)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)
180 	FILE *f=popen(command,"r");
181 	if (f!=NULL){
182 		int err;
183 		*result=ms_malloc(4096);
184 		err=(int)fread(*result,1,4096-1,f);
185 		if (err<0){
186 			ms_warning("Error reading command output:%s",strerror(errno));
187 			ms_free(result);
188 			return FALSE;
189 		}
190 		(*result)[err]=0;
191 		err=pclose(f);
192 		if (command_ret!=NULL) *command_ret=err;
193 		return TRUE;
194 	}
195 #endif /*_WIN32_WCE*/
196 	return FALSE;
197 }
198 
create_socket(int local_port)199 static ortp_socket_t create_socket(int local_port){
200 	struct sockaddr_in laddr;
201 	ortp_socket_t sock;
202 	int optval;
203 	sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
204 	if (sock<0) {
205 		ms_error("Fail to create socket");
206 		return -1;
207 	}
208 	memset (&laddr,0,sizeof(laddr));
209 	laddr.sin_family=AF_INET;
210 	laddr.sin_addr.s_addr=INADDR_ANY;
211 	laddr.sin_port=htons(local_port);
212 	if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
213 		ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
214 		close_socket(sock);
215 		return -1;
216 	}
217 	optval=1;
218 	if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
219 				(SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
220 		ms_warning("Fail to set SO_REUSEADDR");
221 	}
222 	set_non_blocking_socket(sock);
223 	return sock;
224 }
225 
send_stun_request(int sock,const struct sockaddr * server,socklen_t addrlen,int id,bool_t change_addr)226 static int send_stun_request(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t change_addr){
227 	char *buf = NULL;
228 	size_t len;
229 	int err = 0;
230 	MSStunMessage *req = ms_stun_binding_request_create();
231 	UInt96 tr_id = ms_stun_message_get_tr_id(req);
232 	tr_id.octet[0] = id;
233 	ms_stun_message_set_tr_id(req, tr_id);
234 	ms_stun_message_enable_change_ip(req, change_addr);
235 	ms_stun_message_enable_change_port(req, change_addr);
236 	len = ms_stun_message_encode(req, &buf);
237 	if (len <= 0) {
238 		ms_error("Fail to encode stun message.");
239 		err = -1;
240 	} else {
241 		err = bctbx_sendto(sock, buf, len, 0, server, addrlen);
242 		if (err < 0) {
243 			ms_error("sendto failed: %s",strerror(errno));
244 			err = -1;
245 		}
246 	}
247 	if (buf != NULL) ms_free(buf);
248 	ms_free(req);
249 	return err;
250 }
251 
linphone_parse_host_port(const char * input,char * host,size_t hostlen,int * port)252 int linphone_parse_host_port(const char *input, char *host, size_t hostlen, int *port){
253 	char tmphost[NI_MAXHOST]={0};
254 	char *p1, *p2;
255 
256 	if ((sscanf(input, "[%64[^]]]:%d", tmphost, port) == 2) || (sscanf(input, "[%64[^]]]", tmphost) == 1)) {
257 
258 	} else {
259 		p1 = strchr(input, ':');
260 		p2 = strrchr(input, ':');
261 		if (p1 && p2 && (p1 != p2)) {/* an ipv6 address without port*/
262 			strncpy(tmphost, input, sizeof(tmphost) - 1);
263 		} else if (sscanf(input, "%[^:]:%d", tmphost, port) != 2) {
264 			/*no port*/
265 			strncpy(tmphost, input, sizeof(tmphost) - 1);
266 		}
267 	}
268 	strncpy(host,tmphost,hostlen);
269 	return 0;
270 }
271 
parse_hostname_to_addr(const char * server,struct sockaddr_storage * ss,socklen_t * socklen,int default_port)272 int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen, int default_port){
273 	struct addrinfo hints,*res=NULL;
274 	char port[6];
275 	char host[NI_MAXHOST];
276 	int port_int=default_port;
277 	int ret;
278 
279 	linphone_parse_host_port(server,host,sizeof(host),&port_int);
280 
281 	snprintf(port, sizeof(port), "%d", port_int);
282 	memset(&hints,0,sizeof(hints));
283 	hints.ai_family=AF_UNSPEC;
284 	hints.ai_socktype=SOCK_DGRAM;
285 	hints.ai_protocol=IPPROTO_UDP;
286 	ret=getaddrinfo(host,port,&hints,&res);
287 	if (ret!=0){
288 		ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
289 		return -1;
290 	}
291 	if (!res) return -1;
292 	memcpy(ss,res->ai_addr,res->ai_addrlen);
293 	*socklen=(socklen_t)res->ai_addrlen;
294 	freeaddrinfo(res);
295 	return 0;
296 }
297 
recv_stun_response(ortp_socket_t sock,char * ipaddr,int * port,int * id)298 static int recv_stun_response(ortp_socket_t sock, char *ipaddr, int *port, int *id) {
299 	char buf[MS_STUN_MAX_MESSAGE_SIZE];
300 	int len = MS_STUN_MAX_MESSAGE_SIZE;
301 	MSStunMessage *resp;
302 
303 	len = recv(sock, buf, len, 0);
304 	if (len > 0) {
305 		struct in_addr ia;
306 		resp = ms_stun_message_create_from_buffer_parsing((uint8_t *)buf, (ssize_t)len);
307 		if (resp != NULL) {
308 			const MSStunAddress *stun_addr;
309 			UInt96 tr_id = ms_stun_message_get_tr_id(resp);
310 			*id = tr_id.octet[0];
311 			stun_addr = ms_stun_message_get_xor_mapped_address(resp);
312 			if (stun_addr != NULL) {
313 				*port = stun_addr->ip.v4.port;
314 				ia.s_addr = htonl(stun_addr->ip.v4.addr);
315 			} else {
316 				stun_addr = ms_stun_message_get_mapped_address(resp);
317 				if (stun_addr != NULL) {
318 					*port = stun_addr->ip.v4.port;
319 					ia.s_addr = htonl(stun_addr->ip.v4.addr);
320 				} else len = -1;
321 			}
322 			if (len > 0) strncpy(ipaddr, inet_ntoa(ia), LINPHONE_IPADDR_SIZE);
323 		}
324 	}
325 	return len;
326 }
327 
328 /* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
linphone_core_run_stun_tests(LinphoneCore * lc,LinphoneCall * call)329 int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
330 	const char *server=linphone_core_get_stun_server(lc);
331 	StunCandidate *ac=&call->ac;
332 	StunCandidate *vc=&call->vc;
333 	StunCandidate *tc=&call->tc;
334 
335 	if (lc->sip_conf.ipv6_enabled){
336 		ms_warning("stun support is not implemented for ipv6");
337 		return -1;
338 	}
339 	if (call->media_ports[call->main_audio_stream_index].rtp_port==-1){
340 		ms_warning("Stun-only support not available for system random port");
341 		return -1;
342 	}
343 	if (server!=NULL){
344 		const struct addrinfo *ai=linphone_core_get_stun_server_addrinfo(lc);
345 		ortp_socket_t sock1=-1, sock2=-1, sock3=-1;
346 		int loops=0;
347 		bool_t video_enabled=linphone_core_video_enabled(lc);
348 		bool_t got_audio,got_video,got_text;
349 		bool_t cone_audio=FALSE,cone_video=FALSE,cone_text=FALSE;
350 		struct timeval init,cur;
351 		double elapsed;
352 		int ret=0;
353 
354 		if (ai==NULL){
355 			ms_error("Could not obtain stun server addrinfo.");
356 			return -1;
357 		}
358 		linphone_core_notify_display_status(lc,_("Stun lookup in progress..."));
359 
360 		/*create the two audio and video RTP sockets, and send STUN message to our stun server */
361 		sock1=create_socket(call->media_ports[call->main_audio_stream_index].rtp_port);
362 		if (sock1==-1) return -1;
363 		if (video_enabled){
364 			sock2=create_socket(call->media_ports[call->main_video_stream_index].rtp_port);
365 			if (sock2==-1) return -1;
366 		}
367 		sock3=create_socket(call->media_ports[call->main_text_stream_index].rtp_port);
368 		if (sock3==-1) return -1;
369 
370 		got_audio=FALSE;
371 		got_video=FALSE;
372 		got_text=FALSE;
373 		ortp_gettimeofday(&init,NULL);
374 		do{
375 
376 			int id;
377 			if (loops%20==0){
378 				ms_message("Sending stun requests...");
379 				send_stun_request((int)sock1,ai->ai_addr,(socklen_t)ai->ai_addrlen,11,TRUE);
380 				send_stun_request((int)sock1,ai->ai_addr,(socklen_t)ai->ai_addrlen,1,FALSE);
381 				if (sock2!=-1){
382 					send_stun_request((int)sock2,ai->ai_addr,(socklen_t)ai->ai_addrlen,22,TRUE);
383 					send_stun_request((int)sock2,ai->ai_addr,(socklen_t)ai->ai_addrlen,2,FALSE);
384 				}
385 				if (sock3!=-1){
386 					send_stun_request((int)sock3,ai->ai_addr,(socklen_t)ai->ai_addrlen,33,TRUE);
387 					send_stun_request((int)sock3,ai->ai_addr,(socklen_t)ai->ai_addrlen,3,FALSE);
388 				}
389 			}
390 			ms_usleep(10000);
391 
392 			if (recv_stun_response(sock1, ac->addr, &ac->port, &id) > 0) {
393 				ms_message("STUN test result: local audio port maps to %s:%i", ac->addr, ac->port);
394 				if (id==11) cone_audio=TRUE;
395 				got_audio=TRUE;
396 			}
397 			if (recv_stun_response(sock2, vc->addr, &vc->port, &id) > 0) {
398 				ms_message("STUN test result: local video port maps to %s:%i", vc->addr, vc->port);
399 				if (id==22) cone_video=TRUE;
400 				got_video=TRUE;
401 			}
402 			if (recv_stun_response(sock3, tc->addr, &tc->port, &id)>0) {
403 				ms_message("STUN test result: local text port maps to %s:%i", tc->addr, tc->port);
404 				if (id==33) cone_text=TRUE;
405 				got_text=TRUE;
406 			}
407 			ortp_gettimeofday(&cur,NULL);
408 			elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
409 			if (elapsed>2000)  {
410 				ms_message("Stun responses timeout, going ahead.");
411 				ret=-1;
412 				break;
413 			}
414 			loops++;
415 		}while(!(got_audio && (got_video||sock2==-1) && (got_text||sock3==-1)  ) );
416 		if (ret==0) ret=(int)elapsed;
417 		if (!got_audio){
418 			ms_error("No stun server response for audio port.");
419 		}else{
420 			if (!cone_audio) {
421 				ms_message("NAT is symmetric for audio port");
422 			}
423 		}
424 		if (sock2!=-1){
425 			if (!got_video){
426 				ms_error("No stun server response for video port.");
427 			}else{
428 				if (!cone_video) {
429 					ms_message("NAT is symmetric for video port.");
430 				}
431 			}
432 		}
433 		if (sock3!=-1){
434 			if (!got_text){
435 				ms_error("No stun server response for text port.");
436 			}else{
437 				if (!cone_text) {
438 					ms_message("NAT is symmetric for text port.");
439 				}
440 			}
441 		}
442 		close_socket(sock1);
443 		if (sock2!=-1) close_socket(sock2);
444 		if (sock3!=-1) close_socket(sock3);
445 		return ret;
446 	}
447 	return -1;
448 }
449 
linphone_core_get_edge_bw(LinphoneCore * lc)450 int linphone_core_get_edge_bw(LinphoneCore *lc){
451 	int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
452 	return edge_bw;
453 }
454 
linphone_core_get_edge_ptime(LinphoneCore * lc)455 int linphone_core_get_edge_ptime(LinphoneCore *lc){
456 	int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
457 	return edge_ptime;
458 }
459 
linphone_core_adapt_to_network(LinphoneCore * lc,int ping_time_ms,LinphoneCallParams * params)460 void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
461 	int threshold;
462 	if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
463 		ms_message("Stun server ping time is %i ms",ping_time_ms);
464 		threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
465 
466 		if (ping_time_ms>threshold){
467 			/* we might be in a 2G network*/
468 			params->low_bandwidth=TRUE;
469 		}/*else use default settings */
470 	}
471 	if (params->low_bandwidth){
472 		params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc);
473 		params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc);
474 		params->has_video=FALSE;
475 	}
476 }
477 
478 
linphone_core_resolve_stun_server(LinphoneCore * lc)479 void linphone_core_resolve_stun_server(LinphoneCore *lc){
480 	if (lc->nat_policy != NULL) {
481 		linphone_nat_policy_resolve_stun_server(lc->nat_policy);
482 	} else {
483 		ms_error("linphone_core_resolve_stun_server(): called without nat_policy, this should not happen.");
484 	}
485 }
486 
linphone_core_get_stun_server_addrinfo(LinphoneCore * lc)487 const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc){
488 	if (lc->nat_policy != NULL) {
489 		return linphone_nat_policy_get_stun_server_addrinfo(lc->nat_policy);
490 	} else {
491 		ms_error("linphone_core_get_stun_server_addrinfo(): called without nat_policy, this should not happen.");
492 	}
493 	return NULL;
494 }
495 
linphone_core_enable_forced_ice_relay(LinphoneCore * lc,bool_t enable)496 void linphone_core_enable_forced_ice_relay(LinphoneCore *lc, bool_t enable) {
497 	lc->forced_ice_relay = enable;
498 }
499 
linphone_core_enable_short_turn_refresh(LinphoneCore * lc,bool_t enable)500 void linphone_core_enable_short_turn_refresh(LinphoneCore *lc, bool_t enable) {
501 	lc->short_turn_refresh = enable;
502 }
503 
stun_auth_requested_cb(LinphoneCall * call,const char * realm,const char * nonce,const char ** username,const char ** password,const char ** ha1)504 static void stun_auth_requested_cb(LinphoneCall *call, const char *realm, const char *nonce, const char **username, const char **password, const char **ha1) {
505 	LinphoneProxyConfig *proxy = NULL;
506 	const LinphoneNatPolicy *nat_policy = NULL;
507 	const LinphoneAddress *addr = NULL;
508 	const LinphoneAuthInfo *auth_info = NULL;
509 	LinphoneCore *lc = call->core;
510 	const char *user = NULL;
511 
512 	// Get the username from the nat policy or the proxy config
513 	if (call->dest_proxy != NULL) proxy = call->dest_proxy;
514 	else proxy = linphone_core_get_default_proxy_config(call->core);
515 	if (proxy == NULL) return;
516 	nat_policy = linphone_proxy_config_get_nat_policy(proxy);
517 	if (nat_policy != NULL) {
518 		user = linphone_nat_policy_get_stun_server_username(nat_policy);
519 	} else {
520 		nat_policy = call->nat_policy;
521 		if (nat_policy != NULL) {
522 			user = linphone_nat_policy_get_stun_server_username(nat_policy);
523 		}
524 	}
525 	if (user == NULL) {
526 		/* If the username has not been found in the nat_policy, take the username from the currently used proxy config. */
527 		addr = linphone_proxy_config_get_identity_address(proxy);
528 		if (addr == NULL) return;
529 		user = linphone_address_get_username(addr);
530 	}
531 	if (user == NULL) return;
532 
533 	auth_info = linphone_core_find_auth_info(lc, realm, user, NULL);
534 	if (auth_info != NULL) {
535 		const char *hash = linphone_auth_info_get_ha1(auth_info);
536 		if (hash != NULL) {
537 			*ha1 = hash;
538 		} else {
539 			*password = linphone_auth_info_get_passwd(auth_info);
540 		}
541 		*username = user;
542 	} else {
543 		ms_warning("No auth info found for STUN auth request");
544 	}
545 }
546 
linphone_core_add_local_ice_candidates(LinphoneCall * call,int family,const char * addr,IceCheckList * audio_cl,IceCheckList * video_cl,IceCheckList * text_cl)547 static void linphone_core_add_local_ice_candidates(LinphoneCall *call, int family, const char *addr, IceCheckList *audio_cl, IceCheckList *video_cl, IceCheckList *text_cl) {
548 	if ((ice_check_list_state(audio_cl) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_cl) == FALSE)) {
549 		ice_add_local_candidate(audio_cl, "host", family, addr, call->media_ports[call->main_audio_stream_index].rtp_port, 1, NULL);
550 		ice_add_local_candidate(audio_cl, "host", family, addr, call->media_ports[call->main_audio_stream_index].rtcp_port, 2, NULL);
551 		call->audio_stats->ice_state = LinphoneIceStateInProgress;
552 	}
553 	if (linphone_core_video_enabled(call->core) && (video_cl != NULL)
554 		&& (ice_check_list_state(video_cl) != ICL_Completed) && (ice_check_list_candidates_gathered(video_cl) == FALSE)) {
555 		ice_add_local_candidate(video_cl, "host", family, addr, call->media_ports[call->main_video_stream_index].rtp_port, 1, NULL);
556 		ice_add_local_candidate(video_cl, "host", family, addr, call->media_ports[call->main_video_stream_index].rtcp_port, 2, NULL);
557 		call->video_stats->ice_state = LinphoneIceStateInProgress;
558 	}
559 	if (call->params->realtimetext_enabled && (text_cl != NULL)
560 		&& (ice_check_list_state(text_cl) != ICL_Completed) && (ice_check_list_candidates_gathered(text_cl) == FALSE)) {
561 		ice_add_local_candidate(text_cl, "host", family, addr, call->media_ports[call->main_text_stream_index].rtp_port, 1, NULL);
562 		ice_add_local_candidate(text_cl, "host", family, addr, call->media_ports[call->main_text_stream_index].rtcp_port, 2, NULL);
563 		call->text_stats->ice_state = LinphoneIceStateInProgress;
564 	}
565 }
566 
find_nat64_addrinfo(const struct addrinfo * ai)567 static const struct addrinfo * find_nat64_addrinfo(const struct addrinfo *ai) {
568 	while (ai != NULL) {
569 		if (ai->ai_family == AF_INET6) {
570 			struct sockaddr_storage ss;
571 			socklen_t sslen = sizeof(ss);
572 			bctbx_sockaddr_remove_nat64_mapping(ai->ai_addr, (struct sockaddr *)&ss, &sslen);
573 			if (ss.ss_family == AF_INET) break;
574 		}
575 		ai = ai->ai_next;
576 	}
577 	return ai;
578 }
579 
find_ipv4_addrinfo(const struct addrinfo * ai)580 static const struct addrinfo * find_ipv4_addrinfo(const struct addrinfo *ai) {
581 	while (ai != NULL) {
582 		if (ai->ai_family == AF_INET) break;
583 		ai = ai->ai_next;
584 	}
585 	return ai;
586 }
587 
find_ipv6_addrinfo(const struct addrinfo * ai)588 static const struct addrinfo * find_ipv6_addrinfo(const struct addrinfo *ai) {
589 	while (ai != NULL) {
590 		if (ai->ai_family == AF_INET6) break;
591 		ai = ai->ai_next;
592 	}
593 	return ai;
594 }
595 
596 /**
597  * Choose the preferred IP address to use to contact the STUN server from the list of IP addresses
598  * the DNS resolution returned. If a NAT64 address is present, use it, otherwise if an IPv4 address
599  * is present, use it, otherwise use an IPv6 address if it is present.
600  */
get_preferred_stun_server_addrinfo(const struct addrinfo * ai)601 static const struct addrinfo * get_preferred_stun_server_addrinfo(const struct addrinfo *ai) {
602 	const struct addrinfo *preferred_ai = find_nat64_addrinfo(ai);
603 	if (!preferred_ai) preferred_ai = find_ipv4_addrinfo(ai);
604 	if (!preferred_ai) preferred_ai = find_ipv6_addrinfo(ai);
605 	return preferred_ai;
606 }
607 
608 /* Return values:
609  * 1 :  STUN gathering is started
610  * 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)
611  * -1: no gathering started and something went wrong with local candidates. There is no way to start the ICE session.
612  */
linphone_core_gather_ice_candidates(LinphoneCore * lc,LinphoneCall * call)613 int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call){
614 	char local_addr[64];
615 	const struct addrinfo *ai = NULL;
616 	IceCheckList *audio_cl;
617 	IceCheckList *video_cl;
618 	IceCheckList *text_cl;
619 	LinphoneNatPolicy *nat_policy = call->nat_policy;
620 	const char *server = NULL;
621 
622 	if (nat_policy != NULL) server = linphone_nat_policy_get_stun_server(nat_policy);
623 
624 	if (call->ice_session == NULL) return -1;
625 	audio_cl = ice_session_check_list(call->ice_session, call->main_audio_stream_index);
626 	video_cl = ice_session_check_list(call->ice_session, call->main_video_stream_index);
627 	text_cl = ice_session_check_list(call->ice_session, call->main_text_stream_index);
628 	if ((audio_cl == NULL) && (video_cl == NULL) && (text_cl == NULL)) return -1;
629 
630 	if ((nat_policy != NULL) && (server != NULL) && (server[0] != '\0')) {
631 		ai=linphone_nat_policy_get_stun_server_addrinfo(nat_policy);
632 		if (ai==NULL){
633 			ms_warning("Fail to resolve STUN server for ICE gathering, continuing without stun.");
634 		} else {
635 			ai = get_preferred_stun_server_addrinfo(ai);
636 		}
637 	}else{
638 		ms_warning("Ice is used without stun server.");
639 	}
640 	linphone_core_notify_display_status(lc, _("ICE local candidates gathering in progress..."));
641 
642 	ice_session_enable_forced_relay(call->ice_session, lc->forced_ice_relay);
643 	ice_session_enable_short_turn_refresh(call->ice_session, lc->short_turn_refresh);
644 
645 	/* Gather local host candidates. */
646 	if (call->af == AF_INET6) {
647 		if (linphone_core_get_local_ip_for(AF_INET6, NULL, local_addr) < 0) {
648 			ms_error("Fail to get local IPv6");
649 			return -1;
650 		} else {
651 			linphone_core_add_local_ice_candidates(call, AF_INET6, local_addr, audio_cl, video_cl, text_cl);
652 		}
653 	}
654 	if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) {
655 		if (call->af != AF_INET6) {
656 			ms_error("Fail to get local IPv4");
657 			return -1;
658 		}
659 	} else {
660 		linphone_core_add_local_ice_candidates(call, AF_INET, local_addr, audio_cl, video_cl, text_cl);
661 	}
662 	if ((ai != NULL) && (nat_policy != NULL)
663 		&& (linphone_nat_policy_stun_enabled(nat_policy) || linphone_nat_policy_turn_enabled(nat_policy))) {
664 		bool_t gathering_in_progress;
665 		ms_message("ICE: gathering candidate from [%s] using %s", server, linphone_nat_policy_turn_enabled(nat_policy) ? "TURN" : "STUN");
666 		/* Gather local srflx candidates. */
667 		ice_session_enable_turn(call->ice_session, linphone_nat_policy_turn_enabled(nat_policy));
668 		ice_session_set_stun_auth_requested_cb(call->ice_session, (MSStunAuthRequestedCb)stun_auth_requested_cb, call);
669 		gathering_in_progress = ice_session_gather_candidates(call->ice_session, ai->ai_addr, (socklen_t)ai->ai_addrlen);
670 		return (gathering_in_progress == FALSE) ? 0 : 1;
671 	} else {
672 		ms_message("ICE: bypass candidates gathering");
673 		ice_session_compute_candidates_foundations(call->ice_session);
674 		ice_session_eliminate_redundant_candidates(call->ice_session);
675 		ice_session_choose_default_candidates(call->ice_session);
676 	}
677 	return 0;
678 }
679 
linphone_ice_state_to_string(LinphoneIceState state)680 const char *linphone_ice_state_to_string(LinphoneIceState state){
681 	switch(state){
682 		case LinphoneIceStateFailed:
683 			return "IceStateFailed";
684 		case LinphoneIceStateHostConnection:
685 			return "IceStateHostConnection";
686 		case LinphoneIceStateInProgress:
687 			return "IceStateInProgress";
688 		case LinphoneIceStateNotActivated:
689 			return "IceStateNotActivated";
690 		case LinphoneIceStateReflexiveConnection:
691 			return "IceStateReflexiveConnection";
692 		case LinphoneIceStateRelayConnection:
693 			return "IceStateRelayConnection";
694 	}
695 	return "invalid";
696 }
697 
linphone_call_update_ice_state_in_call_stats(LinphoneCall * call)698 void linphone_call_update_ice_state_in_call_stats(LinphoneCall *call) {
699 	IceCheckList *audio_check_list;
700 	IceCheckList *video_check_list;
701 	IceCheckList *text_check_list;
702 	IceSessionState session_state;
703 
704 	if (call->ice_session == NULL) return;
705 	audio_check_list = ice_session_check_list(call->ice_session, call->main_audio_stream_index);
706 	video_check_list = ice_session_check_list(call->ice_session, call->main_video_stream_index);
707 	text_check_list = ice_session_check_list(call->ice_session, call->main_text_stream_index);
708 	if ((audio_check_list == NULL) && (video_check_list == NULL) && (text_check_list == NULL)) return;
709 
710 	session_state = ice_session_state(call->ice_session);
711 	if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
712 		if (call->params->has_audio && (audio_check_list != NULL)) {
713 			if (ice_check_list_state(audio_check_list) == ICL_Completed) {
714 				switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
715 					case ICT_HostCandidate:
716 						call->audio_stats->ice_state = LinphoneIceStateHostConnection;
717 						break;
718 					case ICT_ServerReflexiveCandidate:
719 					case ICT_PeerReflexiveCandidate:
720 						call->audio_stats->ice_state = LinphoneIceStateReflexiveConnection;
721 						break;
722 					case ICT_RelayedCandidate:
723 						call->audio_stats->ice_state = LinphoneIceStateRelayConnection;
724 						break;
725 					case ICT_CandidateInvalid:
726 					case ICT_CandidateTypeMax:
727 						/*shall not happen*/
728 						break;
729 				}
730 			} else {
731 				call->audio_stats->ice_state = LinphoneIceStateFailed;
732 			}
733 		}else call->audio_stats->ice_state = LinphoneIceStateNotActivated;
734 
735 		if (call->params->has_video && (video_check_list != NULL)) {
736 			if (ice_check_list_state(video_check_list) == ICL_Completed) {
737 				switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
738 					case ICT_HostCandidate:
739 						call->video_stats->ice_state = LinphoneIceStateHostConnection;
740 						break;
741 					case ICT_ServerReflexiveCandidate:
742 					case ICT_PeerReflexiveCandidate:
743 						call->video_stats->ice_state = LinphoneIceStateReflexiveConnection;
744 						break;
745 					case ICT_RelayedCandidate:
746 						call->video_stats->ice_state = LinphoneIceStateRelayConnection;
747 						break;
748 					case ICT_CandidateInvalid:
749 					case ICT_CandidateTypeMax:
750 						/*shall not happen*/
751 						break;
752 				}
753 			} else {
754 				call->video_stats->ice_state = LinphoneIceStateFailed;
755 			}
756 		}else call->video_stats->ice_state = LinphoneIceStateNotActivated;
757 
758 		if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
759 			if (ice_check_list_state(text_check_list) == ICL_Completed) {
760 				switch (ice_check_list_selected_valid_candidate_type(text_check_list)) {
761 					case ICT_HostCandidate:
762 						call->text_stats->ice_state = LinphoneIceStateHostConnection;
763 						break;
764 					case ICT_ServerReflexiveCandidate:
765 					case ICT_PeerReflexiveCandidate:
766 						call->text_stats->ice_state = LinphoneIceStateReflexiveConnection;
767 						break;
768 					case ICT_RelayedCandidate:
769 						call->text_stats->ice_state = LinphoneIceStateRelayConnection;
770 						break;
771 					case ICT_CandidateInvalid:
772 					case ICT_CandidateTypeMax:
773 						/*shall not happen*/
774 						break;
775 				}
776 			} else {
777 				call->text_stats->ice_state = LinphoneIceStateFailed;
778 			}
779 		}else call->text_stats->ice_state = LinphoneIceStateNotActivated;
780 	} else if (session_state == IS_Running) {
781 		call->audio_stats->ice_state = LinphoneIceStateInProgress;
782 		if (call->params->has_video && (video_check_list != NULL)) {
783 			call->video_stats->ice_state = LinphoneIceStateInProgress;
784 		}
785 		if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
786 			call->text_stats->ice_state = LinphoneIceStateInProgress;
787 		}
788 	} else {
789 		call->audio_stats->ice_state = LinphoneIceStateFailed;
790 		if (call->params->has_video && (video_check_list != NULL)) {
791 			call->video_stats->ice_state = LinphoneIceStateFailed;
792 		}
793 		if (call->params->realtimetext_enabled && (text_check_list != NULL)) {
794 			call->text_stats->ice_state = LinphoneIceStateFailed;
795 		}
796 	}
797 	ms_message("Call [%p] New ICE state: audio: [%s]    video: [%s]    text: [%s]", call,
798 		   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));
799 }
800 
linphone_call_stop_ice_for_inactive_streams(LinphoneCall * call,SalMediaDescription * desc)801 void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call, SalMediaDescription *desc) {
802 	int i;
803 	IceSession *session = call->ice_session;
804 
805 	if (session == NULL) return;
806 	if (ice_session_state(session) == IS_Completed) return;
807 
808 	for (i = 0; i < desc->nb_streams; i++) {
809 		IceCheckList *cl = ice_session_check_list(session, i);
810 		if (!sal_stream_description_active(&desc->streams[i]) && cl) {
811 			ice_session_remove_check_list(session, cl);
812 			clear_ice_check_list(call, cl);
813 		}
814 	}
815 
816 	linphone_call_update_ice_state_in_call_stats(call);
817 }
818 
_update_local_media_description_from_ice(SalMediaDescription * desc,IceSession * session,bool_t use_nortpproxy)819 void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session, bool_t use_nortpproxy) {
820 	IceCandidate *rtp_candidate = NULL;
821 	IceCandidate *rtcp_candidate = NULL;
822 	IceSessionState session_state = ice_session_state(session);
823 	int nb_candidates;
824 	int i;
825 	int j;
826 	bool_t result = FALSE;
827 
828 	if (session_state == IS_Completed) {
829 		IceCheckList *first_cl = NULL;
830 		for (i = 0; i < desc->nb_streams; i++) {
831 			IceCheckList *cl = ice_session_check_list(session, i);
832 			if (cl != NULL) {
833 				first_cl = cl;
834 				break;
835 			}
836 		}
837 		if (first_cl != NULL) {
838 			result = ice_check_list_selected_valid_local_candidate(first_cl, &rtp_candidate, NULL);
839 		}
840 		if (result == TRUE) {
841 			strncpy(desc->addr, rtp_candidate->taddr.ip, sizeof(desc->addr));
842 		} else {
843 			ms_warning("If ICE has completed successfully, rtp_candidate should be set!");
844 		}
845 	}
846 
847 	strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd));
848 	strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag));
849 	for (i = 0; i < desc->nb_streams; i++) {
850 		SalStreamDescription *stream = &desc->streams[i];
851 		IceCheckList *cl = ice_session_check_list(session, i);
852 		nb_candidates = 0;
853 		rtp_candidate = rtcp_candidate = NULL;
854 		if (!sal_stream_description_active(stream) || (cl == NULL)) continue;
855 		if (ice_check_list_state(cl) == ICL_Completed) {
856 			if (use_nortpproxy) stream->set_nortpproxy = TRUE;
857 			result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_candidate, &rtcp_candidate);
858 		} else {
859 			stream->set_nortpproxy = FALSE;
860 			result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_candidate, &rtcp_candidate);
861 		}
862 		if (result == TRUE) {
863 			strncpy(stream->rtp_addr, rtp_candidate->taddr.ip, sizeof(stream->rtp_addr));
864 			strncpy(stream->rtcp_addr, rtcp_candidate->taddr.ip, sizeof(stream->rtcp_addr));
865 			stream->rtp_port = rtp_candidate->taddr.port;
866 			stream->rtcp_port = rtcp_candidate->taddr.port;
867 		} else {
868 			memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
869 			memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
870 		}
871 		if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd)))
872 			strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd));
873 		else
874 			memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
875 		if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag)))
876 			strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag));
877 		else
878 			memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
879 		stream->ice_mismatch = ice_check_list_is_mismatch(cl);
880 		if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
881 			memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
882 			for (j = 0; j < MIN((int)bctbx_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
883 				SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
884 				IceCandidate *ice_candidate = bctbx_list_nth_data(cl->local_candidates, j);
885 				const char *default_addr = NULL;
886 				int default_port = 0;
887 				if (ice_candidate->componentID == 1) {
888 					default_addr = stream->rtp_addr;
889 					default_port = stream->rtp_port;
890 				} else if (ice_candidate->componentID == 2) {
891 					default_addr = stream->rtcp_addr;
892 					default_port = stream->rtcp_port;
893 				} else continue;
894 				if (default_addr[0] == '\0') default_addr = desc->addr;
895 				/* 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. */
896 				if ((ice_check_list_state(cl) == ICL_Completed)
897 					&& !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0)))
898 					continue;
899 				strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation));
900 				sal_candidate->componentID = ice_candidate->componentID;
901 				sal_candidate->priority = ice_candidate->priority;
902 				strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type));
903 				strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr));
904 				sal_candidate->port = ice_candidate->taddr.port;
905 				if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) {
906 					strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr));
907 					sal_candidate->rport = ice_candidate->base->taddr.port;
908 				}
909 				nb_candidates++;
910 			}
911 		}
912 		if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
913 			memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
914 			if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_candidate, &rtcp_candidate) == TRUE) {
915 				strncpy(stream->ice_remote_candidates[0].addr, rtp_candidate->taddr.ip, sizeof(stream->ice_remote_candidates[0].addr));
916 				stream->ice_remote_candidates[0].port = rtp_candidate->taddr.port;
917 				strncpy(stream->ice_remote_candidates[1].addr, rtcp_candidate->taddr.ip, sizeof(stream->ice_remote_candidates[1].addr));
918 				stream->ice_remote_candidates[1].port = rtcp_candidate->taddr.port;
919 			} else {
920 				ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
921 			}
922 		} else {
923 			for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
924 				stream->ice_remote_candidates[j].addr[0] = '\0';
925 				stream->ice_remote_candidates[j].port = 0;
926 			}
927 		}
928 	}
929 }
930 
get_default_addr_and_port(uint16_t componentID,const SalMediaDescription * md,const SalStreamDescription * stream,const char ** addr,int * port)931 static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
932 {
933 	if (componentID == 1) {
934 		*addr = stream->rtp_addr;
935 		*port = stream->rtp_port;
936 	} else if (componentID == 2) {
937 		*addr = stream->rtcp_addr;
938 		*port = stream->rtcp_port;
939 	} else return;
940 	if ((*addr)[0] == '\0') *addr = md->addr;
941 }
942 
clear_ice_check_list(LinphoneCall * call,IceCheckList * removed)943 static void clear_ice_check_list(LinphoneCall *call, IceCheckList *removed){
944 	if (call->audiostream && call->audiostream->ms.ice_check_list==removed)
945 		call->audiostream->ms.ice_check_list=NULL;
946 	if (call->videostream && call->videostream->ms.ice_check_list==removed)
947 		call->videostream->ms.ice_check_list=NULL;
948 	if (call->textstream && call->textstream->ms.ice_check_list==removed)
949 		call->textstream->ms.ice_check_list=NULL;
950 }
951 
linphone_call_clear_unused_ice_candidates(LinphoneCall * call,const SalMediaDescription * md)952 void linphone_call_clear_unused_ice_candidates(LinphoneCall *call, const SalMediaDescription *md){
953 	int i;
954 
955 	if (!call->localdesc) return;
956 	for (i = 0; i < md->nb_streams; i++) {
957 		const SalStreamDescription *local_stream = &call->localdesc->streams[i];
958 		const SalStreamDescription *stream = &md->streams[i];
959 		IceCheckList *cl = ice_session_check_list(call->ice_session, i);
960 		if (!cl || !local_stream) continue;
961 
962 		if (stream->rtcp_mux && local_stream->rtcp_mux){
963 			ice_check_list_remove_rtcp_candidates(cl);
964 		}
965 	}
966 }
967 
linphone_core_media_description_contains_video_stream(const SalMediaDescription * md)968 bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md){
969 	int i;
970 
971 	for (i = 0; md && i < md->nb_streams; i++) {
972 		if (md->streams[i].type == SalVideo && md->streams[i].rtp_port!=0)
973 			return TRUE;
974 	}
975 	return FALSE;
976 }
977 
linphone_core_get_audio_features(LinphoneCore * lc)978 unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
979 	unsigned int ret=0;
980 	const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
981 	if (features){
982 		char tmp[256]={0};
983 		char name[256];
984 		char *p,*n;
985 		strncpy(tmp,features,sizeof(tmp)-1);
986 		for(p=tmp;*p!='\0';p++){
987 			if (*p==' ') continue;
988 			n=strchr(p,'|');
989 			if (n) *n='\0';
990 			sscanf(p,"%s",name);
991 			ms_message("Found audio feature %s",name);
992 			if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
993 			else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
994 			else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
995 			else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
996 			else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
997 			else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
998 			else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
999 			else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1000 			else if (strcasecmp(name,"LOCAL_PLAYING")==0) ret|=AUDIO_STREAM_FEATURE_LOCAL_PLAYING;
1001 			else if (strcasecmp(name,"REMOTE_PLAYING")==0) ret|=AUDIO_STREAM_FEATURE_REMOTE_PLAYING;
1002 			else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
1003 			else if (strcasecmp(name,"NONE")==0) ret=0;
1004 			else ms_error("Unsupported audio feature %s requested in config file.",name);
1005 			if (!n) break;
1006 			p=n;
1007 		}
1008 	}else ret=AUDIO_STREAM_FEATURE_ALL;
1009 
1010 	if (ret==AUDIO_STREAM_FEATURE_ALL){
1011 		/*since call recording is specified before creation of the stream in linphonecore,
1012 		* it will be requested on demand. It is not necessary to include it all the time*/
1013 		ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1014 	}
1015 	return ret;
1016 }
1017 
linphone_core_tone_indications_enabled(LinphoneCore * lc)1018 bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc){
1019 	return lp_config_get_int(lc->config,"sound","tone_indications",1);
1020 }
1021 
linphone_core_get_local_ip_for(int type,const char * dest,char * result)1022 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
1023 	return bctbx_get_local_ip_for(type, dest, 5060, result, LINPHONE_IPADDR_SIZE);
1024 }
1025 
linphone_core_get_local_ip(LinphoneCore * lc,int af,const char * dest,char * result)1026 void linphone_core_get_local_ip(LinphoneCore *lc, int af, const char *dest, char *result) {
1027 	if (af == AF_UNSPEC) {
1028 		if (linphone_core_ipv6_enabled(lc)) {
1029 			bool_t has_ipv6 = linphone_core_get_local_ip_for(AF_INET6, dest, result) == 0;
1030 			if (strcmp(result, "::1") != 0)
1031 				return; /*this machine has real ipv6 connectivity*/
1032 			if ((linphone_core_get_local_ip_for(AF_INET, dest, result) == 0) && (strcmp(result, "127.0.0.1") != 0))
1033 				return; /*this machine has only ipv4 connectivity*/
1034 			if (has_ipv6) {
1035 				/*this machine has only local loopback for both ipv4 and ipv6, so prefer ipv6*/
1036 				strncpy(result, "::1", LINPHONE_IPADDR_SIZE);
1037 				return;
1038 			}
1039 		}
1040 		/*in all other cases use IPv4*/
1041 		af = AF_INET;
1042 	}
1043 	linphone_core_get_local_ip_for(af, dest, result);
1044 }
1045 
linphone_reason_to_sal(LinphoneReason reason)1046 SalReason linphone_reason_to_sal(LinphoneReason reason){
1047 	switch(reason){
1048 		case LinphoneReasonNone:
1049 			return SalReasonNone;
1050 		case LinphoneReasonNoResponse:
1051 			return SalReasonRequestTimeout;
1052 		case LinphoneReasonForbidden:
1053 			return SalReasonForbidden;
1054 		case LinphoneReasonDeclined:
1055 			return SalReasonDeclined;
1056 		case LinphoneReasonNotFound:
1057 			return SalReasonNotFound;
1058 		case LinphoneReasonTemporarilyUnavailable:
1059 			return SalReasonTemporarilyUnavailable;
1060 		case LinphoneReasonBusy:
1061 			return SalReasonBusy;
1062 		case LinphoneReasonNotAcceptable:
1063 			return SalReasonNotAcceptable;
1064 		case LinphoneReasonIOError:
1065 			return SalReasonServiceUnavailable;
1066 		case LinphoneReasonDoNotDisturb:
1067 			return SalReasonDoNotDisturb;
1068 		case LinphoneReasonUnauthorized:
1069 			return SalReasonUnauthorized;
1070 		case LinphoneReasonUnsupportedContent:
1071 			return SalReasonUnsupportedContent;
1072 		case LinphoneReasonNoMatch:
1073 			return SalReasonNoMatch;
1074 		case LinphoneReasonMovedPermanently:
1075 			return SalReasonMovedPermanently;
1076 		case LinphoneReasonGone:
1077 			return SalReasonGone;
1078 		case LinphoneReasonAddressIncomplete:
1079 			return SalReasonAddressIncomplete;
1080 		case LinphoneReasonNotImplemented:
1081 			return SalReasonNotImplemented;
1082 		case LinphoneReasonBadGateway:
1083 			return SalReasonBadGateway;
1084 		case LinphoneReasonServerTimeout:
1085 			return SalReasonServerTimeout;
1086 		case LinphoneReasonNotAnswered:
1087 			return SalReasonRequestTimeout;
1088 		case LinphoneReasonUnknown:
1089 			return SalReasonUnknown;
1090 	}
1091 	return SalReasonUnknown;
1092 }
1093 
linphone_reason_from_sal(SalReason r)1094 LinphoneReason linphone_reason_from_sal(SalReason r){
1095 	LinphoneReason ret=LinphoneReasonNone;
1096 	switch(r){
1097 		case SalReasonNone:
1098 			ret=LinphoneReasonNone;
1099 			break;
1100 		case SalReasonIOError:
1101 			ret=LinphoneReasonIOError;
1102 			break;
1103 		case SalReasonUnknown:
1104 		case SalReasonInternalError:
1105 			ret=LinphoneReasonUnknown;
1106 			break;
1107 		case SalReasonBusy:
1108 			ret=LinphoneReasonBusy;
1109 			break;
1110 		case SalReasonDeclined:
1111 			ret=LinphoneReasonDeclined;
1112 			break;
1113 		case SalReasonDoNotDisturb:
1114 			ret=LinphoneReasonDoNotDisturb;
1115 			break;
1116 		case SalReasonForbidden:
1117 			ret=LinphoneReasonBadCredentials;
1118 			break;
1119 		case SalReasonNotAcceptable:
1120 			ret=LinphoneReasonNotAcceptable;
1121 			break;
1122 		case SalReasonNotFound:
1123 			ret=LinphoneReasonNotFound;
1124 			break;
1125 		case SalReasonRedirect:
1126 			ret=LinphoneReasonNone;
1127 			break;
1128 		case SalReasonTemporarilyUnavailable:
1129 			ret=LinphoneReasonTemporarilyUnavailable;
1130 			break;
1131 		case SalReasonServiceUnavailable:
1132 			ret=LinphoneReasonIOError;
1133 			break;
1134 		case SalReasonRequestPending:
1135 			ret=LinphoneReasonTemporarilyUnavailable; /*might not be exactly the perfect matching, but better than LinphoneReasonNone*/
1136 			break;
1137 		case SalReasonUnauthorized:
1138 			ret=LinphoneReasonUnauthorized;
1139 			break;
1140 		case SalReasonUnsupportedContent:
1141 			ret=LinphoneReasonUnsupportedContent;
1142 		break;
1143 		case SalReasonNoMatch:
1144 			ret=LinphoneReasonNoMatch;
1145 		break;
1146 		case SalReasonRequestTimeout:
1147 			ret=LinphoneReasonNotAnswered;
1148 		break;
1149 		case SalReasonMovedPermanently:
1150 			ret=LinphoneReasonMovedPermanently;
1151 		break;
1152 		case SalReasonGone:
1153 			ret=LinphoneReasonGone;
1154 		break;
1155 		case SalReasonAddressIncomplete:
1156 			ret=LinphoneReasonAddressIncomplete;
1157 		break;
1158 		case SalReasonNotImplemented:
1159 			ret=LinphoneReasonNotImplemented;
1160 		break;
1161 		case SalReasonBadGateway:
1162 			ret=LinphoneReasonBadGateway;
1163 		break;
1164 		case SalReasonServerTimeout:
1165 			ret=LinphoneReasonServerTimeout;
1166 		break;
1167 	}
1168 	return ret;
1169 }
1170 
1171 /**
1172  * Set the name of the mediastreamer2 filter to be used for rendering video.
1173  * This is for advanced users of the library, mainly to workaround hardware/driver bugs.
1174  * @ingroup media_parameters
1175 **/
linphone_core_set_video_display_filter(LinphoneCore * lc,const char * filter_name)1176 void linphone_core_set_video_display_filter(LinphoneCore *lc, const char *filter_name){
1177 	lp_config_set_string(lc->config,"video","displaytype",filter_name);
1178 }
1179 
linphone_core_get_video_display_filter(LinphoneCore * lc)1180 const char *linphone_core_get_video_display_filter(LinphoneCore *lc){
1181 	return lp_config_get_string(lc->config,"video","displaytype",NULL);
1182 }
1183 
linphone_core_set_echo_canceller_filter_name(LinphoneCore * lc,const char * filtername)1184 void linphone_core_set_echo_canceller_filter_name(LinphoneCore *lc, const char *filtername) {
1185 	lp_config_set_string(lc->config, "sound", "ec_filter", filtername);
1186 	if (filtername != NULL) {
1187 		ms_factory_set_echo_canceller_filter_name(lc->factory, filtername);
1188 	}
1189 }
1190 
linphone_core_get_echo_canceller_filter_name(const LinphoneCore * lc)1191 const char * linphone_core_get_echo_canceller_filter_name(const LinphoneCore *lc) {
1192 	return lp_config_get_string(lc->config, "sound", "ec_filter", NULL);
1193 }
1194 
1195 /**
1196  * Queue a task into the main loop. The data pointer must remain valid until the task is completed.
1197  * task_fun must return BELLE_SIP_STOP when job is finished.
1198 **/
linphone_core_queue_task(LinphoneCore * lc,belle_sip_source_func_t task_fun,void * data,const char * task_description)1199 void linphone_core_queue_task(LinphoneCore *lc, belle_sip_source_func_t task_fun, void *data, const char *task_description){
1200 	belle_sip_source_t *s=sal_create_timer(lc->sal,task_fun,data, 20, task_description);
1201 	belle_sip_object_unref(s);
1202 }
1203 
get_unique_transport(LinphoneCore * lc,LinphoneTransportType * type,int * port)1204 static int get_unique_transport(LinphoneCore *lc, LinphoneTransportType *type, int *port){
1205 	LinphoneSipTransports tp;
1206 	linphone_core_get_sip_transports(lc,&tp);
1207 	if (tp.tcp_port==0 && tp.tls_port==0 && tp.udp_port!=0){
1208 		*type=LinphoneTransportUdp;
1209 		*port=tp.udp_port;
1210 		return 0;
1211 	}else if (tp.tcp_port==0 && tp.udp_port==0 && tp.tls_port!=0){
1212 		*type=LinphoneTransportTls;
1213 		*port=tp.tls_port;
1214 		return 0;
1215 	}else if (tp.tcp_port!=0 && tp.udp_port==0 && tp.tls_port==0){
1216 		*type=LinphoneTransportTcp;
1217 		*port=tp.tcp_port;
1218 		return 0;
1219 	}
1220 	return -1;
1221 }
1222 
linphone_core_migrate_proxy_config(LinphoneCore * lc,LinphoneTransportType type)1223 static void linphone_core_migrate_proxy_config(LinphoneCore *lc, LinphoneTransportType type){
1224 	const bctbx_list_t *elem;
1225 	for(elem=linphone_core_get_proxy_config_list(lc);elem!=NULL;elem=elem->next){
1226 		LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
1227 		const char *proxy=linphone_proxy_config_get_addr(cfg);
1228 		const char *route=linphone_proxy_config_get_route(cfg);
1229 		LinphoneAddress *proxy_addr=linphone_address_new(proxy);
1230 		LinphoneAddress *route_addr=NULL;
1231 		char *tmp;
1232 		if (route) route_addr=linphone_address_new(route);
1233 		if (proxy_addr){
1234 			linphone_address_set_transport(proxy_addr,type);
1235 			tmp=linphone_address_as_string(proxy_addr);
1236 			linphone_proxy_config_set_server_addr(cfg,tmp);
1237 			ms_free(tmp);
1238 			linphone_address_unref(proxy_addr);
1239 		}
1240 		if (route_addr){
1241 			linphone_address_set_transport(route_addr,type);
1242 			tmp=linphone_address_as_string(route_addr);
1243 			linphone_proxy_config_set_route(cfg,tmp);
1244 			ms_free(tmp);
1245 			linphone_address_unref(route_addr);
1246 		}
1247 	}
1248 }
1249 
linphone_core_migrate_to_multi_transport(LinphoneCore * lc)1250 LinphoneStatus linphone_core_migrate_to_multi_transport(LinphoneCore *lc){
1251 	if (!lp_config_get_int(lc->config,"sip","multi_transport_migration_done",0)){
1252 		LinphoneTransportType tpt;
1253 		int port;
1254 		if (get_unique_transport(lc,&tpt,&port)==0){
1255 			LinphoneSipTransports newtp={0};
1256 			if (lp_config_get_int(lc->config,"sip","sip_random_port",0))
1257 				port=-1;
1258 			ms_message("Core is using a single SIP transport, migrating proxy config and enabling multi-transport.");
1259 			linphone_core_migrate_proxy_config(lc,tpt);
1260 			newtp.udp_port=port;
1261 			newtp.tcp_port=port;
1262 			newtp.tls_port=LC_SIP_TRANSPORT_RANDOM;
1263 			lp_config_set_string(lc->config, "sip","sip_random_port",NULL); /*remove*/
1264 			linphone_core_set_sip_transports(lc,&newtp);
1265 		}
1266 		lp_config_set_int(lc->config,"sip","multi_transport_migration_done",1);
1267 		return 1;
1268 	}
1269 	return 0;
1270 }
1271 
linphone_tone_description_new(LinphoneReason reason,LinphoneToneID id,const char * audiofile)1272 LinphoneToneDescription * linphone_tone_description_new(LinphoneReason reason, LinphoneToneID id, const char *audiofile){
1273 	LinphoneToneDescription *obj=ms_new0(LinphoneToneDescription,1);
1274 	obj->reason=reason;
1275 	obj->toneid=id;
1276 	obj->audiofile=audiofile ? ms_strdup(audiofile) : NULL;
1277 	return obj;
1278 }
1279 
linphone_tone_description_destroy(LinphoneToneDescription * obj)1280 void linphone_tone_description_destroy(LinphoneToneDescription *obj){
1281 	if (obj->audiofile) ms_free(obj->audiofile);
1282 	ms_free(obj);
1283 }
1284 
linphone_core_lookup_tone(const LinphoneCore * lc,LinphoneReason reason,LinphoneToneID id)1285 static LinphoneToneDescription *linphone_core_lookup_tone(const LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id){
1286 	const bctbx_list_t *elem;
1287 	for (elem=lc->tones;elem!=NULL;elem=elem->next){
1288 		LinphoneToneDescription *tone=(LinphoneToneDescription*)elem->data;
1289 		if (reason == LinphoneReasonNone){
1290 			if (tone->toneid == id && tone->reason == LinphoneReasonNone) return tone;
1291 		}else{
1292 			if (tone->reason==reason) return tone;
1293 		}
1294 	}
1295 	return NULL;
1296 }
1297 
linphone_core_get_call_error_tone(const LinphoneCore * lc,LinphoneReason reason)1298 LinphoneToneDescription *linphone_core_get_call_error_tone(const LinphoneCore *lc, LinphoneReason reason){
1299 	return linphone_core_lookup_tone(lc, reason, LinphoneToneUndefined);
1300 }
1301 
linphone_core_get_tone_file(const LinphoneCore * lc,LinphoneToneID id)1302 const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id){
1303 	LinphoneToneDescription *tone = linphone_core_lookup_tone(lc, LinphoneReasonNone, id);
1304 	return tone ? tone->audiofile : NULL;
1305 }
1306 
_linphone_core_set_tone(LinphoneCore * lc,LinphoneReason reason,LinphoneToneID id,const char * audiofile)1307 void _linphone_core_set_tone(LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id, const char *audiofile){
1308 	LinphoneToneDescription *tone = linphone_core_lookup_tone(lc,reason, id);
1309 	if (tone){
1310 		lc->tones=bctbx_list_remove(lc->tones,tone);
1311 		linphone_tone_description_destroy(tone);
1312 	}
1313 	tone=linphone_tone_description_new(reason,id,audiofile);
1314 	lc->tones=bctbx_list_append(lc->tones,tone);
1315 }
1316 
linphone_core_set_call_error_tone(LinphoneCore * lc,LinphoneReason reason,const char * audiofile)1317 void linphone_core_set_call_error_tone(LinphoneCore *lc, LinphoneReason reason, const char *audiofile){
1318 	_linphone_core_set_tone(lc,reason,LinphoneToneUndefined, audiofile);
1319 }
1320 
linphone_core_set_tone(LinphoneCore * lc,LinphoneToneID id,const char * audiofile)1321 void linphone_core_set_tone(LinphoneCore *lc, LinphoneToneID id, const char *audiofile){
1322 	_linphone_core_set_tone(lc, LinphoneReasonNone, id, audiofile);
1323 }
1324 
linphone_core_get_srtp_crypto_suites(LinphoneCore * lc)1325 const MSCryptoSuite * linphone_core_get_srtp_crypto_suites(LinphoneCore *lc){
1326 	const char *config= lp_config_get_string(lc->config, "sip", "srtp_crypto_suites", "AES_CM_128_HMAC_SHA1_80, AES_CM_128_HMAC_SHA1_32, AES_256_CM_HMAC_SHA1_80, AES_256_CM_HMAC_SHA1_32");
1327 	char *tmp=ms_strdup(config);
1328 
1329 	char *sep;
1330 	char *pos;
1331 	char *nextpos;
1332 	char *params;
1333 	int found=0;
1334 	MSCryptoSuite *result=NULL;
1335 	pos=tmp;
1336 	do{
1337 		sep=strchr(pos,',');
1338 		if (!sep) {
1339 			sep=pos+strlen(pos);
1340 			nextpos=NULL;
1341 		}else {
1342 			*sep='\0';
1343 			nextpos=sep+1;
1344 		}
1345 		while(*pos==' ') ++pos; /*strip leading spaces*/
1346 		params=strchr(pos,' '); /*look for params that arrive after crypto suite name*/
1347 		if (params){
1348 			while(*params==' ') ++params; /*strip parameters leading space*/
1349 		}
1350 		if (sep-pos>0){
1351 			MSCryptoSuiteNameParams np;
1352 			MSCryptoSuite suite;
1353 			np.name=pos;
1354 			np.params=params;
1355 			suite=ms_crypto_suite_build_from_name_params(&np);
1356 			if (suite!=MS_CRYPTO_SUITE_INVALID){
1357 				result=ms_realloc(result,(found+2)*sizeof(MSCryptoSuite));
1358 				result[found]=suite;
1359 				result[found+1]=MS_CRYPTO_SUITE_INVALID;
1360 				found++;
1361 				ms_message("Configured srtp crypto suite: %s %s",np.name,np.params ? np.params : "");
1362 			}
1363 		}
1364 		pos=nextpos;
1365 	}while(pos);
1366 	ms_free(tmp);
1367 	if (lc->rtp_conf.srtp_suites){
1368 		ms_free(lc->rtp_conf.srtp_suites);
1369 		lc->rtp_conf.srtp_suites=NULL;
1370 	}
1371 	lc->rtp_conf.srtp_suites=result;
1372 	return result;
1373 }
1374 
seperate_string_list(char ** str)1375 static char * seperate_string_list(char **str) {
1376 	char *ret;
1377 
1378 	if (str == NULL) return NULL;
1379 	if (*str == NULL) return NULL;
1380 	if (**str == '\0') return NULL;
1381 
1382 	ret = *str;
1383 	for ( ; **str!='\0' && **str!=' ' && **str!=','; (*str)++);
1384 	if (**str == '\0') {
1385 		return ret;
1386 	} else {
1387 		**str = '\0';
1388 		do { (*str)++; } while (**str!='\0' && (**str==' ' || **str==','));
1389 		return ret;
1390 	}
1391 }
1392 
linphone_core_get_zrtp_key_agreement_suites(LinphoneCore * lc,MSZrtpKeyAgreement keyAgreements[MS_MAX_ZRTP_CRYPTO_TYPES])1393 MsZrtpCryptoTypesCount linphone_core_get_zrtp_key_agreement_suites(LinphoneCore *lc, MSZrtpKeyAgreement keyAgreements[MS_MAX_ZRTP_CRYPTO_TYPES]){
1394 	char * zrtpConfig = (char*)lp_config_get_string(lc->config, "sip", "zrtp_key_agreements_suites", NULL);
1395 	MsZrtpCryptoTypesCount key_agreements_count = 0;
1396 	char * entry, * origPtr;
1397 	if (zrtpConfig == NULL) {
1398 	        return 0;
1399 	}
1400 
1401 	origPtr = ms_strdup(zrtpConfig);
1402 	zrtpConfig = origPtr;
1403 	while ((entry = seperate_string_list(&zrtpConfig))) {
1404 		const MSZrtpKeyAgreement agreement = ms_zrtp_key_agreement_from_string(entry);
1405 		if (agreement != MS_ZRTP_KEY_AGREEMENT_INVALID) {
1406 			ms_message("Configured zrtp key agreement: '%s'", ms_zrtp_key_agreement_to_string(agreement));
1407 			keyAgreements[key_agreements_count++] = agreement;
1408 		}
1409 	}
1410 
1411 	ms_free(origPtr);
1412 	return key_agreements_count;
1413 }
1414 
linphone_core_get_zrtp_cipher_suites(LinphoneCore * lc,MSZrtpCipher ciphers[MS_MAX_ZRTP_CRYPTO_TYPES])1415 MsZrtpCryptoTypesCount linphone_core_get_zrtp_cipher_suites(LinphoneCore *lc, MSZrtpCipher ciphers[MS_MAX_ZRTP_CRYPTO_TYPES]){
1416 	char * zrtpConfig = (char*)lp_config_get_string(lc->config, "sip", "zrtp_cipher_suites", NULL);
1417 	MsZrtpCryptoTypesCount cipher_count = 0;
1418 	char * entry, * origPtr;
1419 	if (zrtpConfig == NULL) {
1420 	        return 0;
1421 	}
1422 
1423 	origPtr = ms_strdup(zrtpConfig);
1424 	zrtpConfig = origPtr;
1425 	while ((entry = seperate_string_list(&zrtpConfig))) {
1426 		const MSZrtpCipher cipher = ms_zrtp_cipher_from_string(entry);
1427 		if (cipher != MS_ZRTP_CIPHER_INVALID) {
1428 			ms_message("Configured zrtp cipher: '%s'", ms_zrtp_cipher_to_string(cipher));
1429 			ciphers[cipher_count++] = cipher;
1430 		}
1431 	}
1432 
1433 	ms_free(origPtr);
1434 	return cipher_count;
1435 }
1436 
linphone_core_get_zrtp_hash_suites(LinphoneCore * lc,MSZrtpHash hashes[MS_MAX_ZRTP_CRYPTO_TYPES])1437 MsZrtpCryptoTypesCount linphone_core_get_zrtp_hash_suites(LinphoneCore *lc, MSZrtpHash hashes[MS_MAX_ZRTP_CRYPTO_TYPES]){
1438 	char * zrtpConfig = (char*)lp_config_get_string(lc->config, "sip", "zrtp_hash_suites", NULL);
1439 	MsZrtpCryptoTypesCount hash_count = 0;
1440 	char * entry, * origPtr;
1441 	if (zrtpConfig == NULL) {
1442         	return 0;
1443 	}
1444 
1445 	origPtr = ms_strdup(zrtpConfig);
1446 	zrtpConfig = origPtr;
1447 	while ((entry = seperate_string_list(&zrtpConfig))) {
1448 		const MSZrtpHash hash = ms_zrtp_hash_from_string(entry);
1449 		if (hash != MS_ZRTP_HASH_INVALID) {
1450 			ms_message("Configured zrtp hash: '%s'", ms_zrtp_hash_to_string(hash));
1451 			hashes[hash_count++] = hash;
1452 		}
1453 	}
1454 
1455 	ms_free(origPtr);
1456 	return hash_count;
1457 }
1458 
linphone_core_get_zrtp_auth_suites(LinphoneCore * lc,MSZrtpAuthTag authTags[MS_MAX_ZRTP_CRYPTO_TYPES])1459 MsZrtpCryptoTypesCount linphone_core_get_zrtp_auth_suites(LinphoneCore *lc, MSZrtpAuthTag authTags[MS_MAX_ZRTP_CRYPTO_TYPES]){
1460 	char * zrtpConfig = (char*)lp_config_get_string(lc->config, "sip", "zrtp_auth_suites", NULL);
1461 	MsZrtpCryptoTypesCount auth_tag_count = 0;
1462 	char * entry, * origPtr;
1463 	if (zrtpConfig == NULL) {
1464 		return 0;
1465 	}
1466 
1467 	origPtr = ms_strdup(zrtpConfig);
1468 	zrtpConfig = origPtr;
1469 	while ((entry = seperate_string_list(&zrtpConfig))) {
1470 		const MSZrtpAuthTag authTag = ms_zrtp_auth_tag_from_string(entry);
1471 		if (authTag != MS_ZRTP_AUTHTAG_INVALID) {
1472 			ms_message("Configured zrtp auth tag: '%s'", ms_zrtp_auth_tag_to_string(authTag));
1473 			authTags[auth_tag_count++] = authTag;
1474 		}
1475 	}
1476 
1477 	ms_free(origPtr);
1478 	return auth_tag_count;
1479 }
1480 
linphone_core_get_zrtp_sas_suites(LinphoneCore * lc,MSZrtpSasType sasTypes[MS_MAX_ZRTP_CRYPTO_TYPES])1481 MsZrtpCryptoTypesCount linphone_core_get_zrtp_sas_suites(LinphoneCore *lc, MSZrtpSasType sasTypes[MS_MAX_ZRTP_CRYPTO_TYPES]){
1482 	char * zrtpConfig = (char*)lp_config_get_string(lc->config, "sip", "zrtp_sas_suites", NULL);
1483 	MsZrtpCryptoTypesCount sas_count = 0;
1484 	char * entry, * origPtr;
1485 	if (zrtpConfig == NULL) {
1486 	        return 0;
1487 	}
1488 
1489 	origPtr = ms_strdup(zrtpConfig);
1490 	zrtpConfig = origPtr;
1491 	while ((entry = seperate_string_list(&zrtpConfig))) {
1492 		const MSZrtpSasType type = ms_zrtp_sas_type_from_string(entry);
1493 		if (type != MS_ZRTP_SAS_INVALID) {
1494 			ms_message("Configured zrtp SAS type: '%s'", ms_zrtp_sas_type_to_string(type));
1495 			sasTypes[sas_count++] = type;
1496 		}
1497 	}
1498 
1499 	ms_free(origPtr);
1500 	return sas_count;
1501 }
1502 
linphone_core_get_supported_file_formats(LinphoneCore * core)1503 const char ** linphone_core_get_supported_file_formats(LinphoneCore *core){
1504 	static const char *mkv="mkv";
1505 	static const char *wav="wav";
1506 	if (core->supported_formats==NULL){
1507 		core->supported_formats=ms_malloc0(3*sizeof(char*));
1508 		core->supported_formats[0]=wav;
1509         if (ms_factory_lookup_filter_by_id(core->factory,MS_MKV_RECORDER_ID)){
1510 			core->supported_formats[1]=mkv;
1511 		}
1512 	}
1513 	return core->supported_formats;
1514 }
1515 
linphone_core_file_format_supported(LinphoneCore * lc,const char * fmt)1516 bool_t linphone_core_file_format_supported(LinphoneCore *lc, const char *fmt){
1517 	const char **formats=linphone_core_get_supported_file_formats(lc);
1518 	for(;*formats!=NULL;++formats){
1519 		if (strcasecmp(*formats,fmt)==0) return TRUE;
1520 	}
1521 	return FALSE;
1522 }
1523 
linphone_core_symmetric_rtp_enabled(LinphoneCore * lc)1524 bool_t linphone_core_symmetric_rtp_enabled(LinphoneCore*lc){
1525 	/* Clients don't really need rtp symmetric, unless they have a public IP address and want
1526 	 * to interoperate with natted client. This case is not frequent with client apps.
1527 	 */
1528 	return lp_config_get_int(lc->config,"rtp","symmetric",0);
1529 }
1530 
linphone_core_set_network_simulator_params(LinphoneCore * lc,const OrtpNetworkSimulatorParams * params)1531 LinphoneStatus linphone_core_set_network_simulator_params(LinphoneCore *lc, const OrtpNetworkSimulatorParams *params){
1532 	if (params!=&lc->net_conf.netsim_params)
1533 		lc->net_conf.netsim_params=*params;
1534 	/*TODO: should we make some sanity checks on the parameters here*/
1535 	return 0;
1536 }
1537 
linphone_core_get_network_simulator_params(const LinphoneCore * lc)1538 const OrtpNetworkSimulatorParams *linphone_core_get_network_simulator_params(const LinphoneCore *lc){
1539 	return &lc->net_conf.netsim_params;
1540 }
1541 
1542 static const char *_tunnel_mode_str[3] = { "disable", "enable", "auto" };
1543 
linphone_tunnel_mode_from_string(const char * string)1544 LinphoneTunnelMode linphone_tunnel_mode_from_string(const char *string) {
1545 	if(string != NULL) {
1546 		int i;
1547 		for(i=0; i<3 && strcmp(string, _tunnel_mode_str[i]) != 0; i++);
1548 		if(i<3) {
1549 			return (LinphoneTunnelMode)i;
1550 		} else {
1551 			ms_error("Invalid tunnel mode '%s'", string);
1552 			return LinphoneTunnelModeDisable;
1553 		}
1554 	} else {
1555 		return LinphoneTunnelModeDisable;
1556 	}
1557 }
1558 
linphone_tunnel_mode_to_string(LinphoneTunnelMode mode)1559 const char *linphone_tunnel_mode_to_string(LinphoneTunnelMode mode) {
1560 	switch(mode){
1561 		case LinphoneTunnelModeAuto:
1562 			return "auto";
1563 		case LinphoneTunnelModeDisable:
1564 			return "disable";
1565 		case LinphoneTunnelModeEnable:
1566 			return "enable";
1567 	}
1568 	return "invalid";
1569 }
1570 
1571 
1572 typedef struct Hook{
1573 	LinphoneCoreIterateHook fun;
1574 	void *data;
1575 }Hook;
1576 
linphone_task_list_init(LinphoneTaskList * t)1577 void linphone_task_list_init(LinphoneTaskList *t){
1578 	t->hooks = NULL;
1579 }
1580 
hook_new(LinphoneCoreIterateHook hook,void * hook_data)1581 static Hook *hook_new(LinphoneCoreIterateHook hook, void *hook_data){
1582 	Hook *h=ms_new0(Hook,1);
1583 	h->fun=hook;
1584 	h->data=hook_data;
1585 	return h;
1586 }
1587 
hook_invoke(Hook * h)1588 static void hook_invoke(Hook *h){
1589 	h->fun(h->data);
1590 }
1591 
linphone_task_list_add(LinphoneTaskList * t,LinphoneCoreIterateHook hook,void * hook_data)1592 void linphone_task_list_add(LinphoneTaskList *t, LinphoneCoreIterateHook hook, void *hook_data){
1593 	t->hooks = bctbx_list_append(t->hooks,hook_new(hook,hook_data));
1594 }
1595 
linphone_task_list_remove(LinphoneTaskList * t,LinphoneCoreIterateHook hook,void * hook_data)1596 void linphone_task_list_remove(LinphoneTaskList *t, LinphoneCoreIterateHook hook, void *hook_data){
1597 	bctbx_list_t *elem;
1598 	for(elem=t->hooks;elem!=NULL;elem=elem->next){
1599 		Hook *h=(Hook*)elem->data;
1600 		if (h->fun==hook && h->data==hook_data){
1601 			t->hooks = bctbx_list_erase_link(t->hooks,elem);
1602 			ms_free(h);
1603 			return;
1604 		}
1605 	}
1606 	ms_error("linphone_task_list_remove(): No such hook found.");
1607 }
1608 
linphone_task_list_run(LinphoneTaskList * t)1609 void linphone_task_list_run(LinphoneTaskList *t){
1610 	bctbx_list_for_each(t->hooks,(void (*)(void*))hook_invoke);
1611 }
1612 
linphone_task_list_free(LinphoneTaskList * t)1613 void linphone_task_list_free(LinphoneTaskList *t){
1614 	t->hooks = bctbx_list_free_with_data(t->hooks, (void (*)(void*))ms_free);
1615 }
1616 
_ice_params_found_in_remote_media_description(IceSession * ice_session,const SalMediaDescription * md)1617 static bool_t _ice_params_found_in_remote_media_description(IceSession *ice_session, const SalMediaDescription *md) {
1618 	const SalStreamDescription *stream;
1619 	IceCheckList *cl = NULL;
1620 	int i;
1621 	bool_t ice_params_found = FALSE;
1622 	if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
1623 		ice_params_found=TRUE;
1624 	} else {
1625 		for (i = 0; i < md->nb_streams; i++) {
1626 			stream = &md->streams[i];
1627 			cl = ice_session_check_list(ice_session, i);
1628 			if (cl) {
1629 				if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
1630 					ice_params_found=TRUE;
1631 				} else {
1632 					ice_params_found=FALSE;
1633 					break;
1634 				}
1635 			}
1636 		}
1637 	}
1638 	return ice_params_found;
1639 }
1640 
_check_for_ice_restart_and_set_remote_credentials(IceSession * ice_session,const SalMediaDescription * md,bool_t is_offer)1641 static bool_t _check_for_ice_restart_and_set_remote_credentials(IceSession *ice_session, const SalMediaDescription *md, bool_t is_offer) {
1642 	const SalStreamDescription *stream;
1643 	IceCheckList *cl = NULL;
1644 	bool_t ice_restarted = FALSE;
1645 	int i;
1646 
1647 	if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
1648 		ice_session_restart(ice_session, is_offer ? IR_Controlled : IR_Controlling);
1649 		ice_restarted = TRUE;
1650 	} else {
1651 		for (i = 0; i < md->nb_streams; i++) {
1652 			stream = &md->streams[i];
1653 			cl = ice_session_check_list(ice_session, i);
1654 			if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
1655 				ice_session_restart(ice_session, is_offer ? IR_Controlled : IR_Controlling);
1656 				ice_restarted = TRUE;
1657 				break;
1658 			}
1659 		}
1660 	}
1661 	if ((ice_session_remote_ufrag(ice_session) == NULL) && (ice_session_remote_pwd(ice_session) == NULL)) {
1662 		ice_session_set_remote_credentials(ice_session, md->ice_ufrag, md->ice_pwd);
1663 	} else if (ice_session_remote_credentials_changed(ice_session, md->ice_ufrag, md->ice_pwd)) {
1664 		if (ice_restarted == FALSE) {
1665 			ice_session_restart(ice_session, is_offer ? IR_Controlled : IR_Controlling);
1666 			ice_restarted = TRUE;
1667 		}
1668 		ice_session_set_remote_credentials(ice_session, md->ice_ufrag, md->ice_pwd);
1669 	}
1670 	for (i = 0; i < md->nb_streams; i++) {
1671 		stream = &md->streams[i];
1672 		cl = ice_session_check_list(ice_session, i);
1673 		if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
1674 			if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) {
1675 				if (ice_restarted == FALSE
1676 						&& ice_check_list_get_remote_ufrag(cl)
1677 						&& ice_check_list_get_remote_pwd(cl)) {
1678 						/* restart only if remote ufrag/paswd was already set*/
1679 					ice_session_restart(ice_session, is_offer ? IR_Controlled : IR_Controlling);
1680 					ice_restarted = TRUE;
1681 				}
1682 				ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
1683 				break;
1684 			}
1685 		}
1686 	}
1687 	return ice_restarted;
1688 }
1689 
_create_ice_check_lists_and_parse_ice_attributes(LinphoneCall * call,const SalMediaDescription * md,bool_t ice_restarted)1690 static void _create_ice_check_lists_and_parse_ice_attributes(LinphoneCall *call, const SalMediaDescription *md, bool_t ice_restarted) {
1691 	const SalStreamDescription *stream;
1692 	IceCheckList *cl = NULL;
1693 	bool_t default_candidate = FALSE;
1694 	const char *addr = NULL;
1695 	int port = 0;
1696 	int componentID = 0;
1697 	int remote_family;
1698 	int family;
1699 	int i, j;
1700 
1701 	for (i = 0; i < md->nb_streams; i++) {
1702 		stream = &md->streams[i];
1703 		cl = ice_session_check_list(call->ice_session, i);
1704 
1705 		if (cl==NULL) continue;
1706 		if (stream->ice_mismatch == TRUE) {
1707 			ice_check_list_set_state(cl, ICL_Failed);
1708 			continue;
1709 		}
1710 		if (stream->rtp_port == 0) {
1711 			ice_session_remove_check_list(call->ice_session, cl);
1712 			clear_ice_check_list(call,cl);
1713 			continue;
1714 		}
1715 
1716 		if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
1717 			ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
1718 		for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
1719 			const SalIceCandidate *candidate = &stream->ice_candidates[j];
1720 			default_candidate = FALSE;
1721 			addr = NULL;
1722 			port = 0;
1723 			if (candidate->addr[0] == '\0') break;
1724 			if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
1725 			get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
1726 			if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0))
1727 				default_candidate = TRUE;
1728 			if (strchr(candidate->addr, ':') != NULL) family = AF_INET6;
1729 			else family = AF_INET;
1730 			ice_add_remote_candidate(cl, candidate->type, family, candidate->addr, candidate->port, candidate->componentID,
1731 				candidate->priority, candidate->foundation, default_candidate);
1732 		}
1733 		if (ice_restarted == FALSE) {
1734 			bool_t losing_pairs_added = FALSE;
1735 			for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
1736 				const SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[j];
1737 				addr = NULL;
1738 				port = 0;
1739 				componentID = j + 1;
1740 				if (remote_candidate->addr[0] == '\0') break;
1741 				get_default_addr_and_port(componentID, md, stream, &addr, &port);
1742 				if (j == 0) {
1743 					/* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */
1744 					ice_check_list_unselect_valid_pairs(cl);
1745 				}
1746 				if (strchr(remote_candidate->addr, ':') != NULL) remote_family = AF_INET6;
1747 				else remote_family = AF_INET;
1748 				if (strchr(addr, ':') != NULL) family = AF_INET6;
1749 				else family = AF_INET;
1750 
1751 				ice_add_losing_pair(cl, j + 1, remote_family, remote_candidate->addr, remote_candidate->port, family, addr, port);
1752 				losing_pairs_added = TRUE;
1753 			}
1754 			if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
1755 		}
1756 	}
1757 }
1758 
_update_ice_from_remote_media_description(LinphoneCall * call,const SalMediaDescription * md,bool_t is_offer)1759 static void _update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md, bool_t is_offer) {
1760 	const SalStreamDescription *stream;
1761 	IceCheckList *cl = NULL;
1762 	bool_t ice_restarted = FALSE;
1763 	int i;
1764 
1765 	/* Check for ICE restart and set remote credentials. */
1766 	ice_restarted = _check_for_ice_restart_and_set_remote_credentials(call->ice_session, md, is_offer);
1767 
1768 	/* Create ICE check lists if needed and parse ICE attributes. */
1769 	_create_ice_check_lists_and_parse_ice_attributes(call, md, ice_restarted);
1770 	for (i = 0; i < md->nb_streams; i++) {
1771 		stream = &md->streams[i];
1772 		cl = ice_session_check_list(call->ice_session, i);
1773 		if (!cl) continue;
1774 
1775 		if (!sal_stream_description_active(stream)) {
1776 			ice_session_remove_check_list_from_idx(call->ice_session, i);
1777 			clear_ice_check_list(call, cl);
1778 		}
1779 	}
1780 	linphone_call_clear_unused_ice_candidates(call, md);
1781 	ice_session_check_mismatch(call->ice_session);
1782 }
1783 
linphone_call_update_ice_from_remote_media_description(LinphoneCall * call,const SalMediaDescription * md,bool_t is_offer)1784 void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md, bool_t is_offer){
1785 	if (_ice_params_found_in_remote_media_description(call->ice_session, md) == TRUE) {
1786 		_update_ice_from_remote_media_description(call, md, is_offer);
1787 	} else {
1788 		/* Response from remote does not contain mandatory ICE attributes, delete the session. */
1789 		linphone_call_delete_ice_session(call);
1790 		linphone_call_set_symmetric_rtp(call, linphone_core_symmetric_rtp_enabled(linphone_call_get_core(call)));
1791 		return;
1792 	}
1793 	if (ice_session_nb_check_lists(call->ice_session) == 0) {
1794 		linphone_call_delete_ice_session(call);
1795 		linphone_call_set_symmetric_rtp(call, linphone_core_symmetric_rtp_enabled(linphone_call_get_core(call)));
1796 	}
1797 }
1798 
linphone_core_report_call_log(LinphoneCore * lc,LinphoneCallLog * call_log)1799 void linphone_core_report_call_log(LinphoneCore *lc, LinphoneCallLog *call_log){
1800 	bool_t call_logs_sqlite_db_found = FALSE;
1801 
1802 #ifdef SQLITE_STORAGE_ENABLED
1803 	if (lc->logs_db) {
1804 		call_logs_sqlite_db_found = TRUE;
1805 		linphone_core_store_call_log(lc, call_log);
1806 	}
1807 #endif
1808 	if (!call_logs_sqlite_db_found) {
1809 		lc->call_logs=bctbx_list_prepend(lc->call_logs,linphone_call_log_ref(call_log));
1810 		if (bctbx_list_size(lc->call_logs)>(size_t)lc->max_call_logs){
1811 			bctbx_list_t *elem,*prevelem=NULL;
1812 			/*find the last element*/
1813 			for(elem=lc->call_logs;elem!=NULL;elem=elem->next){
1814 				prevelem = elem;
1815 			}
1816 			elem = prevelem;
1817 			linphone_call_log_unref((LinphoneCallLog*)elem->data);
1818 			lc->call_logs = bctbx_list_erase_link(lc->call_logs,elem);
1819 		}
1820 		call_logs_write_to_config_file(lc);
1821 	}
1822 
1823 	linphone_core_notify_call_log_updated(lc,call_log);
1824 }
1825 
linphone_core_report_early_failed_call(LinphoneCore * lc,LinphoneCallDir dir,LinphoneAddress * from,LinphoneAddress * to,LinphoneErrorInfo * ei)1826 void linphone_core_report_early_failed_call(LinphoneCore *lc, LinphoneCallDir dir, LinphoneAddress *from, LinphoneAddress *to, LinphoneErrorInfo *ei){
1827 	LinphoneCallLog *l = linphone_call_log_new(dir, from, to);
1828 	l->error_info = ei;
1829 	l->status = LinphoneCallEarlyAborted;
1830 	linphone_core_report_call_log(lc, l);
1831 	linphone_call_log_unref(l);
1832 }
1833 
1834 /* Functions to mainpulate the LinphoneRange structure */
1835 
1836 BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneRange);
1837 
1838 BELLE_SIP_INSTANCIATE_VPTR(LinphoneRange, belle_sip_object_t,
1839 	NULL, // destroy
1840 	NULL, // clone
1841 	NULL, // marshal
1842 	FALSE
1843 );
1844 
linphone_range_new()1845 LinphoneRange *linphone_range_new() {
1846 	LinphoneRange *range = belle_sip_object_new(LinphoneRange);
1847 	range->min = 0;
1848 	range->max = 0;
1849 	return range;
1850 }
1851 
linphone_range_ref(LinphoneRange * range)1852 LinphoneRange* linphone_range_ref(LinphoneRange* range) {
1853 	return (LinphoneRange*) belle_sip_object_ref(range);
1854 }
1855 
linphone_range_unref(LinphoneRange * range)1856 void linphone_range_unref(LinphoneRange* range) {
1857 	belle_sip_object_unref(range);
1858 }
1859 
linphone_range_get_user_data(const LinphoneRange * range)1860 void *linphone_range_get_user_data(const LinphoneRange *range) {
1861 	return range->user_data;
1862 }
1863 
linphone_range_set_user_data(LinphoneRange * range,void * data)1864 void linphone_range_set_user_data(LinphoneRange *range, void *data) {
1865 	range->user_data = data;
1866 }
1867 
linphone_range_get_min(const LinphoneRange * range)1868 int linphone_range_get_min(const LinphoneRange *range) {
1869 	return range->min;
1870 }
1871 
linphone_range_get_max(const LinphoneRange * range)1872 int linphone_range_get_max(const LinphoneRange *range) {
1873 	return range->max;
1874 }
1875 
linphone_range_set_min(LinphoneRange * range,int min)1876 void linphone_range_set_min(LinphoneRange *range, int min) {
1877 	range->min = min;
1878 }
1879 
linphone_range_set_max(LinphoneRange * range,int max)1880 void linphone_range_set_max(LinphoneRange *range, int max) {
1881 	range->max = max;
1882 }
1883 
1884 
1885 
linphone_headers_ref(LinphoneHeaders * obj)1886 LinphoneHeaders * linphone_headers_ref(LinphoneHeaders *obj){
1887 	sal_custom_header_ref((SalCustomHeader*)obj);
1888 	return obj;
1889 }
1890 
1891 
linphone_headers_unref(LinphoneHeaders * obj)1892 void linphone_headers_unref(LinphoneHeaders *obj){
1893 	sal_custom_header_unref((SalCustomHeader*)obj);
1894 }
1895 
1896 
linphone_headers_get_value(LinphoneHeaders * obj,const char * header_name)1897 const char* linphone_headers_get_value(LinphoneHeaders *obj, const char *header_name){
1898 	return sal_custom_header_find((SalCustomHeader*)obj, header_name);
1899 }
1900 
linphone_headers_add(LinphoneHeaders * obj,const char * name,const char * value)1901 void linphone_headers_add(LinphoneHeaders *obj, const char *name, const char *value){
1902 	sal_custom_header_append((SalCustomHeader*)obj, name, value);
1903 }
1904 
linphone_headers_remove(LinphoneHeaders * obj,const char * name)1905 void linphone_headers_remove(LinphoneHeaders *obj, const char *name){
1906 	sal_custom_header_remove((SalCustomHeader*)obj, name);
1907 }
1908