4 Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org)
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.
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.
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.
22 #include "mediastreamer2/mediastream.h"
25 #ifdef HAVE_SIGHANDLER_T
27 #endif /*HAVE_SIGHANDLER_T*/
30 #if !defined(_WIN32_WCE)
32 #include <sys/types.h>
39 #include <ortp/stun.h>
41 #ifdef HAVE_GETIFADDRS
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*/
56 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
57 lockfile=fopen(lock_name,"w");
60 printf("Failed to create lock file.\n");
63 fprintf(lockfile,"%i",getpid());
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*/
75 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
76 lockfile=fopen(lock_name,"r");
79 if (fscanf(lockfile,"%i",&pid)!=1){
80 ms_warning("Could not read pid in lock file.");
88 /* remove the lock file if it was set*/
89 int remove_lock_file()
94 err=unlink(lock_name);
102 char *int2str(int number)
104 char *numstr=ms_malloc(10);
105 snprintf(numstr,10,"%i",number);
109 void check_sound_device(LinphoneCore *lc)
116 char *i810_audio=NULL;
117 char *snd_pcm_oss=NULL;
118 char *snd_mixer_oss=NULL;
120 fd=open("/proc/modules",O_RDONLY);
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));
127 if (len==0) ms_warning("/proc/modules has zero size!");
129 /***** fstat does not work on /proc/modules for unknown reason *****/
131 file=ms_malloc(len+1);
133 if (a<len) file=ms_realloc(file,a+1);
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."));*/
142 snd_pcm=strstr(file,"snd-pcm");
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."));
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."));
155 ms_warning("Could not open /proc/modules.");
157 /* now check general volume. Some user forget to rise it and then complain that linphone is
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);
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);
169 if (file!=NULL) ms_free(file);
175 #define RTP_HDR_SZ 12
176 #define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/
178 static void payload_type_set_enable(PayloadType *pt,int value)
180 if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
181 else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
184 static bool_t payload_type_enabled(PayloadType *pt) {
185 return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
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);
192 ms_error("Getting enablement status of codec not in audio or video list of PayloadType !");
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);
201 ms_error("Enabling codec not in audio or video list of PayloadType !");
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);
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){
224 return pt->normal_bitrate;
227 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
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;
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);
241 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
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){
255 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
259 bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit)
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){
275 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
278 if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
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)
291 int allowed_bw,video_bw;
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));
299 video_bw=1500; /*around 1.5 Mbit/s*/
301 video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
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){
314 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
318 pt->normal_bitrate=video_bw*1000;
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");
332 *result=ms_malloc(4096);
333 err=fread(*result,1,4096-1,f);
335 ms_warning("Error reading command output:%s",strerror(errno));
341 if (command_ret!=NULL) *command_ret=err;
344 #endif /*_WIN32_WCE*/
348 #if defined(HAVE_GETIFADDRS) && defined(INET6)
349 #include <sys/types.h>
350 #include <sys/socket.h>
352 bool_t host_has_ipv6_network()
355 struct ifaddrs *ifpstart;
356 bool_t ipv6_present=FALSE;
358 if (getifaddrs (&ifpstart) < 0)
363 for (ifp=ifpstart; ifp != NULL; ifp = ifp->ifa_next)
368 switch (ifp->ifa_addr->sa_family) {
380 freeifaddrs (ifpstart);
386 bool_t host_has_ipv6_network()
394 static ortp_socket_t create_socket(int local_port){
395 struct sockaddr_in laddr;
398 sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
400 ms_error("Fail to create socket");
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());
413 if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
414 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
415 ms_warning("Fail to set SO_REUSEADDR");
417 set_non_blocking_socket(sock);
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;
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);
434 ms_error("Fail to encode stun message.");
437 err=sendto(sock,buf,len,0,server,addrlen);
439 ms_error("sendto failed: %s",strerror(errno));
445 static int parse_stun_server_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
446 struct addrinfo hints,*res=NULL;
449 char host[NI_MAXHOST];
451 host[NI_MAXHOST-1]='\0';
452 strncpy(host,server,sizeof(host)-1);
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);
464 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
468 memcpy(ss,res->ai_addr,res->ai_addrlen);
469 *socklen=res->ai_addrlen;
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;
478 len=recv(sock,buf,len,0);
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);
490 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
495 void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
496 const char *server=linphone_core_get_stun_server(lc);
498 if (lc->sip_conf.ipv6_enabled){
499 ms_warning("stun support is not implemented for ipv6");
503 struct sockaddr_storage ss;
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;
512 ac=&call->localdesc->streams[0].candidates[0];
513 vc=&call->localdesc->streams[1].candidates[0];
515 if (parse_stun_server_addr(server,&ss,&ss_len)<0){
516 ms_error("Fail to parser stun server address: %s",server);
519 if (lc->vtable.display_status!=NULL)
520 lc->vtable.display_status(lc,_("Stun lookup in progress..."));
522 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
523 sock1=create_socket(call->audio_port);
526 sock2=create_socket(call->video_port);
527 if (sock2<0) return ;
529 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
530 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
532 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
533 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
537 gettimeofday(&init,NULL);
547 if (recvStunResponse(sock1,ac->addr,
549 ms_message("STUN test result: local audio port maps to %s:%i",
556 if (recvStunResponse(sock2,vc->addr,
558 ms_message("STUN test result: local video port maps to %s:%i",
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) ) );
570 ms_error("No stun server response for audio port.");
573 ms_warning("NAT is symmetric for audio port");
582 ms_error("No stun server response for video port.");
585 ms_warning("NAT is symmetric for video port.");
593 if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0)
595 strcpy(call->localdesc->addr,ac->addr);
598 if (sock2>=0) close_socket(sock2);
602 static int extract_sip_port(const char *config){
606 FILE *f=fopen(config,"r");
608 while(fgets(line,sizeof(line),f)!=NULL){
609 if (fmtp_get_value(line,"sip_port",port,sizeof(port))){
618 int linphone_core_wake_up_possible_already_running_instance(
619 const char * config_file, const char * addr_to_call)
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"
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"
635 "Call-ID: %u@onsantape\r\n"
636 "Content-length: 0\r\n\r\n";
638 /*make sure ortp is initialized (it initializes win32 socket api)*/
641 struct sockaddr_storage ss;
644 snprintf(tmp,sizeof(tmp),"127.0.0.1:%i",port);
645 if (parse_stun_server_addr(tmp,&ss,&sslen)==0){
647 ortp_socket_t sock=create_socket(locport);
648 if (sock<0) sock=create_socket(++locport);
651 if (addr_to_call != NULL)
652 snprintf(req, sizeof(req), call, locport,
653 random(), random(), addr_to_call, random());
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*/
663 if (recv(sock,req,sizeof(req),0)>0){
666 }else if (getSocketErrorCode()!=EWOULDBLOCK){
676 ms_message("sendto() of WAKEUP request failed, nobody to wakeup.");
685 #ifdef HAVE_GETIFADDRS
688 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
691 struct ifaddrs *ifpstart;
694 if (getifaddrs(&ifpstart) < 0) {
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))
702 getnameinfo(ifp->ifa_addr,
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);*/
713 freeifaddrs(ifpstart);
719 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
721 struct addrinfo hints;
722 struct addrinfo *res=NULL;
723 struct sockaddr_storage addr;
724 struct sockaddr *p_addr=(struct sockaddr*)&addr;
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);
734 ms_error("getaddrinfo() error: %s",gai_strerror(err));
738 ms_error("bug: getaddrinfo returned nothing.");
741 sock=socket(res->ai_family,SOCK_DGRAM,0);
743 err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
745 ms_warning("Error in setsockopt: %s",strerror(errno));
747 err=connect(sock,res->ai_addr,res->ai_addrlen);
749 ms_error("Error in connect: %s",strerror(errno));
757 err=getsockname(sock,(struct sockaddr*)&addr,&s);
759 ms_error("Error in getsockname: %s",strerror(errno));
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){
770 err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
772 ms_error("getnameinfo error: %s",strerror(errno));
775 ms_message("Local interface to reach %s is %s.",dest,result);
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
783 /*we use getifaddrs for lookup of default interface */
786 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
789 }else if (found_ifs<=0){
790 /*absolutely no network on this machine */
795 /*else use connect to find the best local ip address */
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);
808 void _linphone_core_configure_resolver(){
809 /*bionic declares _res but does not define nor export it !!*/
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);
815 _res.retrans=1; /*retransmit every second*/
816 _res.retry=2; /*only two times per DNS server*/
822 void _linphone_core_configure_resolver(){