diff --git a/coreapi/bellesip_sal/sal_address_impl.c b/coreapi/bellesip_sal/sal_address_impl.c index 26e29b321..01a5b6229 100644 --- a/coreapi/bellesip_sal/sal_address_impl.c +++ b/coreapi/bellesip_sal/sal_address_impl.c @@ -179,6 +179,17 @@ void sal_address_unref(SalAddress *addr){ belle_sip_object_unref(BELLE_SIP_HEADER_ADDRESS(addr)); } +bool_t sal_address_is_ipv6(SalAddress *addr){ + belle_sip_header_address_t* header_addr = BELLE_SIP_HEADER_ADDRESS(addr); + belle_sip_uri_t* uri = belle_sip_header_address_get_uri(header_addr); + if (uri){ + const char *host=belle_sip_uri_get_host(uri); + if (host && strchr(host,':')!=NULL) + return TRUE; + } + return FALSE; +} + void sal_address_destroy(SalAddress *addr){ sal_address_unref(addr); } diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index 43e94227b..47958ac6e 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -612,3 +612,29 @@ bool_t sal_op_is_secure(const SalOp* op) { void sal_op_set_manual_refresher_mode(SalOp *op, bool_t enabled){ op->manual_refresher=enabled; } + +bool_t sal_op_is_ipv6(SalOp *op){ + belle_sip_transaction_t *tr=NULL; + belle_sip_header_address_t *contact; + belle_sip_request_t *req; + + if (op->refresher) + tr=(belle_sip_transaction_t *)belle_sip_refresher_get_transaction(op->refresher); + + if (tr==NULL) + tr=(belle_sip_transaction_t *)op->pending_client_trans; + if (tr==NULL) + tr=(belle_sip_transaction_t *)op->pending_server_trans; + + if (tr==NULL){ + ms_error("Unable to determine IP version from signaling operation."); + return FALSE; + } + req=belle_sip_transaction_get_request(tr); + contact=(belle_sip_header_address_t*)belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t); + if (!contact){ + ms_error("Unable to determine IP version from signaling operation, no contact header found."); + } + return sal_address_is_ipv6((SalAddress*)contact); +} + diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 908157e48..19caa2059 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -501,12 +501,32 @@ void linphone_call_create_op(LinphoneCall *call){ /*else privacy might be set by proxy */ } +/* + * Choose IP version we are going to use for RTP socket. + * The algorithm is as follows: + * - if ipv6 is disabled at the core level, it is always AF_INET + * - Otherwise, if the destination address for the call is an IPv6 address, use IPv6. + * - Otherwise, if the call is done through a known proxy config, then use the information obtained during REGISTER + * to know if IPv6 is supported by the server. +**/ +static void linphone_call_outgoing_select_ip_version(LinphoneCall *call, LinphoneAddress *to, LinphoneProxyConfig *cfg){ + if (linphone_core_ipv6_enabled(call->core)){ + call->af=AF_INET; + if (sal_address_is_ipv6((SalAddress*)to)){ + call->af=AF_INET6; + }else if (cfg && cfg->op){ + call->af=sal_op_is_ipv6(cfg->op) ? AF_INET6 : AF_INET; + } + }else call->af=AF_INET; +} + LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params, LinphoneProxyConfig *cfg){ LinphoneCall *call=ms_new0(LinphoneCall,1); call->dir=LinphoneCallOutgoing; call->core=lc; - linphone_core_get_local_ip(lc,NULL,call->localip); + linphone_call_outgoing_select_ip_version(call,to,cfg); + linphone_core_get_local_ip(lc,call->af,call->localip); linphone_call_init_common(call,from,to); _linphone_call_params_copy(&call->params,params); @@ -535,6 +555,12 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr return call; } +static void linphone_call_incoming_select_ip_version(LinphoneCall *call){ + if (linphone_core_ipv6_enabled(call->core)){ + call->af=sal_op_is_ipv6(call->op) ? AF_INET6 : AF_INET; + }else call->af=AF_INET; +} + LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ LinphoneCall *call=ms_new0(LinphoneCall,1); char *from_str; @@ -544,7 +570,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro sal_op_set_user_pointer(op,call); call->op=op; call->core=lc; - + linphone_call_incoming_select_ip_version(call); if (lc->sip_conf.ping_with_options){ #ifdef BUILD_UPNP @@ -565,7 +591,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro } linphone_address_clean(from); - linphone_core_get_local_ip(lc,NULL,call->localip); + linphone_core_get_local_ip(lc,call->af,call->localip); linphone_call_init_common(call, from, to); call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/ linphone_core_init_default_params(lc, &call->params); @@ -1331,7 +1357,7 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ int dscp; if (call->audiostream != NULL) return; - call->audiostream=audiostream=audio_stream_new(call->audio_port,call->audio_port+1,linphone_core_ipv6_enabled(lc)); + call->audiostream=audiostream=audio_stream_new(call->audio_port,call->audio_port+1,call->af==AF_INET6); dscp=linphone_core_get_audio_dscp(lc); if (dscp!=-1) audio_stream_set_dscp(audiostream,dscp); @@ -1395,7 +1421,7 @@ void linphone_call_init_video_stream(LinphoneCall *call){ int dscp=linphone_core_get_video_dscp(lc); const char *display_filter=linphone_core_get_video_display_filter(lc); - call->videostream=video_stream_new(call->video_port,call->video_port+1,linphone_core_ipv6_enabled(lc)); + call->videostream=video_stream_new(call->video_port,call->video_port+1,call->af==AF_INET6); if (dscp!=-1) video_stream_set_dscp(call->videostream,dscp); video_stream_enable_display_filter_auto_rotate(call->videostream, lp_config_get_int(lc->config,"video","display_filter_auto_rotate",0)); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index c9cd717a2..bc256c466 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1443,8 +1443,9 @@ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) } +/*Returns the local ip that routes to the internet, or guessed by other special means (upnp)*/ /*result must be an array of chars at least LINPHONE_IPADDR_SIZE */ -void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){ +void linphone_core_get_local_ip(LinphoneCore *lc, int af, char *result){ const char *ip; if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseNatAddress && (ip=linphone_core_get_nat_address_resolved(lc))!=NULL){ @@ -1459,10 +1460,23 @@ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result return; } #endif //BUILD_UPNP - if (linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,dest,result)==0) + if (af==AF_UNSPEC){ + if (linphone_core_ipv6_enabled(lc)){ + bool_t has_ipv6; + has_ipv6=linphone_core_get_local_ip_for(AF_INET6,NULL,result)==0; + if (strcmp(result,"::1")!=0) + return; /*this machine has real ipv6 connectivity*/ + if (linphone_core_get_local_ip_for(AF_INET,NULL,result)==0 && strcmp(result,"::1")!=0) + return; /*this machine has only ipv4 connectivity*/ + if (has_ipv6){ + /*this machine has only local loopback for both ipv4 and ipv6, so prefer ipv6*/ + strncpy(result,"::1",LINPHONE_IPADDR_SIZE); + return; + } + }else af=AF_INET; + } + if (linphone_core_get_local_ip_for(af,NULL,result)==0) return; - /*else fallback to SAL routine that will attempt to find the most realistic interface */ - sal_get_default_local_ip(lc->sal,lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,result,LINPHONE_IPADDR_SIZE); } static void update_primary_contact(LinphoneCore *lc){ @@ -1479,7 +1493,7 @@ static void update_primary_contact(LinphoneCore *lc){ ms_error("Could not parse identity contact !"); url=linphone_address_new("sip:unknown@unkwownhost"); } - linphone_core_get_local_ip(lc, NULL, tmp); + linphone_core_get_local_ip(lc, AF_UNSPEC, tmp); if (strcmp(tmp,"127.0.0.1")==0 || strcmp(tmp,"::1")==0 ){ ms_warning("Local loopback network only !"); lc->sip_conf.loopback_only=TRUE; @@ -2027,7 +2041,7 @@ static void monitor_network_state(LinphoneCore *lc, time_t curtime){ /* only do the network up checking every five seconds */ if (lc->network_last_check==0 || (curtime-lc->network_last_check)>=5){ - linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,NULL,result); + linphone_core_get_local_ip(lc,AF_UNSPEC,result); if (strcmp(result,"::1")!=0 && strcmp(result,"127.0.0.1")!=0){ new_status=TRUE; }else new_status=FALSE; diff --git a/coreapi/misc.c b/coreapi/misc.c index c64ace809..aaf8c2167 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -637,12 +637,16 @@ static void stun_server_resolved(LinphoneCore *lc, const char *name, struct addr } void linphone_core_resolve_stun_server(LinphoneCore *lc){ + /* + * WARNING: stun server resolution only done in IPv4. + * TODO: use IPv6 resolution if linphone_core_ipv6_enabled()==TRUE and use V4Mapped addresses for ICE gathering. + */ const char *server=lc->net_conf.stun_server; if (lc->sal && server){ char host[NI_MAXHOST]; int port=3478; linphone_parse_host_port(server,host,sizeof(host),&port); - lc->net_conf.stun_res=sal_resolve_a(lc->sal,host,port,AF_UNSPEC,(SalResolverCallback)stun_server_resolved,lc); + lc->net_conf.stun_res=sal_resolve_a(lc->sal,host,port,AF_INET,(SalResolverCallback)stun_server_resolved,lc); } } @@ -685,8 +689,8 @@ int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) video_check_list = ice_session_check_list(call->ice_session, 1); if (audio_check_list == NULL) return -1; - if (lc->sip_conf.ipv6_enabled){ - ms_warning("stun support is not implemented for ipv6"); + if (call->af==AF_INET6){ + ms_warning("Ice gathering is not implemented for ipv6"); return -1; } ai=linphone_core_get_stun_server_addrinfo(lc); @@ -1133,12 +1137,12 @@ static int get_local_ip_for_with_connect(int type, const char *dest, char *resul socklen_t s; memset(&hints,0,sizeof(hints)); - hints.ai_family=(type==AF_INET6) ? PF_INET6 : PF_INET; + hints.ai_family=type; hints.ai_socktype=SOCK_DGRAM; /*hints.ai_flags=AI_NUMERICHOST|AI_CANONNAME;*/ err=getaddrinfo(dest,"5060",&hints,&res); if (err!=0){ - ms_error("getaddrinfo() error: %s",gai_strerror(err)); + ms_error("getaddrinfo() error for %s : %s",dest, gai_strerror(err)); return -1; } if (res==NULL){ @@ -1153,7 +1157,8 @@ static int get_local_ip_for_with_connect(int type, const char *dest, char *resul } err=connect(sock,res->ai_addr,res->ai_addrlen); if (err<0) { - ms_error("Error in connect: %s",strerror(errno)); + /*the network isn't reachable*/ + if (getSocketErrorCode()!=ENETUNREACH) ms_error("Error in connect: %s",strerror(errno)); freeaddrinfo(res); close_socket(sock); return -1; diff --git a/coreapi/private.h b/coreapi/private.h index 3f198579c..69ee5b61e 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -154,6 +154,7 @@ struct _LinphoneCall { int magic; /*used to distinguish from proxy config*/ struct _LinphoneCore *core; + int af; /*the address family to prefer for RTP path, guessed from signaling path*/ SalMediaDescription *localdesc; SalMediaDescription *resultdesc; LinphoneCallDir dir; @@ -263,7 +264,7 @@ int set_lock_file(); int get_lock_file(); int remove_lock_file(); void check_sound_device(LinphoneCore *lc); -void linphone_core_get_local_ip(LinphoneCore *lc, const char *to, char *result); +void linphone_core_get_local_ip(LinphoneCore *lc, int af, char *result); bool_t host_has_ipv6_network(); bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret); diff --git a/include/sal/sal.h b/include/sal/sal.h index 2d7f8994d..b76d70b05 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -104,6 +104,7 @@ void sal_address_set_transport(SalAddress* addr,SalTransport transport); void sal_address_set_transport_name(SalAddress* addr,const char* transport); void sal_address_set_params(SalAddress *addr, const char *params); void sal_address_set_uri_params(SalAddress *addr, const char *params); +bool_t sal_address_is_ipv6(SalAddress *addr); Sal * sal_init(); void sal_uninit(Sal* sal); @@ -556,6 +557,7 @@ const SalAddress* sal_op_get_service_route(const SalOp *op); void sal_op_set_service_route(SalOp *op,const SalAddress* service_route); void sal_op_set_manual_refresher_mode(SalOp *op, bool_t enabled); +bool_t sal_op_is_ipv6(SalOp *op); /*Call API*/ int sal_call_set_local_media_description(SalOp *h, SalMediaDescription *desc); diff --git a/oRTP b/oRTP index fcc51caa1..123ef1a55 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit fcc51caa15def815189a388e878877a5354850e6 +Subproject commit 123ef1a55c321826966a3a375bee09b558ae170d