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>
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*/
51 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
52 lockfile=fopen(lock_name,"w");
55 printf("Failed to create lock file.\n");
58 fprintf(lockfile,"%i",getpid());
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*/
70 snprintf(lock_name,80,"/tmp/linphone.%i",getuid());
71 lockfile=fopen(lock_name,"r");
74 if (fscanf(lockfile,"%i",&pid)!=1){
75 ms_warning("Could not read pid in lock file.");
83 /* remove the lock file if it was set*/
84 int remove_lock_file()
89 err=unlink(lock_name);
97 char *int2str(int number)
99 char *numstr=ms_malloc(10);
100 snprintf(numstr,10,"%i",number);
104 void check_sound_device(LinphoneCore *lc)
111 char *i810_audio=NULL;
112 char *snd_pcm_oss=NULL;
113 char *snd_mixer_oss=NULL;
115 fd=open("/proc/modules",O_RDONLY);
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));
122 if (len==0) ms_warning("/proc/modules has zero size!");
124 /***** fstat does not work on /proc/modules for unknown reason *****/
126 file=ms_malloc(len+1);
128 if (a<len) file=ms_realloc(file,a+1);
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."));*/
137 snd_pcm=strstr(file,"snd-pcm");
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."));
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."));
150 ms_warning("Could not open /proc/modules.");
152 /* now check general volume. Some user forget to rise it and then complain that linphone is
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);
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);
164 if (file!=NULL) ms_free(file);
170 #define RTP_HDR_SZ 12
171 #define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/
173 static void payload_type_set_enable(PayloadType *pt,int value)
175 if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
176 else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
179 static bool_t payload_type_enabled(PayloadType *pt) {
180 return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
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);
187 ms_error("Getting enablement status of codec not in audio or video list of PayloadType !");
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);
196 ms_error("Enabling codec not in audio or video list of PayloadType !");
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);
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){
219 return pt->normal_bitrate;
222 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
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;
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);
234 linphone_core_set_download_bandwidth(lc,lc->net_conf.download_bw);
235 linphone_core_set_upload_bandwidth(lc,lc->net_conf.upload_bw);
238 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
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){
252 linphone_core_update_allocated_audio_bandwidth_in_call(lc,max);
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)
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
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);
275 case PAYLOAD_AUDIO_CONTINUOUS:
276 case PAYLOAD_AUDIO_PACKETIZED:
277 codec_band=get_audio_payload_bandwidth(lc,pt);
278 ret=bandwidth_is_greater(min_audio_bw*1000,codec_band);
279 /*hack to avoid using uwb codecs when having low bitrate and video*/
280 if (bandwidth_is_greater(199,min_audio_bw)){
281 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
285 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
288 if (min_video_bw!=0) {/* infinite (-1) or strictly positive*/
289 /*let the video use all the bandwidth minus the maximum bandwidth used by audio */
291 pt->normal_bitrate=min_video_bw*1000;
293 pt->normal_bitrate=1500000; /*around 1.5 Mbit/s*/
299 /*if (!ret) ms_warning("Payload %s is not usable with your internet connection.",pt->mime_type);*/
304 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
305 #if !defined(_WIN32_WCE)
306 FILE *f=popen(command,"r");
309 *result=ms_malloc(4096);
310 err=fread(*result,1,4096-1,f);
312 ms_warning("Error reading command output:%s",strerror(errno));
318 if (command_ret!=NULL) *command_ret=err;
321 #endif /*_WIN32_WCE*/
325 #if defined(HAVE_GETIFADDRS) && defined(INET6)
326 #include <sys/types.h>
327 #include <sys/socket.h>
329 bool_t host_has_ipv6_network()
332 struct ifaddrs *ifpstart;
333 bool_t ipv6_present=FALSE;
335 if (getifaddrs (&ifpstart) < 0)
340 for (ifp=ifpstart; ifp != NULL; ifp = ifp->ifa_next)
345 switch (ifp->ifa_addr->sa_family) {
357 freeifaddrs (ifpstart);
363 bool_t host_has_ipv6_network()
371 static ortp_socket_t create_socket(int local_port){
372 struct sockaddr_in laddr;
375 sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
377 ms_error("Fail to create socket");
380 memset (&laddr,0,sizeof(laddr));
381 laddr.sin_family=AF_INET;
382 laddr.sin_addr.s_addr=INADDR_ANY;
383 laddr.sin_port=htons(local_port);
384 if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
385 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
390 if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
391 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
392 ms_warning("Fail to set SO_REUSEADDR");
394 set_non_blocking_socket(sock);
398 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
399 char buf[STUN_MAX_MESSAGE_SIZE];
400 int len = STUN_MAX_MESSAGE_SIZE;
401 StunAtrString username;
402 StunAtrString password;
405 memset(&req, 0, sizeof(StunMessage));
406 memset(&username,0,sizeof(username));
407 memset(&password,0,sizeof(password));
408 stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
409 len = stunEncodeMessage( &req, buf, len, &password);
411 ms_error("Fail to encode stun message.");
414 err=sendto(sock,buf,len,0,server,addrlen);
416 ms_error("sendto failed: %s",strerror(errno));
422 static int parse_stun_server_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
423 struct addrinfo hints,*res=NULL;
426 char host[NI_MAXHOST];
428 host[NI_MAXHOST-1]='\0';
429 strncpy(host,server,sizeof(host)-1);
435 memset(&hints,0,sizeof(hints));
436 hints.ai_family=PF_INET;
437 hints.ai_socktype=SOCK_DGRAM;
438 hints.ai_protocol=IPPROTO_UDP;
439 ret=getaddrinfo(host,port,&hints,&res);
441 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
445 memcpy(ss,res->ai_addr,res->ai_addrlen);
446 *socklen=res->ai_addrlen;
451 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
452 char buf[STUN_MAX_MESSAGE_SIZE];
453 int len = STUN_MAX_MESSAGE_SIZE;
455 len=recv(sock,buf,len,0);
458 stunParseMessage(buf,len, &resp );
459 *id=resp.msgHdr.tr_id.octet[0];
460 *port = resp.mappedAddress.ipv4.port;
461 ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
462 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
467 void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
468 const char *server=linphone_core_get_stun_server(lc);
470 if (lc->sip_conf.ipv6_enabled){
471 ms_warning("stun support is not implemented for ipv6");
475 struct sockaddr_storage ss;
477 ortp_socket_t sock1=-1, sock2=-1;
478 bool_t video_enabled=linphone_core_video_enabled(lc);
479 bool_t got_audio,got_video;
480 bool_t cone_audio=FALSE,cone_video=FALSE;
481 struct timeval init,cur;
482 SalEndpointCandidate *ac,*vc;
484 ac=&call->localdesc->streams[0].candidates[0];
485 vc=&call->localdesc->streams[1].candidates[0];
487 if (parse_stun_server_addr(server,&ss,&ss_len)<0){
488 ms_error("Fail to parser stun server address: %s",server);
491 if (lc->vtable.display_status!=NULL)
492 lc->vtable.display_status(lc,_("Stun lookup in progress..."));
494 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
495 sock1=create_socket(linphone_core_get_audio_port(lc));
498 sock2=create_socket(linphone_core_get_video_port(lc));
499 if (sock2<0) return ;
501 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
502 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
504 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
505 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
509 gettimeofday(&init,NULL);
519 if (recvStunResponse(sock1,ac->addr,
521 ms_message("STUN test result: local audio port maps to %s:%i",
528 if (recvStunResponse(sock2,vc->addr,
530 ms_message("STUN test result: local video port maps to %s:%i",
537 gettimeofday(&cur,NULL);
538 elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0);
539 if (elapsed>2000) break;
540 }while(!(got_audio && (got_video||sock2<0) ) );
542 ms_error("No stun server response for audio port.");
545 ms_warning("NAT is symmetric for audio port");
552 ms_error("No stun server response for video port.");
555 ms_warning("NAT is symmetric for video port.");
562 if (sock2>=0) close_socket(sock2);
566 static int extract_sip_port(const char *config){
570 FILE *f=fopen(config,"r");
572 while(fgets(line,sizeof(line),f)!=NULL){
573 if (fmtp_get_value(line,"sip_port",port,sizeof(port))){
582 int linphone_core_wake_up_possible_already_running_instance(
583 const char * config_file, const char * addr_to_call)
585 int port=extract_sip_port(config_file);
586 const char *wakeup="WAKEUP sip:127.0.0.1 SIP/2.0\r\n"
587 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
588 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
589 "To: <sip:you@127.0.0.1>\r\n"
591 "Call-ID: %u@onsantape\r\n"
592 "Content-length: 0\r\n\r\n";
593 const char * call = "REFER sip:127.0.0.1 SIP/2.0\r\n"
594 "Via: SIP/2.0/UDP 127.0.0.1:%i;rport;branch=z9hG4bK%u\r\n"
595 "From: <sip:another_linphone@127.0.0.1>;tag=%u\r\n"
596 "To: <sip:you@127.0.0.1>\r\n"
599 "Call-ID: %u@onsantape\r\n"
600 "Content-length: 0\r\n\r\n";
602 /*make sure ortp is initialized (it initializes win32 socket api)*/
605 struct sockaddr_storage ss;
608 snprintf(tmp,sizeof(tmp),"127.0.0.1:%i",port);
609 if (parse_stun_server_addr(tmp,&ss,&sslen)==0){
611 ortp_socket_t sock=create_socket(locport);
612 if (sock<0) sock=create_socket(++locport);
615 if (addr_to_call != NULL)
616 snprintf(req, sizeof(req), call, locport,
617 random(), random(), addr_to_call, random());
619 snprintf(req, sizeof(req), wakeup, locport,
620 random(), random(), random());
621 if (connect(sock,(struct sockaddr*)&ss,sslen)<0){
622 fprintf(stderr,"connect failed: %s\n",getSocketError());
623 }else if (send(sock,req,strlen(req),0)>0){
624 /*wait a bit for a response*/
627 if (recv(sock,req,sizeof(req),0)>0){
630 }else if (getSocketErrorCode()!=EWOULDBLOCK){
640 ms_message("sendto() of WAKEUP request failed, nobody to wakeup.");
649 int linphone_core_get_local_ip_for(const char *dest, char *result){
651 struct addrinfo hints;
652 struct addrinfo *res=NULL;
653 struct sockaddr_storage addr;
654 struct sockaddr *p_addr=(struct sockaddr*)&addr;
658 memset(&hints,0,sizeof(hints));
659 hints.ai_family=PF_UNSPEC;
660 hints.ai_socktype=SOCK_DGRAM;
661 /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
662 err=getaddrinfo(dest,"5060",&hints,&res);
664 ms_error("getaddrinfo() error: %s",gai_strerror(err));
668 ms_error("bug: getaddrinfo returned nothing.");
671 sock=socket(res->ai_family,SOCK_DGRAM,0);
673 err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
675 ms_warning("Error in setsockopt: %s",strerror(errno));
677 err=connect(sock,res->ai_addr,res->ai_addrlen);
679 ms_error("Error in connect: %s",strerror(errno));
687 err=getsockname(sock,(struct sockaddr*)&addr,&s);
689 ms_error("Error in getsockname: %s",strerror(errno));
693 if (p_addr->sa_family==AF_INET){
694 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
695 if (p_sin->sin_addr.s_addr==0){
700 err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
702 ms_error("getnameinfo error: %s",strerror(errno));
705 ms_message("Local interface to reach %s is %s.",dest,result);