]> sjero.net Git - linphone/blob - coreapi/misc.c
allow setting of a nat address as hostname
[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                 return _(desc->text);
209         }
210         return NULL;
211 }
212
213
214 /*this function makes a special case for speex/8000.
215 This codec is variable bitrate. The 8kbit/s mode is interesting when having a low upload bandwidth, but its quality
216 is not very good. We 'd better use its 15kbt/s mode when we have enough bandwidth*/
217 static int get_codec_bitrate(LinphoneCore *lc, const PayloadType *pt){
218         int upload_bw=linphone_core_get_upload_bandwidth(lc);
219         if (bandwidth_is_greater(upload_bw,129) || (bandwidth_is_greater(upload_bw,33) && !linphone_core_video_enabled(lc)) ) {
220                 if (strcmp(pt->mime_type,"speex")==0 && pt->clock_rate==8000){
221                         return 15000;
222                 }
223         }
224         return pt->normal_bitrate;
225 }
226
227 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
228         double npacket=50;
229         double packet_size;
230         int bitrate;
231         bitrate=get_codec_bitrate(lc,pt);
232         packet_size= (((double)bitrate)/(50*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
233         return packet_size*8.0*npacket;
234 }
235
236 void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
237         call->audio_bw=(int)(get_audio_payload_bandwidth(call->core,pt)/1000.0);
238         ms_message("Audio bandwidth for this call is %i",call->audio_bw);
239 }
240
241 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
242         const MSList *elem;
243         PayloadType *max=NULL;
244         for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
245                 PayloadType *pt=(PayloadType*)elem->data;
246                 if (payload_type_enabled(pt)){
247                         int pt_bitrate=get_codec_bitrate(lc,pt);
248                         if (max==NULL) max=pt;
249                         else if (max->normal_bitrate<pt_bitrate){
250                                 max=pt;
251                         }
252                 }
253         }
254         if (max) {
255                 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
256         }
257 }
258
259 bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt,  int bandwidth_limit)
260 {
261         double codec_band;
262         bool_t ret=FALSE;
263         
264         switch (pt->type){
265                 case PAYLOAD_AUDIO_CONTINUOUS:
266                 case PAYLOAD_AUDIO_PACKETIZED:
267                         codec_band=get_audio_payload_bandwidth(lc,pt);
268                         ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
269                         /*hack to avoid using uwb codecs when having low bitrate and video*/
270                         if (bandwidth_is_greater(199,bandwidth_limit)){
271                                 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
272                                         ret=FALSE;
273                                 }
274                         }
275                         //ms_message("Payload %s: %g",pt->mime_type,codec_band);
276                         break;
277                 case PAYLOAD_VIDEO:
278                         if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
279                                 ret=TRUE;
280                         }
281                         else ret=FALSE;
282                         break;
283         }
284         return ret;
285 }
286
287 /* return TRUE if codec can be used with bandwidth, FALSE else*/
288 bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
289 {
290         double codec_band;
291         int allowed_bw,video_bw;
292         bool_t ret=FALSE;
293
294         linphone_core_update_allocated_audio_bandwidth(lc);
295         allowed_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
296                                         linphone_core_get_upload_bandwidth(lc));
297         if (allowed_bw==0) {
298                 allowed_bw=-1;
299                 video_bw=1500; /*around 1.5 Mbit/s*/
300         }else
301                 video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
302
303         switch (pt->type){
304                 case PAYLOAD_AUDIO_CONTINUOUS:
305                 case PAYLOAD_AUDIO_PACKETIZED:
306                         codec_band=get_audio_payload_bandwidth(lc,pt);
307                         ret=bandwidth_is_greater(allowed_bw*1000,codec_band);
308                         /*hack to avoid using uwb codecs when having low bitrate and video*/
309                         if (bandwidth_is_greater(199,allowed_bw)){
310                                 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
311                                         ret=FALSE;
312                                 }
313                         }
314                         //ms_message("Payload %s: %g",pt->mime_type,codec_band);
315                         break;
316                 case PAYLOAD_VIDEO:
317                         if (video_bw>0){
318                                 pt->normal_bitrate=video_bw*1000;
319                                 ret=TRUE;
320                         }
321                         else ret=FALSE;
322                         break;
323         }
324         return ret;
325 }
326
327 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
328 #if !defined(_WIN32_WCE)
329         FILE *f=popen(command,"r");
330         if (f!=NULL){
331                 int err;
332                 *result=ms_malloc(4096);
333                 err=fread(*result,1,4096-1,f);
334                 if (err<0){
335                         ms_warning("Error reading command output:%s",strerror(errno));
336                         ms_free(result);
337                         return FALSE;
338                 }
339                 (*result)[err]=0;
340                 err=pclose(f);
341                 if (command_ret!=NULL) *command_ret=err;
342                 return TRUE;
343         }
344 #endif /*_WIN32_WCE*/
345         return FALSE;
346 }
347
348 static ortp_socket_t create_socket(int local_port){
349         struct sockaddr_in laddr;
350         ortp_socket_t sock;
351         int optval;
352         sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
353         if (sock<0) {
354                 ms_error("Fail to create socket");
355                 return -1;
356         }
357         memset (&laddr,0,sizeof(laddr));
358         laddr.sin_family=AF_INET;
359         laddr.sin_addr.s_addr=INADDR_ANY;
360         laddr.sin_port=htons(local_port);
361         if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
362                 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
363                 close_socket(sock);
364                 return -1;
365         }
366         optval=1;
367         if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
368                                 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
369                 ms_warning("Fail to set SO_REUSEADDR");
370         }
371         set_non_blocking_socket(sock);
372         return sock;
373 }
374
375 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
376         char buf[STUN_MAX_MESSAGE_SIZE];
377         int len = STUN_MAX_MESSAGE_SIZE;
378         StunAtrString username;
379         StunAtrString password;
380         StunMessage req;
381         int err;
382         memset(&req, 0, sizeof(StunMessage));
383         memset(&username,0,sizeof(username));
384         memset(&password,0,sizeof(password));
385         stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
386         len = stunEncodeMessage( &req, buf, len, &password);
387         if (len<=0){
388                 ms_error("Fail to encode stun message.");
389                 return -1;
390         }
391         err=sendto(sock,buf,len,0,server,addrlen);
392         if (err<0){
393                 ms_error("sendto failed: %s",strerror(errno));
394                 return -1;
395         }
396         return 0;
397 }
398
399 int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
400         struct addrinfo hints,*res=NULL;
401         int ret;
402         const char *port;
403         char host[NI_MAXHOST];
404         char *p;
405         host[NI_MAXHOST-1]='\0';
406         strncpy(host,server,sizeof(host)-1);
407         p=strchr(host,':');
408         if (p) {
409                 *p='\0';
410                 port=p+1;
411         }else port="3478";
412         memset(&hints,0,sizeof(hints));
413         hints.ai_family=PF_INET;
414         hints.ai_socktype=SOCK_DGRAM;
415         hints.ai_protocol=IPPROTO_UDP;
416         ret=getaddrinfo(host,port,&hints,&res);
417         if (ret!=0){
418                 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
419                 return -1;
420         }
421         if (!res) return -1;
422         memcpy(ss,res->ai_addr,res->ai_addrlen);
423         *socklen=res->ai_addrlen;
424         freeaddrinfo(res);
425         return 0;
426 }
427
428 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
429         char buf[STUN_MAX_MESSAGE_SIZE];
430         int len = STUN_MAX_MESSAGE_SIZE;
431         StunMessage resp;
432         len=recv(sock,buf,len,0);
433         if (len>0){
434                 struct in_addr ia;
435                 stunParseMessage(buf,len, &resp );
436                 *id=resp.msgHdr.tr_id.octet[0];
437                 if (resp.hasXorMappedAddress){
438                         *port = resp.xorMappedAddress.ipv4.port;
439                         ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
440                 }else if (resp.hasMappedAddress){
441                         *port = resp.mappedAddress.ipv4.port;
442                         ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
443                 }else return -1;
444                 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
445         }
446         return len;
447 }
448
449 void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
450         const char *server=linphone_core_get_stun_server(lc);
451
452         if (lc->sip_conf.ipv6_enabled){
453                 ms_warning("stun support is not implemented for ipv6");
454                 return;
455         }
456         if (server!=NULL){
457                 struct sockaddr_storage ss;
458                 socklen_t ss_len;
459                 ortp_socket_t sock1=-1, sock2=-1;
460                 bool_t video_enabled=linphone_core_video_enabled(lc);
461                 bool_t got_audio,got_video;
462                 bool_t cone_audio=FALSE,cone_video=FALSE;
463                 struct timeval init,cur;
464                 SalEndpointCandidate *ac,*vc;
465                 
466                 ac=&call->localdesc->streams[0].candidates[0];
467                 vc=&call->localdesc->streams[1].candidates[0];
468                 
469                 if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
470                         ms_error("Fail to parser stun server address: %s",server);
471                         return;
472                 }
473                 if (lc->vtable.display_status!=NULL)
474                         lc->vtable.display_status(lc,_("Stun lookup in progress..."));
475
476                 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
477                 sock1=create_socket(call->audio_port);
478                 if (sock1<0) return;
479                 if (video_enabled){
480                         sock2=create_socket(call->video_port);
481                         if (sock2<0) return ;
482                 }
483                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
484                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
485                 if (sock2>=0){
486                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
487                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
488                 }
489                 got_audio=FALSE;
490                 got_video=FALSE;
491                 gettimeofday(&init,NULL);
492                 do{
493                         double elapsed;
494                         int id;
495 #ifdef WIN32
496                         Sleep(10);
497 #else
498                         usleep(10000);
499 #endif
500
501                         if (recvStunResponse(sock1,ac->addr,
502                                                 &ac->port,&id)>0){
503                                 ms_message("STUN test result: local audio port maps to %s:%i",
504                                                 ac->addr,
505                                                 ac->port);
506                                 if (id==11)
507                                         cone_audio=TRUE;
508                                 got_audio=TRUE;
509                         }
510                         if (recvStunResponse(sock2,vc->addr,
511                                                         &vc->port,&id)>0){
512                                 ms_message("STUN test result: local video port maps to %s:%i",
513                                         vc->addr,
514                                         vc->port);
515                                 if (id==22)
516                                         cone_video=TRUE;
517                                 got_video=TRUE;
518                         }
519                         gettimeofday(&cur,NULL);
520                         elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
521                         if (elapsed>2000)  break;
522                 }while(!(got_audio && (got_video||sock2<0)  ) );
523                 if (!got_audio){
524                         ms_error("No stun server response for audio port.");
525                 }else{
526                         if (!cone_audio) {
527                                 ms_warning("NAT is symmetric for audio port");
528                                 /*
529                                 ac->addr[0]='\0';
530                                 ac->port=0;
531                                 */
532                         }
533                 }
534                 if (sock2>=0){
535                         if (!got_video){
536                                 ms_error("No stun server response for video port.");
537                         }else{
538                                 if (!cone_video) {
539                                         ms_warning("NAT is symmetric for video port.");
540                                         /*
541                                         vc->addr[0]='\0';
542                                         vc->port=0;
543                                         */
544                                 }
545                         }
546                 }
547                 if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0)
548                     || sock2==-1){
549                         strcpy(call->localdesc->addr,ac->addr);
550                 }
551                 close_socket(sock1);
552                 if (sock2>=0) close_socket(sock2);
553         }
554 }
555
556 static int extract_sip_port(const char *config){
557         char line[512];
558         char port[12];
559         int ret=-1;
560         FILE *f=fopen(config,"r");
561         if (f){
562                 while(fgets(line,sizeof(line),f)!=NULL){
563                         if (fmtp_get_value(line,"sip_port",port,sizeof(port))){
564                                 ret=atoi(port);
565                         }
566                 }
567                 fclose(f);
568         }
569         return ret;
570 }
571
572 int linphone_core_wake_up_possible_already_running_instance(
573     const char * config_file, const char * addr_to_call)
574 {
575         int port=extract_sip_port(config_file);
576         const char *wakeup="WAKEUP sip:127.0.0.1 SIP/2.0\r\n"
577                 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
578                 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
579                 "To:   <sip:you@127.0.0.1>\r\n"
580                 "CSeq: 1 WAKEUP\r\n"
581                 "Call-ID: %u@onsantape\r\n"
582                 "Content-length: 0\r\n\r\n";
583         const char * call = "REFER sip:127.0.0.1 SIP/2.0\r\n"
584                 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
585                 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
586                 "To:   <sip:you@127.0.0.1>\r\n"
587                 "Refer-To: %s\r\n"
588                 "CSeq: 1 WAKEUP\r\n"
589                 "Call-ID: %u@onsantape\r\n"
590                 "Content-length: 0\r\n\r\n";
591
592         /*make sure ortp is initialized (it initializes win32 socket api)*/
593         ortp_init();
594         if (port>0){
595                 struct sockaddr_storage ss;
596                 socklen_t sslen;
597                 char tmp[100];
598                 snprintf(tmp,sizeof(tmp),"127.0.0.1:%i",port);
599                 if (parse_hostname_to_addr(tmp,&ss,&sslen)==0){
600                         int locport=57123;
601                         ortp_socket_t sock=create_socket(locport);
602                         if (sock<0) sock=create_socket(++locport);
603                         if (sock>=0){
604                                 char req[512];
605                                 if (addr_to_call != NULL)
606                                         snprintf(req, sizeof(req), call, locport,
607                                         random(), random(), addr_to_call, random());
608                                 else
609                                         snprintf(req, sizeof(req), wakeup, locport,
610                                         random(), random(), random());
611                                 if (connect(sock,(struct sockaddr*)&ss,sslen)<0){
612                                         fprintf(stderr,"connect failed: %s\n",getSocketError());
613                                 }else if (send(sock,req,strlen(req),0)>0){
614                                         /*wait a bit for a response*/
615                                         int i;
616                                         for(i=0;i<10;++i){
617                                                 if (recv(sock,req,sizeof(req),0)>0){
618                                                         close_socket(sock);
619                                                         return 0;
620                                                 }else if (getSocketErrorCode()!=EWOULDBLOCK){
621                                                         break;
622                                                 }
623 #ifdef WIN32
624                                                 Sleep(100);
625 #else
626                                                 usleep(100000);
627 #endif
628                                         }
629                                 }else{
630                                         ms_message("sendto() of WAKEUP request failed, nobody to wakeup.");
631                                 }
632                         }
633                         close_socket(sock);
634                 }
635         }
636         return -1;
637 }
638
639 #ifdef HAVE_GETIFADDRS
640
641 #include <ifaddrs.h>
642 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
643 {
644         struct ifaddrs *ifp;
645         struct ifaddrs *ifpstart;
646         int ret = 0;
647
648         if (getifaddrs(&ifpstart) < 0) {
649                 return -1;
650         }
651
652         for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
653                 if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
654                         && (ifp->ifa_flags & IFF_RUNNING) && !(ifp->ifa_flags & IFF_LOOPBACK))
655                 {
656                         getnameinfo(ifp->ifa_addr,
657                                                 (type == AF_INET6) ?
658                                                 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
659                                                 address, size, NULL, 0, NI_NUMERICHOST);
660                         if (strchr(address, '%') == NULL) {     /*avoid ipv6 link-local addresses */
661                                 /*ms_message("getifaddrs() found %s",address);*/
662                                 ret++;
663                                 break;
664                         }
665                 }
666         }
667         freeifaddrs(ifpstart);
668         return ret;
669 }
670 #endif
671
672
673 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
674         int err,tmp;
675         struct addrinfo hints;
676         struct addrinfo *res=NULL;
677         struct sockaddr_storage addr;
678         struct sockaddr *p_addr=(struct sockaddr*)&addr;
679         ortp_socket_t sock;
680         socklen_t s;
681
682         memset(&hints,0,sizeof(hints));
683         hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
684         hints.ai_socktype=SOCK_DGRAM;
685         /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
686         err=getaddrinfo(dest,"5060",&hints,&res);
687         if (err!=0){
688                 ms_error("getaddrinfo() error: %s",gai_strerror(err));
689                 return -1;
690         }
691         if (res==NULL){
692                 ms_error("bug: getaddrinfo returned nothing.");
693                 return -1;
694         }
695         sock=socket(res->ai_family,SOCK_DGRAM,0);
696         tmp=1;
697         err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
698         if (err<0){
699                 ms_warning("Error in setsockopt: %s",strerror(errno));
700         }
701         err=connect(sock,res->ai_addr,res->ai_addrlen);
702         if (err<0) {
703                 ms_error("Error in connect: %s",strerror(errno));
704                 freeaddrinfo(res);
705                 close_socket(sock);
706                 return -1;
707         }
708         freeaddrinfo(res);
709         res=NULL;
710         s=sizeof(addr);
711         err=getsockname(sock,(struct sockaddr*)&addr,&s);
712         if (err!=0) {
713                 ms_error("Error in getsockname: %s",strerror(errno));
714                 close_socket(sock);
715                 return -1;
716         }
717         if (p_addr->sa_family==AF_INET){
718                 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
719                 if (p_sin->sin_addr.s_addr==0){
720                         close_socket(sock);
721                         return -1;
722                 }
723         }
724         err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
725         if (err!=0){
726                 ms_error("getnameinfo error: %s",strerror(errno));
727         }
728         close_socket(sock);
729         ms_message("Local interface to reach %s is %s.",dest,result);
730         return 0;
731 }
732
733 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
734         strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
735 #ifdef HAVE_GETIFADDRS
736         if (dest==NULL) {
737                 /*we use getifaddrs for lookup of default interface */
738                 int found_ifs;
739         
740                 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
741                 if (found_ifs==1){
742                         return 0;
743                 }else if (found_ifs<=0){
744                         /*absolutely no network on this machine */
745                         return -1;
746                 }
747         }
748 #endif
749         /*else use connect to find the best local ip address */
750         if (type==AF_INET)
751                 dest="87.98.157.38"; /*a public IP address*/
752         else dest="2a00:1450:8002::68";
753         return get_local_ip_for_with_connect(type,dest,result);
754 }
755
756 #ifndef WIN32
757 #include <resolv.h>
758
759
760
761
762 void _linphone_core_configure_resolver(){
763 /*bionic declares _res but does not define nor export it !!*/
764 #ifdef ANDROID
765         /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
766         setenv("RES_OPTIONS","timeout:2 attempts:2 retrans:2 retry:2",1);
767 #else
768         res_init();
769         _res.retrans=2; /*retransmit every two seconds*/
770         _res.retry=2; /*only two times per DNS server*/
771 #endif
772 }
773
774 #else
775
776 void _linphone_core_configure_resolver(){
777 }
778
779 #endif