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