]> sjero.net Git - linphone/blob - coreapi/misc.c
bugfixes again for bandwidth management
[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 #if defined(HAVE_GETIFADDRS) && defined(INET6)
349 #include <sys/types.h>
350 #include <sys/socket.h>
351 #include <ifaddrs.h>
352 bool_t host_has_ipv6_network()
353 {
354         struct ifaddrs *ifp;
355         struct ifaddrs *ifpstart;
356         bool_t ipv6_present=FALSE;
357
358         if (getifaddrs (&ifpstart) < 0)
359         {
360                 return FALSE;
361         }
362
363         for (ifp=ifpstart; ifp != NULL; ifp = ifp->ifa_next)
364         {
365                 if (!ifp->ifa_addr)
366                   continue;
367
368                 switch (ifp->ifa_addr->sa_family) {
369                 case AF_INET:
370
371                         break;
372                 case AF_INET6:
373                     ipv6_present=TRUE;
374                         break;
375                 default:
376                         continue;
377                 }
378         }
379
380         freeifaddrs (ifpstart);
381
382         return ipv6_present;
383 }
384 #else
385
386 bool_t host_has_ipv6_network()
387 {
388         return FALSE;
389 }
390
391
392 #endif
393
394 static ortp_socket_t create_socket(int local_port){
395         struct sockaddr_in laddr;
396         ortp_socket_t sock;
397         int optval;
398         sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
399         if (sock<0) {
400                 ms_error("Fail to create socket");
401                 return -1;
402         }
403         memset (&laddr,0,sizeof(laddr));
404         laddr.sin_family=AF_INET;
405         laddr.sin_addr.s_addr=INADDR_ANY;
406         laddr.sin_port=htons(local_port);
407         if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
408                 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
409                 close_socket(sock);
410                 return -1;
411         }
412         optval=1;
413         if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
414                                 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
415                 ms_warning("Fail to set SO_REUSEADDR");
416         }
417         set_non_blocking_socket(sock);
418         return sock;
419 }
420
421 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
422         char buf[STUN_MAX_MESSAGE_SIZE];
423         int len = STUN_MAX_MESSAGE_SIZE;
424         StunAtrString username;
425         StunAtrString password;
426         StunMessage req;
427         int err;
428         memset(&req, 0, sizeof(StunMessage));
429         memset(&username,0,sizeof(username));
430         memset(&password,0,sizeof(password));
431         stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
432         len = stunEncodeMessage( &req, buf, len, &password);
433         if (len<=0){
434                 ms_error("Fail to encode stun message.");
435                 return -1;
436         }
437         err=sendto(sock,buf,len,0,server,addrlen);
438         if (err<0){
439                 ms_error("sendto failed: %s",strerror(errno));
440                 return -1;
441         }
442         return 0;
443 }
444
445 static int parse_stun_server_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
446         struct addrinfo hints,*res=NULL;
447         int ret;
448         const char *port;
449         char host[NI_MAXHOST];
450         char *p;
451         host[NI_MAXHOST-1]='\0';
452         strncpy(host,server,sizeof(host)-1);
453         p=strchr(host,':');
454         if (p) {
455                 *p='\0';
456                 port=p+1;
457         }else port="3478";
458         memset(&hints,0,sizeof(hints));
459         hints.ai_family=PF_INET;
460         hints.ai_socktype=SOCK_DGRAM;
461         hints.ai_protocol=IPPROTO_UDP;
462         ret=getaddrinfo(host,port,&hints,&res);
463         if (ret!=0){
464                 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
465                 return -1;
466         }
467         if (!res) return -1;
468         memcpy(ss,res->ai_addr,res->ai_addrlen);
469         *socklen=res->ai_addrlen;
470         freeaddrinfo(res);
471         return 0;
472 }
473
474 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
475         char buf[STUN_MAX_MESSAGE_SIZE];
476         int len = STUN_MAX_MESSAGE_SIZE;
477         StunMessage resp;
478         len=recv(sock,buf,len,0);
479         if (len>0){
480                 struct in_addr ia;
481                 stunParseMessage(buf,len, &resp );
482                 *id=resp.msgHdr.tr_id.octet[0];
483                 if (resp.hasXorMappedAddress){
484                         *port = resp.xorMappedAddress.ipv4.port;
485                         ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
486                 }else if (resp.hasMappedAddress){
487                         *port = resp.mappedAddress.ipv4.port;
488                         ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
489                 }else return -1;
490                 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
491         }
492         return len;
493 }
494
495 void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
496         const char *server=linphone_core_get_stun_server(lc);
497
498         if (lc->sip_conf.ipv6_enabled){
499                 ms_warning("stun support is not implemented for ipv6");
500                 return;
501         }
502         if (server!=NULL){
503                 struct sockaddr_storage ss;
504                 socklen_t ss_len;
505                 ortp_socket_t sock1=-1, sock2=-1;
506                 bool_t video_enabled=linphone_core_video_enabled(lc);
507                 bool_t got_audio,got_video;
508                 bool_t cone_audio=FALSE,cone_video=FALSE;
509                 struct timeval init,cur;
510                 SalEndpointCandidate *ac,*vc;
511                 
512                 ac=&call->localdesc->streams[0].candidates[0];
513                 vc=&call->localdesc->streams[1].candidates[0];
514                 
515                 if (parse_stun_server_addr(server,&ss,&ss_len)<0){
516                         ms_error("Fail to parser stun server address: %s",server);
517                         return;
518                 }
519                 if (lc->vtable.display_status!=NULL)
520                         lc->vtable.display_status(lc,_("Stun lookup in progress..."));
521
522                 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
523                 sock1=create_socket(call->audio_port);
524                 if (sock1<0) return;
525                 if (video_enabled){
526                         sock2=create_socket(call->video_port);
527                         if (sock2<0) return ;
528                 }
529                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
530                 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
531                 if (sock2>=0){
532                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
533                         sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
534                 }
535                 got_audio=FALSE;
536                 got_video=FALSE;
537                 gettimeofday(&init,NULL);
538                 do{
539                         double elapsed;
540                         int id;
541 #ifdef WIN32
542                         Sleep(10);
543 #else
544                         usleep(10000);
545 #endif
546
547                         if (recvStunResponse(sock1,ac->addr,
548                                                 &ac->port,&id)>0){
549                                 ms_message("STUN test result: local audio port maps to %s:%i",
550                                                 ac->addr,
551                                                 ac->port);
552                                 if (id==11)
553                                         cone_audio=TRUE;
554                                 got_audio=TRUE;
555                         }
556                         if (recvStunResponse(sock2,vc->addr,
557                                                         &vc->port,&id)>0){
558                                 ms_message("STUN test result: local video port maps to %s:%i",
559                                         vc->addr,
560                                         vc->port);
561                                 if (id==22)
562                                         cone_video=TRUE;
563                                 got_video=TRUE;
564                         }
565                         gettimeofday(&cur,NULL);
566                         elapsed=((cur.tv_sec-init.tv_sec)*1000.0) +  ((cur.tv_usec-init.tv_usec)/1000.0);
567                         if (elapsed>2000)  break;
568                 }while(!(got_audio && (got_video||sock2<0)  ) );
569                 if (!got_audio){
570                         ms_error("No stun server response for audio port.");
571                 }else{
572                         if (!cone_audio) {
573                                 ms_warning("NAT is symmetric for audio port");
574                                 /*
575                                 ac->addr[0]='\0';
576                                 ac->port=0;
577                                 */
578                         }
579                 }
580                 if (sock2>=0){
581                         if (!got_video){
582                                 ms_error("No stun server response for video port.");
583                         }else{
584                                 if (!cone_video) {
585                                         ms_warning("NAT is symmetric for video port.");
586                                         /*
587                                         vc->addr[0]='\0';
588                                         vc->port=0;
589                                         */
590                                 }
591                         }
592                 }
593                 if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0)
594                     || sock2==-1){
595                         strcpy(call->localdesc->addr,ac->addr);
596                 }
597                 close_socket(sock1);
598                 if (sock2>=0) close_socket(sock2);
599         }
600 }
601
602 static int extract_sip_port(const char *config){
603         char line[512];
604         char port[12];
605         int ret=-1;
606         FILE *f=fopen(config,"r");
607         if (f){
608                 while(fgets(line,sizeof(line),f)!=NULL){
609                         if (fmtp_get_value(line,"sip_port",port,sizeof(port))){
610                                 ret=atoi(port);
611                         }
612                 }
613                 fclose(f);
614         }
615         return ret;
616 }
617
618 int linphone_core_wake_up_possible_already_running_instance(
619     const char * config_file, const char * addr_to_call)
620 {
621         int port=extract_sip_port(config_file);
622         const char *wakeup="WAKEUP sip:127.0.0.1 SIP/2.0\r\n"
623                 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
624                 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
625                 "To:   <sip:you@127.0.0.1>\r\n"
626                 "CSeq: 1 WAKEUP\r\n"
627                 "Call-ID: %u@onsantape\r\n"
628                 "Content-length: 0\r\n\r\n";
629         const char * call = "REFER sip:127.0.0.1 SIP/2.0\r\n"
630                 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
631                 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
632                 "To:   <sip:you@127.0.0.1>\r\n"
633                 "Refer-To: %s\r\n"
634                 "CSeq: 1 WAKEUP\r\n"
635                 "Call-ID: %u@onsantape\r\n"
636                 "Content-length: 0\r\n\r\n";
637
638         /*make sure ortp is initialized (it initializes win32 socket api)*/
639         ortp_init();
640         if (port>0){
641                 struct sockaddr_storage ss;
642                 socklen_t sslen;
643                 char tmp[100];
644                 snprintf(tmp,sizeof(tmp),"127.0.0.1:%i",port);
645                 if (parse_stun_server_addr(tmp,&ss,&sslen)==0){
646                         int locport=57123;
647                         ortp_socket_t sock=create_socket(locport);
648                         if (sock<0) sock=create_socket(++locport);
649                         if (sock>=0){
650                                 char req[512];
651                                 if (addr_to_call != NULL)
652                                         snprintf(req, sizeof(req), call, locport,
653                                         random(), random(), addr_to_call, random());
654                                 else
655                                         snprintf(req, sizeof(req), wakeup, locport,
656                                         random(), random(), random());
657                                 if (connect(sock,(struct sockaddr*)&ss,sslen)<0){
658                                         fprintf(stderr,"connect failed: %s\n",getSocketError());
659                                 }else if (send(sock,req,strlen(req),0)>0){
660                                         /*wait a bit for a response*/
661                                         int i;
662                                         for(i=0;i<10;++i){
663                                                 if (recv(sock,req,sizeof(req),0)>0){
664                                                         close_socket(sock);
665                                                         return 0;
666                                                 }else if (getSocketErrorCode()!=EWOULDBLOCK){
667                                                         break;
668                                                 }
669 #ifdef WIN32
670                                                 Sleep(100);
671 #else
672                                                 usleep(100000);
673 #endif
674                                         }
675                                 }else{
676                                         ms_message("sendto() of WAKEUP request failed, nobody to wakeup.");
677                                 }
678                         }
679                         close_socket(sock);
680                 }
681         }
682         return -1;
683 }
684
685 #ifdef HAVE_GETIFADDRS
686
687 #include <ifaddrs.h>
688 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
689 {
690         struct ifaddrs *ifp;
691         struct ifaddrs *ifpstart;
692         int ret = 0;
693
694         if (getifaddrs(&ifpstart) < 0) {
695                 return -1;
696         }
697
698         for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
699                 if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
700                         && (ifp->ifa_flags & IFF_RUNNING) && !(ifp->ifa_flags & IFF_LOOPBACK))
701                 {
702                         getnameinfo(ifp->ifa_addr,
703                                                 (type == AF_INET6) ?
704                                                 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
705                                                 address, size, NULL, 0, NI_NUMERICHOST);
706                         if (strchr(address, '%') == NULL) {     /*avoid ipv6 link-local addresses */
707                                 /*ms_message("getifaddrs() found %s",address);*/
708                                 ret++;
709                                 break;
710                         }
711                 }
712         }
713         freeifaddrs(ifpstart);
714         return ret;
715 }
716 #endif
717
718
719 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
720         int err,tmp;
721         struct addrinfo hints;
722         struct addrinfo *res=NULL;
723         struct sockaddr_storage addr;
724         struct sockaddr *p_addr=(struct sockaddr*)&addr;
725         ortp_socket_t sock;
726         socklen_t s;
727
728         memset(&hints,0,sizeof(hints));
729         hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
730         hints.ai_socktype=SOCK_DGRAM;
731         /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
732         err=getaddrinfo(dest,"5060",&hints,&res);
733         if (err!=0){
734                 ms_error("getaddrinfo() error: %s",gai_strerror(err));
735                 return -1;
736         }
737         if (res==NULL){
738                 ms_error("bug: getaddrinfo returned nothing.");
739                 return -1;
740         }
741         sock=socket(res->ai_family,SOCK_DGRAM,0);
742         tmp=1;
743         err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
744         if (err<0){
745                 ms_warning("Error in setsockopt: %s",strerror(errno));
746         }
747         err=connect(sock,res->ai_addr,res->ai_addrlen);
748         if (err<0) {
749                 ms_error("Error in connect: %s",strerror(errno));
750                 freeaddrinfo(res);
751                 close_socket(sock);
752                 return -1;
753         }
754         freeaddrinfo(res);
755         res=NULL;
756         s=sizeof(addr);
757         err=getsockname(sock,(struct sockaddr*)&addr,&s);
758         if (err!=0) {
759                 ms_error("Error in getsockname: %s",strerror(errno));
760                 close_socket(sock);
761                 return -1;
762         }
763         if (p_addr->sa_family==AF_INET){
764                 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
765                 if (p_sin->sin_addr.s_addr==0){
766                         close_socket(sock);
767                         return -1;
768                 }
769         }
770         err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
771         if (err!=0){
772                 ms_error("getnameinfo error: %s",strerror(errno));
773         }
774         close_socket(sock);
775         ms_message("Local interface to reach %s is %s.",dest,result);
776         return 0;
777 }
778
779 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
780         strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
781 #ifdef HAVE_GETIFADDRS
782         if (dest==NULL) {
783                 /*we use getifaddrs for lookup of default interface */
784                 int found_ifs;
785         
786                 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
787                 if (found_ifs==1){
788                         return 0;
789                 }else if (found_ifs<=0){
790                         /*absolutely no network on this machine */
791                         return -1;
792                 }
793         }
794 #endif
795         /*else use connect to find the best local ip address */
796         if (type==AF_INET)
797                 dest="87.98.157.38"; /*a public IP address*/
798         else dest="2a00:1450:8002::68";
799         return get_local_ip_for_with_connect(type,dest,result);
800 }
801
802 #ifndef WIN32
803 #include <resolv.h>
804
805
806
807
808 void _linphone_core_configure_resolver(){
809 /*bionic declares _res but does not define nor export it !!*/
810 #ifdef ANDROID
811         /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
812         setenv("RES_OPTIONS","timeout:1 attempts:2 retrans:1 retry:2",1);
813 #else
814         res_init();
815         _res.retrans=1; /*retransmit every second*/
816         _res.retry=2; /*only two times per DNS server*/
817 #endif
818 }
819
820 #else
821
822 void _linphone_core_configure_resolver(){
823 }
824
825 #endif