4 Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org)
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "mediastreamer2/mediastream.h"
26 #ifdef HAVE_SIGHANDLER_T
28 #endif /*HAVE_SIGHANDLER_T*/
31 #if !defined(_WIN32_WCE)
33 #include <sys/types.h>
40 #include <ortp/stun.h>
42 #ifdef HAVE_GETIFADDRS
50 static char lock_name[80];
51 static char lock_set=0;
52 /* put a lock file in /tmp. this is called when linphone runs as a daemon*/
57 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
58 lockfile=fopen(lock_name,"w");
61 printf("Failed to create lock file.\n");
64 fprintf(lockfile,"%i",getpid());
70 /* looks if there is a lock file. If presents return its content (the pid of the already running linphone), if not found, returns -1*/
76 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
77 lockfile=fopen(lock_name,"r");
80 if (fscanf(lockfile,"%i",&pid)!=1){
81 ms_warning("Could not read pid in lock file.");
89 /* remove the lock file if it was set*/
90 int remove_lock_file()
95 err=unlink(lock_name);
103 char *int2str(int number)
105 char *numstr=ms_malloc(10);
106 snprintf(numstr,10,"%i",number);
110 void check_sound_device(LinphoneCore *lc)
117 char *i810_audio=NULL;
118 char *snd_pcm_oss=NULL;
119 char *snd_mixer_oss=NULL;
121 fd=open("/proc/modules",O_RDONLY);
124 /* read the entire /proc/modules file and check if sound conf seems correct */
125 /*a=fstat(fd,&statbuf);
126 if (a<0) ms_warning("Can't stat /proc/modules:%s.",strerror(errno));
128 if (len==0) ms_warning("/proc/modules has zero size!");
130 /***** fstat does not work on /proc/modules for unknown reason *****/
132 file=ms_malloc(len+1);
134 if (a<len) file=ms_realloc(file,a+1);
136 i810_audio=strstr(file,"i810_audio");
137 if (i810_audio!=NULL){
138 /* I'm sorry i put this warning in comments because
139 * i don't use yet the right driver !! */
140 /* lc->vtable.display_warning(lc,_("You are currently using the i810_audio driver.\nThis driver is buggy and so does not work with Linphone.\nWe suggest that you replace it by its equivalent ALSA driver,\neither with packages from your distribution, or by downloading\nALSA drivers at http://www.alsa-project.org."));*/
143 snd_pcm=strstr(file,"snd-pcm");
145 snd_pcm_oss=strstr(file,"snd-pcm-oss");
146 snd_mixer_oss=strstr(file,"snd-mixer-oss");
147 if (snd_pcm_oss==NULL){
148 lc->vtable.display_warning(lc,_("Your computer appears to be using ALSA sound drivers.\nThis is the best choice. However the pcm oss emulation module\nis missing and linphone needs it. Please execute\n'modprobe snd-pcm-oss' as root to load it."));
150 if (snd_mixer_oss==NULL){
151 lc->vtable.display_warning(lc,_("Your computer appears to be using ALSA sound drivers.\nThis is the best choice. However the mixer oss emulation module\nis missing and linphone needs it. Please execute\n 'modprobe snd-mixer-oss' as root to load it."));
156 ms_warning("Could not open /proc/modules.");
158 /* now check general volume. Some user forget to rise it and then complain that linphone is
160 /* but some other users complain that linphone should not change levels...
161 if (lc->sound_conf.sndcard!=NULL){
162 a=snd_card_get_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL);
164 ms_warning("General level is quite low (%i). Linphone rises it up for you.",a);
165 snd_card_set_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL,80);
170 if (file!=NULL) ms_free(file);
176 #define RTP_HDR_SZ 12
177 #define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/
179 static void payload_type_set_enable(PayloadType *pt,int value)
181 if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
182 else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
185 static bool_t payload_type_enabled(const PayloadType *pt) {
186 return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
189 bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const PayloadType *pt){
190 if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)pt)){
191 return payload_type_enabled(pt);
193 ms_error("Getting enablement status of codec not in audio or video list of PayloadType !");
197 int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadType *pt, bool_t enabled){
198 if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){
199 payload_type_set_enable(pt,enabled);
200 _linphone_core_codec_config_write(lc);
203 ms_error("Enabling codec not in audio or video list of PayloadType !");
207 int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){
208 return payload_type_get_number(pt);
211 const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){
212 if (ms_filter_codec_supported(pt->mime_type)){
213 MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type);
215 return dgettext("mediastreamer",desc->text);
224 /*this function makes a special case for speex/8000.
225 This codec is variable bitrate. The 8kbit/s mode is interesting when having a low upload bandwidth, but its quality
226 is not very good. We 'd better use its 15kbt/s mode when we have enough bandwidth*/
227 static int get_codec_bitrate(LinphoneCore *lc, const PayloadType *pt){
228 int upload_bw=linphone_core_get_upload_bandwidth(lc);
229 if (bandwidth_is_greater(upload_bw,129) || (bandwidth_is_greater(upload_bw,33) && !linphone_core_video_enabled(lc)) ) {
230 if (strcmp(pt->mime_type,"speex")==0 && pt->clock_rate==8000){
234 return pt->normal_bitrate;
237 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
241 bitrate=get_codec_bitrate(lc,pt);
242 packet_size= (((double)bitrate)/(50*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
243 return packet_size*8.0*npacket;
246 void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
247 call->audio_bw=(int)(get_audio_payload_bandwidth(call->core,pt)/1000.0);
248 ms_message("Audio bandwidth for this call is %i",call->audio_bw);
251 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
253 PayloadType *max=NULL;
254 for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
255 PayloadType *pt=(PayloadType*)elem->data;
256 if (payload_type_enabled(pt)){
257 int pt_bitrate=get_codec_bitrate(lc,pt);
258 if (max==NULL) max=pt;
259 else if (max->normal_bitrate<pt_bitrate){
265 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
269 bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit)
275 case PAYLOAD_AUDIO_CONTINUOUS:
276 case PAYLOAD_AUDIO_PACKETIZED:
277 codec_band=get_audio_payload_bandwidth(lc,pt);
278 ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
279 /*hack to avoid using uwb codecs when having low bitrate and video*/
280 if (bandwidth_is_greater(199,bandwidth_limit)){
281 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
285 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
288 if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
297 /* return TRUE if codec can be used with bandwidth, FALSE else*/
298 bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
301 int allowed_bw,video_bw;
304 linphone_core_update_allocated_audio_bandwidth(lc);
305 allowed_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
306 linphone_core_get_upload_bandwidth(lc));
309 video_bw=1500; /*around 1.5 Mbit/s*/
311 video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
314 case PAYLOAD_AUDIO_CONTINUOUS:
315 case PAYLOAD_AUDIO_PACKETIZED:
316 codec_band=get_audio_payload_bandwidth(lc,pt);
317 ret=bandwidth_is_greater(allowed_bw*1000,codec_band);
318 /*hack to avoid using uwb codecs when having low bitrate and video*/
319 if (bandwidth_is_greater(199,allowed_bw)){
320 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
324 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
328 pt->normal_bitrate=video_bw*1000;
337 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
338 #if !defined(_WIN32_WCE)
339 FILE *f=popen(command,"r");
342 *result=ms_malloc(4096);
343 err=fread(*result,1,4096-1,f);
345 ms_warning("Error reading command output:%s",strerror(errno));
351 if (command_ret!=NULL) *command_ret=err;
354 #endif /*_WIN32_WCE*/
358 static ortp_socket_t create_socket(int local_port){
359 struct sockaddr_in laddr;
362 sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
364 ms_error("Fail to create socket");
367 memset (&laddr,0,sizeof(laddr));
368 laddr.sin_family=AF_INET;
369 laddr.sin_addr.s_addr=INADDR_ANY;
370 laddr.sin_port=htons(local_port);
371 if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
372 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
377 if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
378 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
379 ms_warning("Fail to set SO_REUSEADDR");
381 set_non_blocking_socket(sock);
385 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
386 char buf[STUN_MAX_MESSAGE_SIZE];
387 int len = STUN_MAX_MESSAGE_SIZE;
388 StunAtrString username;
389 StunAtrString password;
392 memset(&req, 0, sizeof(StunMessage));
393 memset(&username,0,sizeof(username));
394 memset(&password,0,sizeof(password));
395 stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
396 len = stunEncodeMessage( &req, buf, len, &password);
398 ms_error("Fail to encode stun message.");
401 err=sendto(sock,buf,len,0,server,addrlen);
403 ms_error("sendto failed: %s",strerror(errno));
409 int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
410 struct addrinfo hints,*res=NULL;
411 int family = PF_INET;
415 char host[NI_MAXHOST];
417 if ((sscanf(server, "[%64[^]]]:%d", host, &port_int) == 2) || (sscanf(server, "[%64[^]]]", host) == 1)) {
420 p1 = strchr(server, ':');
421 p2 = strrchr(server, ':');
422 if (p1 && p2 && (p1 != p2)) {
424 host[NI_MAXHOST-1]='\0';
425 strncpy(host, server, sizeof(host) - 1);
426 } else if (sscanf(server, "%[^:]:%d", host, &port_int) != 2) {
427 host[NI_MAXHOST-1]='\0';
428 strncpy(host, server, sizeof(host) - 1);
431 snprintf(port, sizeof(port), "%d", port_int);
432 memset(&hints,0,sizeof(hints));
433 hints.ai_family=family;
434 hints.ai_socktype=SOCK_DGRAM;
435 hints.ai_protocol=IPPROTO_UDP;
436 ret=getaddrinfo(host,port,&hints,&res);
438 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
442 memcpy(ss,res->ai_addr,res->ai_addrlen);
443 *socklen=res->ai_addrlen;
448 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
449 char buf[STUN_MAX_MESSAGE_SIZE];
450 int len = STUN_MAX_MESSAGE_SIZE;
452 len=recv(sock,buf,len,0);
455 stunParseMessage(buf,len, &resp );
456 *id=resp.msgHdr.tr_id.octet[0];
457 if (resp.hasXorMappedAddress){
458 *port = resp.xorMappedAddress.ipv4.port;
459 ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
460 }else if (resp.hasMappedAddress){
461 *port = resp.mappedAddress.ipv4.port;
462 ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
464 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
469 /* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
470 int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call, StunCandidate *ac, StunCandidate *vc){
471 const char *server=linphone_core_get_stun_server(lc);
473 if (lc->sip_conf.ipv6_enabled){
474 ms_warning("stun support is not implemented for ipv6");
478 struct sockaddr_storage ss;
480 ortp_socket_t sock1=-1, sock2=-1;
482 bool_t video_enabled=linphone_core_video_enabled(lc);
483 bool_t got_audio,got_video;
484 bool_t cone_audio=FALSE,cone_video=FALSE;
485 struct timeval init,cur;
489 if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
490 ms_error("Fail to parser stun server address: %s",server);
493 if (lc->vtable.display_status!=NULL)
494 lc->vtable.display_status(lc,_("Stun lookup in progress..."));
496 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
497 sock1=create_socket(call->audio_port);
498 if (sock1==-1) return -1;
500 sock2=create_socket(call->video_port);
501 if (sock2==-1) return -1;
505 gettimeofday(&init,NULL);
510 ms_message("Sending stun requests...");
511 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
512 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
514 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
515 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
524 if (recvStunResponse(sock1,ac->addr,
526 ms_message("STUN test result: local audio port maps to %s:%i",
533 if (recvStunResponse(sock2,vc->addr,
535 ms_message("STUN test result: local video port maps to %s:%i",
542 gettimeofday(&cur,NULL);
543 elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0);
545 ms_message("Stun responses timeout, going ahead.");
550 }while(!(got_audio && (got_video||sock2==-1) ) );
551 if (ret==0) ret=(int)elapsed;
553 ms_error("No stun server response for audio port.");
556 ms_message("NAT is symmetric for audio port");
561 ms_error("No stun server response for video port.");
564 ms_message("NAT is symmetric for video port.");
569 if (sock2!=-1) close_socket(sock2);
575 void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
576 if (lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
577 ms_message("Stun server ping time is %i ms",ping_time_ms);
578 int threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
580 if (ping_time_ms>threshold){
581 int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
582 int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
583 /* we are in a 2G network*/
584 params->up_bw=params->down_bw=edge_bw;
585 params->up_ptime=params->down_ptime=edge_ptime;
586 params->has_video=FALSE;
588 }/*else use default settings */
594 int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
597 struct sockaddr_storage ss;
599 IceCheckList *audio_check_list;
600 IceCheckList *video_check_list;
601 const char *server = linphone_core_get_stun_server(lc);
603 if ((server == NULL) || (call->ice_session == NULL)) return -1;
604 audio_check_list = ice_session_check_list(call->ice_session, 0);
605 video_check_list = ice_session_check_list(call->ice_session, 1);
606 if (audio_check_list == NULL) return -1;
608 if (lc->sip_conf.ipv6_enabled){
609 ms_warning("stun support is not implemented for ipv6");
613 if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) {
614 ms_error("Fail to parser stun server address: %s", server);
617 if (lc->vtable.display_status != NULL)
618 lc->vtable.display_status(lc, _("ICE local candidates gathering in progress..."));
620 /* Gather local host candidates. */
621 if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) {
622 ms_error("Fail to get local ip");
625 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL);
626 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL);
627 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
628 if (call->params.has_video && (video_check_list != NULL)) {
629 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL);
630 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL);
631 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
634 ms_message("ICE: gathering candidate from [%s]",server);
635 /* Gather local srflx candidates. */
636 ice_session_gather_candidates(call->ice_session, ss, ss_len);
640 void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
642 IceCheckList *audio_check_list;
643 IceCheckList *video_check_list;
644 IceSessionState session_state;
646 if (call->ice_session == NULL) return;
647 audio_check_list = ice_session_check_list(call->ice_session, 0);
648 video_check_list = ice_session_check_list(call->ice_session, 1);
649 if (audio_check_list == NULL) return;
651 session_state = ice_session_state(call->ice_session);
652 if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
653 switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
654 case ICT_HostCandidate:
655 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
657 case ICT_ServerReflexiveCandidate:
658 case ICT_PeerReflexiveCandidate:
659 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
661 case ICT_RelayedCandidate:
662 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
665 if (call->params.has_video && (video_check_list != NULL)) {
666 switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
667 case ICT_HostCandidate:
668 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection;
670 case ICT_ServerReflexiveCandidate:
671 case ICT_PeerReflexiveCandidate:
672 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
674 case ICT_RelayedCandidate:
675 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
680 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
681 if (call->params.has_video && (video_check_list != NULL)) {
682 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
687 void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
689 const char *rtp_addr, *rtcp_addr;
690 IceSessionState session_state = ice_session_state(session);
695 if (session_state == IS_Completed) {
696 desc->ice_completed = TRUE;
697 result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL);
698 if (result == TRUE) {
699 strncpy(desc->addr, rtp_addr, sizeof(desc->addr));
701 ms_warning("If ICE has completed successfully, rtp_addr should be set!");
705 desc->ice_completed = FALSE;
707 strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd));
708 strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag));
709 for (i = 0; i < desc->nstreams; i++) {
710 SalStreamDescription *stream = &desc->streams[i];
711 IceCheckList *cl = ice_session_check_list(session, i);
713 if (cl == NULL) continue;
714 if (ice_check_list_state(cl) == ICL_Completed) {
715 stream->ice_completed = TRUE;
716 result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
718 stream->ice_completed = FALSE;
719 result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
721 if (result == TRUE) {
722 strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
723 strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
725 memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
726 memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
728 if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd)))
729 strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd));
731 memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
732 if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag)))
733 strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag));
735 memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
736 stream->ice_mismatch = ice_check_list_is_mismatch(cl);
737 if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
738 memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
739 for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
740 SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
741 IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j);
742 const char *default_addr = NULL;
743 int default_port = 0;
744 if (ice_candidate->componentID == 1) {
745 default_addr = stream->rtp_addr;
746 default_port = stream->rtp_port;
747 } else if (ice_candidate->componentID == 2) {
748 default_addr = stream->rtcp_addr;
749 default_port = stream->rtcp_port;
751 if (default_addr[0] == '\0') default_addr = desc->addr;
752 /* 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. */
753 if ((ice_check_list_state(cl) == ICL_Completed)
754 && !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0)))
756 strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation));
757 sal_candidate->componentID = ice_candidate->componentID;
758 sal_candidate->priority = ice_candidate->priority;
759 strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type));
760 strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr));
761 sal_candidate->port = ice_candidate->taddr.port;
762 if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) {
763 strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr));
764 sal_candidate->rport = ice_candidate->base->taddr.port;
769 if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
770 int rtp_port, rtcp_port;
771 memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
772 ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port);
773 strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr));
774 stream->ice_remote_candidates[0].port = rtp_port;
775 strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr));
776 stream->ice_remote_candidates[1].port = rtcp_port;
781 static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
783 if (componentID == 1) {
784 *addr = stream->rtp_addr;
785 *port = stream->rtp_port;
786 } else if (componentID == 2) {
787 *addr = stream->rtcp_addr;
788 *port = stream->rtcp_port;
790 if ((*addr)[0] == '\0') *addr = md->addr;
793 void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
795 bool_t ice_restarted = FALSE;
797 if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
800 /* Check for ICE restart and set remote credentials. */
801 if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
802 ice_session_restart(call->ice_session);
803 ice_restarted = TRUE;
805 for (i = 0; i < md->nstreams; i++) {
806 const SalStreamDescription *stream = &md->streams[i];
807 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
808 if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
809 ice_session_restart(call->ice_session);
810 ice_restarted = TRUE;
815 if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) {
816 ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
817 } else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) {
818 if (ice_restarted == FALSE) {
819 ice_session_restart(call->ice_session);
820 ice_restarted = TRUE;
822 ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
824 for (i = 0; i < md->nstreams; i++) {
825 const SalStreamDescription *stream = &md->streams[i];
826 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
827 if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
828 if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) {
829 if (ice_restarted == FALSE) {
830 ice_session_restart(call->ice_session);
831 ice_restarted = TRUE;
833 ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
839 /* Create ICE check lists if needed and parse ICE attributes. */
840 for (i = 0; i < md->nstreams; i++) {
841 const SalStreamDescription *stream = &md->streams[i];
842 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
844 cl = ice_check_list_new();
845 ice_session_add_check_list(call->ice_session, cl);
846 switch (stream->type) {
848 if (call->audiostream != NULL) call->audiostream->ice_check_list = cl;
851 if (call->videostream != NULL) call->videostream->ice_check_list = cl;
857 if ((stream->ice_mismatch == TRUE) || (stream->rtp_port == 0)) {
858 ice_check_list_set_state(cl, ICL_Failed);
860 if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
861 ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
862 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
863 const SalIceCandidate *candidate = &stream->ice_candidates[j];
864 bool_t default_candidate = FALSE;
865 const char *addr = NULL;
867 if (candidate->addr[0] == '\0') break;
868 if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
869 get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
870 if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0))
871 default_candidate = TRUE;
872 ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID,
873 candidate->priority, candidate->foundation, default_candidate);
875 if (ice_restarted == FALSE) {
876 bool_t losing_pairs_added = FALSE;
877 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
878 const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j];
879 const char *addr = NULL;
881 int componentID = j + 1;
882 if (candidate->addr[0] == '\0') break;
883 get_default_addr_and_port(componentID, md, stream, &addr, &port);
884 ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
885 losing_pairs_added = TRUE;
887 if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
891 for (i = ice_session_nb_check_lists(call->ice_session); i > md->nstreams; i--) {
892 ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1));
894 ice_session_check_mismatch(call->ice_session);
896 /* Response from remote does not contain mandatory ICE attributes, delete the session. */
897 linphone_call_delete_ice_session(call);
900 if (ice_session_nb_check_lists(call->ice_session) == 0) {
901 linphone_call_delete_ice_session(call);
905 void linphone_core_deactivate_ice_for_deactivated_media_streams(LinphoneCall *call, const SalMediaDescription *md)
908 for (i = 0; i < md->nstreams; i++) {
909 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
910 if (cl && (md->streams[i].rtp_port == 0)) {
911 if (ice_check_list_state(cl) != ICL_Completed) ice_check_list_set_state(cl, ICL_Failed);
916 LinphoneCall * is_a_linphone_call(void *user_pointer){
917 LinphoneCall *call=(LinphoneCall*)user_pointer;
918 if (call==NULL) return NULL;
919 return call->magic==linphone_call_magic ? call : NULL;
922 LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer){
923 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)user_pointer;
924 if (cfg==NULL) return NULL;
925 return cfg->magic==linphone_proxy_config_magic ? cfg : NULL;
928 unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
930 const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
935 strncpy(tmp,features,sizeof(tmp)-1);
936 for(p=tmp;*p!='\0';p++){
937 if (*p==' ') continue;
941 ms_message("Found audio feature %s",name);
942 if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
943 else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
944 else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
945 else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
946 else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
947 else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
948 else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
949 else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
950 else if (strcasecmp(name,"NONE")==0) ret=0;
951 else ms_error("Unsupported audio feature %s requested in config file.",name);
955 }else ret=AUDIO_STREAM_FEATURE_ALL;
960 #ifdef HAVE_GETIFADDRS
963 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
966 struct ifaddrs *ifpstart;
969 if (getifaddrs(&ifpstart) < 0) {
973 for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
974 if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
975 && (ifp->ifa_flags & IFF_RUNNING) && !(ifp->ifa_flags & IFF_LOOPBACK))
977 getnameinfo(ifp->ifa_addr,
979 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
980 address, size, NULL, 0, NI_NUMERICHOST);
981 if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */
982 /*ms_message("getifaddrs() found %s",address);*/
988 freeifaddrs(ifpstart);
994 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
996 struct addrinfo hints;
997 struct addrinfo *res=NULL;
998 struct sockaddr_storage addr;
999 struct sockaddr *p_addr=(struct sockaddr*)&addr;
1003 memset(&hints,0,sizeof(hints));
1004 hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
1005 hints.ai_socktype=SOCK_DGRAM;
1006 /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
1007 err=getaddrinfo(dest,"5060",&hints,&res);
1009 ms_error("getaddrinfo() error: %s",gai_strerror(err));
1013 ms_error("bug: getaddrinfo returned nothing.");
1016 sock=socket(res->ai_family,SOCK_DGRAM,0);
1018 err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
1020 ms_warning("Error in setsockopt: %s",strerror(errno));
1022 err=connect(sock,res->ai_addr,res->ai_addrlen);
1024 ms_error("Error in connect: %s",strerror(errno));
1032 err=getsockname(sock,(struct sockaddr*)&addr,&s);
1034 ms_error("Error in getsockname: %s",strerror(errno));
1038 if (p_addr->sa_family==AF_INET){
1039 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
1040 if (p_sin->sin_addr.s_addr==0){
1045 err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
1047 ms_error("getnameinfo error: %s",strerror(errno));
1050 ms_message("Local interface to reach %s is %s.",dest,result);
1054 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
1055 strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
1056 #ifdef HAVE_GETIFADDRS
1058 /*we use getifaddrs for lookup of default interface */
1061 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
1064 }else if (found_ifs<=0){
1065 /*absolutely no network on this machine */
1070 /*else use connect to find the best local ip address */
1072 dest="87.98.157.38"; /*a public IP address*/
1073 else dest="2a00:1450:8002::68";
1074 return get_local_ip_for_with_connect(type,dest,result);
1082 void _linphone_core_configure_resolver(){
1083 /*bionic declares _res but does not define nor export it !!*/
1085 /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
1086 setenv("RES_OPTIONS","timeout:2 attempts:2 retrans:2 retry:2",1);
1089 _res.retrans=2; /*retransmit every two seconds*/
1090 _res.retry=2; /*only two times per DNS server*/
1096 void _linphone_core_configure_resolver(){