]> sjero.net Git - linphone/blob - coreapi/misc.c
35a58e2000cb1566b665d64da33db4a42d883ff2
[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 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)(ceil(get_audio_payload_bandwidth(call->core,pt)/1000.0)); /*rounding codec bandwidth should be avoid, specially for AMR*/
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){
471         const char *server=linphone_core_get_stun_server(lc);
472         StunCandidate *ac=&call->ac;
473         StunCandidate *vc=&call->vc;
474         
475         if (lc->sip_conf.ipv6_enabled){
476                 ms_warning("stun support is not implemented for ipv6");
477                 return -1;
478         }
479         if (server!=NULL){
480                 struct sockaddr_storage ss;
481                 socklen_t ss_len;
482                 ortp_socket_t sock1=-1, sock2=-1;
483                 int loops=0;
484                 bool_t video_enabled=linphone_core_video_enabled(lc);
485                 bool_t got_audio,got_video;
486                 bool_t cone_audio=FALSE,cone_video=FALSE;
487                 struct timeval init,cur;
488                 double elapsed;
489                 int ret=0;
490                 
491                 if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
492                         ms_error("Fail to parser stun server address: %s",server);
493                         return -1;
494                 }
495                 if (lc->vtable.display_status!=NULL)
496                         lc->vtable.display_status(lc,_("Stun lookup in progress..."));
497
498                 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
499                 sock1=create_socket(call->audio_port);
500                 if (sock1==-1) return -1;
501                 if (video_enabled){
502                         sock2=create_socket(call->video_port);
503                         if (sock2==-1) return -1;
504                 }
505                 got_audio=FALSE;
506                 got_video=FALSE;
507                 gettimeofday(&init,NULL);
508                 do{
509                         
510                         int id;
511                         if (loops%20==0){
512                                 ms_message("Sending stun requests...");
513                                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
514                                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
515                                 if (sock2!=-1){
516                                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
517                                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
518                                 }
519                         }
520 #ifdef WIN32
521                         Sleep(10);
522 #else
523                         usleep(10000);
524 #endif
525
526                         if (recvStunResponse(sock1,ac->addr,
527                                                 &ac->port,&id)>0){
528                                 ms_message("STUN test result: local audio port maps to %s:%i",
529                                                 ac->addr,
530                                                 ac->port);
531                                 if (id==11)
532                                         cone_audio=TRUE;
533                                 got_audio=TRUE;
534                         }
535                         if (recvStunResponse(sock2,vc->addr,
536                                                         &vc->port,&id)>0){
537                                 ms_message("STUN test result: local video port maps to %s:%i",
538                                         vc->addr,
539                                         vc->port);
540                                 if (id==22)
541                                         cone_video=TRUE;
542                                 got_video=TRUE;
543                         }
544                         gettimeofday(&cur,NULL);
545                         elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
546                         if (elapsed>2000)  {
547                                 ms_message("Stun responses timeout, going ahead.");
548                                 ret=-1;
549                                 break;
550                         }
551                         loops++;
552                 }while(!(got_audio && (got_video||sock2==-1)  ) );
553                 if (ret==0) ret=(int)elapsed;
554                 if (!got_audio){
555                         ms_error("No stun server response for audio port.");
556                 }else{
557                         if (!cone_audio) {
558                                 ms_message("NAT is symmetric for audio port");
559                         }
560                 }
561                 if (sock2!=-1){
562                         if (!got_video){
563                                 ms_error("No stun server response for video port.");
564                         }else{
565                                 if (!cone_video) {
566                                         ms_message("NAT is symmetric for video port.");
567                                 }
568                         }
569                 }
570                 close_socket(sock1);
571                 if (sock2!=-1) close_socket(sock2);
572                 return ret;
573         }
574         return -1;
575 }
576
577 int linphone_core_get_edge_bw(LinphoneCore *lc){
578         int edge_bw=lp_config_get_int(lc->config,"net","edge_bw",20);
579         return edge_bw;
580 }
581
582 int linphone_core_get_edge_ptime(LinphoneCore *lc){
583         int edge_ptime=lp_config_get_int(lc->config,"net","edge_ptime",100);
584         return edge_ptime;
585 }
586
587 void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params){
588         if (ping_time_ms>0 && lp_config_get_int(lc->config,"net","activate_edge_workarounds",0)==1){
589                 ms_message("Stun server ping time is %i ms",ping_time_ms);
590                 int threshold=lp_config_get_int(lc->config,"net","edge_ping_time",500);
591                 
592                 if (ping_time_ms>threshold){
593                         /* we might be in a 2G network*/
594                         params->low_bandwidth=TRUE;
595                 }/*else use default settings */
596         }
597         if (params->low_bandwidth){
598                 params->up_bw=params->down_bw=linphone_core_get_edge_bw(lc);
599                 params->up_ptime=params->down_ptime=linphone_core_get_edge_ptime(lc);
600                 params->has_video=FALSE;
601         }
602 }
603
604
605
606 int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call)
607 {
608         char local_addr[64];
609         struct sockaddr_storage ss;
610         socklen_t ss_len;
611         IceCheckList *audio_check_list;
612         IceCheckList *video_check_list;
613         const char *server = linphone_core_get_stun_server(lc);
614
615         if ((server == NULL) || (call->ice_session == NULL)) return -1;
616         audio_check_list = ice_session_check_list(call->ice_session, 0);
617         video_check_list = ice_session_check_list(call->ice_session, 1);
618         if (audio_check_list == NULL) return -1;
619
620         if (lc->sip_conf.ipv6_enabled){
621                 ms_warning("stun support is not implemented for ipv6");
622                 return -1;
623         }
624
625         if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) {
626                 ms_error("Fail to parser stun server address: %s", server);
627                 return -1;
628         }
629         if (lc->vtable.display_status != NULL)
630                 lc->vtable.display_status(lc, _("ICE local candidates gathering in progress..."));
631
632         /* Gather local host candidates. */
633         if (linphone_core_get_local_ip_for(AF_INET, server, local_addr) < 0) {
634                 ms_error("Fail to get local ip");
635                 return -1;
636         }
637         if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) {
638                 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port, 1, NULL);
639                 ice_add_local_candidate(audio_check_list, "host", local_addr, call->audio_port + 1, 2, NULL);
640                 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
641         }
642         if (call->params.has_video && (video_check_list != NULL)
643                 && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) {
644                 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port, 1, NULL);
645                 ice_add_local_candidate(video_check_list, "host", local_addr, call->video_port + 1, 2, NULL);
646                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
647         }
648
649         ms_message("ICE: gathering candidate from [%s]",server);
650         /* Gather local srflx candidates. */
651         ice_session_gather_candidates(call->ice_session, ss, ss_len);
652         return 0;
653 }
654
655 void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call)
656 {
657         IceCheckList *audio_check_list;
658         IceCheckList *video_check_list;
659         IceSessionState session_state;
660
661         if (call->ice_session == NULL) return;
662         audio_check_list = ice_session_check_list(call->ice_session, 0);
663         video_check_list = ice_session_check_list(call->ice_session, 1);
664         if (audio_check_list == NULL) return;
665
666         session_state = ice_session_state(call->ice_session);
667         if ((session_state == IS_Completed) || ((session_state == IS_Failed) && (ice_session_has_completed_check_list(call->ice_session) == TRUE))) {
668                 if (ice_check_list_state(audio_check_list) == ICL_Completed) {
669                         switch (ice_check_list_selected_valid_candidate_type(audio_check_list)) {
670                                 case ICT_HostCandidate:
671                                         call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateHostConnection;
672                                         break;
673                                 case ICT_ServerReflexiveCandidate:
674                                 case ICT_PeerReflexiveCandidate:
675                                         call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateReflexiveConnection;
676                                         break;
677                                 case ICT_RelayedCandidate:
678                                         call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateRelayConnection;
679                                         break;
680                         }
681                 } else {
682                         call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
683                 }
684                 if (call->params.has_video && (video_check_list != NULL)) {
685                         if (ice_check_list_state(video_check_list) == ICL_Completed) {
686                                 switch (ice_check_list_selected_valid_candidate_type(video_check_list)) {
687                                         case ICT_HostCandidate:
688                                                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateHostConnection;
689                                                 break;
690                                         case ICT_ServerReflexiveCandidate:
691                                         case ICT_PeerReflexiveCandidate:
692                                                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateReflexiveConnection;
693                                                 break;
694                                         case ICT_RelayedCandidate:
695                                                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateRelayConnection;
696                                                 break;
697                                 }
698                         } else {
699                                 call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
700                         }
701                 }
702         } else if (session_state == IS_Running) {
703                 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress;
704                 if (call->params.has_video && (video_check_list != NULL)) {
705                         call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress;
706                 }
707         } else {
708                 call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateFailed;
709                 if (call->params.has_video && (video_check_list != NULL)) {
710                         call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateFailed;
711                 }
712         }
713 }
714
715 void linphone_core_update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session)
716 {
717         const char *rtp_addr, *rtcp_addr;
718         IceSessionState session_state = ice_session_state(session);
719         int nb_candidates;
720         int i, j;
721         bool_t result;
722
723         if (session_state == IS_Completed) {
724                 desc->ice_completed = TRUE;
725                 result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL);
726                 if (result == TRUE) {
727                         strncpy(desc->addr, rtp_addr, sizeof(desc->addr));
728                 } else {
729                         ms_warning("If ICE has completed successfully, rtp_addr should be set!");
730                 }
731         }
732         else {
733                 desc->ice_completed = FALSE;
734         }
735         strncpy(desc->ice_pwd, ice_session_local_pwd(session), sizeof(desc->ice_pwd));
736         strncpy(desc->ice_ufrag, ice_session_local_ufrag(session), sizeof(desc->ice_ufrag));
737         for (i = 0; i < desc->n_active_streams; i++) {
738                 SalStreamDescription *stream = &desc->streams[i];
739                 IceCheckList *cl = ice_session_check_list(session, i);
740                 nb_candidates = 0;
741                 if (cl == NULL) continue;
742                 if (ice_check_list_state(cl) == ICL_Completed) {
743                         stream->ice_completed = TRUE;
744                         result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
745                 } else {
746                         stream->ice_completed = FALSE;
747                         result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port);
748                 }
749                 if (result == TRUE) {
750                         strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr));
751                         strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr));
752                 } else {
753                         memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr));
754                         memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr));
755                 }
756                 if ((strlen(ice_check_list_local_pwd(cl)) != strlen(desc->ice_pwd)) || (strcmp(ice_check_list_local_pwd(cl), desc->ice_pwd)))
757                         strncpy(stream->ice_pwd, ice_check_list_local_pwd(cl), sizeof(stream->ice_pwd));
758                 else
759                         memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
760                 if ((strlen(ice_check_list_local_ufrag(cl)) != strlen(desc->ice_ufrag)) || (strcmp(ice_check_list_local_ufrag(cl), desc->ice_ufrag)))
761                         strncpy(stream->ice_ufrag, ice_check_list_local_ufrag(cl), sizeof(stream->ice_ufrag));
762                 else
763                         memset(stream->ice_pwd, 0, sizeof(stream->ice_pwd));
764                 stream->ice_mismatch = ice_check_list_is_mismatch(cl);
765                 if ((ice_check_list_state(cl) == ICL_Running) || (ice_check_list_state(cl) == ICL_Completed)) {
766                         memset(stream->ice_candidates, 0, sizeof(stream->ice_candidates));
767                         for (j = 0; j < MIN(ms_list_size(cl->local_candidates), SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES); j++) {
768                                 SalIceCandidate *sal_candidate = &stream->ice_candidates[nb_candidates];
769                                 IceCandidate *ice_candidate = ms_list_nth_data(cl->local_candidates, j);
770                                 const char *default_addr = NULL;
771                                 int default_port = 0;
772                                 if (ice_candidate->componentID == 1) {
773                                         default_addr = stream->rtp_addr;
774                                         default_port = stream->rtp_port;
775                                 } else if (ice_candidate->componentID == 2) {
776                                         default_addr = stream->rtcp_addr;
777                                         default_port = stream->rtcp_port;
778                                 } else continue;
779                                 if (default_addr[0] == '\0') default_addr = desc->addr;
780                                 /* 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. */
781                                 if ((ice_check_list_state(cl) == ICL_Completed)
782                                         && !((ice_candidate->taddr.port == default_port) && (strlen(ice_candidate->taddr.ip) == strlen(default_addr)) && (strcmp(ice_candidate->taddr.ip, default_addr) == 0)))
783                                         continue;
784                                 strncpy(sal_candidate->foundation, ice_candidate->foundation, sizeof(sal_candidate->foundation));
785                                 sal_candidate->componentID = ice_candidate->componentID;
786                                 sal_candidate->priority = ice_candidate->priority;
787                                 strncpy(sal_candidate->type, ice_candidate_type(ice_candidate), sizeof(sal_candidate->type));
788                                 strncpy(sal_candidate->addr, ice_candidate->taddr.ip, sizeof(sal_candidate->addr));
789                                 sal_candidate->port = ice_candidate->taddr.port;
790                                 if ((ice_candidate->base != NULL) && (ice_candidate->base != ice_candidate)) {
791                                         strncpy(sal_candidate->raddr, ice_candidate->base->taddr.ip, sizeof(sal_candidate->raddr));
792                                         sal_candidate->rport = ice_candidate->base->taddr.port;
793                                 }
794                                 nb_candidates++;
795                         }
796                 }
797                 if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) {
798                         int rtp_port, rtcp_port;
799                         memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates));
800                         if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) {
801                                 strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr));
802                                 stream->ice_remote_candidates[0].port = rtp_port;
803                                 strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr));
804                                 stream->ice_remote_candidates[1].port = rtcp_port;
805                         } else {
806                                 ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state");
807                         }
808                 } else {
809                         for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
810                                 stream->ice_remote_candidates[j].addr[0] = '\0';
811                                 stream->ice_remote_candidates[j].port = 0;
812                         }
813                 }
814         }
815 }
816
817 static void get_default_addr_and_port(uint16_t componentID, const SalMediaDescription *md, const SalStreamDescription *stream, const char **addr, int *port)
818 {
819         if (componentID == 1) {
820                 *addr = stream->rtp_addr;
821                 *port = stream->rtp_port;
822         } else if (componentID == 2) {
823                 *addr = stream->rtcp_addr;
824                 *port = stream->rtcp_port;
825         } else return;
826         if ((*addr)[0] == '\0') *addr = md->addr;
827 }
828
829 void linphone_core_update_ice_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md)
830 {
831         bool_t ice_restarted = FALSE;
832
833         if ((md->ice_pwd[0] != '\0') && (md->ice_ufrag[0] != '\0')) {
834                 int i, j;
835
836                 /* Check for ICE restart and set remote credentials. */
837                 if ((strcmp(md->addr, "0.0.0.0") == 0) || (strcmp(md->addr, "::0") == 0)) {
838                         ice_session_restart(call->ice_session);
839                         ice_restarted = TRUE;
840                 } else {
841                         for (i = 0; i < md->n_total_streams; i++) {
842                                 const SalStreamDescription *stream = &md->streams[i];
843                                 IceCheckList *cl = ice_session_check_list(call->ice_session, i);
844                                 if (cl && (strcmp(stream->rtp_addr, "0.0.0.0") == 0)) {
845                                         ice_session_restart(call->ice_session);
846                                         ice_restarted = TRUE;
847                                         break;
848                                 }
849                         }
850                 }
851                 if ((ice_session_remote_ufrag(call->ice_session) == NULL) && (ice_session_remote_pwd(call->ice_session) == NULL)) {
852                         ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
853                 } else if (ice_session_remote_credentials_changed(call->ice_session, md->ice_ufrag, md->ice_pwd)) {
854                         if (ice_restarted == FALSE) {
855                                 ice_session_restart(call->ice_session);
856                                 ice_restarted = TRUE;
857                         }
858                         ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
859                 }
860                 for (i = 0; i < md->n_total_streams; i++) {
861                         const SalStreamDescription *stream = &md->streams[i];
862                         IceCheckList *cl = ice_session_check_list(call->ice_session, i);
863                         if (cl && (stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0')) {
864                                 if (ice_check_list_remote_credentials_changed(cl, stream->ice_ufrag, stream->ice_pwd)) {
865                                         if (ice_restarted == FALSE) {
866                                                 ice_session_restart(call->ice_session);
867                                                 ice_restarted = TRUE;
868                                         }
869                                         ice_session_set_remote_credentials(call->ice_session, md->ice_ufrag, md->ice_pwd);
870                                         break;
871                                 }
872                         }
873                 }
874
875                 /* Create ICE check lists if needed and parse ICE attributes. */
876                 for (i = 0; i < md->n_total_streams; i++) {
877                         const SalStreamDescription *stream = &md->streams[i];
878                         IceCheckList *cl = ice_session_check_list(call->ice_session, i);
879                         if ((cl == NULL) && (i < md->n_active_streams)) {
880                                 cl = ice_check_list_new();
881                                 ice_session_add_check_list(call->ice_session, cl);
882                                 switch (stream->type) {
883                                         case SalAudio:
884                                                 if (call->audiostream != NULL) call->audiostream->ms.ice_check_list = cl;
885                                                 break;
886                                         case SalVideo:
887                                                 if (call->videostream != NULL) call->videostream->ms.ice_check_list = cl;
888                                                 break;
889                                         default:
890                                                 break;
891                                 }
892                         }
893                         if (stream->ice_mismatch == TRUE) {
894                                 ice_check_list_set_state(cl, ICL_Failed);
895                         } else if (stream->rtp_port == 0) {
896                                 ice_session_remove_check_list(call->ice_session, cl);
897                         } else {
898                                 if ((stream->ice_pwd[0] != '\0') && (stream->ice_ufrag[0] != '\0'))
899                                         ice_check_list_set_remote_credentials(cl, stream->ice_ufrag, stream->ice_pwd);
900                                 for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) {
901                                         const SalIceCandidate *candidate = &stream->ice_candidates[j];
902                                         bool_t default_candidate = FALSE;
903                                         const char *addr = NULL;
904                                         int port = 0;
905                                         if (candidate->addr[0] == '\0') break;
906                                         if ((candidate->componentID == 0) || (candidate->componentID > 2)) continue;
907                                         get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port);
908                                         if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0))
909                                                 default_candidate = TRUE;
910                                         ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID,
911                                                 candidate->priority, candidate->foundation, default_candidate);
912                                 }
913                                 if (ice_restarted == FALSE) {
914                                         bool_t losing_pairs_added = FALSE;
915                                         for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_REMOTE_CANDIDATES; j++) {
916                                                 const SalIceRemoteCandidate *candidate = &stream->ice_remote_candidates[j];
917                                                 const char *addr = NULL;
918                                                 int port = 0;
919                                                 int componentID = j + 1;
920                                                 if (candidate->addr[0] == '\0') break;
921                                                 get_default_addr_and_port(componentID, md, stream, &addr, &port);
922                                                 if (j == 0) {
923                                                         /* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */
924                                                         ice_check_list_unselect_valid_pairs(cl);
925                                                 }
926                                                 ice_add_losing_pair(cl, j + 1, candidate->addr, candidate->port, addr, port);
927                                                 losing_pairs_added = TRUE;
928                                         }
929                                         if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl);
930                                 }
931                         }
932                 }
933                 for (i = ice_session_nb_check_lists(call->ice_session); i > md->n_active_streams; i--) {
934                         ice_session_remove_check_list(call->ice_session, ice_session_check_list(call->ice_session, i - 1));
935                 }
936                 ice_session_check_mismatch(call->ice_session);
937         } else {
938                 /* Response from remote does not contain mandatory ICE attributes, delete the session. */
939                 linphone_call_delete_ice_session(call);
940                 return;
941         }
942         if (ice_session_nb_check_lists(call->ice_session) == 0) {
943                 linphone_call_delete_ice_session(call);
944         }
945 }
946
947 bool_t linphone_core_media_description_contains_video_stream(const SalMediaDescription *md)
948 {
949         int i;
950
951         for (i = 0; i < md->n_active_streams; i++) {
952                 if (md->streams[i].type == SalVideo)
953                         return TRUE;
954         }
955         return FALSE;
956 }
957
958 LinphoneCall * is_a_linphone_call(void *user_pointer){
959         LinphoneCall *call=(LinphoneCall*)user_pointer;
960         if (call==NULL) return NULL;
961         return call->magic==linphone_call_magic ? call : NULL;
962 }
963
964 LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer){
965         LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)user_pointer;
966         if (cfg==NULL) return NULL;
967         return cfg->magic==linphone_proxy_config_magic ? cfg : NULL;
968 }
969
970 unsigned int linphone_core_get_audio_features(LinphoneCore *lc){
971         unsigned int ret=0;
972         const char *features=lp_config_get_string(lc->config,"sound","features",NULL);
973         if (features){
974                 char tmp[256]={0};
975                 char name[256];
976                 char *p,*n;
977                 strncpy(tmp,features,sizeof(tmp)-1);
978                 for(p=tmp;*p!='\0';p++){
979                         if (*p==' ') continue;
980                         n=strchr(p,'|');
981                         if (n) *n='\0';
982                         sscanf(p,"%s",name);
983                         ms_message("Found audio feature %s",name);
984                         if (strcasecmp(name,"PLC")==0) ret|=AUDIO_STREAM_FEATURE_PLC;
985                         else if (strcasecmp(name,"EC")==0) ret|=AUDIO_STREAM_FEATURE_EC;
986                         else if (strcasecmp(name,"EQUALIZER")==0) ret|=AUDIO_STREAM_FEATURE_EQUALIZER;
987                         else if (strcasecmp(name,"VOL_SND")==0) ret|=AUDIO_STREAM_FEATURE_VOL_SND;
988                         else if (strcasecmp(name,"VOL_RCV")==0) ret|=AUDIO_STREAM_FEATURE_VOL_RCV;
989                         else if (strcasecmp(name,"DTMF")==0) ret|=AUDIO_STREAM_FEATURE_DTMF;
990                         else if (strcasecmp(name,"DTMF_ECHO")==0) ret|=AUDIO_STREAM_FEATURE_DTMF_ECHO;
991                         else if (strcasecmp(name,"MIXED_RECORDING")==0) ret|=AUDIO_STREAM_FEATURE_MIXED_RECORDING;
992                         else if (strcasecmp(name,"ALL")==0) ret|=AUDIO_STREAM_FEATURE_ALL;
993                         else if (strcasecmp(name,"NONE")==0) ret=0;
994                         else ms_error("Unsupported audio feature %s requested in config file.",name);
995                         if (!n) break;
996                         p=n;
997                 }
998         }else ret=AUDIO_STREAM_FEATURE_ALL;
999         
1000         if (ret==AUDIO_STREAM_FEATURE_ALL){
1001                 /*since call recording is specified before creation of the stream in linphonecore,
1002                 * it will be requested on demand. It is not necessary to include it all the time*/
1003                 ret&=~AUDIO_STREAM_FEATURE_MIXED_RECORDING;
1004         }
1005         return ret;
1006 }
1007
1008 bool_t linphone_core_tone_indications_enabled(LinphoneCore*lc){
1009         return lp_config_get_int(lc->config,"sound","tone_indications",1);
1010 }
1011
1012 #ifdef HAVE_GETIFADDRS
1013
1014 #include <ifaddrs.h>
1015 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
1016 {
1017         struct ifaddrs *ifp;
1018         struct ifaddrs *ifpstart;
1019         int ret = 0;
1020
1021         if (getifaddrs(&ifpstart) < 0) {
1022                 return -1;
1023         }
1024 #ifndef __linux
1025         #define UP_FLAG IFF_UP /* interface is up */
1026 #else
1027         #define UP_FLAG IFF_RUNNING /* resources allocated */
1028 #endif
1029         
1030         for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
1031                 if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
1032                         && (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK))
1033                 {
1034                         if(getnameinfo(ifp->ifa_addr,
1035                                                 (type == AF_INET6) ?
1036                                                 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
1037                                                 address, size, NULL, 0, NI_NUMERICHOST) == 0) {
1038                                 if (strchr(address, '%') == NULL) {     /*avoid ipv6 link-local addresses */
1039                                         /*ms_message("getifaddrs() found %s",address);*/
1040                                         ret++;
1041                                         break;
1042                                 }
1043                         }
1044                 }
1045         }
1046         freeifaddrs(ifpstart);
1047         return ret;
1048 }
1049 #endif
1050
1051
1052 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
1053         int err,tmp;
1054         struct addrinfo hints;
1055         struct addrinfo *res=NULL;
1056         struct sockaddr_storage addr;
1057         struct sockaddr *p_addr=(struct sockaddr*)&addr;
1058         ortp_socket_t sock;
1059         socklen_t s;
1060
1061         memset(&hints,0,sizeof(hints));
1062         hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
1063         hints.ai_socktype=SOCK_DGRAM;
1064         /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
1065         err=getaddrinfo(dest,"5060",&hints,&res);
1066         if (err!=0){
1067                 ms_error("getaddrinfo() error: %s",gai_strerror(err));
1068                 return -1;
1069         }
1070         if (res==NULL){
1071                 ms_error("bug: getaddrinfo returned nothing.");
1072                 return -1;
1073         }
1074         sock=socket(res->ai_family,SOCK_DGRAM,0);
1075         tmp=1;
1076         err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
1077         if (err<0){
1078                 ms_warning("Error in setsockopt: %s",strerror(errno));
1079         }
1080         err=connect(sock,res->ai_addr,res->ai_addrlen);
1081         if (err<0) {
1082                 ms_error("Error in connect: %s",strerror(errno));
1083                 freeaddrinfo(res);
1084                 close_socket(sock);
1085                 return -1;
1086         }
1087         freeaddrinfo(res);
1088         res=NULL;
1089         s=sizeof(addr);
1090         err=getsockname(sock,(struct sockaddr*)&addr,&s);
1091         if (err!=0) {
1092                 ms_error("Error in getsockname: %s",strerror(errno));
1093                 close_socket(sock);
1094                 return -1;
1095         }
1096         if (p_addr->sa_family==AF_INET){
1097                 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
1098                 if (p_sin->sin_addr.s_addr==0){
1099                         close_socket(sock);
1100                         return -1;
1101                 }
1102         }
1103         err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
1104         if (err!=0){
1105                 ms_error("getnameinfo error: %s",strerror(errno));
1106         }
1107         close_socket(sock);
1108         ms_message("Local interface to reach %s is %s.",dest,result);
1109         return 0;
1110 }
1111
1112 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
1113         strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
1114 #ifdef HAVE_GETIFADDRS
1115         if (dest==NULL) {
1116                 /*we use getifaddrs for lookup of default interface */
1117                 int found_ifs;
1118
1119                 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
1120                 if (found_ifs==1){
1121                         return 0;
1122                 }else if (found_ifs<=0){
1123                         /*absolutely no network on this machine */
1124                         return -1;
1125                 }
1126         }
1127 #endif
1128         /*else use connect to find the best local ip address */
1129         if (type==AF_INET)
1130                 dest="87.98.157.38"; /*a public IP address*/
1131         else dest="2a00:1450:8002::68";
1132         return get_local_ip_for_with_connect(type,dest,result);
1133 }
1134
1135 #ifndef WIN32
1136 #include <resolv.h>
1137
1138
1139
1140 void _linphone_core_configure_resolver(){
1141 /*bionic declares _res but does not define nor export it !!*/
1142 #ifdef ANDROID
1143         /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
1144         setenv("RES_OPTIONS","timeout:2 attempts:2 retrans:2 retry:2",1);
1145 #else
1146         res_init();
1147         _res.retrans=2; /*retransmit every two seconds*/
1148         _res.retry=2; /*only two times per DNS server*/
1149 #endif
1150 }
1151
1152 #else
1153
1154 void _linphone_core_configure_resolver(){
1155 }
1156
1157 #endif