]> sjero.net Git - linphone/blob - coreapi/misc.c
Merge branch 'master' of git.linphone.org:linphone into tunnel
[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 "mediastreamer2/mediastream.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #ifdef HAVE_SIGHANDLER_T
26 #include <signal.h>
27 #endif /*HAVE_SIGHANDLER_T*/
28
29 #include <string.h>
30 #if !defined(_WIN32_WCE)
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #endif /*_WIN32_WCE*/
37
38 #undef snprintf
39 #include <ortp/stun.h>
40
41 #ifdef HAVE_GETIFADDRS
42 #include <net/if.h>
43 #include <ifaddrs.h>
44 #endif
45
46
47 #if !defined(WIN32)
48
49 static char lock_name[80];
50 static char lock_set=0;
51 /* put a lock file in /tmp. this is called when linphone runs as a daemon*/
52 int set_lock_file()
53 {
54         FILE *lockfile;
55
56         snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
57         lockfile=fopen(lock_name,"w");
58         if (lockfile==NULL)
59         {
60                 printf("Failed to create lock file.\n");
61                 return(-1);
62         }
63         fprintf(lockfile,"%i",getpid());
64         fclose(lockfile);
65         lock_set=1;
66         return(0);
67 }
68
69 /* looks if there is a lock file. If presents return its content (the pid of the already running linphone), if not found, returns -1*/
70 int get_lock_file()
71 {
72         int pid;
73         FILE *lockfile;
74
75         snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
76         lockfile=fopen(lock_name,"r");
77         if (lockfile==NULL)
78                 return(-1);
79         if (fscanf(lockfile,"%i",&pid)!=1){
80                 ms_warning("Could not read pid in lock file.");
81                 fclose(lockfile);
82                 return -1;
83         }
84         fclose(lockfile);
85         return pid;
86 }
87
88 /* remove the lock file if it was set*/
89 int remove_lock_file()
90 {
91         int err=0;
92         if (lock_set)
93         {
94                 err=unlink(lock_name);
95                 lock_set=0;
96         }
97         return(err);
98 }
99
100 #endif
101
102 char *int2str(int number)
103 {
104         char *numstr=ms_malloc(10);
105         snprintf(numstr,10,"%i",number);
106         return numstr;
107 }
108
109 void check_sound_device(LinphoneCore *lc)
110 {
111 #ifdef _linux
112         int fd=0;
113         int len;
114         int a;
115         char *file=NULL;
116         char *i810_audio=NULL;
117         char *snd_pcm_oss=NULL;
118         char *snd_mixer_oss=NULL;
119         char *snd_pcm=NULL;
120         fd=open("/proc/modules",O_RDONLY);
121
122         if (fd>0){
123                 /* read the entire /proc/modules file and check if sound conf seems correct */
124                 /*a=fstat(fd,&statbuf);
125                 if (a<0) ms_warning("Can't stat /proc/modules:%s.",strerror(errno));
126                 len=statbuf.st_size;
127                 if (len==0) ms_warning("/proc/modules has zero size!");
128                 */
129                 /***** fstat does not work on /proc/modules for unknown reason *****/
130                 len=6000;
131                 file=ms_malloc(len+1);
132                 a=read(fd,file,len);
133                 if (a<len) file=ms_realloc(file,a+1);
134                 file[a]='\0';
135                 i810_audio=strstr(file,"i810_audio");
136                 if (i810_audio!=NULL){
137                         /* I'm sorry i put this warning in comments because
138                          * i don't use yet the right driver !! */
139 /*                      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."));*/
140                         goto end;
141                 }
142                 snd_pcm=strstr(file,"snd-pcm");
143                 if (snd_pcm!=NULL){
144                         snd_pcm_oss=strstr(file,"snd-pcm-oss");
145                         snd_mixer_oss=strstr(file,"snd-mixer-oss");
146                         if (snd_pcm_oss==NULL){
147                                 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."));
148                         }
149                         if (snd_mixer_oss==NULL){
150                                 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."));
151                         }
152                 }
153         }else {
154
155                 ms_warning("Could not open /proc/modules.");
156         }
157         /* now check general volume. Some user forget to rise it and then complain that linphone is
158         not working */
159         /* but some other users complain that linphone should not change levels...
160         if (lc->sound_conf.sndcard!=NULL){
161                 a=snd_card_get_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL);
162                 if (a<50){
163                         ms_warning("General level is quite low (%i). Linphone rises it up for you.",a);
164                         snd_card_set_level(lc->sound_conf.sndcard,SND_CARD_LEVEL_GENERAL,80);
165                 }
166         }
167         */
168         end:
169         if (file!=NULL) ms_free(file);
170         if (fd>0) close(fd);
171 #endif
172 }
173
174 #define UDP_HDR_SZ 8
175 #define RTP_HDR_SZ 12
176 #define IP4_HDR_SZ 20   /*20 is the minimum, but there may be some options*/
177
178 static void payload_type_set_enable(PayloadType *pt,int value)
179 {
180         if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
181         else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
182 }
183
184 static bool_t payload_type_enabled(PayloadType *pt) {
185         return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
186 }
187
188 bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, PayloadType *pt){
189         if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){
190                 return payload_type_enabled(pt);
191         }
192         ms_error("Getting enablement status of codec not in audio or video list of PayloadType !");
193         return FALSE;
194 }
195
196 int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadType *pt, bool_t enabled){
197         if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){
198                 payload_type_set_enable(pt,enabled);
199                 return 0;
200         }
201         ms_error("Enabling codec not in audio or video list of PayloadType !");
202         return -1;
203 }
204
205 const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){
206         if (ms_filter_codec_supported(pt->mime_type)){
207                 MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type);
208 #ifdef ENABLE_NLS
209                 return dgettext("mediastreamer",desc->text);
210 #else
211                 return desc->text;
212 #endif
213         }
214         return NULL;
215 }
216
217
218 /*this function makes a special case for speex/8000.
219 This codec is variable bitrate. The 8kbit/s mode is interesting when having a low upload bandwidth, but its quality
220 is not very good. We 'd better use its 15kbt/s mode when we have enough bandwidth*/
221 static int get_codec_bitrate(LinphoneCore *lc, const PayloadType *pt){
222         int upload_bw=linphone_core_get_upload_bandwidth(lc);
223         if (bandwidth_is_greater(upload_bw,129) || (bandwidth_is_greater(upload_bw,33) && !linphone_core_video_enabled(lc)) ) {
224                 if (strcmp(pt->mime_type,"speex")==0 && pt->clock_rate==8000){
225                         return 15000;
226                 }
227         }
228         return pt->normal_bitrate;
229 }
230
231 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
232         double npacket=50;
233         double packet_size;
234         int bitrate;
235         bitrate=get_codec_bitrate(lc,pt);
236         packet_size= (((double)bitrate)/(50*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
237         return packet_size*8.0*npacket;
238 }
239
240 void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
241         call->audio_bw=(int)(get_audio_payload_bandwidth(call->core,pt)/1000.0);
242         ms_message("Audio bandwidth for this call is %i",call->audio_bw);
243 }
244
245 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
246         const MSList *elem;
247         PayloadType *max=NULL;
248         for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
249                 PayloadType *pt=(PayloadType*)elem->data;
250                 if (payload_type_enabled(pt)){
251                         int pt_bitrate=get_codec_bitrate(lc,pt);
252                         if (max==NULL) max=pt;
253                         else if (max->normal_bitrate<pt_bitrate){
254                                 max=pt;
255                         }
256                 }
257         }
258         if (max) {
259                 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
260         }
261 }
262
263 bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt,  int bandwidth_limit)
264 {
265         double codec_band;
266         bool_t ret=FALSE;
267         
268         switch (pt->type){
269                 case PAYLOAD_AUDIO_CONTINUOUS:
270                 case PAYLOAD_AUDIO_PACKETIZED:
271                         codec_band=get_audio_payload_bandwidth(lc,pt);
272                         ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
273                         /*hack to avoid using uwb codecs when having low bitrate and video*/
274                         if (bandwidth_is_greater(199,bandwidth_limit)){
275                                 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
276                                         ret=FALSE;
277                                 }
278                         }
279                         //ms_message("Payload %s: %g",pt->mime_type,codec_band);
280                         break;
281                 case PAYLOAD_VIDEO:
282                         if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
283                                 ret=TRUE;
284                         }
285                         else ret=FALSE;
286                         break;
287         }
288         return ret;
289 }
290
291 /* return TRUE if codec can be used with bandwidth, FALSE else*/
292 bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
293 {
294         double codec_band;
295         int allowed_bw,video_bw;
296         bool_t ret=FALSE;
297
298         linphone_core_update_allocated_audio_bandwidth(lc);
299         allowed_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
300                                         linphone_core_get_upload_bandwidth(lc));
301         if (allowed_bw==0) {
302                 allowed_bw=-1;
303                 video_bw=1500; /*around 1.5 Mbit/s*/
304         }else
305                 video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
306
307         switch (pt->type){
308                 case PAYLOAD_AUDIO_CONTINUOUS:
309                 case PAYLOAD_AUDIO_PACKETIZED:
310                         codec_band=get_audio_payload_bandwidth(lc,pt);
311                         ret=bandwidth_is_greater(allowed_bw*1000,codec_band);
312                         /*hack to avoid using uwb codecs when having low bitrate and video*/
313                         if (bandwidth_is_greater(199,allowed_bw)){
314                                 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
315                                         ret=FALSE;
316                                 }
317                         }
318                         //ms_message("Payload %s: %g",pt->mime_type,codec_band);
319                         break;
320                 case PAYLOAD_VIDEO:
321                         if (video_bw>0){
322                                 pt->normal_bitrate=video_bw*1000;
323                                 ret=TRUE;
324                         }
325                         else ret=FALSE;
326                         break;
327         }
328         return ret;
329 }
330
331 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
332 #if !defined(_WIN32_WCE)
333         FILE *f=popen(command,"r");
334         if (f!=NULL){
335                 int err;
336                 *result=ms_malloc(4096);
337                 err=fread(*result,1,4096-1,f);
338                 if (err<0){
339                         ms_warning("Error reading command output:%s",strerror(errno));
340                         ms_free(result);
341                         return FALSE;
342                 }
343                 (*result)[err]=0;
344                 err=pclose(f);
345                 if (command_ret!=NULL) *command_ret=err;
346                 return TRUE;
347         }
348 #endif /*_WIN32_WCE*/
349         return FALSE;
350 }
351
352 static ortp_socket_t create_socket(int local_port){
353         struct sockaddr_in laddr;
354         ortp_socket_t sock;
355         int optval;
356         sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
357         if (sock<0) {
358                 ms_error("Fail to create socket");
359                 return -1;
360         }
361         memset (&laddr,0,sizeof(laddr));
362         laddr.sin_family=AF_INET;
363         laddr.sin_addr.s_addr=INADDR_ANY;
364         laddr.sin_port=htons(local_port);
365         if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
366                 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
367                 close_socket(sock);
368                 return -1;
369         }
370         optval=1;
371         if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
372                                 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
373                 ms_warning("Fail to set SO_REUSEADDR");
374         }
375         set_non_blocking_socket(sock);
376         return sock;
377 }
378
379 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
380         char buf[STUN_MAX_MESSAGE_SIZE];
381         int len = STUN_MAX_MESSAGE_SIZE;
382         StunAtrString username;
383         StunAtrString password;
384         StunMessage req;
385         int err;
386         memset(&req, 0, sizeof(StunMessage));
387         memset(&username,0,sizeof(username));
388         memset(&password,0,sizeof(password));
389         stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
390         len = stunEncodeMessage( &req, buf, len, &password);
391         if (len<=0){
392                 ms_error("Fail to encode stun message.");
393                 return -1;
394         }
395         err=sendto(sock,buf,len,0,server,addrlen);
396         if (err<0){
397                 ms_error("sendto failed: %s",strerror(errno));
398                 return -1;
399         }
400         return 0;
401 }
402
403 int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
404         struct addrinfo hints,*res=NULL;
405         int ret;
406         const char *port;
407         char host[NI_MAXHOST];
408         char *p;
409         host[NI_MAXHOST-1]='\0';
410         strncpy(host,server,sizeof(host)-1);
411         p=strchr(host,':');
412         if (p) {
413                 *p='\0';
414                 port=p+1;
415         }else port="3478";
416         memset(&hints,0,sizeof(hints));
417         hints.ai_family=PF_INET;
418         hints.ai_socktype=SOCK_DGRAM;
419         hints.ai_protocol=IPPROTO_UDP;
420         ret=getaddrinfo(host,port,&hints,&res);
421         if (ret!=0){
422                 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
423                 return -1;
424         }
425         if (!res) return -1;
426         memcpy(ss,res->ai_addr,res->ai_addrlen);
427         *socklen=res->ai_addrlen;
428         freeaddrinfo(res);
429         return 0;
430 }
431
432 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
433         char buf[STUN_MAX_MESSAGE_SIZE];
434         int len = STUN_MAX_MESSAGE_SIZE;
435         StunMessage resp;
436         len=recv(sock,buf,len,0);
437         if (len>0){
438                 struct in_addr ia;
439                 stunParseMessage(buf,len, &resp );
440                 *id=resp.msgHdr.tr_id.octet[0];
441                 if (resp.hasXorMappedAddress){
442                         *port = resp.xorMappedAddress.ipv4.port;
443                         ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
444                 }else if (resp.hasMappedAddress){
445                         *port = resp.mappedAddress.ipv4.port;
446                         ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
447                 }else return -1;
448                 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
449         }
450         return len;
451 }
452
453 void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
454         const char *server=linphone_core_get_stun_server(lc);
455
456         if (lc->sip_conf.ipv6_enabled){
457                 ms_warning("stun support is not implemented for ipv6");
458                 return;
459         }
460         if (server!=NULL){
461                 struct sockaddr_storage ss;
462                 socklen_t ss_len;
463                 ortp_socket_t sock1=-1, sock2=-1;
464                 int loops=0;
465                 bool_t video_enabled=linphone_core_video_enabled(lc);
466                 bool_t got_audio,got_video;
467                 bool_t cone_audio=FALSE,cone_video=FALSE;
468                 struct timeval init,cur;
469                 SalEndpointCandidate *ac,*vc;
470                 
471                 ac=&call->localdesc->streams[0].candidates[0];
472                 vc=&call->localdesc->streams[1].candidates[0];
473                 
474                 if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
475                         ms_error("Fail to parser stun server address: %s",server);
476                         return;
477                 }
478                 if (lc->vtable.display_status!=NULL)
479                         lc->vtable.display_status(lc,_("Stun lookup in progress..."));
480
481                 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
482                 sock1=create_socket(call->audio_port);
483                 if (sock1==-1) return;
484                 if (video_enabled){
485                         sock2=create_socket(call->video_port);
486                         if (sock2==-1) return ;
487                 }
488                 got_audio=FALSE;
489                 got_video=FALSE;
490                 gettimeofday(&init,NULL);
491                 do{
492                         double elapsed;
493                         int id;
494                         if (loops%20==0){
495                                 ms_message("Sending stun requests...");
496                                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
497                                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
498                                 if (sock2!=-1){
499                                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
500                                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
501                                 }
502                         }
503 #ifdef WIN32
504                         Sleep(10);
505 #else
506                         usleep(10000);
507 #endif
508
509                         if (recvStunResponse(sock1,ac->addr,
510                                                 &ac->port,&id)>0){
511                                 ms_message("STUN test result: local audio port maps to %s:%i",
512                                                 ac->addr,
513                                                 ac->port);
514                                 if (id==11)
515                                         cone_audio=TRUE;
516                                 got_audio=TRUE;
517                         }
518                         if (recvStunResponse(sock2,vc->addr,
519                                                         &vc->port,&id)>0){
520                                 ms_message("STUN test result: local video port maps to %s:%i",
521                                         vc->addr,
522                                         vc->port);
523                                 if (id==22)
524                                         cone_video=TRUE;
525                                 got_video=TRUE;
526                         }
527                         gettimeofday(&cur,NULL);
528                         elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
529                         if (elapsed>2000)  {
530                                 ms_message("Stun responses timeout, going ahead.");
531                                 break;
532                         }
533                         loops++;
534                 }while(!(got_audio && (got_video||sock2==-1)  ) );
535                 if (!got_audio){
536                         ms_error("No stun server response for audio port.");
537                 }else{
538                         if (!cone_audio) {
539                                 ms_message("NAT is symmetric for audio port");
540                         }
541                 }
542                 if (sock2!=-1){
543                         if (!got_video){
544                                 ms_error("No stun server response for video port.");
545                         }else{
546                                 if (!cone_video) {
547                                         ms_message("NAT is symmetric for video port.");
548                                 }
549                         }
550                 }
551                 if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0)
552                     || sock2==-1){
553                         strcpy(call->localdesc->addr,ac->addr);
554                 }
555                 close_socket(sock1);
556                 if (sock2!=-1) close_socket(sock2);
557         }
558 }
559
560 #ifdef HAVE_GETIFADDRS
561
562 #include <ifaddrs.h>
563 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
564 {
565         struct ifaddrs *ifp;
566         struct ifaddrs *ifpstart;
567         int ret = 0;
568
569         if (getifaddrs(&ifpstart) < 0) {
570                 return -1;
571         }
572
573         for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
574                 if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
575                         && (ifp->ifa_flags & IFF_RUNNING) && !(ifp->ifa_flags & IFF_LOOPBACK))
576                 {
577                         getnameinfo(ifp->ifa_addr,
578                                                 (type == AF_INET6) ?
579                                                 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
580                                                 address, size, NULL, 0, NI_NUMERICHOST);
581                         if (strchr(address, '%') == NULL) {     /*avoid ipv6 link-local addresses */
582                                 /*ms_message("getifaddrs() found %s",address);*/
583                                 ret++;
584                                 break;
585                         }
586                 }
587         }
588         freeifaddrs(ifpstart);
589         return ret;
590 }
591 #endif
592
593
594 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
595         int err,tmp;
596         struct addrinfo hints;
597         struct addrinfo *res=NULL;
598         struct sockaddr_storage addr;
599         struct sockaddr *p_addr=(struct sockaddr*)&addr;
600         ortp_socket_t sock;
601         socklen_t s;
602
603         memset(&hints,0,sizeof(hints));
604         hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
605         hints.ai_socktype=SOCK_DGRAM;
606         /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
607         err=getaddrinfo(dest,"5060",&hints,&res);
608         if (err!=0){
609                 ms_error("getaddrinfo() error: %s",gai_strerror(err));
610                 return -1;
611         }
612         if (res==NULL){
613                 ms_error("bug: getaddrinfo returned nothing.");
614                 return -1;
615         }
616         sock=socket(res->ai_family,SOCK_DGRAM,0);
617         tmp=1;
618         err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
619         if (err<0){
620                 ms_warning("Error in setsockopt: %s",strerror(errno));
621         }
622         err=connect(sock,res->ai_addr,res->ai_addrlen);
623         if (err<0) {
624                 ms_error("Error in connect: %s",strerror(errno));
625                 freeaddrinfo(res);
626                 close_socket(sock);
627                 return -1;
628         }
629         freeaddrinfo(res);
630         res=NULL;
631         s=sizeof(addr);
632         err=getsockname(sock,(struct sockaddr*)&addr,&s);
633         if (err!=0) {
634                 ms_error("Error in getsockname: %s",strerror(errno));
635                 close_socket(sock);
636                 return -1;
637         }
638         if (p_addr->sa_family==AF_INET){
639                 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
640                 if (p_sin->sin_addr.s_addr==0){
641                         close_socket(sock);
642                         return -1;
643                 }
644         }
645         err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
646         if (err!=0){
647                 ms_error("getnameinfo error: %s",strerror(errno));
648         }
649         close_socket(sock);
650         ms_message("Local interface to reach %s is %s.",dest,result);
651         return 0;
652 }
653
654 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
655         strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
656 #ifdef HAVE_GETIFADDRS
657         if (dest==NULL) {
658                 /*we use getifaddrs for lookup of default interface */
659                 int found_ifs;
660         
661                 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
662                 if (found_ifs==1){
663                         return 0;
664                 }else if (found_ifs<=0){
665                         /*absolutely no network on this machine */
666                         return -1;
667                 }
668         }
669 #endif
670         /*else use connect to find the best local ip address */
671         if (type==AF_INET)
672                 dest="87.98.157.38"; /*a public IP address*/
673         else dest="2a00:1450:8002::68";
674         return get_local_ip_for_with_connect(type,dest,result);
675 }
676
677 #ifndef WIN32
678 #include <resolv.h>
679
680
681
682
683 void _linphone_core_configure_resolver(){
684 /*bionic declares _res but does not define nor export it !!*/
685 #ifdef ANDROID
686         /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
687         setenv("RES_OPTIONS","timeout:2 attempts:2 retrans:2 retry:2",1);
688 #else
689         res_init();
690         _res.retrans=2; /*retransmit every two seconds*/
691         _res.retry=2; /*only two times per DNS server*/
692 #endif
693 }
694
695 #else
696
697 void _linphone_core_configure_resolver(){
698 }
699
700 #endif