]> sjero.net Git - linphone/blob - coreapi/misc.c
Merge branch 'master' into dev_sal
[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                         //ms_message("Payload %s: %g",pt->mime_type,codec_band);
280                         break;
281                 case PAYLOAD_VIDEO:
282                         if (min_video_bw!=0) {/* infinite (-1) or strictly positive*/
283                                 /*let the video use all the bandwidth minus the maximum bandwidth used by audio */
284                                 if (min_video_bw>0)
285                                         pt->normal_bitrate=min_video_bw*1000;
286                                 else
287                                         pt->normal_bitrate=1500000; /*around 1.5 Mbit/s*/
288                                 ret=TRUE;
289                         }
290                         else ret=FALSE;
291                         break;
292         }
293         /*if (!ret) ms_warning("Payload %s is not usable with your internet connection.",pt->mime_type);*/
294
295         return ret;
296 }
297
298 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
299 #if !defined(_WIN32_WCE)
300         FILE *f=popen(command,"r");
301         if (f!=NULL){
302                 int err;
303                 *result=ms_malloc(4096);
304                 err=fread(*result,1,4096-1,f);
305                 if (err<0){
306                         ms_warning("Error reading command output:%s",strerror(errno));
307                         ms_free(result);
308                         return FALSE;
309                 }
310                 (*result)[err]=0;
311                 err=pclose(f);
312                 if (command_ret!=NULL) *command_ret=err;
313                 return TRUE;
314         }
315 #endif /*_WIN32_WCE*/
316         return FALSE;
317 }
318
319 #if defined(HAVE_GETIFADDRS) && defined(INET6)
320 #include <sys/types.h>
321 #include <sys/socket.h>
322 #include <ifaddrs.h>
323 bool_t host_has_ipv6_network()
324 {
325         struct ifaddrs *ifp;
326         struct ifaddrs *ifpstart;
327         bool_t ipv6_present=FALSE;
328
329         if (getifaddrs (&ifpstart) < 0)
330         {
331                 return FALSE;
332         }
333
334         for (ifp=ifpstart; ifp != NULL; ifp = ifp->ifa_next)
335         {
336                 if (!ifp->ifa_addr)
337                   continue;
338
339                 switch (ifp->ifa_addr->sa_family) {
340                 case AF_INET:
341
342                         break;
343                 case AF_INET6:
344                     ipv6_present=TRUE;
345                         break;
346                 default:
347                         continue;
348                 }
349         }
350
351         freeifaddrs (ifpstart);
352
353         return ipv6_present;
354 }
355 #else
356
357 bool_t host_has_ipv6_network()
358 {
359         return FALSE;
360 }
361
362
363 #endif
364
365 static ortp_socket_t create_socket(int local_port){
366         struct sockaddr_in laddr;
367         ortp_socket_t sock;
368         int optval;
369         sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
370         if (sock<0) {
371                 ms_error("Fail to create socket");
372                 return -1;
373         }
374         memset (&laddr,0,sizeof(laddr));
375         laddr.sin_family=AF_INET;
376         laddr.sin_addr.s_addr=INADDR_ANY;
377         laddr.sin_port=htons(local_port);
378         if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
379                 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
380                 close_socket(sock);
381                 return -1;
382         }
383         optval=1;
384         if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
385                                 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
386                 ms_warning("Fail to set SO_REUSEADDR");
387         }
388         set_non_blocking_socket(sock);
389         return sock;
390 }
391
392 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
393         char buf[STUN_MAX_MESSAGE_SIZE];
394         int len = STUN_MAX_MESSAGE_SIZE;
395         StunAtrString username;
396         StunAtrString password;
397         StunMessage req;
398         int err;
399         memset(&req, 0, sizeof(StunMessage));
400         memset(&username,0,sizeof(username));
401         memset(&password,0,sizeof(password));
402         stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
403         len = stunEncodeMessage( &req, buf, len, &password);
404         if (len<=0){
405                 ms_error("Fail to encode stun message.");
406                 return -1;
407         }
408         err=sendto(sock,buf,len,0,server,addrlen);
409         if (err<0){
410                 ms_error("sendto failed: %s",strerror(errno));
411                 return -1;
412         }
413         return 0;
414 }
415
416 static int parse_stun_server_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
417         struct addrinfo hints,*res=NULL;
418         int ret;
419         const char *port;
420         char host[NI_MAXHOST];
421         char *p;
422         host[NI_MAXHOST-1]='\0';
423         strncpy(host,server,sizeof(host)-1);
424         p=strchr(host,':');
425         if (p) {
426                 *p='\0';
427                 port=p+1;
428         }else port="3478";
429         memset(&hints,0,sizeof(hints));
430         hints.ai_family=PF_INET;
431         hints.ai_socktype=SOCK_DGRAM;
432         hints.ai_protocol=IPPROTO_UDP;
433         ret=getaddrinfo(host,port,&hints,&res);
434         if (ret!=0){
435                 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
436                 return -1;
437         }
438         if (!res) return -1;
439         memcpy(ss,res->ai_addr,res->ai_addrlen);
440         *socklen=res->ai_addrlen;
441         freeaddrinfo(res);
442         return 0;
443 }
444
445 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
446         char buf[STUN_MAX_MESSAGE_SIZE];
447         int len = STUN_MAX_MESSAGE_SIZE;
448         StunMessage resp;
449         len=recv(sock,buf,len,0);
450         if (len>0){
451                 struct in_addr ia;
452                 stunParseMessage(buf,len, &resp );
453                 *id=resp.msgHdr.tr_id.octet[0];
454                 *port = resp.mappedAddress.ipv4.port;
455                 ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
456                 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
457         }
458         return len;
459 }
460
461 void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
462         const char *server=linphone_core_get_stun_server(lc);
463
464         if (lc->sip_conf.ipv6_enabled){
465                 ms_warning("stun support is not implemented for ipv6");
466                 return;
467         }
468         if (server!=NULL){
469                 struct sockaddr_storage ss;
470                 socklen_t ss_len;
471                 ortp_socket_t sock1=-1, sock2=-1;
472                 bool_t video_enabled=linphone_core_video_enabled(lc);
473                 bool_t got_audio,got_video;
474                 bool_t cone_audio=FALSE,cone_video=FALSE;
475                 struct timeval init,cur;
476                 SalEndpointCandidate *ac,*vc;
477                 
478                 ac=&call->localdesc->streams[0].candidates[0];
479                 vc=&call->localdesc->streams[1].candidates[0];
480                 
481                 if (parse_stun_server_addr(server,&ss,&ss_len)<0){
482                         ms_error("Fail to parser stun server address: %s",server);
483                         return;
484                 }
485                 if (lc->vtable.display_status!=NULL)
486                         lc->vtable.display_status(lc,_("Stun lookup in progress..."));
487
488                 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
489                 sock1=create_socket(linphone_core_get_audio_port(lc));
490                 if (sock1<0) return;
491                 if (video_enabled){
492                         sock2=create_socket(linphone_core_get_video_port(lc));
493                         if (sock2<0) return ;
494                 }
495                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
496                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
497                 if (sock2>=0){
498                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
499                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
500                 }
501                 got_audio=FALSE;
502                 got_video=FALSE;
503                 gettimeofday(&init,NULL);
504                 do{
505                         double elapsed;
506                         int id;
507 #ifdef WIN32
508                         Sleep(10);
509 #else
510                         usleep(10000);
511 #endif
512
513                         if (recvStunResponse(sock1,ac->addr,
514                                                 &ac->port,&id)>0){
515                                 ms_message("STUN test result: local audio port maps to %s:%i",
516                                                 ac->addr,
517                                                 ac->port);
518                                 if (id==11)
519                                         cone_audio=TRUE;
520                                 got_audio=TRUE;
521                         }
522                         if (recvStunResponse(sock2,vc->addr,
523                                                         &vc->port,&id)>0){
524                                 ms_message("STUN test result: local video port maps to %s:%i",
525                                         vc->addr,
526                                         vc->port);
527                                 if (id==22)
528                                         cone_video=TRUE;
529                                 got_video=TRUE;
530                         }
531                         gettimeofday(&cur,NULL);
532                         elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
533                         if (elapsed>2000)  break;
534                 }while(!(got_audio && (got_video||sock2<0)  ) );
535                 if (!got_audio){
536                         ms_error("No stun server response for audio port.");
537                 }else{
538                         if (!cone_audio) {
539                                 ms_warning("NAT is symmetric for audio port");
540                                 ac->addr[0]='\0';
541                                 ac->port=0;
542                         }
543                 }
544                 if (sock2>=0){
545                         if (!got_video){
546                                 ms_error("No stun server response for video port.");
547                         }else{
548                                 if (!cone_video) {
549                                         ms_warning("NAT is symmetric for video port.");
550                                         vc->addr[0]='\0';
551                                         vc->port=0;
552                                 }
553                         }
554                 }
555                 close_socket(sock1);
556                 if (sock2>=0) close_socket(sock2);
557         }
558 }
559
560 static int extract_sip_port(const char *config){
561         char line[512];
562         char port[12];
563         int ret=-1;
564         FILE *f=fopen(config,"r");
565         if (f){
566                 while(fgets(line,sizeof(line),f)!=NULL){
567                         if (fmtp_get_value(line,"sip_port",port,sizeof(port))){
568                                 ret=atoi(port);
569                         }
570                 }
571                 fclose(f);
572         }
573         return ret;
574 }
575
576 int linphone_core_wake_up_possible_already_running_instance(
577     const char * config_file, const char * addr_to_call)
578 {
579         int port=extract_sip_port(config_file);
580         const char *wakeup="WAKEUP sip:127.0.0.1 SIP/2.0\r\n"
581                 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
582                 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
583                 "To:   <sip:you@127.0.0.1>\r\n"
584                 "CSeq: 1 WAKEUP\r\n"
585                 "Call-ID: %u@onsantape\r\n"
586                 "Content-length: 0\r\n\r\n";
587         const char * call = "REFER sip:127.0.0.1 SIP/2.0\r\n"
588                 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
589                 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
590                 "To:   <sip:you@127.0.0.1>\r\n"
591                 "Refer-To: %s\r\n"
592                 "CSeq: 1 WAKEUP\r\n"
593                 "Call-ID: %u@onsantape\r\n"
594                 "Content-length: 0\r\n\r\n";
595
596         /*make sure ortp is initialized (it initializes win32 socket api)*/
597         ortp_init();
598         if (port>0){
599                 struct sockaddr_storage ss;
600                 socklen_t sslen;
601                 char tmp[100];
602                 snprintf(tmp,sizeof(tmp),"127.0.0.1:%i",port);
603                 if (parse_stun_server_addr(tmp,&ss,&sslen)==0){
604                         int locport=57123;
605                         ortp_socket_t sock=create_socket(locport);
606                         if (sock<0) sock=create_socket(++locport);
607                         if (sock>=0){
608                                 char req[512];
609                                 if (addr_to_call != NULL)
610                                         snprintf(req, sizeof(req), call, locport,
611                                         random(), random(), addr_to_call, random());
612                                 else
613                                         snprintf(req, sizeof(req), wakeup, locport,
614                                         random(), random(), random());
615                                 if (connect(sock,(struct sockaddr*)&ss,sslen)<0){
616                                         fprintf(stderr,"connect failed: %s\n",getSocketError());
617                                 }else if (send(sock,req,strlen(req),0)>0){
618                                         /*wait a bit for a response*/
619                                         int i;
620                                         for(i=0;i<10;++i){
621                                                 if (recv(sock,req,sizeof(req),0)>0){
622                                                         close_socket(sock);
623                                                         return 0;
624                                                 }else if (getSocketErrorCode()!=EWOULDBLOCK){
625                                                         break;
626                                                 }
627 #ifdef WIN32
628                                                 Sleep(100);
629 #else
630                                                 usleep(100000);
631 #endif
632                                         }
633                                 }else{
634                                         ms_message("sendto() of WAKEUP request failed, nobody to wakeup.");
635                                 }
636                         }
637                         close_socket(sock);
638                 }
639         }
640         return -1;
641 }
642
643 int linphone_core_get_local_ip_for(const char *dest, char *result){
644         int err,tmp;
645         struct addrinfo hints;
646         struct addrinfo *res=NULL;
647         struct sockaddr_storage addr;
648         struct sockaddr *p_addr=(struct sockaddr*)&addr;
649         ortp_socket_t sock;
650         socklen_t s;
651
652         memset(&hints,0,sizeof(hints));
653         hints.ai_family=PF_UNSPEC;
654         hints.ai_socktype=SOCK_DGRAM;
655         /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
656         err=getaddrinfo(dest,"5060",&hints,&res);
657         if (err!=0){
658                 ms_error("getaddrinfo() error: %s",gai_strerror(err));
659                 return -1;
660         }
661         if (res==NULL){
662                 ms_error("bug: getaddrinfo returned nothing.");
663                 return -1;
664         }
665         sock=socket(res->ai_family,SOCK_DGRAM,0);
666         tmp=1;
667         err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
668         if (err<0){
669                 ms_warning("Error in setsockopt: %s",strerror(errno));
670         }
671         err=connect(sock,res->ai_addr,res->ai_addrlen);
672         if (err<0) {
673                 ms_error("Error in connect: %s",strerror(errno));
674                 freeaddrinfo(res);
675                 close_socket(sock);
676                 return -1;
677         }
678         freeaddrinfo(res);
679         res=NULL;
680         s=sizeof(addr);
681         err=getsockname(sock,(struct sockaddr*)&addr,&s);
682         if (err!=0) {
683                 ms_error("Error in getsockname: %s",strerror(errno));
684                 close_socket(sock);
685                 return -1;
686         }
687         if (p_addr->sa_family==AF_INET){
688                 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
689                 if (p_sin->sin_addr.s_addr==0){
690                         close_socket(sock);
691                         return -1;
692                 }
693         }
694         err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
695         if (err!=0){
696                 ms_error("getnameinfo error: %s",strerror(errno));
697         }
698         close_socket(sock);
699         ms_message("Local interface to reach %s is %s.",dest,result);
700         return 0;
701 }