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(const PayloadType *pt) {
185 return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0);
188 bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, const PayloadType *pt){
189 if (ms_list_find(lc->codecs_conf.audio_codecs, (PayloadType*) pt) || ms_list_find(lc->codecs_conf.video_codecs, (PayloadType*)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 int linphone_core_get_payload_type_number(LinphoneCore *lc, const PayloadType *pt){
206 return payload_type_get_number(pt);
209 const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){
210 if (ms_filter_codec_supported(pt->mime_type)){
211 MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type);
213 return dgettext("mediastreamer",desc->text);
222 /*this function makes a special case for speex/8000.
223 This codec is variable bitrate. The 8kbit/s mode is interesting when having a low upload bandwidth, but its quality
224 is not very good. We 'd better use its 15kbt/s mode when we have enough bandwidth*/
225 static int get_codec_bitrate(LinphoneCore *lc, const PayloadType *pt){
226 int upload_bw=linphone_core_get_upload_bandwidth(lc);
227 if (bandwidth_is_greater(upload_bw,129) || (bandwidth_is_greater(upload_bw,33) && !linphone_core_video_enabled(lc)) ) {
228 if (strcmp(pt->mime_type,"speex")==0 && pt->clock_rate==8000){
232 return pt->normal_bitrate;
235 static double get_audio_payload_bandwidth(LinphoneCore *lc, const PayloadType *pt){
239 bitrate=get_codec_bitrate(lc,pt);
240 packet_size= (((double)bitrate)/(50*8))+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
241 return packet_size*8.0*npacket;
244 void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt){
245 call->audio_bw=(int)(get_audio_payload_bandwidth(call->core,pt)/1000.0);
246 ms_message("Audio bandwidth for this call is %i",call->audio_bw);
249 void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc){
251 PayloadType *max=NULL;
252 for(elem=linphone_core_get_audio_codecs(lc);elem!=NULL;elem=elem->next){
253 PayloadType *pt=(PayloadType*)elem->data;
254 if (payload_type_enabled(pt)){
255 int pt_bitrate=get_codec_bitrate(lc,pt);
256 if (max==NULL) max=pt;
257 else if (max->normal_bitrate<pt_bitrate){
263 lc->audio_bw=(int)(get_audio_payload_bandwidth(lc,max)/1000.0);
267 bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, PayloadType *pt, int bandwidth_limit)
273 case PAYLOAD_AUDIO_CONTINUOUS:
274 case PAYLOAD_AUDIO_PACKETIZED:
275 codec_band=get_audio_payload_bandwidth(lc,pt);
276 ret=bandwidth_is_greater(bandwidth_limit*1000,codec_band);
277 /*hack to avoid using uwb codecs when having low bitrate and video*/
278 if (bandwidth_is_greater(199,bandwidth_limit)){
279 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
283 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
286 if (bandwidth_limit!=0) {/* infinite (-1) or strictly positive*/
295 /* return TRUE if codec can be used with bandwidth, FALSE else*/
296 bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt)
299 int allowed_bw,video_bw;
302 linphone_core_update_allocated_audio_bandwidth(lc);
303 allowed_bw=get_min_bandwidth(linphone_core_get_download_bandwidth(lc),
304 linphone_core_get_upload_bandwidth(lc));
307 video_bw=1500; /*around 1.5 Mbit/s*/
309 video_bw=get_video_bandwidth(allowed_bw,lc->audio_bw);
312 case PAYLOAD_AUDIO_CONTINUOUS:
313 case PAYLOAD_AUDIO_PACKETIZED:
314 codec_band=get_audio_payload_bandwidth(lc,pt);
315 ret=bandwidth_is_greater(allowed_bw*1000,codec_band);
316 /*hack to avoid using uwb codecs when having low bitrate and video*/
317 if (bandwidth_is_greater(199,allowed_bw)){
318 if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){
322 //ms_message("Payload %s: %g",pt->mime_type,codec_band);
326 pt->normal_bitrate=video_bw*1000;
335 bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){
336 #if !defined(_WIN32_WCE)
337 FILE *f=popen(command,"r");
340 *result=ms_malloc(4096);
341 err=fread(*result,1,4096-1,f);
343 ms_warning("Error reading command output:%s",strerror(errno));
349 if (command_ret!=NULL) *command_ret=err;
352 #endif /*_WIN32_WCE*/
356 static ortp_socket_t create_socket(int local_port){
357 struct sockaddr_in laddr;
360 sock=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
362 ms_error("Fail to create socket");
365 memset (&laddr,0,sizeof(laddr));
366 laddr.sin_family=AF_INET;
367 laddr.sin_addr.s_addr=INADDR_ANY;
368 laddr.sin_port=htons(local_port);
369 if (bind(sock,(struct sockaddr*)&laddr,sizeof(laddr))<0){
370 ms_error("Bind socket to 0.0.0.0:%i failed: %s",local_port,getSocketError());
375 if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
376 (SOCKET_OPTION_VALUE)&optval, sizeof (optval))<0){
377 ms_warning("Fail to set SO_REUSEADDR");
379 set_non_blocking_socket(sock);
383 static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){
384 char buf[STUN_MAX_MESSAGE_SIZE];
385 int len = STUN_MAX_MESSAGE_SIZE;
386 StunAtrString username;
387 StunAtrString password;
390 memset(&req, 0, sizeof(StunMessage));
391 memset(&username,0,sizeof(username));
392 memset(&password,0,sizeof(password));
393 stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id);
394 len = stunEncodeMessage( &req, buf, len, &password);
396 ms_error("Fail to encode stun message.");
399 err=sendto(sock,buf,len,0,server,addrlen);
401 ms_error("sendto failed: %s",strerror(errno));
407 int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){
408 struct addrinfo hints,*res=NULL;
411 char host[NI_MAXHOST];
413 host[NI_MAXHOST-1]='\0';
414 strncpy(host,server,sizeof(host)-1);
420 memset(&hints,0,sizeof(hints));
421 hints.ai_family=PF_INET;
422 hints.ai_socktype=SOCK_DGRAM;
423 hints.ai_protocol=IPPROTO_UDP;
424 ret=getaddrinfo(host,port,&hints,&res);
426 ms_error("getaddrinfo() failed for %s:%s : %s",host,port,gai_strerror(ret));
430 memcpy(ss,res->ai_addr,res->ai_addrlen);
431 *socklen=res->ai_addrlen;
436 static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){
437 char buf[STUN_MAX_MESSAGE_SIZE];
438 int len = STUN_MAX_MESSAGE_SIZE;
440 len=recv(sock,buf,len,0);
443 stunParseMessage(buf,len, &resp );
444 *id=resp.msgHdr.tr_id.octet[0];
445 if (resp.hasXorMappedAddress){
446 *port = resp.xorMappedAddress.ipv4.port;
447 ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr);
448 }else if (resp.hasMappedAddress){
449 *port = resp.mappedAddress.ipv4.port;
450 ia.s_addr=htonl(resp.mappedAddress.ipv4.addr);
452 strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE);
457 void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){
458 const char *server=linphone_core_get_stun_server(lc);
460 if (lc->sip_conf.ipv6_enabled){
461 ms_warning("stun support is not implemented for ipv6");
465 struct sockaddr_storage ss;
467 ortp_socket_t sock1=-1, sock2=-1;
469 bool_t video_enabled=linphone_core_video_enabled(lc);
470 bool_t got_audio,got_video;
471 bool_t cone_audio=FALSE,cone_video=FALSE;
472 struct timeval init,cur;
473 SalEndpointCandidate *ac,*vc;
475 ac=&call->localdesc->streams[0].candidates[0];
476 vc=&call->localdesc->streams[1].candidates[0];
478 if (parse_hostname_to_addr(server,&ss,&ss_len)<0){
479 ms_error("Fail to parser stun server address: %s",server);
482 if (lc->vtable.display_status!=NULL)
483 lc->vtable.display_status(lc,_("Stun lookup in progress..."));
485 /*create the two audio and video RTP sockets, and send STUN message to our stun server */
486 sock1=create_socket(call->audio_port);
487 if (sock1==-1) return;
489 sock2=create_socket(call->video_port);
490 if (sock2==-1) return ;
494 gettimeofday(&init,NULL);
499 ms_message("Sending stun requests...");
500 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE);
501 sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE);
503 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE);
504 sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE);
513 if (recvStunResponse(sock1,ac->addr,
515 ms_message("STUN test result: local audio port maps to %s:%i",
522 if (recvStunResponse(sock2,vc->addr,
524 ms_message("STUN test result: local video port maps to %s:%i",
531 gettimeofday(&cur,NULL);
532 elapsed=((cur.tv_sec-init.tv_sec)*1000.0) + ((cur.tv_usec-init.tv_usec)/1000.0);
534 ms_message("Stun responses timeout, going ahead.");
538 }while(!(got_audio && (got_video||sock2==-1) ) );
540 ms_error("No stun server response for audio port.");
543 ms_message("NAT is symmetric for audio port");
548 ms_error("No stun server response for video port.");
551 ms_message("NAT is symmetric for video port.");
555 if ((ac->addr[0]!='\0' && vc->addr[0]!='\0' && strcmp(ac->addr,vc->addr)==0)
557 strcpy(call->localdesc->addr,ac->addr);
560 if (sock2!=-1) close_socket(sock2);
564 LinphoneCall * is_a_linphone_call(void *user_pointer){
565 LinphoneCall *call=(LinphoneCall*)user_pointer;
566 if (call==NULL) return NULL;
567 return call->magic==linphone_call_magic ? call : NULL;
570 LinphoneProxyConfig * is_a_linphone_proxy_config(void *user_pointer){
571 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)user_pointer;
572 if (cfg==NULL) return NULL;
573 return cfg->magic==linphone_proxy_config_magic ? cfg : NULL;
577 #ifdef HAVE_GETIFADDRS
580 static int get_local_ip_with_getifaddrs(int type, char *address, int size)
583 struct ifaddrs *ifpstart;
586 if (getifaddrs(&ifpstart) < 0) {
590 for (ifp = ifpstart; ifp != NULL; ifp = ifp->ifa_next) {
591 if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
592 && (ifp->ifa_flags & IFF_RUNNING) && !(ifp->ifa_flags & IFF_LOOPBACK))
594 getnameinfo(ifp->ifa_addr,
596 sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
597 address, size, NULL, 0, NI_NUMERICHOST);
598 if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */
599 /*ms_message("getifaddrs() found %s",address);*/
605 freeifaddrs(ifpstart);
611 static int get_local_ip_for_with_connect(int type, const char *dest, char *result){
613 struct addrinfo hints;
614 struct addrinfo *res=NULL;
615 struct sockaddr_storage addr;
616 struct sockaddr *p_addr=(struct sockaddr*)&addr;
620 memset(&hints,0,sizeof(hints));
621 hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET;
622 hints.ai_socktype=SOCK_DGRAM;
623 /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/
624 err=getaddrinfo(dest,"5060",&hints,&res);
626 ms_error("getaddrinfo() error: %s",gai_strerror(err));
630 ms_error("bug: getaddrinfo returned nothing.");
633 sock=socket(res->ai_family,SOCK_DGRAM,0);
635 err=setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(SOCKET_OPTION_VALUE)&tmp,sizeof(int));
637 ms_warning("Error in setsockopt: %s",strerror(errno));
639 err=connect(sock,res->ai_addr,res->ai_addrlen);
641 ms_error("Error in connect: %s",strerror(errno));
649 err=getsockname(sock,(struct sockaddr*)&addr,&s);
651 ms_error("Error in getsockname: %s",strerror(errno));
655 if (p_addr->sa_family==AF_INET){
656 struct sockaddr_in *p_sin=(struct sockaddr_in*)p_addr;
657 if (p_sin->sin_addr.s_addr==0){
662 err=getnameinfo((struct sockaddr *)&addr,s,result,LINPHONE_IPADDR_SIZE,NULL,0,NI_NUMERICHOST);
664 ms_error("getnameinfo error: %s",strerror(errno));
667 ms_message("Local interface to reach %s is %s.",dest,result);
671 int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
672 strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
673 #ifdef HAVE_GETIFADDRS
675 /*we use getifaddrs for lookup of default interface */
678 found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
681 }else if (found_ifs<=0){
682 /*absolutely no network on this machine */
687 /*else use connect to find the best local ip address */
689 dest="87.98.157.38"; /*a public IP address*/
690 else dest="2a00:1450:8002::68";
691 return get_local_ip_for_with_connect(type,dest,result);
700 void _linphone_core_configure_resolver(){
701 /*bionic declares _res but does not define nor export it !!*/
703 /*timeout and attempts are the same as retrans and retry, but are android specific names.*/
704 setenv("RES_OPTIONS","timeout:2 attempts:2 retrans:2 retry:2",1);
707 _res.retrans=2; /*retransmit every two seconds*/
708 _res.retry=2; /*only two times per DNS server*/
714 void _linphone_core_configure_resolver(){