]> sjero.net Git - linphone/blob - coreapi/misc.c
Merge commit 'a3f10cc'
[linphone] / coreapi / misc.c
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21 #include "private.h"
22 #include "lpconfig.h"
23 #include "mediastreamer2/mediastream.h"
24 #include <stdlib.h>
25 #include <stdio.h>
26 #ifdef HAVE_SIGHANDLER_T
27 #include <signal.h>
28 #endif /*HAVE_SIGHANDLER_T*/
29
30 #include <string.h>
31 #if !defined(_WIN32_WCE)
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #endif /*_WIN32_WCE*/
38
39 #undef snprintf
40 #include <ortp/stun.h>
41
42 #ifdef HAVE_GETIFADDRS
43 #include <net/if.h>
44 #include <ifaddrs.h>
45 #endif
46 #include <math.h>
47
48 #if !defined(WIN32)
49
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*/
53 int set_lock_file()
54 {
55         FILE *lockfile;
56
57         snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
58         lockfile=fopen(lock_name,"w");
59         if (lockfile==NULL)
60         {
61                 printf("Failed to create lock file.\n");
62                 return(-1);
63         }
64         fprintf(lockfile,"%i",getpid());
65         fclose(lockfile);
66         lock_set=1;
67         return(0);
68 }
69
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*/
71 int get_lock_file()
72 {
73         int pid;
74         FILE *lockfile;
75
76         snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
77         lockfile=fopen(lock_name,"r");
78         if (lockfile==NULL)
79                 return(-1);
80         if (fscanf(lockfile,"%i",&pid)!=1){
81                 ms_warning("Could not read pid in lock file.");
82                 fclose(lockfile);
83                 return -1;
84         }
85         fclose(lockfile);
86         return pid;
87 }
88
89 /* remove the lock file if it was set*/
90 int remove_lock_file()
91 {
92         int err=0;
93         if (lock_set)
94         {
95                 err=unlink(lock_name);
96                 lock_set=0;
97         }
98         return(err);
99 }
100
101 #endif
102
103 char *int2str(int number)
104 {
105         char *numstr=ms_malloc(10);
106         snprintf(numstr,10,"%i",number);
107         return numstr;
108 }
109
110 void check_sound_device(LinphoneCore *lc)
111 {
112 #ifdef _linux
113         int fd=0;
114         int len;
115         int a;
116         char *file=NULL;
117         char *i810_audio=NULL;
118         char *snd_pcm_oss=NULL;
119         char *snd_mixer_oss=NULL;
120         char *snd_pcm=NULL;
121         fd=open("/proc/modules",O_RDONLY);
122
123         if (fd>0){
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));
127                 len=statbuf.st_size;
128                 if (len==0) ms_warning("/proc/modules has zero size!");
129                 */
130                 /***** fstat does not work on /proc/modules for unknown reason *****/
131                 len=6000;
132                 file=ms_malloc(len+1);
133                 a=read(fd,file,len);
134                 if (a<len) file=ms_realloc(file,a+1);
135                 file[a]='\0';
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."));*/
141                         goto end;
142                 }
143                 snd_pcm=strstr(file,"snd-pcm");
144                 if (snd_pcm!=NULL){
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."));
149                         }
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."));
152                         }
153                 }
154         }else {
155
156                 ms_warning("Could not open /proc/modules.");
157         }
158         /* now check general volume. Some user forget to rise it and then complain that linphone is
159         not working */
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);
163                 if (a<50){
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);
166                 }
167         }
168         */
169         end:
170         if (file!=NULL) ms_free(file);
171         if (fd>0) close(fd);
172 #endif
173 }
174
175 #define UDP_HDR_SZ 8
176 #define RTP_HDR_SZ 12
177 #define IP4_HDR_SZ 20   /*20 is the minimum, but there may be some options*/
178
179 static void payload_type_set_enable(PayloadType *pt,int value)
180 {
181         if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
182         else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
183 }
184
185 static bool_t payload_type_enabled(const PayloadType *pt) {
186         return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
187 }
188
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);
192         }
193         ms_error("Getting enablement status of codec not in audio or video list of PayloadType !");
194         return FALSE;
195 }
196
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);
201                 return 0;
202         }
203         ms_error("Enabling codec not in audio or video list of PayloadType !");
204         return -1;
205 }
206
207 int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){
208        return payload_type_get_number(pt);
209 }
210
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);
214 #ifdef ENABLE_NLS
215                 return dgettext("mediastreamer",desc->text);
216 #else
217                 return desc->text;
218 #endif
219         }
220         return NULL;
221 }
222
223
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){
231                         return 15000;
232                 }
233         }
234         return pt->normal_bitrate;
235 }
236
237 /*
238  *((codec-birate*ptime/8) + RTP header + UDP header + IP header)*8/ptime;
239  *ptime=1/npacket
240  */
241 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
242         double npacket=50;
243         double packet_size;
244         int bitrate;
245         if (strcmp(payload_type_get_mime(&payload_type_aaceld_44k), payload_type_get_mime(pt))==0) {
246                 /*special case of aac 44K because ptime= 10ms*/
247                 npacket=100;
248         }
249                 
250         bitrate=get_codec_bitrate(lc,pt);
251         packet_size= (((double)bitrate)/(npacket*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
252         return packet_size*8.0*npacket;
253 }
254
255 void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
256         call->audio_bw=(int)(ceil(get_audio_payload_bandwidth(call->core,pt)/1000.0)); /*rounding codec bandwidth should be avoid, specially for AMR*/
257         ms_message("Audio bandwidth for this call is %i",call->audio_bw);
258 }
259
260 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
261         const MSList *elem;
262         PayloadType *max=NULL;
263         for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
264                 PayloadType *pt=(PayloadType*)elem->data;
265                 if (payload_type_enabled(pt)){
266                         int pt_bitrate=get_codec_bitrate(lc,pt);
267                         if (max==NULL) max=pt;
268                         else if (max->normal_bitrate<pt_bitrate){
269                                 max=pt;
270                         }
271                 }
272         }
273         if (max) {
274                 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
275         }
276 }
277
278 bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt,  int bandwidth_limit)
279 {
280         double codec_band;
281         bool_t ret=FALSE;
282         
283         switch (pt->type){
284                 case PAYLOAD_AUDIO_CONTINUOUS:
285                 case PAYLOAD_AUDIO_PACKETIZED:
286                         codec_band=get_audio_payload_bandwidth(lc,pt);
287                         ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
288                         /*hack to avoid using uwb codecs when having low bitrate and video*/
289                         if (bandwidth_is_greater(199,bandwidth_limit)){
290                                 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
291                                         ret=FALSE;
292                                 }
293                         }
294                         //ms_message("Payload %s: %g",pt->mime_type,codec_band);
295                         break;
296                 case PAYLOAD_VIDEO:
297                         if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
298                                 ret=TRUE;
299                         }
300                         else ret=FALSE;
301                         break;
302         }
303         return ret;
304 }
305
306 /* return TRUE if codec can be used with bandwidth, FALSE else*/
307 bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
308 {
309         double codec_band;
310         int allowed_bw,video_bw;
311         bool_t ret=FALSE;
312
313         linphone_core_update_allocated_audio_bandwidth(lc);
314         allowed_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
315                                         linphone_core_get_upload_bandwidth(lc));
316         if (allowed_bw==0) {
317                 allowed_bw=-1;
318                 video_bw=1500; /*around 1.5 Mbit/s*/
319         }else
320                 video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
321
322         switch (pt->type){
323                 case PAYLOAD_AUDIO_CONTINUOUS:
324                 case PAYLOAD_AUDIO_PACKETIZED:
325                         codec_band=get_audio_payload_bandwidth(lc,pt);
326                         ret=bandwidth_is_greater(allowed_bw*1000,codec_band);
327                         /*hack to avoid using uwb codecs when having low bitrate and video*/
328                         if (bandwidth_is_greater(199,allowed_bw)){
329                                 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
330                                         ret=FALSE;
331                                 }
332                         }
333                         //ms_message("Payload %s: %g",pt->mime_type,codec_band);
334                         break;
335                 case PAYLOAD_VIDEO:
336                         if (video_bw>0){
337                                 pt->normal_bitrate=video_bw*1000;
338                                 ret=TRUE;
339                         }
340                         else ret=FALSE;
341                         break;
342         }
343         return ret;
344 }
345
346 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
347 #if !defined(_WIN32_WCE)
348         FILE *f=popen(command,"r");
349         if (f!=NULL){
350                 int err;
351                 *result=ms_malloc(4096);
352                 err=fread(*result,1,4096-1,f);
353                 if (err<0){
354                         ms_warning("Error reading command output:%s",strerror(errno));
355                         ms_free(result);
356                         return FALSE;
357                 }
358                 (*result)[err]=0;
359                 err=pclose(f);
360                 if (command_ret!=NULL) *command_ret=err;
361                 return TRUE;
362         }
363 #endif /*_WIN32_WCE*/
364         return FALSE;
365 }
366
367 static ortp_socket_t create_socket(int local_port){
368         struct sockaddr_in laddr;
369         ortp_socket_t sock;
370         int optval;
371         sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
372         if (sock<0) {
373                 ms_error("Fail to create socket");
374                 return -1;
375         }
376         memset (&laddr,0,sizeof(laddr));
377         laddr.sin_family=AF_INET;
378         laddr.sin_addr.s_addr=INADDR_ANY;
379         laddr.sin_port=htons(local_port);
380         if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
381                 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
382                 close_socket(sock);
383                 return -1;
384         }
385         optval=1;
386         if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
387                                 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
388                 ms_warning("Fail to set SO_REUSEADDR");
389         }
390         set_non_blocking_socket(sock);
391         return sock;
392 }
393
394 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
395         char buf[STUN_MAX_MESSAGE_SIZE];
396         int len = STUN_MAX_MESSAGE_SIZE;
397         StunAtrString username;
398         StunAtrString password;
399         StunMessage req;
400         int err;
401         memset(&req, 0, sizeof(StunMessage));
402         memset(&username,0,sizeof(username));
403         memset(&password,0,sizeof(password));
404         stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
405         len = stunEncodeMessage( &req, buf, len, &password);
406         if (len<=0){
407                 ms_error("Fail to encode stun message.");
408                 return -1;
409         }
410         err=sendto(sock,buf,len,0,server,addrlen);
411         if (err<0){
412                 ms_error("sendto failed: %s",strerror(errno));
413                 return -1;
414         }
415         return 0;
416 }
417
418 int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
419         struct addrinfo hints,*res=NULL;
420         int family = PF_INET;
421         int port_int = 3478;
422         int ret;
423         char port[6];
424         char host[NI_MAXHOST];
425         char *p1, *p2;
426         if ((sscanf(server, "[%64[^]]]:%d", host, &port_int) == 2) || (sscanf(server, "[%64[^]]]", host) == 1)) {
427                 family = PF_INET6;
428         } else {
429                 p1 = strchr(server, ':');
430                 p2 = strrchr(server, ':');
431                 if (p1 && p2 && (p1 != p2)) {
432                         family = PF_INET6;
433                         host[NI_MAXHOST-1]='\0';
434                         strncpy(host, server, sizeof(host) - 1);
435                 } else if (sscanf(server, "%[^:]:%d", host, &port_int) != 2) {
436                         host[NI_MAXHOST-1]='\0';
437                         strncpy(host, server, sizeof(host) - 1);
438                 }
439         }
440         snprintf(port, sizeof(port), "%d", port_int);
441         memset(&hints,0,sizeof(hints));
442         hints.ai_family=family;
443         hints.ai_socktype=SOCK_DGRAM;
444         hints.ai_protocol=IPPROTO_UDP;
445         ret=getaddrinfo(host,port,&hints,&res);
446         if (ret!=0){
447                 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
448                 return -1;
449         }
450         if (!res) return -1;
451         memcpy(ss,res->ai_addr,res->ai_addrlen);
452         *socklen=res->ai_addrlen;
453         freeaddrinfo(res);
454         return 0;
455 }
456
457 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
458         char buf[STUN_MAX_MESSAGE_SIZE];
459         int len = STUN_MAX_MESSAGE_SIZE;
460         StunMessage resp;
461         len=recv(sock,buf,len,0);
462         if (len>0){
463                 struct in_addr ia;
464                 stunParseMessage(buf,len, &resp );
465                 *id=resp.msgHdr.tr_id.octet[0];
466                 if (resp.hasXorMappedAddress){
467                         *port = resp.xorMappedAddress.ipv4.port;
468                         ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
469                 }else if (resp.hasMappedAddress){
470                         *port = resp.mappedAddress.ipv4.port;
471                         ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
472                 }else return -1;
473                 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
474         }
475         return len;
476 }
477
478 /* this functions runs a simple stun test and return the number of milliseconds to complete the tests, or -1 if the test were failed.*/
479 int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
480         const char *server=linphone_core_get_stun_server(lc);
481         StunCandidate *ac=&call->ac;
482         StunCandidate *vc=&call->vc;
483         
484         if (lc->sip_conf.ipv6_enabled){
485                 ms_warning("stun support is not implemented for ipv6");
486                 return -1;
487         }
488         if (server!=NULL){
489                 struct sockaddr_storage ss;
490                 socklen_t ss_len;
491                 ortp_socket_t sock1=-1, sock2=-1;
492                 int loops=0;
493                 bool_t video_enabled=linphone_core_video_enabled(lc);
494                 bool_t got_audio,got_video;
495                 bool_t cone_audio=FALSE,cone_video=FALSE;
496                 struct timeval init,cur;
497                 double elapsed;
498                 int ret=0;
499                 
500                 if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
501                         ms_error("Fail to parser stun server address: %s",server);
502                         return -1;
503                 }
504                 if (lc->vtable.display_status!=NULL)
505                         lc->vtable.display_status(lc,_("Stun lookup in progress..."));
506
507                 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
508                 sock1=create_socket(call->audio_port);
509                 if (sock1==-1) return -1;
510                 if (video_enabled){
511                         sock2=create_socket(call->video_port);
512                         if (sock2==-1) return -1;
513                 }
514                 got_audio=FALSE;
515                 got_video=FALSE;
516                 gettimeofday(&init,NULL);
517                 do{
518                         
519                         int id;
520                         if (loops%20==0){
521                                 ms_message("Sending stun requests...");
522                                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
523                                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
524                                 if (sock2!=-1){
525                                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
526                                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
527                                 }
528                         }
529 #ifdef WIN32
530                         Sleep(10);
531 #else
532                         usleep(10000);
533 #endif
534
535                         if (recvStunResponse(sock1,ac->addr,
536                                                 &ac->port,&id)>0){
537                                 ms_message("STUN test result: local audio port maps to %s:%i",
538                                                 ac->addr,
539                                                 ac->port);
540                                 if (id==11)
541                                         cone_audio=TRUE;
542                                 got_audio=TRUE;
543                         }
544                         if (recvStunResponse(sock2,vc->addr,
545                                                         &vc->port,&id)>0){
546                                 ms_message("STUN test result: local video port maps to %s:%i",
547                                         vc->addr,
548                                         vc->port);
549                                 if (id==22)
550                                         cone_video=TRUE;
551                                 got_video=TRUE;
552                         }
553                         gettimeofday(&cur,NULL);
554                         elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
555                         if (elapsed>2000)  {
556                                 ms_message("Stun responses timeout, going ahead.");
557                                 ret=-1;
558                                 break;
559                         }
560                         loops++;
561                 }while(!(got_audio && (got_video||sock2==-1)  ) );
562                 if (ret==0) ret=(int)elapsed;
563                 if (!got_audio){
564                         ms_error("No stun server response for audio port.");
565                 }else{
566                         if (!cone_audio) {
567                                 ms_message("NAT is symmetric for audio port");
568                         }
569                 }
570                 if (sock2!=-1){
571                         if (!got_video){
572                                 ms_error("No stun server response for video port.");
573                         }else{
574                                 if (!cone_video) {
575                                         ms_message("NAT is symmetric for video port.");
576                                 }
577                         }
578                 }
579                 close_socket(sock1);
580                 if (sock2!=-1) close_socket(sock2);
581                 return ret;
582         }
583         return -1;
584 }
585
586 int linphone_core_get_edge_bw(LinphoneCore *lc){
587         int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
588         return edge_bw;
589 }
590
591 int linphone_core_get_edge_ptime(LinphoneCore *lc){
592         int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
593         return edge_ptime;
594 }
595
596 void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
597         if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
598                 ms_message("Stun server ping time is %i ms",ping_time_ms);
599                 int threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
600                 
601                 if (ping_time_ms>threshold){
602                         /* we might be in a 2G network*/
603                         params->low_bandwidth=TRUE;
604                 }/*else use default settings */
605         }
606         if (params->low_bandwidth){
607                 params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc);
608                 params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc);
609                 params->has_video=FALSE;
610         }
611 }
612
613
614
615 int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
616 {
617         char local_addr[64];
618         struct sockaddr_storage ss;
619         socklen_t ss_len;
620         IceCheckList *audio_check_list;
621         IceCheckList *video_check_list;
622         const char *server = linphone_core_get_stun_server(lc);
623
624         if ((server == NULL) || (call->ice_session == NULL)) return -1;
625         audio_check_list = ice_session_check_list(call->ice_session, 0);
626         video_check_list = ice_session_check_list(call->ice_session, 1);
627         if (audio_check_list == NULL) return -1;
628
629         if (lc->sip_conf.ipv6_enabled){
630                 ms_warning("stun support is not implemented for ipv6");
631                 return -1;
632         }
633
634         if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) {
635                 ms_error("Fail to parser stun server address: %s", server);
636                 return -1;
637         }
638         if (lc->vtable.display_status != NULL)
639                 lc->vtable.display_status(lc, _("ICE local candidates gathering in progress..."));
640
641         /* Gather local host candidates. */
642         if (linphone_core_get_local_ip_for(AF_INET, server, local_addr) < 0) {
643                 ms_error("Fail to get local ip");
644                 return -1;
645         }
646         if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) {
647                 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL);
648                 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL);
649                 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
650         }
651         if (call->params.has_video && (video_check_list != NULL)
652                 && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) {
653                 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL);
654                 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL);
655                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
656         }
657
658         ms_message("ICE: gathering candidate from [%s]",server);
659         /* Gather local srflx candidates. */
660         ice_session_gather_candidates(call->ice_session, ss, ss_len);
661         return 0;
662 }
663
664 void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
665 {
666         IceCheckList *audio_check_list;
667         IceCheckList *video_check_list;
668         IceSessionState session_state;
669
670         if (call->ice_session == NULL) return;
671         audio_check_list = ice_session_check_list(call->ice_session, 0);
672         video_check_list = ice_session_check_list(call->ice_session, 1);
673         if (audio_check_list == NULL) return;
674
675         session_state = ice_session_state(call->ice_session);
676         if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
677                 if (ice_check_list_state(audio_check_list) == ICL_Completed) {
678                         switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
679                                 case ICT_HostCandidate:
680                                         call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
681                                         break;
682                                 case ICT_ServerReflexiveCandidate:
683                                 case ICT_PeerReflexiveCandidate:
684                                         call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
685                                         break;
686                                 case ICT_RelayedCandidate:
687                                         call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
688                                         break;
689                         }
690                 } else {
691                         call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
692                 }
693                 if (call->params.has_video && (video_check_list != NULL)) {
694                         if (ice_check_list_state(video_check_list) == ICL_Completed) {
695                                 switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
696                                         case ICT_HostCandidate:
697                                                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection;
698                                                 break;
699                                         case ICT_ServerReflexiveCandidate:
700                                         case ICT_PeerReflexiveCandidate:
701                                                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
702                                                 break;
703                                         case ICT_RelayedCandidate:
704                                                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
705                                                 break;
706                                 }
707                         } else {
708                                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
709                         }
710                 }
711         } else if (session_state == IS_Running) {
712                 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
713                 if (call->params.has_video && (video_check_list != NULL)) {
714                         call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
715                 }
716         } else {
717                 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
718                 if (call->params.has_video && (video_check_list != NULL)) {
719                         call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
720                 }
721         }
722 }
723
724 void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
725 {
726         const char *rtp_addr, *rtcp_addr;
727         IceSessionState session_state = ice_session_state(session);
728         int nb_candidates;
729         int i, j;
730         bool_t result;
731
732         if (session_state == IS_Completed) {
733                 desc->ice_completed = TRUE;
734                 result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL);
735                 if (result == TRUE) {
736                         strncpy(desc->addr, rtp_addr, sizeof(desc->addr));
737                 } else {
738                         ms_warning("If ICE has completed successfully, rtp_addr should be set!");
739                 }
740         }
741         else {
742                 desc->ice_completed = FALSE;
743         }
744         strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd));
745         strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag));
746         for (i = 0; i < desc->n_active_streams; i++) {
747                 SalStreamDescription *stream = &desc->streams[i];
748                 IceCheckList *cl = ice_session_check_list(session, i);
749                 nb_candidates = 0;
750                 if (cl == NULL) continue;
751                 if (ice_check_list_state(cl) == ICL_Completed) {
752                         stream->ice_completed = TRUE;
753                         result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
754                 } else {
755                         stream->ice_completed = FALSE;
756                         result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
757                 }
758                 if (result == TRUE) {
759                         strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
760                         strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
761                 } else {
762                         memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
763                         memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
764                 }
765                 if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd)))
766                         strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd));
767                 else
768                         memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
769                 if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag)))
770                         strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag));
771                 else
772                         memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
773                 stream->ice_mismatch = ice_check_list_is_mismatch(cl);
774                 if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
775                         memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
776                         for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
777                                 SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
778                                 IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j);
779                                 const char *default_addr = NULL;
780                                 int default_port = 0;
781                                 if (ice_candidate->componentID == 1) {
782                                         default_addr = stream->rtp_addr;
783                                         default_port = stream->rtp_port;
784                                 } else if (ice_candidate->componentID == 2) {
785                                         default_addr = stream->rtcp_addr;
786                                         default_port = stream->rtcp_port;
787                                 } else continue;
788                                 if (default_addr[0] == '\0') default_addr = desc->addr;
789                                 /* 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. */
790                                 if ((ice_check_list_state(cl) == ICL_Completed)
791                                         && !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0)))
792                                         continue;
793                                 strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation));
794                                 sal_candidate->componentID = ice_candidate->componentID;
795                                 sal_candidate->priority = ice_candidate->priority;
796                                 strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type));
797                                 strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr));
798                                 sal_candidate->port = ice_candidate->taddr.port;
799                                 if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) {
800                                         strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr));
801                                         sal_candidate->rport = ice_candidate->base->taddr.port;
802                                 }
803                                 nb_candidates++;
804                         }
805                 }
806                 if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
807                         int rtp_port, rtcp_port;
808                         memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
809                         if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) {
810                                 strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr));
811                                 stream->ice_remote_candidates[0].port = rtp_port;
812                                 strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr));
813                                 stream->ice_remote_candidates[1].port = rtcp_port;
814                         } else {
815                                 ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
816                         }
817                 } else {
818                         for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
819                                 stream->ice_remote_candidates[j].addr[0] = '\0';
820                                 stream->ice_remote_candidates[j].port = 0;
821                         }
822                 }
823         }
824 }
825
826 static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
827 {
828         if (componentID == 1) {
829                 *addr = stream->rtp_addr;
830                 *port = stream->rtp_port;
831         } else if (componentID == 2) {
832                 *addr = stream->rtcp_addr;
833                 *port = stream->rtcp_port;
834         } else return;
835         if ((*addr)[0] == '\0') *addr = md->addr;
836 }
837
838 void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
839 {
840         bool_t ice_restarted = FALSE;
841
842         if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
843                 int i, j;
844
845                 /* Check for ICE restart and set remote credentials. */
846                 if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
847                         ice_session_restart(call->ice_session);
848                         ice_restarted = TRUE;
849                 } else {
850                         for (i = 0; i < md->n_total_streams; i++) {
851                                 const SalStreamDescription *stream = &md->streams[i];
852                                 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
853                                 if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
854                                         ice_session_restart(call->ice_session);
855                                         ice_restarted = TRUE;
856                                         break;
857                                 }
858                         }
859                 }
860                 if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) {
861                         ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
862                 } else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) {
863                         if (ice_restarted == FALSE) {
864                                 ice_session_restart(call->ice_session);
865                                 ice_restarted = TRUE;
866                         }
867                         ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
868                 }
869                 for (i = 0; i < md->n_total_streams; i++) {
870                         const SalStreamDescription *stream = &md->streams[i];
871                         IceCheckList *cl = ice_session_check_list(call->ice_session, i);
872                         if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
873                                 if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) {
874                                         if (ice_restarted == FALSE) {
875                                                 ice_session_restart(call->ice_session);
876                                                 ice_restarted = TRUE;
877                                         }
878                                         ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
879                                         break;
880                                 }
881                         }
882                 }
883
884                 /* Create ICE check lists if needed and parse ICE attributes. */
885                 for (i = 0; i < md->n_total_streams; i++) {
886                         const SalStreamDescription *stream = &md->streams[i];
887                         IceCheckList *cl = ice_session_check_list(call->ice_session, i);
888                         if ((cl == NULL) && (i < md->n_active_streams)) {
889                                 cl = ice_check_list_new();
890                                 ice_session_add_check_list(call->ice_session, cl);
891                                 switch (stream->type) {
892                                         case SalAudio:
893                                                 if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = cl;
894                                                 break;
895                                         case SalVideo:
896                                                 if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl;
897                                                 break;
898                                         default:
899                                                 break;
900                                 }
901                         }
902                         if (stream->ice_mismatch == TRUE) {
903                                 ice_check_list_set_state(cl, ICL_Failed);
904                         } else if (stream->rtp_port == 0) {
905                                 ice_session_remove_check_list(call->ice_session, cl);
906                         } else {
907                                 if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
908                                         ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
909                                 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
910                                         const SalIceCandidate *candidate = &stream->ice_candidates[j];
911                                         bool_t default_candidate = FALSE;
912                                         const char *addr = NULL;
913                                         int port = 0;
914                                         if (candidate->addr[0] == '\0') break;
915                                         if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
916                                         get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
917                                         if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0))
918                                                 default_candidate = TRUE;
919                                         ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID,
920                                                 candidate->priority, candidate->foundation, default_candidate);
921                                 }
922                                 if (ice_restarted == FALSE) {
923                                         bool_t losing_pairs_added = FALSE;
924                                         for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
925                                                 const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j];
926                                                 const char *addr = NULL;
927                                                 int port = 0;
928                                                 int componentID = j + 1;
929                                                 if (candidate->addr[0] == '\0') break;
930                                                 get_default_addr_and_port(componentID, md, stream, &addr, &port);
931                                                 if (j == 0) {
932                                                         /* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */
933                                                         ice_check_list_unselect_valid_pairs(cl);
934                                                 }
935                                                 ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
936                                                 losing_pairs_added = TRUE;
937                                         }
938                                         if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
939                                 }
940                         }
941                 }
942                 for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) {
943                         ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1));
944                 }
945                 ice_session_check_mismatch(call->ice_session);
946         } else {
947                 /* Response from remote does not contain mandatory ICE attributes, delete the session. */
948                 linphone_call_delete_ice_session(call);
949                 return;
950         }
951         if (ice_session_nb_check_lists(call->ice_session) == 0) {
952                 linphone_call_delete_ice_session(call);
953         }
954 }
955
956 bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md)
957 {
958         int i;
959
960         for (i = 0; i < md->n_active_streams; i++) {
961                 if (md->streams[i].type == SalVideo)
962                         return TRUE;
963         }
964         return FALSE;
965 }
966
967 LinphoneCall * is_a_linphone_call(void *user_pointer){
968         LinphoneCall *call=(LinphoneCall*)user_pointer;
969         if (call==NULL) return NULL;
970         return call->magic==linphone_call_magic ? call : NULL;
971 }
972
973 LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer){
974         LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)user_pointer;
975         if (cfg==NULL) return NULL;
976         return cfg->magic==linphone_proxy_config_magic ? cfg : NULL;
977 }
978
979 unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
980         unsigned int ret=0;
981         const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
982         if (features){
983                 char tmp[256]={0};
984                 char name[256];
985                 char *p,*n;
986                 strncpy(tmp,features,sizeof(tmp)-1);
987                 for(p=tmp;*p!='\0';p++){
988                         if (*p==' ') continue;
989                         n=strchr(p,'|');
990                         if (n) *n='\0';
991                         sscanf(p,"%s",name);
992                         ms_message("Found audio feature %s",name);
993                         if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
994                         else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
995                         else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
996                         else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
997                         else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
998                         else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
999                         else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
1000                         else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1001                         else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
1002                         else if (strcasecmp(name,"NONE")==0) ret=0;
1003                         else ms_error("Unsupported audio feature %s requested in config file.",name);
1004                         if (!n) break;
1005                         p=n;
1006                 }
1007         }else ret=AUDIO_STREAM_FEATURE_ALL;
1008         
1009         if (ret==AUDIO_STREAM_FEATURE_ALL){
1010                 /*since call recording is specified before creation of the stream in linphonecore,
1011                 * it will be requested on demand. It is not necessary to include it all the time*/
1012                 ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1013         }
1014         return ret;
1015 }
1016
1017 bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc){
1018         return lp_config_get_int(lc->config,"sound","tone_indications",1);
1019 }
1020
1021 #ifdef HAVE_GETIFADDRS
1022
1023 #include <ifaddrs.h>
1024 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
1025 {
1026         struct ifaddrs *ifp;
1027         struct ifaddrs *ifpstart;
1028         int ret = 0;
1029
1030         if (getifaddrs(&ifpstart) < 0) {
1031                 return -1;
1032         }
1033 #ifndef __linux
1034         #define UP_FLAG IFF_UP /* interface is up */
1035 #else
1036         #define UP_FLAG IFF_RUNNING /* resources allocated */
1037 #endif
1038         
1039         for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
1040                 if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
1041                         && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK))
1042                 {
1043                         if(getnameinfo(ifp->ifa_addr,
1044                                                 (type == AF_INET6) ?
1045                                                 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
1046                                                 address, size, NULL, 0, NI_NUMERICHOST) == 0) {
1047                                 if (strchr(address, '%') == NULL) {     /*avoid ipv6 link-local addresses */
1048                                         /*ms_message("getifaddrs() found %s",address);*/
1049                                         ret++;
1050                                         break;
1051                                 }
1052                         }
1053                 }
1054         }
1055         freeifaddrs(ifpstart);
1056         return ret;
1057 }
1058 #endif
1059
1060
1061 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
1062         int err,tmp;
1063         struct addrinfo hints;
1064         struct addrinfo *res=NULL;
1065         struct sockaddr_storage addr;
1066         struct sockaddr *p_addr=(struct sockaddr*)&addr;
1067         ortp_socket_t sock;
1068         socklen_t s;
1069
1070         memset(&hints,0,sizeof(hints));
1071         hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
1072         hints.ai_socktype=SOCK_DGRAM;
1073         /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
1074         err=getaddrinfo(dest,"5060",&hints,&res);
1075         if (err!=0){
1076                 ms_error("getaddrinfo() error: %s",gai_strerror(err));
1077                 return -1;
1078         }
1079         if (res==NULL){
1080                 ms_error("bug: getaddrinfo returned nothing.");
1081                 return -1;
1082         }
1083         sock=socket(res->ai_family,SOCK_DGRAM,0);
1084         tmp=1;
1085         err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
1086         if (err<0){
1087                 ms_warning("Error in setsockopt: %s",strerror(errno));
1088         }
1089         err=connect(sock,res->ai_addr,res->ai_addrlen);
1090         if (err<0) {
1091                 ms_error("Error in connect: %s",strerror(errno));
1092                 freeaddrinfo(res);
1093                 close_socket(sock);
1094                 return -1;
1095         }
1096         freeaddrinfo(res);
1097         res=NULL;
1098         s=sizeof(addr);
1099         err=getsockname(sock,(struct sockaddr*)&addr,&s);
1100         if (err!=0) {
1101                 ms_error("Error in getsockname: %s",strerror(errno));
1102                 close_socket(sock);
1103                 return -1;
1104         }
1105         if (p_addr->sa_family==AF_INET){
1106                 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
1107                 if (p_sin->sin_addr.s_addr==0){
1108                         close_socket(sock);
1109                         return -1;
1110                 }
1111         }
1112         err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
1113         if (err!=0){
1114                 ms_error("getnameinfo error: %s",strerror(errno));
1115         }
1116         close_socket(sock);
1117         ms_message("Local interface to reach %s is %s.",dest,result);
1118         return 0;
1119 }
1120
1121 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
1122         int err;
1123         strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
1124         
1125         if (dest==NULL){
1126                 if (type==AF_INET)
1127                         dest="87.98.157.38"; /*a public IP address*/
1128                 else dest="2a00:1450:8002::68";
1129         }
1130         err=get_local_ip_for_with_connect(type,dest,result);
1131         if (err==0) return 0;
1132         
1133         /* if the connect method failed, which happens when no default route is set, 
1134          * try to find 'the' running interface with getifaddrs*/
1135         
1136 #ifdef HAVE_GETIFADDRS
1137
1138         /*we use getifaddrs for lookup of default interface */
1139         int found_ifs;
1140
1141         found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
1142         if (found_ifs==1){
1143                 return 0;
1144         }else if (found_ifs<=0){
1145                 /*absolutely no network on this machine */
1146                 return -1;
1147         }
1148 #endif
1149       return 0;  
1150 }
1151
1152 #ifndef WIN32
1153 #include <resolv.h>
1154
1155
1156
1157 void _linphone_core_configure_resolver(){
1158 /*bionic declares _res but does not define nor export it !!*/
1159 #ifdef ANDROID
1160         /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
1161         setenv("RES_OPTIONS","timeout:2 attempts:2 retrans:2 retry:2",1);
1162 #else
1163         res_init();
1164         _res.retrans=2; /*retransmit every two seconds*/
1165         _res.retry=2; /*only two times per DNS server*/
1166 #endif
1167 }
1168
1169 #else
1170
1171 void _linphone_core_configure_resolver(){
1172 }
1173
1174 #endif