]> sjero.net Git - linphone/blob - coreapi/misc.c
Add check to prevent crash.
[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
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 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
238         double npacket=50;
239         double packet_size;
240         int bitrate;
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;
244 }
245
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);
249 }
250
251 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
252         const MSList *elem;
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){
260                                 max=pt;
261                         }
262                 }
263         }
264         if (max) {
265                 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
266         }
267 }
268
269 bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt,  int bandwidth_limit)
270 {
271         double codec_band;
272         bool_t ret=FALSE;
273         
274         switch (pt->type){
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){
282                                         ret=FALSE;
283                                 }
284                         }
285                         //ms_message("Payload %s: %g",pt->mime_type,codec_band);
286                         break;
287                 case PAYLOAD_VIDEO:
288                         if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
289                                 ret=TRUE;
290                         }
291                         else ret=FALSE;
292                         break;
293         }
294         return ret;
295 }
296
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)
299 {
300         double codec_band;
301         int allowed_bw,video_bw;
302         bool_t ret=FALSE;
303
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));
307         if (allowed_bw==0) {
308                 allowed_bw=-1;
309                 video_bw=1500; /*around 1.5 Mbit/s*/
310         }else
311                 video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
312
313         switch (pt->type){
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){
321                                         ret=FALSE;
322                                 }
323                         }
324                         //ms_message("Payload %s: %g",pt->mime_type,codec_band);
325                         break;
326                 case PAYLOAD_VIDEO:
327                         if (video_bw>0){
328                                 pt->normal_bitrate=video_bw*1000;
329                                 ret=TRUE;
330                         }
331                         else ret=FALSE;
332                         break;
333         }
334         return ret;
335 }
336
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");
340         if (f!=NULL){
341                 int err;
342                 *result=ms_malloc(4096);
343                 err=fread(*result,1,4096-1,f);
344                 if (err<0){
345                         ms_warning("Error reading command output:%s",strerror(errno));
346                         ms_free(result);
347                         return FALSE;
348                 }
349                 (*result)[err]=0;
350                 err=pclose(f);
351                 if (command_ret!=NULL) *command_ret=err;
352                 return TRUE;
353         }
354 #endif /*_WIN32_WCE*/
355         return FALSE;
356 }
357
358 static ortp_socket_t create_socket(int local_port){
359         struct sockaddr_in laddr;
360         ortp_socket_t sock;
361         int optval;
362         sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
363         if (sock<0) {
364                 ms_error("Fail to create socket");
365                 return -1;
366         }
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());
373                 close_socket(sock);
374                 return -1;
375         }
376         optval=1;
377         if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
378                                 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
379                 ms_warning("Fail to set SO_REUSEADDR");
380         }
381         set_non_blocking_socket(sock);
382         return sock;
383 }
384
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;
390         StunMessage req;
391         int err;
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);
397         if (len<=0){
398                 ms_error("Fail to encode stun message.");
399                 return -1;
400         }
401         err=sendto(sock,buf,len,0,server,addrlen);
402         if (err<0){
403                 ms_error("sendto failed: %s",strerror(errno));
404                 return -1;
405         }
406         return 0;
407 }
408
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;
412         int port_int = 3478;
413         int ret;
414         char port[6];
415         char host[NI_MAXHOST];
416         char *p1, *p2;
417         if ((sscanf(server, "[%64[^]]]:%d", host, &port_int) == 2) || (sscanf(server, "[%64[^]]]", host) == 1)) {
418                 family = PF_INET6;
419         } else {
420                 p1 = strchr(server, ':');
421                 p2 = strrchr(server, ':');
422                 if (p1 && p2 && (p1 != p2)) {
423                         family = PF_INET6;
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);
429                 }
430         }
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);
437         if (ret!=0){
438                 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
439                 return -1;
440         }
441         if (!res) return -1;
442         memcpy(ss,res->ai_addr,res->ai_addrlen);
443         *socklen=res->ai_addrlen;
444         freeaddrinfo(res);
445         return 0;
446 }
447
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;
451         StunMessage resp;
452         len=recv(sock,buf,len,0);
453         if (len>0){
454                 struct in_addr ia;
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);
463                 }else return -1;
464                 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
465         }
466         return len;
467 }
468
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);
472         
473         if (lc->sip_conf.ipv6_enabled){
474                 ms_warning("stun support is not implemented for ipv6");
475                 return -1;
476         }
477         if (server!=NULL){
478                 struct sockaddr_storage ss;
479                 socklen_t ss_len;
480                 ortp_socket_t sock1=-1, sock2=-1;
481                 int loops=0;
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;
486                 double elapsed;
487                 int ret=0;
488                 
489                 if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
490                         ms_error("Fail to parser stun server address: %s",server);
491                         return -1;
492                 }
493                 if (lc->vtable.display_status!=NULL)
494                         lc->vtable.display_status(lc,_("Stun lookup in progress..."));
495
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;
499                 if (video_enabled){
500                         sock2=create_socket(call->video_port);
501                         if (sock2==-1) return -1;
502                 }
503                 got_audio=FALSE;
504                 got_video=FALSE;
505                 gettimeofday(&init,NULL);
506                 do{
507                         
508                         int id;
509                         if (loops%20==0){
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);
513                                 if (sock2!=-1){
514                                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
515                                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
516                                 }
517                         }
518 #ifdef WIN32
519                         Sleep(10);
520 #else
521                         usleep(10000);
522 #endif
523
524                         if (recvStunResponse(sock1,ac->addr,
525                                                 &ac->port,&id)>0){
526                                 ms_message("STUN test result: local audio port maps to %s:%i",
527                                                 ac->addr,
528                                                 ac->port);
529                                 if (id==11)
530                                         cone_audio=TRUE;
531                                 got_audio=TRUE;
532                         }
533                         if (recvStunResponse(sock2,vc->addr,
534                                                         &vc->port,&id)>0){
535                                 ms_message("STUN test result: local video port maps to %s:%i",
536                                         vc->addr,
537                                         vc->port);
538                                 if (id==22)
539                                         cone_video=TRUE;
540                                 got_video=TRUE;
541                         }
542                         gettimeofday(&cur,NULL);
543                         elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
544                         if (elapsed>2000)  {
545                                 ms_message("Stun responses timeout, going ahead.");
546                                 ret=-1;
547                                 break;
548                         }
549                         loops++;
550                 }while(!(got_audio && (got_video||sock2==-1)  ) );
551                 if (ret==0) ret=(int)elapsed;
552                 if (!got_audio){
553                         ms_error("No stun server response for audio port.");
554                 }else{
555                         if (!cone_audio) {
556                                 ms_message("NAT is symmetric for audio port");
557                         }
558                 }
559                 if (sock2!=-1){
560                         if (!got_video){
561                                 ms_error("No stun server response for video port.");
562                         }else{
563                                 if (!cone_video) {
564                                         ms_message("NAT is symmetric for video port.");
565                                 }
566                         }
567                 }
568                 close_socket(sock1);
569                 if (sock2!=-1) close_socket(sock2);
570                 return ret;
571         }
572         return -1;
573 }
574
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);
579                 
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;
587                         
588                 }/*else use default settings */
589         }
590 }
591
592
593
594 int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
595 {
596         char local_addr[64];
597         struct sockaddr_storage ss;
598         socklen_t ss_len;
599         IceCheckList *audio_check_list;
600         IceCheckList *video_check_list;
601         const char *server = linphone_core_get_stun_server(lc);
602
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;
607
608         if (lc->sip_conf.ipv6_enabled){
609                 ms_warning("stun support is not implemented for ipv6");
610                 return -1;
611         }
612
613         if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) {
614                 ms_error("Fail to parser stun server address: %s", server);
615                 return -1;
616         }
617         if (lc->vtable.display_status != NULL)
618                 lc->vtable.display_status(lc, _("ICE local candidates gathering in progress..."));
619
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");
623                 return -1;
624         }
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;
632         }
633
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);
637         return 0;
638 }
639
640 void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
641 {
642         IceCheckList *audio_check_list;
643         IceCheckList *video_check_list;
644         IceSessionState session_state;
645
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;
650
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;
656                                 break;
657                         case ICT_ServerReflexiveCandidate:
658                         case ICT_PeerReflexiveCandidate:
659                                 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
660                                 break;
661                         case ICT_RelayedCandidate:
662                                 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
663                                 break;
664                 }
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;
669                                         break;
670                                 case ICT_ServerReflexiveCandidate:
671                                 case ICT_PeerReflexiveCandidate:
672                                         call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
673                                         break;
674                                 case ICT_RelayedCandidate:
675                                         call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
676                                         break;
677                         }
678                 }
679         } else {
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;
683                 }
684         }
685 }
686
687 void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
688 {
689         const char *rtp_addr, *rtcp_addr;
690         IceSessionState session_state = ice_session_state(session);
691         int nb_candidates;
692         int i, j;
693         bool_t result;
694
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));
700                 } else {
701                         ms_warning("If ICE has completed successfully, rtp_addr should be set!");
702                 }
703         }
704         else {
705                 desc->ice_completed = FALSE;
706         }
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);
712                 nb_candidates = 0;
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);
717                 } else {
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);
720                 }
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));
724                 } else {
725                         memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
726                         memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
727                 }
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));
730                 else
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));
734                 else
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;
750                                 } else continue;
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)))
755                                         continue;
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;
765                                 }
766                                 nb_candidates++;
767                         }
768                 }
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;
777                 }
778         }
779 }
780
781 static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
782 {
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;
789         } else return;
790         if ((*addr)[0] == '\0') *addr = md->addr;
791 }
792
793 void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
794 {
795         bool_t ice_restarted = FALSE;
796
797         if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
798                 int i, j;
799
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;
804                 } else {
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;
811                                         break;
812                                 }
813                         }
814                 }
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;
821                         }
822                         ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
823                 }
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;
832                                         }
833                                         ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
834                                         break;
835                                 }
836                         }
837                 }
838
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);
843                         if (cl == NULL) {
844                                 cl = ice_check_list_new();
845                                 ice_session_add_check_list(call->ice_session, cl);
846                                 switch (stream->type) {
847                                         case SalAudio:
848                                                 if (call->audiostream != NULL) call->audiostream->ice_check_list = cl;
849                                                 break;
850                                         case SalVideo:
851                                                 if (call->videostream != NULL) call->videostream->ice_check_list = cl;
852                                                 break;
853                                         default:
854                                                 break;
855                                 }
856                         }
857                         if ((stream->ice_mismatch == TRUE) || (stream->rtp_port == 0)) {
858                                 ice_check_list_set_state(cl, ICL_Failed);
859                         } else {
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;
866                                         int port = 0;
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);
874                                 }
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;
880                                                 int port = 0;
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;
886                                         }
887                                         if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
888                                 }
889                         }
890                 }
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));
893                 }
894                 ice_session_check_mismatch(call->ice_session);
895         } else {
896                 /* Response from remote does not contain mandatory ICE attributes, delete the session. */
897                 linphone_call_delete_ice_session(call);
898                 return;
899         }
900         if (ice_session_nb_check_lists(call->ice_session) == 0) {
901                 linphone_call_delete_ice_session(call);
902         }
903 }
904
905 void linphone_core_deactivate_ice_for_deactivated_media_streams(LinphoneCall *call, const SalMediaDescription *md)
906 {
907         int i;
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);
912                 }
913         }
914 }
915
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;
920 }
921
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;
926 }
927
928 unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
929         unsigned int ret=0;
930         const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
931         if (features){
932                 char tmp[256]={0};
933                 char name[256];
934                 char *p,*n;
935                 strncpy(tmp,features,sizeof(tmp)-1);
936                 for(p=tmp;*p!='\0';p++){
937                         if (*p==' ') continue;
938                         n=strchr(p,'|');
939                         if (n) *n='\0';
940                         sscanf(p,"%s",name);
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);
952                         if (!n) break;
953                         p=n;
954                 }
955         }else ret=AUDIO_STREAM_FEATURE_ALL;
956         return ret;
957 }
958
959
960 #ifdef HAVE_GETIFADDRS
961
962 #include <ifaddrs.h>
963 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
964 {
965         struct ifaddrs *ifp;
966         struct ifaddrs *ifpstart;
967         int ret = 0;
968
969         if (getifaddrs(&ifpstart) < 0) {
970                 return -1;
971         }
972
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))
976                 {
977                         getnameinfo(ifp->ifa_addr,
978                                                 (type == AF_INET6) ?
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);*/
983                                 ret++;
984                                 break;
985                         }
986                 }
987         }
988         freeifaddrs(ifpstart);
989         return ret;
990 }
991 #endif
992
993
994 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
995         int err,tmp;
996         struct addrinfo hints;
997         struct addrinfo *res=NULL;
998         struct sockaddr_storage addr;
999         struct sockaddr *p_addr=(struct sockaddr*)&addr;
1000         ortp_socket_t sock;
1001         socklen_t s;
1002
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);
1008         if (err!=0){
1009                 ms_error("getaddrinfo() error: %s",gai_strerror(err));
1010                 return -1;
1011         }
1012         if (res==NULL){
1013                 ms_error("bug: getaddrinfo returned nothing.");
1014                 return -1;
1015         }
1016         sock=socket(res->ai_family,SOCK_DGRAM,0);
1017         tmp=1;
1018         err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
1019         if (err<0){
1020                 ms_warning("Error in setsockopt: %s",strerror(errno));
1021         }
1022         err=connect(sock,res->ai_addr,res->ai_addrlen);
1023         if (err<0) {
1024                 ms_error("Error in connect: %s",strerror(errno));
1025                 freeaddrinfo(res);
1026                 close_socket(sock);
1027                 return -1;
1028         }
1029         freeaddrinfo(res);
1030         res=NULL;
1031         s=sizeof(addr);
1032         err=getsockname(sock,(struct sockaddr*)&addr,&s);
1033         if (err!=0) {
1034                 ms_error("Error in getsockname: %s",strerror(errno));
1035                 close_socket(sock);
1036                 return -1;
1037         }
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){
1041                         close_socket(sock);
1042                         return -1;
1043                 }
1044         }
1045         err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
1046         if (err!=0){
1047                 ms_error("getnameinfo error: %s",strerror(errno));
1048         }
1049         close_socket(sock);
1050         ms_message("Local interface to reach %s is %s.",dest,result);
1051         return 0;
1052 }
1053
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
1057         if (dest==NULL) {
1058                 /*we use getifaddrs for lookup of default interface */
1059                 int found_ifs;
1060         
1061                 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
1062                 if (found_ifs==1){
1063                         return 0;
1064                 }else if (found_ifs<=0){
1065                         /*absolutely no network on this machine */
1066                         return -1;
1067                 }
1068         }
1069 #endif
1070         /*else use connect to find the best local ip address */
1071         if (type==AF_INET)
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);
1075 }
1076
1077 #ifndef WIN32
1078 #include <resolv.h>
1079
1080
1081
1082 void _linphone_core_configure_resolver(){
1083 /*bionic declares _res but does not define nor export it !!*/
1084 #ifdef ANDROID
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);
1087 #else
1088         res_init();
1089         _res.retrans=2; /*retransmit every two seconds*/
1090         _res.retry=2; /*only two times per DNS server*/
1091 #endif
1092 }
1093
1094 #else
1095
1096 void _linphone_core_configure_resolver(){
1097 }
1098
1099 #endif