diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 2bc14ed21..f5235c808 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -840,3 +840,11 @@ void sal_enable_test_features(Sal*ctx, bool_t enabled){ ctx->enable_test_features=enabled; } +unsigned long sal_resolve_a(Sal* sal, const char *name, int port, int family, SalResolverCallback cb, void *data){ + return belle_sip_stack_resolve_a(sal->stack,name,port,family,(belle_sip_resolver_callback_t)cb,data); +} + +void sal_resolve_cancel(Sal *sal, unsigned long id){ + belle_sip_stack_resolve_cancel(sal->stack,id); +} + diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 308e6b425..b37e90936 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -4396,6 +4396,17 @@ void linphone_core_set_stun_server(LinphoneCore *lc, const char *server){ if (server) lc->net_conf.stun_server=ms_strdup(server); else lc->net_conf.stun_server=NULL; + + /* each time the stun server is changed, we must clean the resolved cached addrinfo*/ + if (lc->net_conf.stun_addrinfo){ + freeaddrinfo(lc->net_conf.stun_addrinfo); + lc->net_conf.stun_addrinfo=NULL; + } + /*if a stun server is set, we must request asynchronous resolution immediately to be ready for call*/ + if (lc->net_conf.stun_server){ + linphone_core_resolve_stun_server(lc); + } + if (linphone_core_ready(lc)) lp_config_set_string(lc->config,"net","stun_server",lc->net_conf.stun_server); } @@ -4404,6 +4415,7 @@ const char * linphone_core_get_stun_server(const LinphoneCore *lc){ return lc->net_conf.stun_server; } + bool_t linphone_core_upnp_available(){ #ifdef BUILD_UPNP return TRUE; @@ -4467,7 +4479,7 @@ const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc) if (lc->net_conf.nat_address==NULL) return NULL; - if (parse_hostname_to_addr (lc->net_conf.nat_address, &ss, &ss_len)<0) { + if (parse_hostname_to_addr (lc->net_conf.nat_address, &ss, &ss_len, 5060)<0) { return lc->net_conf.nat_address; } @@ -5399,7 +5411,11 @@ void net_config_uninit(LinphoneCore *lc) net_config_t *config=&lc->net_conf; if (config->stun_server!=NULL){ - ms_free(lc->net_conf.stun_server); + ms_free(config->stun_server); + } + if (config->stun_addrinfo){ + freeaddrinfo(config->stun_addrinfo); + config->stun_addrinfo=NULL; } if (config->nat_address!=NULL){ lp_config_set_string(lc->config,"net","nat_address",config->nat_address); @@ -5677,6 +5693,8 @@ static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t cu if (!lc->network_reachable){ linphone_core_invalidate_friend_subscriptions(lc); sal_reset_transports(lc->sal); + }else{ + linphone_core_resolve_stun_server(lc); } #ifdef BUILD_UPNP if(lc->upnp == NULL) { diff --git a/coreapi/misc.c b/coreapi/misc.c index 2da9806cd..b23a36b61 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -424,31 +424,38 @@ static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t ad return 0; } -int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen){ - struct addrinfo hints,*res=NULL; - int family = PF_INET; - int port_int = 3478; - int ret; - char port[6]; - char host[NI_MAXHOST]; +int linphone_parse_host_port(const char *input, char *host, size_t hostlen, int *port){ + char tmphost[NI_MAXHOST]={0}; char *p1, *p2; - if ((sscanf(server, "[%64[^]]]:%d", host, &port_int) == 2) || (sscanf(server, "[%64[^]]]", host) == 1)) { - family = PF_INET6; + + if ((sscanf(input, "[%64[^]]]:%d", tmphost, port) == 2) || (sscanf(input, "[%64[^]]]", tmphost) == 1)) { + } else { - p1 = strchr(server, ':'); - p2 = strrchr(server, ':'); - if (p1 && p2 && (p1 != p2)) { - family = PF_INET6; - host[NI_MAXHOST-1]='\0'; - strncpy(host, server, sizeof(host) - 1); - } else if (sscanf(server, "%[^:]:%d", host, &port_int) != 2) { - host[NI_MAXHOST-1]='\0'; - strncpy(host, server, sizeof(host) - 1); + p1 = strchr(input, ':'); + p2 = strrchr(input, ':'); + if (p1 && p2 && (p1 != p2)) {/* an ipv6 address without port*/ + strncpy(tmphost, input, sizeof(tmphost) - 1); + } else if (sscanf(input, "%[^:]:%d", tmphost, port) != 2) { + /*no port*/ + strncpy(tmphost, input, sizeof(tmphost) - 1); } } + strncpy(host,tmphost,hostlen); + return 0; +} + +int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen, int default_port){ + struct addrinfo hints,*res=NULL; + char port[6]; + char host[NI_MAXHOST]; + int port_int=default_port; + int ret; + + linphone_parse_host_port(server,host,sizeof(host),&port_int); + snprintf(port, sizeof(port), "%d", port_int); memset(&hints,0,sizeof(hints)); - hints.ai_family=family; + hints.ai_family=AF_UNSPEC; hints.ai_socktype=SOCK_DGRAM; hints.ai_protocol=IPPROTO_UDP; ret=getaddrinfo(host,port,&hints,&res); @@ -495,8 +502,7 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ return -1; } if (server!=NULL){ - struct sockaddr_storage ss; - socklen_t ss_len; + const struct addrinfo *ai=linphone_core_get_stun_server_addrinfo(lc); ortp_socket_t sock1=-1, sock2=-1; int loops=0; bool_t video_enabled=linphone_core_video_enabled(lc); @@ -506,8 +512,8 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ double elapsed; int ret=0; - if (parse_hostname_to_addr(server,&ss,&ss_len)<0){ - ms_error("Fail to parser stun server address: %s",server); + if (ai==NULL){ + ms_error("Could not obtain stun server addrinfo."); return -1; } if (lc->vtable.display_status!=NULL) @@ -528,11 +534,11 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ int id; if (loops%20==0){ ms_message("Sending stun requests..."); - sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,11,TRUE); - sendStunRequest(sock1,(struct sockaddr*)&ss,ss_len,1,FALSE); + sendStunRequest(sock1,ai->ai_addr,ai->ai_addrlen,11,TRUE); + sendStunRequest(sock1,ai->ai_addr,ai->ai_addrlen,1,FALSE); if (sock2!=-1){ - sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,22,TRUE); - sendStunRequest(sock2,(struct sockaddr*)&ss,ss_len,2,FALSE); + sendStunRequest(sock2,ai->ai_addr,ai->ai_addrlen,22,TRUE); + sendStunRequest(sock2,ai->ai_addr,ai->ai_addrlen,2,FALSE); } } ms_usleep(10000); @@ -616,13 +622,60 @@ void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, Linphone } } +static void stun_server_resolved(LinphoneCore *lc, const char *name, struct addrinfo *addrinfo){ + if (lc->net_conf.stun_addrinfo){ + freeaddrinfo(lc->net_conf.stun_addrinfo); + lc->net_conf.stun_addrinfo=NULL; + } + if (addrinfo){ + ms_message("Stun server resolution successful."); + }else{ + ms_warning("Stun server resolution failed."); + } + lc->net_conf.stun_addrinfo=addrinfo; + lc->net_conf.stun_res_id=0; +} +void linphone_core_resolve_stun_server(LinphoneCore *lc){ + 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_id=sal_resolve_a(lc->sal,host,port,AF_UNSPEC,(SalResolverCallback)stun_server_resolved,lc); + } +} + +/* + * This function returns the addrinfo representation of the stun server address. + * It is critical not to block for a long time if it can't be resolved, otherwise this stucks the main thread when making a call. + * On the contrary, a fully asynchronous call initiation is complex to develop. + * The compromise is then: + * - have a cache of the stun server addrinfo + * - this cached value is returned when it is non-null + * - an asynchronous resolution is asked each time this function is called to ensure frequent refreshes of the cached value. + * - if no cached value exists, block for a short time; this case must be unprobable because the resolution will be asked each time the stun server value is + * changed. +**/ +const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc){ + const char *server=linphone_core_get_stun_server(lc); + if (server){ + int wait_ms=0; + int wait_limit=1000; + linphone_core_resolve_stun_server(lc); + while (!lc->net_conf.stun_addrinfo && lc->net_conf.stun_res_id!=0 && wait_mssal); + ms_usleep(50000); + wait_ms+=50; + } + } + return lc->net_conf.stun_addrinfo; +} int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) { char local_addr[64]; - struct sockaddr_storage ss; - socklen_t ss_len; + const struct addrinfo *ai; IceCheckList *audio_check_list; IceCheckList *video_check_list; const char *server = linphone_core_get_stun_server(lc); @@ -636,16 +689,16 @@ int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) ms_warning("stun support is not implemented for ipv6"); return -1; } - - if (parse_hostname_to_addr(server, &ss, &ss_len) < 0) { - ms_error("Fail to parser stun server address: %s", server); + ai=linphone_core_get_stun_server_addrinfo(lc); + if (ai==NULL){ + ms_warning("Fail to resolve STUN server for ICE gathering."); return -1; } if (lc->vtable.display_status != NULL) lc->vtable.display_status(lc, _("ICE local candidates gathering in progress...")); /* Gather local host candidates. */ - if (linphone_core_get_local_ip_for(AF_INET, server, local_addr) < 0) { + if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) { ms_error("Fail to get local ip"); return -1; } @@ -663,7 +716,7 @@ int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call) ms_message("ICE: gathering candidate from [%s]",server); /* Gather local srflx candidates. */ - ice_session_gather_candidates(call->ice_session, ss, ss_len); + ice_session_gather_candidates(call->ice_session, ai->ai_addr, ai->ai_addrlen); return 0; } diff --git a/coreapi/private.h b/coreapi/private.h index 2d817f6bb..7c70a0df2 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -255,7 +255,7 @@ LinphoneFriend *linphone_find_friend_by_inc_subscribe(MSList *l, SalOp *op); LinphoneFriend *linphone_find_friend_by_out_subscribe(MSList *l, SalOp *op); MSList *linphone_find_friend_by_address(MSList *fl, const LinphoneAddress *addr, LinphoneFriend **lf); -int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen); +int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen, int default_port); int set_lock_file(); int get_lock_file(); int remove_lock_file(); @@ -307,6 +307,8 @@ void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc); void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCall *call, const PayloadType *pt); int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_resolve_stun_server(LinphoneCore *lc); +const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc); void linphone_core_adapt_to_network(LinphoneCore *lc, int ping_time_ms, LinphoneCallParams *params); int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call); void linphone_core_update_ice_state_in_call_stats(LinphoneCall *call); @@ -492,6 +494,8 @@ typedef struct net_config char *nat_address; /* may be IP or host name */ char *nat_address_ip; /* ip translated from nat_address */ char *stun_server; + struct addrinfo *stun_addrinfo; + unsigned long stun_res_id; char *relay; int download_bw; int upload_bw; diff --git a/include/sal/sal.h b/include/sal/sal.h index e9b20b522..596722768 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -58,6 +58,8 @@ struct SalCustomHeader; typedef struct SalCustomHeader SalCustomHeader; +struct addrinfo; + typedef enum { SalTransportUDP, /*UDP*/ SalTransportTCP, /*TCP*/ @@ -596,6 +598,10 @@ SalPrivacy sal_op_get_privacy(const SalOp* op); /*misc*/ void sal_get_default_local_ip(Sal *sal, int address_family, char *ip, size_t iplen); +typedef void (*SalResolverCallback)(void *data, const char *name, struct addrinfo *ai_list); + +unsigned long sal_resolve_a(Sal* sal, const char *name, int port, int family, SalResolverCallback cb, void *data); +void sal_resolve_cancel(Sal *sal, unsigned long id); SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value); const char *sal_custom_header_find(const SalCustomHeader *ch, const char *name);