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